"context"
"encoding/binary"
"encoding/json"
+ "sort"
+ "strings"
"sync"
"time"
aliasPrefix = "ALI:"
accountPrefix = "ACC:"
accountCPPrefix = "ACP:"
- keyNextIndex = "NextIndex"
indexPrefix = "ACIDX:"
)
+var miningAddressKey = []byte("miningAddress")
+
// pre-define errors for supporting bytom errorFormatter
var (
ErrDuplicateAlias = errors.New("duplicate account alias")
ErrFindAccount = errors.New("fail to find account")
ErrMarshalAccount = errors.New("failed marshal account")
ErrMarshalTags = errors.New("failed marshal account to update tags")
- ErrStandardQuorum = errors.New("need single key pair account to create standard transaction")
)
func aliasKey(name string) []byte {
return []byte(aliasPrefix + name)
}
-func indexKey(xpub chainkd.XPub) []byte {
- return []byte(indexPrefix + xpub.String())
+func indexKeys(xpubs []chainkd.XPub) []byte {
+ xpubStrings := make([]string, len(xpubs))
+ for i, xpub := range xpubs {
+ xpubStrings[i] = xpub.String()
+ }
+ sort.Strings(xpubStrings)
+ suffix := strings.Join(xpubStrings, "")
+
+ return []byte(indexPrefix + suffix)
}
//Key account store prefix
return append([]byte(accountCPPrefix), hash[:]...)
}
+func convertUnit64ToBytes(nextIndex uint64) []byte {
+ buf := make([]byte, 8)
+ binary.PutUvarint(buf, nextIndex)
+ return buf
+}
+
+func convertBytesToUint64(rawIndex []byte) uint64 {
+ result, _ := binary.Uvarint(rawIndex)
+ return result
+}
+
// NewManager creates a new account manager
func NewManager(walletDB dbm.DB, chain *protocol.Chain) *Manager {
return &Manager{
delayedACPsMu sync.Mutex
delayedACPs map[*txbuilder.TemplateBuilder][]*CtrlProgram
- acpMu sync.Mutex
acpIndexCap uint64 // points to end of block
accIndexMu sync.Mutex
}
// Account is structure of Bytom account
type Account struct {
*signers.Signer
- ID string `json:"id"`
- Alias string `json:"alias"`
- Tags map[string]interface{} `json:"tags"`
+ ID string
+ Alias string
+ Tags map[string]interface{}
}
-func (m *Manager) getNextAccountIndex(xpubs []chainkd.XPub) (*uint64, error) {
+func (m *Manager) getNextXpubsIndex(xpubs []chainkd.XPub) uint64 {
m.accIndexMu.Lock()
defer m.accIndexMu.Unlock()
- var nextIndex uint64 = 1
- if rawIndex := m.db.Get(indexKey(xpubs[0])); rawIndex != nil {
- nextIndex = binary.LittleEndian.Uint64(rawIndex) + 1
+ var nextIndex uint64 = 1
+ if rawIndexBytes := m.db.Get(indexKeys(xpubs)); rawIndexBytes != nil {
+ nextIndex = convertBytesToUint64(rawIndexBytes) + 1
}
- buf := make([]byte, 8)
- binary.LittleEndian.PutUint64(buf, nextIndex)
- m.db.Set(indexKey(xpubs[0]), buf)
+ m.db.Set(indexKeys(xpubs), convertUnit64ToBytes(nextIndex))
- return &nextIndex, nil
+ return nextIndex
}
// Create creates a new Account.
return nil, ErrDuplicateAlias
}
- nextAccountIndex, err := m.getNextAccountIndex(xpubs)
- if err != nil {
- return nil, errors.Wrap(err, "get account index error")
- }
+ nextAccountIndex := m.getNextXpubsIndex(xpubs)
- id, signer, err := signers.Create("account", xpubs, quorum, *nextAccountIndex)
+ signer, err := signers.Create("account", xpubs, quorum, nextAccountIndex)
+ id := signers.IDGenerate()
if err != nil {
return nil, errors.Wrap(err)
}
}
func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool) (*CtrlProgram, error) {
- idx := m.nextIndex(account)
+ idx := m.nextAccountIndex(account)
path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
derivedPK := derivedXPubs[0].PublicKey()
}
func (m *Manager) createP2SH(ctx context.Context, account *Account, change bool) (*CtrlProgram, error) {
- idx := m.nextIndex(account)
+ idx := m.nextAccountIndex(account)
path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
derivedPKs := chainkd.XPubKeys(derivedXPubs)
return nil
}
+// IsLocalControlProgram check is the input control program belong to local
+func (m *Manager) IsLocalControlProgram(prog []byte) bool {
+ var hash common.Hash
+ sha3pool.Sum256(hash[:], prog)
+ bytes := m.db.Get(CPKey(hash))
+ return bytes != nil
+}
+
// GetCoinbaseControlProgram will return a coinbase script
func (m *Manager) GetCoinbaseControlProgram() ([]byte, error) {
+ if data := m.db.Get(miningAddressKey); data != nil {
+ cp := &CtrlProgram{}
+ return cp.ControlProgram, json.Unmarshal(data, cp)
+ }
+
accountIter := m.db.IteratorPrefix([]byte(accountPrefix))
defer accountIter.Release()
if !accountIter.Next() {
log.Warningf("GetCoinbaseControlProgram: can't find any account in db")
return vmutil.DefaultCoinbaseProgram()
}
- rawAccount := accountIter.Value()
account := &Account{}
- if err := json.Unmarshal(rawAccount, account); err != nil {
+ if err := json.Unmarshal(accountIter.Value(), account); err != nil {
return nil, err
}
if err != nil {
return nil, err
}
- return program.ControlProgram, nil
-}
-
-func (m *Manager) nextIndex(account *Account) uint64 {
- m.acpMu.Lock()
- defer m.acpMu.Unlock()
-
- key := make([]byte, 0)
- key = append(key, account.Signer.XPubs[0].Bytes()...)
-
- accountIndex := make([]byte, 8)
- binary.LittleEndian.PutUint64(accountIndex[:], account.Signer.KeyIndex)
- key = append(key, accountIndex[:]...)
- var nextIndex uint64 = 1
- if rawIndex := m.db.Get(key); rawIndex != nil {
- nextIndex = uint64(binary.LittleEndian.Uint64(rawIndex)) + 1
+ rawCP, err := json.Marshal(program)
+ if err != nil {
+ return nil, err
}
- buf := make([]byte, 8)
- binary.LittleEndian.PutUint64(buf, nextIndex)
- m.db.Set(key, buf)
+ m.db.Set(miningAddressKey, rawCP)
+ return program.ControlProgram, nil
+}
- return nextIndex
+func (m *Manager) nextAccountIndex(account *Account) uint64 {
+ return m.getNextXpubsIndex(account.Signer.XPubs)
}
// DeleteAccount deletes the account's ID or alias matching accountInfo.
return accounts, nil
}
+
+// ListControlProgram return all the local control program
+func (m *Manager) ListControlProgram() ([]*CtrlProgram, error) {
+ cps := []*CtrlProgram{}
+ cpIter := m.db.IteratorPrefix([]byte(accountCPPrefix))
+ defer cpIter.Release()
+
+ for cpIter.Next() {
+ cp := &CtrlProgram{}
+ if err := json.Unmarshal(cpIter.Value(), cp); err != nil {
+ return nil, err
+ }
+ cps = append(cps, cp)
+ }
+
+ return cps, nil
+}