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