OSDN Git Service

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