OSDN Git Service

Try (#1165)
[bytom/bytom.git] / account / accounts.go
index cfa6b23..5990868 100644 (file)
@@ -1,14 +1,10 @@
-// Package account stores and tracks accounts within a Chain Core.
+// Package account stores and tracks accounts within a Bytom Core.
 package account
 
 import (
-       "context"
-       "encoding/binary"
        "encoding/json"
-       "sort"
        "strings"
        "sync"
-       "time"
 
        "github.com/golang/groupcache/lru"
        log "github.com/sirupsen/logrus"
@@ -18,85 +14,77 @@ import (
        "github.com/bytom/blockchain/txbuilder"
        "github.com/bytom/common"
        "github.com/bytom/consensus"
+       "github.com/bytom/consensus/segwit"
        "github.com/bytom/crypto"
        "github.com/bytom/crypto/ed25519/chainkd"
        "github.com/bytom/crypto/sha3pool"
        "github.com/bytom/errors"
        "github.com/bytom/protocol"
+       "github.com/bytom/protocol/bc"
        "github.com/bytom/protocol/vm/vmutil"
 )
 
 const (
        maxAccountCache = 1000
-       aliasPrefix     = "ALI:"
-       accountPrefix   = "ACC:"
-       accountCPPrefix = "ACP:"
-       indexPrefix     = "ACIDX:"
 )
 
-var miningAddressKey = []byte("miningAddress")
+var (
+       accountIndexKey     = []byte("AccountIndex")
+       accountPrefix       = []byte("Account:")
+       aliasPrefix         = []byte("AccountAlias:")
+       contractIndexPrefix = []byte("ContractIndex")
+       contractPrefix      = []byte("Contract:")
+       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")
+       ErrDuplicateAlias  = errors.New("duplicate account alias")
+       ErrFindAccount     = errors.New("fail to find account")
+       ErrMarshalAccount  = errors.New("failed marshal account")
+       ErrInvalidAddress  = errors.New("invalid address")
+       ErrFindCtrlProgram = errors.New("fail to find account control program")
 )
 
 func aliasKey(name string) []byte {
-       return []byte(aliasPrefix + name)
+       return append(aliasPrefix, []byte(name)...)
 }
 
-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
+// Key account store prefix
 func Key(name string) []byte {
-       return []byte(accountPrefix + name)
+       return append(accountPrefix, []byte(name)...)
 }
 
-//CPKey account control promgram store prefix
-func CPKey(hash common.Hash) []byte {
-       return append([]byte(accountCPPrefix), hash[:]...)
+// ContractKey account control promgram store prefix
+func ContractKey(hash common.Hash) []byte {
+       return append(contractPrefix, hash[:]...)
 }
 
-func convertUnit64ToBytes(nextIndex uint64) []byte {
-       buf := make([]byte, 8)
-       binary.PutUvarint(buf, nextIndex)
-       return buf
+func contractIndexKey(accountID string) []byte {
+       return append(contractIndexPrefix, []byte(accountID)...)
 }
 
-func convertBytesToUint64(rawIndex []byte) uint64 {
-       result, _ := binary.Uvarint(rawIndex)
-       return result
+// Account is structure of Bytom account
+type Account struct {
+       *signers.Signer
+       ID    string `json:"id"`
+       Alias string `json:"alias"`
 }
 
-// NewManager creates a new account manager
-func NewManager(walletDB dbm.DB, chain *protocol.Chain) *Manager {
-       return &Manager{
-               db:          walletDB,
-               chain:       chain,
-               utxoDB:      newReserver(chain, walletDB),
-               cache:       lru.New(maxAccountCache),
-               aliasCache:  lru.New(maxAccountCache),
-               delayedACPs: make(map[*txbuilder.TemplateBuilder][]*CtrlProgram),
-       }
+//CtrlProgram is structure of account control program
+type CtrlProgram struct {
+       AccountID      string
+       Address        string
+       KeyIndex       uint64
+       ControlProgram []byte
+       Change         bool // Mark whether this control program is for UTXO change
 }
 
 // Manager stores accounts and their associated control programs.
 type Manager struct {
-       db     dbm.DB
-       chain  *protocol.Chain
-       utxoDB *reserver
+       db         dbm.DB
+       chain      *protocol.Chain
+       utxoKeeper *utxoKeeper
 
        cacheMu    sync.Mutex
        cache      *lru.Cache
@@ -105,109 +93,93 @@ type Manager struct {
        delayedACPsMu sync.Mutex
        delayedACPs   map[*txbuilder.TemplateBuilder][]*CtrlProgram
 
-       accIndexMu  sync.Mutex
+       accIndexMu sync.Mutex
+       accountMu  sync.Mutex
 }
 
-// ExpireReservations removes reservations that have expired periodically.
-// It blocks until the context is canceled.
-func (m *Manager) ExpireReservations(ctx context.Context, period time.Duration) {
-       ticks := time.Tick(period)
-       for {
-               select {
-               case <-ctx.Done():
-                       log.Info("Deposed, ExpireReservations exiting")
-                       return
-               case <-ticks:
-                       err := m.utxoDB.ExpireReservations(ctx)
-                       if err != nil {
-                               log.WithField("error", err).Error("Expire reservations")
-                       }
-               }
+// NewManager creates a new account manager
+func NewManager(walletDB dbm.DB, chain *protocol.Chain) *Manager {
+       return &Manager{
+               db:          walletDB,
+               chain:       chain,
+               utxoKeeper:  newUtxoKeeper(chain.BestBlockHeight, walletDB),
+               cache:       lru.New(maxAccountCache),
+               aliasCache:  lru.New(maxAccountCache),
+               delayedACPs: make(map[*txbuilder.TemplateBuilder][]*CtrlProgram),
        }
 }
 
-// Account is structure of Bytom account
-type Account struct {
-       *signers.Signer
-       ID    string
-       Alias string
-       Tags  map[string]interface{}
-}
-
-func (m *Manager) getNextXpubsIndex(xpubs []chainkd.XPub) uint64 {
-       m.accIndexMu.Lock()
-       defer m.accIndexMu.Unlock()
-
-       var nextIndex uint64 = 1
-       if rawIndexBytes := m.db.Get(indexKeys(xpubs)); rawIndexBytes != nil {
-               nextIndex = convertBytesToUint64(rawIndexBytes) + 1
-       }
-
-       m.db.Set(indexKeys(xpubs), convertUnit64ToBytes(nextIndex))
-
-       return nextIndex
+// AddUnconfirmedUtxo add untxo list to utxoKeeper
+func (m *Manager) AddUnconfirmedUtxo(utxos []*UTXO) {
+       m.utxoKeeper.AddUnconfirmedUtxo(utxos)
 }
 
 // Create creates a new Account.
-func (m *Manager) Create(ctx context.Context, xpubs []chainkd.XPub, quorum int, alias string, tags map[string]interface{}) (*Account, error) {
+func (m *Manager) Create(xpubs []chainkd.XPub, quorum int, alias string) (*Account, error) {
+       m.accountMu.Lock()
+       defer m.accountMu.Unlock()
+
        normalizedAlias := strings.ToLower(strings.TrimSpace(alias))
        if existed := m.db.Get(aliasKey(normalizedAlias)); existed != nil {
                return nil, ErrDuplicateAlias
        }
 
-       nextAccountIndex := m.getNextXpubsIndex(xpubs)
-
-       signer, err := signers.Create("account", xpubs, quorum, nextAccountIndex)
+       signer, err := signers.Create("account", xpubs, quorum, m.getNextAccountIndex())
        id := signers.IDGenerate()
        if err != nil {
                return nil, errors.Wrap(err)
        }
 
-       account := &Account{Signer: signer, ID: id, Alias: normalizedAlias, Tags: tags}
+       account := &Account{Signer: signer, ID: id, Alias: normalizedAlias}
        rawAccount, err := json.Marshal(account)
        if err != nil {
                return nil, ErrMarshalAccount
        }
-       storeBatch := m.db.NewBatch()
 
        accountID := Key(id)
+       storeBatch := m.db.NewBatch()
        storeBatch.Set(accountID, rawAccount)
-       storeBatch.Set(aliasKey(alias), []byte(id))
+       storeBatch.Set(aliasKey(normalizedAlias), []byte(id))
        storeBatch.Write()
-
        return account, nil
 }
 
-// UpdateTags modifies the tags of the specified account. The account may be
-// identified either by ID or Alias, but not both.
-func (m *Manager) UpdateTags(ctx context.Context, accountInfo string, tags map[string]interface{}) (err error) {
+// CreateAddress generate an address for the select account
+func (m *Manager) CreateAddress(accountID string, change bool) (cp *CtrlProgram, err error) {
+       account, err := m.FindByID(accountID)
+       if err != nil {
+               return nil, err
+       }
+       return m.createAddress(account, change)
+}
+
+// DeleteAccount deletes the account's ID or alias matching accountInfo.
+func (m *Manager) DeleteAccount(aliasOrID string) (err error) {
        account := &Account{}
-       if account, err = m.FindByAlias(nil, accountInfo); err != nil {
-               if account, err = m.findByID(ctx, accountInfo); err != nil {
+       if account, err = m.FindByAlias(aliasOrID); err != nil {
+               if account, err = m.FindByID(aliasOrID); err != nil {
                        return err
                }
        }
 
-       account.Tags = tags
-       rawAccount, err := json.Marshal(account)
-       if err != nil {
-               return ErrMarshalTags
-       }
-
-       m.db.Set(Key(account.ID), rawAccount)
        m.cacheMu.Lock()
-       m.cache.Add(account.ID, account)
+       m.aliasCache.Remove(account.Alias)
        m.cacheMu.Unlock()
+
+       storeBatch := m.db.NewBatch()
+       storeBatch.Delete(aliasKey(account.Alias))
+       storeBatch.Delete(Key(account.ID))
+       storeBatch.Write()
        return nil
 }
 
 // FindByAlias retrieves an account's Signer record by its alias
-func (m *Manager) FindByAlias(ctx context.Context, alias string) (*Account, error) {
+func (m *Manager) FindByAlias(alias string) (*Account, error) {
        m.cacheMu.Lock()
        cachedID, ok := m.aliasCache.Get(alias)
        m.cacheMu.Unlock()
        if ok {
-               return m.findByID(ctx, cachedID.(string))
+               return m.FindByID(cachedID.(string))
        }
 
        rawID := m.db.Get(aliasKey(alias))
@@ -219,11 +191,11 @@ func (m *Manager) FindByAlias(ctx context.Context, alias string) (*Account, erro
        m.cacheMu.Lock()
        m.aliasCache.Add(alias, accountID)
        m.cacheMu.Unlock()
-       return m.findByID(ctx, accountID)
+       return m.FindByID(accountID)
 }
 
-// findByID returns an account's Signer record by its ID.
-func (m *Manager) findByID(ctx context.Context, id string) (*Account, error) {
+// FindByID returns an account's Signer record by its ID.
+func (m *Manager) FindByID(id string) (*Account, error) {
        m.cacheMu.Lock()
        cachedAccount, ok := m.cache.Get(id)
        m.cacheMu.Unlock()
@@ -247,58 +219,186 @@ func (m *Manager) findByID(ctx context.Context, id string) (*Account, error) {
        return account, nil
 }
 
-// GetAliasByID return the account alias by given ID
-func (m *Manager) GetAliasByID(id string) string {
+// GetAccountByProgram return Account by given CtrlProgram
+func (m *Manager) GetAccountByProgram(program *CtrlProgram) (*Account, error) {
+       rawAccount := m.db.Get(Key(program.AccountID))
+       if rawAccount == nil {
+               return nil, ErrFindAccount
+       }
+
        account := &Account{}
+       return account, json.Unmarshal(rawAccount, account)
+}
 
+// GetAliasByID return the account alias by given ID
+func (m *Manager) GetAliasByID(id string) string {
        rawAccount := m.db.Get(Key(id))
        if rawAccount == nil {
-               log.Warn("fail to find account")
+               log.Warn("GetAliasByID fail to find account")
                return ""
        }
 
+       account := &Account{}
        if err := json.Unmarshal(rawAccount, account); err != nil {
                log.Warn(err)
-               return ""
        }
        return account.Alias
 }
 
-// CreateAddress generate an address for the select account
-func (m *Manager) CreateAddress(ctx context.Context, accountID string, change bool) (cp *CtrlProgram, err error) {
-       account, err := m.findByID(ctx, accountID)
+// 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()
+       }
+
+       account := &Account{}
+       if err := json.Unmarshal(accountIter.Value(), account); err != nil {
+               return nil, err
+       }
+
+       program, err := m.createAddress(account, false)
        if err != nil {
                return nil, err
        }
-       return m.createAddress(ctx, account, change)
+
+       rawCP, err := json.Marshal(program)
+       if err != nil {
+               return nil, err
+       }
+
+       m.db.Set(miningAddressKey, rawCP)
+       return program.ControlProgram, nil
 }
 
-// CreateAddress generate an address for the select account
-func (m *Manager) createAddress(ctx context.Context, account *Account, change bool) (cp *CtrlProgram, err error) {
-       if len(account.XPubs) == 1 {
-               cp, err = m.createP2PKH(ctx, account, change)
-       } else {
-               cp, err = m.createP2SH(ctx, account, change)
+// GetContractIndex return the current index
+func (m *Manager) GetContractIndex(accountID string) uint64 {
+       m.accIndexMu.Lock()
+       defer m.accIndexMu.Unlock()
+
+       index := uint64(1)
+       if rawIndexBytes := m.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
+               index = common.BytesToUnit64(rawIndexBytes)
        }
+       return index
+}
+
+// GetProgramByAddress return CtrlProgram by given address
+func (m *Manager) GetProgramByAddress(address string) (*CtrlProgram, error) {
+       addr, err := common.DecodeAddress(address, &consensus.ActiveNetParams)
        if err != nil {
                return nil, err
        }
 
-       if err = m.insertAccountControlProgram(ctx, cp); err != nil {
+       redeemContract := addr.ScriptAddress()
+       program := []byte{}
+       switch addr.(type) {
+       case *common.AddressWitnessPubKeyHash:
+               program, err = vmutil.P2WPKHProgram(redeemContract)
+       case *common.AddressWitnessScriptHash:
+               program, err = vmutil.P2WSHProgram(redeemContract)
+       default:
+               return nil, ErrInvalidAddress
+       }
+       if err != nil {
                return nil, err
        }
-       return cp, nil
+
+       var hash [32]byte
+       sha3pool.Sum256(hash[:], program)
+       rawProgram := m.db.Get(ContractKey(hash))
+       if rawProgram == nil {
+               return nil, ErrFindCtrlProgram
+       }
+
+       cp := &CtrlProgram{}
+       return cp, json.Unmarshal(rawProgram, cp)
 }
 
-func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool) (*CtrlProgram, error) {
-       idx := m.getNextXpubsIndex(account.Signer.XPubs)
+// 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(ContractKey(hash))
+       return bytes != nil
+}
+
+// ListAccounts will return the accounts in the db
+func (m *Manager) ListAccounts(id string) ([]*Account, error) {
+       accounts := []*Account{}
+       accountIter := m.db.IteratorPrefix(Key(strings.TrimSpace(id)))
+       defer accountIter.Release()
+
+       for accountIter.Next() {
+               account := &Account{}
+               if err := json.Unmarshal(accountIter.Value(), &account); err != nil {
+                       return nil, err
+               }
+               accounts = append(accounts, account)
+       }
+       return accounts, nil
+}
+
+// ListControlProgram return all the local control program
+func (m *Manager) ListControlProgram() ([]*CtrlProgram, error) {
+       cps := []*CtrlProgram{}
+       cpIter := m.db.IteratorPrefix(contractPrefix)
+       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
+}
+
+func (m *Manager) ListUnconfirmedUtxo(isSmartContract bool) []*UTXO {
+       utxos := m.utxoKeeper.ListUnconfirmed()
+       result := []*UTXO{}
+       for _, utxo := range utxos {
+               if segwit.IsP2WScript(utxo.ControlProgram) != isSmartContract {
+                       result = append(result, utxo)
+               }
+       }
+       return result
+}
+
+// RemoveUnconfirmedUtxo remove utxos from the utxoKeeper
+func (m *Manager) RemoveUnconfirmedUtxo(hashes []*bc.Hash) {
+       m.utxoKeeper.RemoveUnconfirmedUtxo(hashes)
+}
+
+// CreateAddress generate an address for the select account
+func (m *Manager) createAddress(account *Account, change bool) (cp *CtrlProgram, err error) {
+       if len(account.XPubs) == 1 {
+               cp, err = m.createP2PKH(account, change)
+       } else {
+               cp, err = m.createP2SH(account, change)
+       }
+       if err != nil {
+               return nil, err
+       }
+       return cp, m.insertControlPrograms(cp)
+}
+
+func (m *Manager) createP2PKH(account *Account, change bool) (*CtrlProgram, error) {
+       idx := m.getNextContractIndex(account.ID)
        path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
        derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
        derivedPK := derivedXPubs[0].PublicKey()
        pubHash := crypto.Ripemd160(derivedPK)
 
-       // TODO: pass different params due to config
-       address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.MainNetParams)
+       address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
        if err != nil {
                return nil, err
        }
@@ -317,8 +417,8 @@ func (m *Manager) createP2PKH(ctx context.Context, account *Account, change bool
        }, nil
 }
 
-func (m *Manager) createP2SH(ctx context.Context, account *Account, change bool) (*CtrlProgram, error) {
-       idx := m.getNextXpubsIndex(account.Signer.XPubs)
+func (m *Manager) createP2SH(account *Account, change bool) (*CtrlProgram, error) {
+       idx := m.getNextContractIndex(account.ID)
        path := signers.Path(account.Signer, signers.AccountKeySpace, idx)
        derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
        derivedPKs := chainkd.XPubKeys(derivedXPubs)
@@ -328,8 +428,7 @@ func (m *Manager) createP2SH(ctx context.Context, account *Account, change bool)
        }
        scriptHash := crypto.Sha256(signScript)
 
-       // TODO: pass different params due to config
-       address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.MainNetParams)
+       address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
        if err != nil {
                return nil, err
        }
@@ -348,124 +447,40 @@ func (m *Manager) createP2SH(ctx context.Context, account *Account, change bool)
        }, nil
 }
 
-//CtrlProgram is structure of account control program
-type CtrlProgram struct {
-       AccountID      string
-       Address        string
-       KeyIndex       uint64
-       ControlProgram []byte
-       Change         bool
-}
-
-func (m *Manager) insertAccountControlProgram(ctx context.Context, progs ...*CtrlProgram) error {
-       var hash common.Hash
-       for _, prog := range progs {
-               accountCP, err := json.Marshal(prog)
-               if err != nil {
-                       return err
-               }
+func (m *Manager) getNextAccountIndex() uint64 {
+       m.accIndexMu.Lock()
+       defer m.accIndexMu.Unlock()
 
-               sha3pool.Sum256(hash[:], prog.ControlProgram)
-               m.db.Set(CPKey(hash), accountCP)
+       var nextIndex uint64 = 1
+       if rawIndexBytes := m.db.Get(accountIndexKey); rawIndexBytes != nil {
+               nextIndex = common.BytesToUnit64(rawIndexBytes) + 1
        }
-       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
+       m.db.Set(accountIndexKey, common.Unit64ToBytes(nextIndex))
+       return nextIndex
 }
 
-// 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()
-       }
-
-       account := &Account{}
-       if err := json.Unmarshal(accountIter.Value(), account); err != nil {
-               return nil, err
-       }
-
-       program, err := m.createAddress(nil, account, false)
-       if err != nil {
-               return nil, err
-       }
+func (m *Manager) getNextContractIndex(accountID string) uint64 {
+       m.accIndexMu.Lock()
+       defer m.accIndexMu.Unlock()
 
-       rawCP, err := json.Marshal(program)
-       if err != nil {
-               return nil, err
+       nextIndex := uint64(1)
+       if rawIndexBytes := m.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
+               nextIndex = common.BytesToUnit64(rawIndexBytes) + 1
        }
-
-       m.db.Set(miningAddressKey, rawCP)
-       return program.ControlProgram, nil
+       m.db.Set(contractIndexKey(accountID), common.Unit64ToBytes(nextIndex))
+       return nextIndex
 }
 
-// DeleteAccount deletes the account's ID or alias matching accountInfo.
-func (m *Manager) DeleteAccount(in struct {
-       AccountInfo string `json:"account_info"`
-}) (err error) {
-       account := &Account{}
-       if account, err = m.FindByAlias(nil, in.AccountInfo); err != nil {
-               if account, err = m.findByID(nil, in.AccountInfo); err != nil {
+func (m *Manager) insertControlPrograms(progs ...*CtrlProgram) error {
+       var hash common.Hash
+       for _, prog := range progs {
+               accountCP, err := json.Marshal(prog)
+               if err != nil {
                        return err
                }
-       }
-
-       storeBatch := m.db.NewBatch()
-
-       m.cacheMu.Lock()
-       m.aliasCache.Remove(account.Alias)
-       m.cacheMu.Unlock()
-
-       storeBatch.Delete(aliasKey(account.Alias))
-       storeBatch.Delete(Key(account.ID))
-       storeBatch.Write()
-
-       return nil
-}
-
-// ListAccounts will return the accounts in the db
-func (m *Manager) ListAccounts(id string) ([]*Account, error) {
-       accounts := []*Account{}
-       accountIter := m.db.IteratorPrefix([]byte(accountPrefix + id))
-       defer accountIter.Release()
-
-       for accountIter.Next() {
-               account := &Account{}
-               if err := json.Unmarshal(accountIter.Value(), &account); err != nil {
-                       return nil, err
-               }
-               accounts = append(accounts, account)
-       }
 
-       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)
+               sha3pool.Sum256(hash[:], prog.ControlProgram)
+               m.db.Set(ContractKey(hash), accountCP)
        }
-
-       return cps, nil
+       return nil
 }