OSDN Git Service

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