OSDN Git Service

Merge pull request #16 from Bytom/dev
[bytom/vapor.git] / account / accounts.go
1 // Package account stores and tracks accounts within a Bytom Core.
2 package account
3
4 import (
5         "encoding/json"
6         "reflect"
7         "sort"
8         "strings"
9         "sync"
10
11         "github.com/golang/groupcache/lru"
12         log "github.com/sirupsen/logrus"
13         dbm "github.com/tendermint/tmlibs/db"
14
15         "github.com/vapor/blockchain/signers"
16         "github.com/vapor/blockchain/txbuilder"
17         "github.com/vapor/common"
18         "github.com/vapor/consensus"
19         "github.com/vapor/consensus/segwit"
20         "github.com/vapor/crypto"
21         "github.com/vapor/crypto/ed25519/chainkd"
22         "github.com/vapor/crypto/sha3pool"
23         "github.com/vapor/errors"
24         "github.com/vapor/protocol"
25         "github.com/vapor/protocol/bc"
26         "github.com/vapor/protocol/vm/vmutil"
27 )
28
29 const (
30         maxAccountCache = 1000
31
32         // HardenedKeyStart bip32 hierarchical deterministic wallets
33         // keys with index ≥ 0x80000000 are hardened keys
34         HardenedKeyStart = 0x80000000
35 )
36
37 var (
38         accountIndexPrefix  = []byte("AccountIndex:")
39         accountPrefix       = []byte("Account:")
40         aliasPrefix         = []byte("AccountAlias:")
41         contractIndexPrefix = []byte("ContractIndex")
42         contractPrefix      = []byte("Contract:")
43         miningAddressKey    = []byte("MiningAddress")
44         CoinbaseAbKey       = []byte("CoinbaseArbitrary")
45 )
46
47 // pre-define errors for supporting bytom errorFormatter
48 var (
49         ErrDuplicateAlias  = errors.New("duplicate account alias")
50         ErrDuplicateIndex  = errors.New("duplicate account with same xPubs and index")
51         ErrFindAccount     = errors.New("fail to find account")
52         ErrMarshalAccount  = errors.New("failed marshal account")
53         ErrInvalidAddress  = errors.New("invalid address")
54         ErrFindCtrlProgram = errors.New("fail to find account control program")
55         ErrDeriveRule      = errors.New("invalid key derive rule")
56         ErrContractIndex   = errors.New("exceed the maximum addresses per account")
57         ErrAccountIndex    = errors.New("exceed the maximum accounts per xpub")
58         ErrFindTransaction = errors.New("no transaction")
59 )
60
61 // ContractKey account control promgram store prefix
62 func ContractKey(hash common.Hash) []byte {
63         return append(contractPrefix, hash[:]...)
64 }
65
66 // Key account store prefix
67 func Key(name string) []byte {
68         return append(accountPrefix, []byte(name)...)
69 }
70
71 func aliasKey(name string) []byte {
72         return append(aliasPrefix, []byte(name)...)
73 }
74
75 func bip44ContractIndexKey(accountID string, change bool) []byte {
76         key := append(contractIndexPrefix, accountID...)
77         if change {
78                 return append(key, []byte{1}...)
79         }
80         return append(key, []byte{0}...)
81 }
82
83 func contractIndexKey(accountID string) []byte {
84         return append(contractIndexPrefix, []byte(accountID)...)
85 }
86
87 // Account is structure of Bytom account
88 type Account struct {
89         *signers.Signer
90         ID    string `json:"id"`
91         Alias string `json:"alias"`
92 }
93
94 //CtrlProgram is structure of account control program
95 type CtrlProgram struct {
96         AccountID      string
97         Address        string
98         KeyIndex       uint64
99         ControlProgram []byte
100         Change         bool // Mark whether this control program is for UTXO change
101 }
102
103 // Manager stores accounts and their associated control programs.
104 type Manager struct {
105         db         dbm.DB
106         chain      *protocol.Chain
107         utxoKeeper *utxoKeeper
108
109         cacheMu    sync.Mutex
110         cache      *lru.Cache
111         aliasCache *lru.Cache
112
113         delayedACPsMu sync.Mutex
114         delayedACPs   map[*txbuilder.TemplateBuilder][]*CtrlProgram
115
116         addressMu sync.Mutex
117         accountMu sync.Mutex
118 }
119
120 // NewManager creates a new account manager
121 func NewManager(walletDB dbm.DB, chain *protocol.Chain) *Manager {
122         return &Manager{
123                 db:          walletDB,
124                 chain:       chain,
125                 utxoKeeper:  newUtxoKeeper(chain.BestBlockHeight, walletDB),
126                 cache:       lru.New(maxAccountCache),
127                 aliasCache:  lru.New(maxAccountCache),
128                 delayedACPs: make(map[*txbuilder.TemplateBuilder][]*CtrlProgram),
129         }
130 }
131
132 // AddUnconfirmedUtxo add untxo list to utxoKeeper
133 func (m *Manager) AddUnconfirmedUtxo(utxos []*UTXO) {
134         m.utxoKeeper.AddUnconfirmedUtxo(utxos)
135 }
136
137 // CreateAccount creates a new Account.
138 func CreateAccount(xpubs []chainkd.XPub, quorum int, alias string, acctIndex uint64, deriveRule uint8) (*Account, error) {
139         if acctIndex >= HardenedKeyStart {
140                 return nil, ErrAccountIndex
141         }
142
143         signer, err := signers.Create("account", xpubs, quorum, acctIndex, deriveRule)
144         if err != nil {
145                 return nil, errors.Wrap(err)
146         }
147
148         id := signers.IDGenerate()
149         return &Account{Signer: signer, ID: id, Alias: strings.ToLower(strings.TrimSpace(alias))}, nil
150 }
151
152 func (m *Manager) saveAccount(account *Account, updateIndex bool) error {
153         rawAccount, err := json.Marshal(account)
154         if err != nil {
155                 return ErrMarshalAccount
156         }
157
158         storeBatch := m.db.NewBatch()
159         storeBatch.Set(Key(account.ID), rawAccount)
160         storeBatch.Set(aliasKey(account.Alias), []byte(account.ID))
161         if updateIndex {
162                 storeBatch.Set(GetAccountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
163         }
164         storeBatch.Write()
165         return nil
166 }
167
168 // SaveAccount save a new account.
169 func (m *Manager) SaveAccount(account *Account) error {
170         m.accountMu.Lock()
171         defer m.accountMu.Unlock()
172
173         if existed := m.db.Get(aliasKey(account.Alias)); existed != nil {
174                 return ErrDuplicateAlias
175         }
176
177         acct, err := m.GetAccountByXPubsIndex(account.XPubs, account.KeyIndex)
178         if err != nil {
179                 return err
180         }
181
182         if acct != nil {
183                 return ErrDuplicateIndex
184         }
185
186         currentIndex := uint64(0)
187         if rawIndexBytes := m.db.Get(GetAccountIndexKey(account.XPubs)); rawIndexBytes != nil {
188                 currentIndex = common.BytesToUnit64(rawIndexBytes)
189         }
190         return m.saveAccount(account, account.KeyIndex > currentIndex)
191 }
192
193 // Create creates and save a new Account.
194 func (m *Manager) Create(xpubs []chainkd.XPub, quorum int, alias string, deriveRule uint8) (*Account, error) {
195         m.accountMu.Lock()
196         defer m.accountMu.Unlock()
197
198         if existed := m.db.Get(aliasKey(alias)); existed != nil {
199                 return nil, ErrDuplicateAlias
200         }
201
202         acctIndex := uint64(1)
203         if rawIndexBytes := m.db.Get(GetAccountIndexKey(xpubs)); rawIndexBytes != nil {
204                 acctIndex = common.BytesToUnit64(rawIndexBytes) + 1
205         }
206         account, err := CreateAccount(xpubs, quorum, alias, acctIndex, deriveRule)
207         if err != nil {
208                 return nil, err
209         }
210
211         if err := m.saveAccount(account, true); err != nil {
212                 return nil, err
213         }
214
215         return account, nil
216 }
217
218 func (m *Manager) UpdateAccountAlias(accountID string, newAlias string) (err error) {
219         m.accountMu.Lock()
220         defer m.accountMu.Unlock()
221
222         account, err := m.FindByID(accountID)
223         if err != nil {
224                 return err
225         }
226         oldAlias := account.Alias
227
228         normalizedAlias := strings.ToLower(strings.TrimSpace(newAlias))
229         if existed := m.db.Get(aliasKey(normalizedAlias)); existed != nil {
230                 return ErrDuplicateAlias
231         }
232
233         m.cacheMu.Lock()
234         m.aliasCache.Remove(oldAlias)
235         m.cacheMu.Unlock()
236
237         account.Alias = normalizedAlias
238         rawAccount, err := json.Marshal(account)
239         if err != nil {
240                 return ErrMarshalAccount
241         }
242
243         storeBatch := m.db.NewBatch()
244         storeBatch.Delete(aliasKey(oldAlias))
245         storeBatch.Set(Key(accountID), rawAccount)
246         storeBatch.Set(aliasKey(normalizedAlias), []byte(accountID))
247         storeBatch.Write()
248         return nil
249 }
250
251 // CreateAddress generate an address for the select account
252 func (m *Manager) CreateAddress(accountID string, change bool) (cp *CtrlProgram, err error) {
253         m.addressMu.Lock()
254         defer m.addressMu.Unlock()
255
256         account, err := m.FindByID(accountID)
257         if err != nil {
258                 return nil, err
259         }
260
261         currentIdx, err := m.getCurrentContractIndex(account, change)
262         if err != nil {
263                 return nil, err
264         }
265
266         cp, err = CreateCtrlProgram(account, currentIdx+1, change)
267         if err != nil {
268                 return nil, err
269         }
270
271         return cp, m.saveControlProgram(cp, true)
272 }
273
274 // CreateBatchAddresses generate a batch of addresses for the select account
275 func (m *Manager) CreateBatchAddresses(accountID string, change bool, stopIndex uint64) error {
276         m.addressMu.Lock()
277         defer m.addressMu.Unlock()
278
279         account, err := m.FindByID(accountID)
280         if err != nil {
281                 return err
282         }
283
284         currentIndex, err := m.getCurrentContractIndex(account, change)
285         if err != nil {
286                 return err
287         }
288
289         for currentIndex++; currentIndex <= stopIndex; currentIndex++ {
290                 cp, err := CreateCtrlProgram(account, currentIndex, change)
291                 if err != nil {
292                         return err
293                 }
294
295                 if err := m.saveControlProgram(cp, true); err != nil {
296                         return err
297                 }
298         }
299
300         return nil
301 }
302
303 // deleteAccountControlPrograms deletes control program matching accountID
304 func (m *Manager) deleteAccountControlPrograms(accountID string) error {
305         cps, err := m.ListControlProgram()
306         if err != nil {
307                 return err
308         }
309
310         var hash common.Hash
311         for _, cp := range cps {
312                 if cp.AccountID == accountID {
313                         sha3pool.Sum256(hash[:], cp.ControlProgram)
314                         m.db.Delete(ContractKey(hash))
315                 }
316         }
317         return nil
318 }
319
320 // deleteAccountUtxos deletes utxos matching accountID
321 func (m *Manager) deleteAccountUtxos(accountID string) error {
322         accountUtxoIter := m.db.IteratorPrefix([]byte(UTXOPreFix))
323         defer accountUtxoIter.Release()
324         for accountUtxoIter.Next() {
325                 accountUtxo := &UTXO{}
326                 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
327                         return err
328                 }
329
330                 if accountID == accountUtxo.AccountID {
331                         m.db.Delete(StandardUTXOKey(accountUtxo.OutputID))
332                 }
333         }
334         return nil
335 }
336
337 // DeleteAccount deletes the account's ID or alias matching account ID.
338 func (m *Manager) DeleteAccount(accountID string) (err error) {
339         m.accountMu.Lock()
340         defer m.accountMu.Unlock()
341
342         account, err := m.FindByID(accountID)
343         if err != nil {
344                 return err
345         }
346
347         if err := m.deleteAccountControlPrograms(accountID); err != nil {
348                 return err
349         }
350         if err := m.deleteAccountUtxos(accountID); err != nil {
351                 return err
352         }
353
354         m.cacheMu.Lock()
355         m.aliasCache.Remove(account.Alias)
356         m.cacheMu.Unlock()
357
358         storeBatch := m.db.NewBatch()
359         storeBatch.Delete(aliasKey(account.Alias))
360         storeBatch.Delete(Key(account.ID))
361         storeBatch.Write()
362         return nil
363 }
364
365 // FindByAlias retrieves an account's Signer record by its alias
366 func (m *Manager) FindByAlias(alias string) (*Account, error) {
367         m.cacheMu.Lock()
368         cachedID, ok := m.aliasCache.Get(alias)
369         m.cacheMu.Unlock()
370         if ok {
371                 return m.FindByID(cachedID.(string))
372         }
373
374         rawID := m.db.Get(aliasKey(alias))
375         if rawID == nil {
376                 return nil, ErrFindAccount
377         }
378
379         accountID := string(rawID)
380         m.cacheMu.Lock()
381         m.aliasCache.Add(alias, accountID)
382         m.cacheMu.Unlock()
383         return m.FindByID(accountID)
384 }
385
386 // FindByID returns an account's Signer record by its ID.
387 func (m *Manager) FindByID(id string) (*Account, error) {
388         m.cacheMu.Lock()
389         cachedAccount, ok := m.cache.Get(id)
390         m.cacheMu.Unlock()
391         if ok {
392                 return cachedAccount.(*Account), nil
393         }
394
395         rawAccount := m.db.Get(Key(id))
396         if rawAccount == nil {
397                 return nil, ErrFindAccount
398         }
399
400         account := &Account{}
401         if err := json.Unmarshal(rawAccount, account); err != nil {
402                 return nil, err
403         }
404
405         m.cacheMu.Lock()
406         m.cache.Add(id, account)
407         m.cacheMu.Unlock()
408         return account, nil
409 }
410
411 // GetAccountByProgram return Account by given CtrlProgram
412 func (m *Manager) GetAccountByProgram(program *CtrlProgram) (*Account, error) {
413         rawAccount := m.db.Get(Key(program.AccountID))
414         if rawAccount == nil {
415                 return nil, ErrFindAccount
416         }
417
418         account := &Account{}
419         return account, json.Unmarshal(rawAccount, account)
420 }
421
422 // GetAccountByXPubsIndex get account by xPubs and index
423 func (m *Manager) GetAccountByXPubsIndex(xPubs []chainkd.XPub, index uint64) (*Account, error) {
424         accounts, err := m.ListAccounts("")
425         if err != nil {
426                 return nil, err
427         }
428
429         for _, account := range accounts {
430                 if reflect.DeepEqual(account.XPubs, xPubs) && account.KeyIndex == index {
431                         return account, nil
432                 }
433         }
434         return nil, nil
435 }
436
437 // GetAliasByID return the account alias by given ID
438 func (m *Manager) GetAliasByID(id string) string {
439         rawAccount := m.db.Get(Key(id))
440         if rawAccount == nil {
441                 log.Warn("GetAliasByID fail to find account")
442                 return ""
443         }
444
445         account := &Account{}
446         if err := json.Unmarshal(rawAccount, account); err != nil {
447                 log.Warn(err)
448         }
449         return account.Alias
450 }
451
452 func (m *Manager) GetCoinbaseArbitrary() []byte {
453         if arbitrary := m.db.Get(CoinbaseAbKey); arbitrary != nil {
454                 return arbitrary
455         }
456         return []byte{}
457 }
458
459 // GetCoinbaseControlProgram will return a coinbase script
460 func (m *Manager) GetCoinbaseControlProgram() ([]byte, error) {
461         cp, err := m.GetCoinbaseCtrlProgram()
462         if err == ErrFindAccount {
463                 log.Warningf("GetCoinbaseControlProgram: can't find any account in db")
464                 return vmutil.DefaultCoinbaseProgram()
465         }
466         if err != nil {
467                 return nil, err
468         }
469         return cp.ControlProgram, nil
470 }
471
472 // GetCoinbaseCtrlProgram will return the coinbase CtrlProgram
473 func (m *Manager) GetCoinbaseCtrlProgram() (*CtrlProgram, error) {
474         if data := m.db.Get(miningAddressKey); data != nil {
475                 cp := &CtrlProgram{}
476                 return cp, json.Unmarshal(data, cp)
477         }
478
479         accountIter := m.db.IteratorPrefix([]byte(accountPrefix))
480         defer accountIter.Release()
481         if !accountIter.Next() {
482                 return nil, ErrFindAccount
483         }
484
485         account := &Account{}
486         if err := json.Unmarshal(accountIter.Value(), account); err != nil {
487                 return nil, err
488         }
489
490         program, err := m.CreateAddress(account.ID, false)
491         if err != nil {
492                 return nil, err
493         }
494
495         rawCP, err := json.Marshal(program)
496         if err != nil {
497                 return nil, err
498         }
499
500         m.db.Set(miningAddressKey, rawCP)
501         return program, nil
502 }
503
504 // GetContractIndex return the current index
505 func (m *Manager) GetContractIndex(accountID string) uint64 {
506         index := uint64(0)
507         if rawIndexBytes := m.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
508                 index = common.BytesToUnit64(rawIndexBytes)
509         }
510         return index
511 }
512
513 // GetBip44ContractIndex return the current bip44 contract index
514 func (m *Manager) GetBip44ContractIndex(accountID string, change bool) uint64 {
515         index := uint64(0)
516         if rawIndexBytes := m.db.Get(bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil {
517                 index = common.BytesToUnit64(rawIndexBytes)
518         }
519         return index
520 }
521
522 // GetLocalCtrlProgramByAddress return CtrlProgram by given address
523 func (m *Manager) GetLocalCtrlProgramByAddress(address string) (*CtrlProgram, error) {
524         program, err := m.getProgramByAddress(address)
525         if err != nil {
526                 return nil, err
527         }
528
529         var hash [32]byte
530         sha3pool.Sum256(hash[:], program)
531         rawProgram := m.db.Get(ContractKey(hash))
532         if rawProgram == nil {
533                 return nil, ErrFindCtrlProgram
534         }
535
536         cp := &CtrlProgram{}
537         return cp, json.Unmarshal(rawProgram, cp)
538 }
539
540 // GetMiningAddress will return the mining address
541 func (m *Manager) GetMiningAddress() (string, error) {
542         cp, err := m.GetCoinbaseCtrlProgram()
543         if err != nil {
544                 return "", err
545         }
546         return cp.Address, nil
547 }
548
549 // IsLocalControlProgram check is the input control program belong to local
550 func (m *Manager) IsLocalControlProgram(prog []byte) bool {
551         var hash common.Hash
552         sha3pool.Sum256(hash[:], prog)
553         bytes := m.db.Get(ContractKey(hash))
554         return bytes != nil
555 }
556
557 // ListAccounts will return the accounts in the db
558 func (m *Manager) ListAccounts(id string) ([]*Account, error) {
559         accounts := []*Account{}
560         accountIter := m.db.IteratorPrefix(Key(strings.TrimSpace(id)))
561         defer accountIter.Release()
562
563         for accountIter.Next() {
564                 account := &Account{}
565                 if err := json.Unmarshal(accountIter.Value(), &account); err != nil {
566                         return nil, err
567                 }
568                 accounts = append(accounts, account)
569         }
570         return accounts, nil
571 }
572
573 // ListControlProgram return all the local control program
574 func (m *Manager) ListControlProgram() ([]*CtrlProgram, error) {
575         cps := []*CtrlProgram{}
576         cpIter := m.db.IteratorPrefix(contractPrefix)
577         defer cpIter.Release()
578
579         for cpIter.Next() {
580                 cp := &CtrlProgram{}
581                 if err := json.Unmarshal(cpIter.Value(), cp); err != nil {
582                         return nil, err
583                 }
584                 cps = append(cps, cp)
585         }
586         return cps, nil
587 }
588
589 func (m *Manager) ListUnconfirmedUtxo(accountID string, isSmartContract bool) []*UTXO {
590         utxos := m.utxoKeeper.ListUnconfirmed()
591         result := []*UTXO{}
592         for _, utxo := range utxos {
593                 if segwit.IsP2WScript(utxo.ControlProgram) != isSmartContract && (accountID == utxo.AccountID || accountID == "") {
594                         result = append(result, utxo)
595                 }
596         }
597         return result
598 }
599
600 // RemoveUnconfirmedUtxo remove utxos from the utxoKeeper
601 func (m *Manager) RemoveUnconfirmedUtxo(hashes []*bc.Hash) {
602         m.utxoKeeper.RemoveUnconfirmedUtxo(hashes)
603 }
604
605 // SetMiningAddress will set the mining address
606 func (m *Manager) SetMiningAddress(miningAddress string) (string, error) {
607         program, err := m.getProgramByAddress(miningAddress)
608         if err != nil {
609                 return "", err
610         }
611
612         cp := &CtrlProgram{
613                 Address:        miningAddress,
614                 ControlProgram: program,
615         }
616         rawCP, err := json.Marshal(cp)
617         if err != nil {
618                 return "", err
619         }
620
621         m.db.Set(miningAddressKey, rawCP)
622         return m.GetMiningAddress()
623 }
624
625 func (m *Manager) SetCoinbaseArbitrary(arbitrary []byte) {
626         m.db.Set(CoinbaseAbKey, arbitrary)
627 }
628
629 // CreateCtrlProgram generate an address for the select account
630 func CreateCtrlProgram(account *Account, addrIdx uint64, change bool) (cp *CtrlProgram, err error) {
631         path, err := signers.Path(account.Signer, signers.AccountKeySpace, change, addrIdx)
632         if err != nil {
633                 return nil, err
634         }
635
636         if len(account.XPubs) == 1 {
637                 cp, err = createP2PKH(account, path)
638         } else {
639                 cp, err = createP2SH(account, path)
640         }
641         if err != nil {
642                 return nil, err
643         }
644         cp.KeyIndex, cp.Change = addrIdx, change
645         return cp, nil
646 }
647
648 func createP2PKH(account *Account, path [][]byte) (*CtrlProgram, error) {
649         derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
650         derivedPK := derivedXPubs[0].PublicKey()
651         pubHash := crypto.Ripemd160(derivedPK)
652
653         address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
654         if err != nil {
655                 return nil, err
656         }
657
658         control, err := vmutil.P2WPKHProgram([]byte(pubHash))
659         if err != nil {
660                 return nil, err
661         }
662
663         return &CtrlProgram{
664                 AccountID:      account.ID,
665                 Address:        address.EncodeAddress(),
666                 ControlProgram: control,
667         }, nil
668 }
669
670 func createP2SH(account *Account, path [][]byte) (*CtrlProgram, error) {
671         derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
672         derivedPKs := chainkd.XPubKeys(derivedXPubs)
673         signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, account.Quorum)
674         if err != nil {
675                 return nil, err
676         }
677         scriptHash := crypto.Sha256(signScript)
678
679         address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
680         if err != nil {
681                 return nil, err
682         }
683
684         control, err := vmutil.P2WSHProgram(scriptHash)
685         if err != nil {
686                 return nil, err
687         }
688
689         return &CtrlProgram{
690                 AccountID:      account.ID,
691                 Address:        address.EncodeAddress(),
692                 ControlProgram: control,
693         }, nil
694 }
695
696 func GetAccountIndexKey(xpubs []chainkd.XPub) []byte {
697         var hash [32]byte
698         var xPubs []byte
699         cpy := append([]chainkd.XPub{}, xpubs[:]...)
700         sort.Sort(signers.SortKeys(cpy))
701         for _, xpub := range cpy {
702                 xPubs = append(xPubs, xpub[:]...)
703         }
704         sha3pool.Sum256(hash[:], xPubs)
705         return append(accountIndexPrefix, hash[:]...)
706 }
707
708 func (m *Manager) getCurrentContractIndex(account *Account, change bool) (uint64, error) {
709         switch account.DeriveRule {
710         case signers.BIP0032:
711                 return m.GetContractIndex(account.ID), nil
712         case signers.BIP0044:
713                 return m.GetBip44ContractIndex(account.ID, change), nil
714         }
715         return 0, ErrDeriveRule
716 }
717
718 func (m *Manager) getProgramByAddress(address string) ([]byte, error) {
719         addr, err := common.DecodeAddress(address, &consensus.ActiveNetParams)
720         if err != nil {
721                 return nil, err
722         }
723         redeemContract := addr.ScriptAddress()
724         program := []byte{}
725         switch addr.(type) {
726         case *common.AddressWitnessPubKeyHash:
727                 program, err = vmutil.P2WPKHProgram(redeemContract)
728         case *common.AddressWitnessScriptHash:
729                 program, err = vmutil.P2WSHProgram(redeemContract)
730         default:
731                 return nil, ErrInvalidAddress
732         }
733         if err != nil {
734                 return nil, err
735         }
736         return program, nil
737 }
738
739 func (m *Manager) saveControlProgram(prog *CtrlProgram, updateIndex bool) error {
740         var hash common.Hash
741
742         sha3pool.Sum256(hash[:], prog.ControlProgram)
743         acct, err := m.GetAccountByProgram(prog)
744         if err != nil {
745                 return err
746         }
747
748         accountCP, err := json.Marshal(prog)
749         if err != nil {
750                 return err
751         }
752
753         storeBatch := m.db.NewBatch()
754         storeBatch.Set(ContractKey(hash), accountCP)
755         if updateIndex {
756                 switch acct.DeriveRule {
757                 case signers.BIP0032:
758                         storeBatch.Set(contractIndexKey(acct.ID), common.Unit64ToBytes(prog.KeyIndex))
759                 case signers.BIP0044:
760                         storeBatch.Set(bip44ContractIndexKey(acct.ID, prog.Change), common.Unit64ToBytes(prog.KeyIndex))
761                 }
762         }
763         storeBatch.Write()
764
765         return nil
766 }
767
768 // SaveControlPrograms save account control programs
769 func (m *Manager) SaveControlPrograms(progs ...*CtrlProgram) error {
770         m.addressMu.Lock()
771         defer m.addressMu.Unlock()
772
773         for _, prog := range progs {
774                 acct, err := m.GetAccountByProgram(prog)
775                 if err != nil {
776                         return err
777                 }
778
779                 currentIndex, err := m.getCurrentContractIndex(acct, prog.Change)
780                 if err != nil {
781                         return err
782                 }
783
784                 m.saveControlProgram(prog, prog.KeyIndex > currentIndex)
785         }
786         return nil
787 }
788
789 func (m *Manager) CreatePeginAddress(accountID string, change bool) (string, []byte, error) {
790         // 通过配置获取
791         claimCtrlProg, _ := m.CreateAddress(accountID, change)
792         claimScript := claimCtrlProg.ControlProgram
793
794         federationRedeemScript := vmutil.CalculateContract(consensus.ActiveNetParams.FedpegXPubs, claimScript)
795
796         scriptHash := crypto.Sha256(federationRedeemScript)
797
798         address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
799         if err != nil {
800                 return "", nil, err
801         }
802
803         return address.EncodeAddress(), claimScript, nil
804
805 }
806
807 func (m *Manager) GetPeginControlPrograms(claimScript []byte) (string, []byte) {
808         federationRedeemScript := vmutil.CalculateContract(consensus.ActiveNetParams.FedpegXPubs, claimScript)
809         scriptHash := crypto.Sha256(federationRedeemScript)
810
811         address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
812         if err != nil {
813                 return "", nil
814         }
815
816         redeemContract := address.ScriptAddress()
817
818         program := []byte{}
819         program, err = vmutil.P2WSHProgram(redeemContract)
820         if err != nil {
821                 return "", nil
822         }
823
824         return address.EncodeAddress(), program
825 }