OSDN Git Service

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