OSDN Git Service

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