OSDN Git Service

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