OSDN Git Service

update master (#487)
[bytom/bytom.git] / account / accounts.go
similarity index 82%
rename from blockchain/account/accounts.go
rename to account/accounts.go
index 71825ac..a9edb7a 100755 (executable)
@@ -5,6 +5,8 @@ import (
        "context"
        "encoding/binary"
        "encoding/json"
+       "sort"
+       "strings"
        "sync"
        "time"
 
@@ -29,25 +31,32 @@ const (
        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
@@ -60,6 +69,17 @@ func CPKey(hash common.Hash) []byte {
        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{
@@ -85,7 +105,6 @@ type Manager struct {
        delayedACPsMu sync.Mutex
        delayedACPs   map[*txbuilder.TemplateBuilder][]*CtrlProgram
 
-       acpMu       sync.Mutex
        acpIndexCap uint64 // points to end of block
        accIndexMu  sync.Mutex
 }
@@ -111,25 +130,23 @@ func (m *Manager) ExpireReservations(ctx context.Context, period time.Duration)
 // 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.
@@ -138,12 +155,10 @@ func (m *Manager) Create(ctx context.Context, xpubs []chainkd.XPub, quorum int,
                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)
        }
@@ -276,7 +291,7 @@ func (m *Manager) createAddress(ctx context.Context, account *Account, change bo
 }
 
 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()
@@ -303,7 +318,7 @@ func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool
 }
 
 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)
@@ -356,18 +371,30 @@ func (m *Manager) insertAccountControlProgram(ctx context.Context, progs ...*Ctr
        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
        }
 
@@ -375,30 +402,18 @@ func (m *Manager) GetCoinbaseControlProgram() ([]byte, error) {
        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.
@@ -441,3 +456,20 @@ func (m *Manager) ListAccounts(id string) ([]*Account, error) {
 
        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
+}