1 // Package account stores and tracks accounts within a Bytom Core.
14 "github.com/golang/groupcache/lru"
15 log "github.com/sirupsen/logrus"
16 dbm "github.com/tendermint/tmlibs/db"
18 "github.com/vapor/blockchain/signers"
19 "github.com/vapor/blockchain/txbuilder"
20 "github.com/vapor/common"
21 "github.com/vapor/consensus"
22 "github.com/vapor/consensus/segwit"
23 "github.com/vapor/crypto"
24 "github.com/vapor/crypto/ed25519"
25 "github.com/vapor/crypto/ed25519/chainkd"
26 "github.com/vapor/crypto/sha3pool"
27 chainjson "github.com/vapor/encoding/json"
28 "github.com/vapor/equity/compiler"
29 "github.com/vapor/errors"
30 "github.com/vapor/protocol"
31 "github.com/vapor/protocol/bc"
32 "github.com/vapor/protocol/vm/vmutil"
36 maxAccountCache = 1000
38 // HardenedKeyStart bip32 hierarchical deterministic wallets
39 // keys with index ≥ 0x80000000 are hardened keys
40 HardenedKeyStart = 0x80000000
44 accountIndexPrefix = []byte("AccountIndex:")
45 accountPrefix = []byte("Account:")
46 aliasPrefix = []byte("AccountAlias:")
47 contractIndexPrefix = []byte("ContractIndex")
48 contractPrefix = []byte("Contract:")
49 miningAddressKey = []byte("MiningAddress")
50 CoinbaseAbKey = []byte("CoinbaseArbitrary")
53 // pre-define errors for supporting bytom errorFormatter
55 ErrDuplicateAlias = errors.New("duplicate account alias")
56 ErrDuplicateIndex = errors.New("duplicate account with same xPubs and index")
57 ErrFindAccount = errors.New("fail to find account")
58 ErrMarshalAccount = errors.New("failed marshal account")
59 ErrInvalidAddress = errors.New("invalid address")
60 ErrFindCtrlProgram = errors.New("fail to find account control program")
61 ErrDeriveRule = errors.New("invalid key derive rule")
62 ErrContractIndex = errors.New("exceed the maximum addresses per account")
63 ErrAccountIndex = errors.New("exceed the maximum accounts per xpub")
64 ErrFindTransaction = errors.New("no transaction")
67 // ContractKey account control promgram store prefix
68 func ContractKey(hash common.Hash) []byte {
69 return append(contractPrefix, hash[:]...)
72 // Key account store prefix
73 func Key(name string) []byte {
74 return append(accountPrefix, []byte(name)...)
77 func aliasKey(name string) []byte {
78 return append(aliasPrefix, []byte(name)...)
81 func bip44ContractIndexKey(accountID string, change bool) []byte {
82 key := append(contractIndexPrefix, accountID...)
84 return append(key, []byte{1}...)
86 return append(key, []byte{0}...)
89 func contractIndexKey(accountID string) []byte {
90 return append(contractIndexPrefix, []byte(accountID)...)
93 // Account is structure of Bytom account
97 Alias string `json:"alias"`
100 //CtrlProgram is structure of account control program
101 type CtrlProgram struct {
105 ControlProgram []byte
106 Change bool // Mark whether this control program is for UTXO change
109 // Manager stores accounts and their associated control programs.
110 type Manager struct {
112 chain *protocol.Chain
113 utxoKeeper *utxoKeeper
117 aliasCache *lru.Cache
119 delayedACPsMu sync.Mutex
120 delayedACPs map[*txbuilder.TemplateBuilder][]*CtrlProgram
126 // NewManager creates a new account manager
127 func NewManager(walletDB dbm.DB, chain *protocol.Chain) *Manager {
131 utxoKeeper: newUtxoKeeper(chain.BestBlockHeight, walletDB),
132 cache: lru.New(maxAccountCache),
133 aliasCache: lru.New(maxAccountCache),
134 delayedACPs: make(map[*txbuilder.TemplateBuilder][]*CtrlProgram),
138 // AddUnconfirmedUtxo add untxo list to utxoKeeper
139 func (m *Manager) AddUnconfirmedUtxo(utxos []*UTXO) {
140 m.utxoKeeper.AddUnconfirmedUtxo(utxos)
143 // CreateAccount creates a new Account.
144 func CreateAccount(xpubs []chainkd.XPub, quorum int, alias string, acctIndex uint64, deriveRule uint8) (*Account, error) {
145 if acctIndex >= HardenedKeyStart {
146 return nil, ErrAccountIndex
149 signer, err := signers.Create("account", xpubs, quorum, acctIndex, deriveRule)
151 return nil, errors.Wrap(err)
154 id := signers.IDGenerate()
155 return &Account{Signer: signer, ID: id, Alias: strings.ToLower(strings.TrimSpace(alias))}, nil
158 func (m *Manager) saveAccount(account *Account, updateIndex bool) error {
159 rawAccount, err := json.Marshal(account)
161 return ErrMarshalAccount
164 storeBatch := m.db.NewBatch()
165 storeBatch.Set(Key(account.ID), rawAccount)
166 storeBatch.Set(aliasKey(account.Alias), []byte(account.ID))
168 storeBatch.Set(GetAccountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
174 // SaveAccount save a new account.
175 func (m *Manager) SaveAccount(account *Account) error {
177 defer m.accountMu.Unlock()
179 if existed := m.db.Get(aliasKey(account.Alias)); existed != nil {
180 return ErrDuplicateAlias
183 acct, err := m.GetAccountByXPubsIndex(account.XPubs, account.KeyIndex)
189 return ErrDuplicateIndex
192 currentIndex := uint64(0)
193 if rawIndexBytes := m.db.Get(GetAccountIndexKey(account.XPubs)); rawIndexBytes != nil {
194 currentIndex = common.BytesToUnit64(rawIndexBytes)
196 return m.saveAccount(account, account.KeyIndex > currentIndex)
199 // Create creates and save a new Account.
200 func (m *Manager) Create(xpubs []chainkd.XPub, quorum int, alias string, deriveRule uint8) (*Account, error) {
202 defer m.accountMu.Unlock()
204 if existed := m.db.Get(aliasKey(alias)); existed != nil {
205 return nil, ErrDuplicateAlias
208 acctIndex := uint64(1)
209 if rawIndexBytes := m.db.Get(GetAccountIndexKey(xpubs)); rawIndexBytes != nil {
210 acctIndex = common.BytesToUnit64(rawIndexBytes) + 1
212 account, err := CreateAccount(xpubs, quorum, alias, acctIndex, deriveRule)
217 if err := m.saveAccount(account, true); err != nil {
224 func (m *Manager) UpdateAccountAlias(accountID string, newAlias string) (err error) {
226 defer m.accountMu.Unlock()
228 account, err := m.FindByID(accountID)
232 oldAlias := account.Alias
234 normalizedAlias := strings.ToLower(strings.TrimSpace(newAlias))
235 if existed := m.db.Get(aliasKey(normalizedAlias)); existed != nil {
236 return ErrDuplicateAlias
240 m.aliasCache.Remove(oldAlias)
243 account.Alias = normalizedAlias
244 rawAccount, err := json.Marshal(account)
246 return ErrMarshalAccount
249 storeBatch := m.db.NewBatch()
250 storeBatch.Delete(aliasKey(oldAlias))
251 storeBatch.Set(Key(accountID), rawAccount)
252 storeBatch.Set(aliasKey(normalizedAlias), []byte(accountID))
257 // CreateAddress generate an address for the select account
258 func (m *Manager) CreateAddress(accountID string, change bool) (cp *CtrlProgram, err error) {
260 defer m.addressMu.Unlock()
262 account, err := m.FindByID(accountID)
267 currentIdx, err := m.getCurrentContractIndex(account, change)
272 cp, err = CreateCtrlProgram(account, currentIdx+1, change)
277 return cp, m.saveControlProgram(cp, true)
280 // CreateBatchAddresses generate a batch of addresses for the select account
281 func (m *Manager) CreateBatchAddresses(accountID string, change bool, stopIndex uint64) error {
283 defer m.addressMu.Unlock()
285 account, err := m.FindByID(accountID)
290 currentIndex, err := m.getCurrentContractIndex(account, change)
295 for currentIndex++; currentIndex <= stopIndex; currentIndex++ {
296 cp, err := CreateCtrlProgram(account, currentIndex, change)
301 if err := m.saveControlProgram(cp, true); err != nil {
309 // deleteAccountControlPrograms deletes control program matching accountID
310 func (m *Manager) deleteAccountControlPrograms(accountID string) error {
311 cps, err := m.ListControlProgram()
317 for _, cp := range cps {
318 if cp.AccountID == accountID {
319 sha3pool.Sum256(hash[:], cp.ControlProgram)
320 m.db.Delete(ContractKey(hash))
326 // deleteAccountUtxos deletes utxos matching accountID
327 func (m *Manager) deleteAccountUtxos(accountID string) error {
328 accountUtxoIter := m.db.IteratorPrefix([]byte(UTXOPreFix))
329 defer accountUtxoIter.Release()
330 for accountUtxoIter.Next() {
331 accountUtxo := &UTXO{}
332 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
336 if accountID == accountUtxo.AccountID {
337 m.db.Delete(StandardUTXOKey(accountUtxo.OutputID))
343 // DeleteAccount deletes the account's ID or alias matching account ID.
344 func (m *Manager) DeleteAccount(accountID string) (err error) {
346 defer m.accountMu.Unlock()
348 account, err := m.FindByID(accountID)
353 if err := m.deleteAccountControlPrograms(accountID); err != nil {
356 if err := m.deleteAccountUtxos(accountID); err != nil {
361 m.aliasCache.Remove(account.Alias)
364 storeBatch := m.db.NewBatch()
365 storeBatch.Delete(aliasKey(account.Alias))
366 storeBatch.Delete(Key(account.ID))
371 // FindByAlias retrieves an account's Signer record by its alias
372 func (m *Manager) FindByAlias(alias string) (*Account, error) {
374 cachedID, ok := m.aliasCache.Get(alias)
377 return m.FindByID(cachedID.(string))
380 rawID := m.db.Get(aliasKey(alias))
382 return nil, ErrFindAccount
385 accountID := string(rawID)
387 m.aliasCache.Add(alias, accountID)
389 return m.FindByID(accountID)
392 // FindByID returns an account's Signer record by its ID.
393 func (m *Manager) FindByID(id string) (*Account, error) {
395 cachedAccount, ok := m.cache.Get(id)
398 return cachedAccount.(*Account), nil
401 rawAccount := m.db.Get(Key(id))
402 if rawAccount == nil {
403 return nil, ErrFindAccount
406 account := &Account{}
407 if err := json.Unmarshal(rawAccount, account); err != nil {
412 m.cache.Add(id, account)
417 // GetAccountByProgram return Account by given CtrlProgram
418 func (m *Manager) GetAccountByProgram(program *CtrlProgram) (*Account, error) {
419 rawAccount := m.db.Get(Key(program.AccountID))
420 if rawAccount == nil {
421 return nil, ErrFindAccount
424 account := &Account{}
425 return account, json.Unmarshal(rawAccount, account)
428 // GetAccountByXPubsIndex get account by xPubs and index
429 func (m *Manager) GetAccountByXPubsIndex(xPubs []chainkd.XPub, index uint64) (*Account, error) {
430 accounts, err := m.ListAccounts("")
435 for _, account := range accounts {
436 if reflect.DeepEqual(account.XPubs, xPubs) && account.KeyIndex == index {
443 // GetAliasByID return the account alias by given ID
444 func (m *Manager) GetAliasByID(id string) string {
445 rawAccount := m.db.Get(Key(id))
446 if rawAccount == nil {
447 log.Warn("GetAliasByID fail to find account")
451 account := &Account{}
452 if err := json.Unmarshal(rawAccount, account); err != nil {
458 func (m *Manager) GetCoinbaseArbitrary() []byte {
459 if arbitrary := m.db.Get(CoinbaseAbKey); arbitrary != nil {
465 // GetCoinbaseControlProgram will return a coinbase script
466 func (m *Manager) GetCoinbaseControlProgram() ([]byte, error) {
467 cp, err := m.GetCoinbaseCtrlProgram()
468 if err == ErrFindAccount {
469 log.Warningf("GetCoinbaseControlProgram: can't find any account in db")
470 return vmutil.DefaultCoinbaseProgram()
475 return cp.ControlProgram, nil
478 // GetCoinbaseCtrlProgram will return the coinbase CtrlProgram
479 func (m *Manager) GetCoinbaseCtrlProgram() (*CtrlProgram, error) {
480 if data := m.db.Get(miningAddressKey); data != nil {
482 return cp, json.Unmarshal(data, cp)
485 accountIter := m.db.IteratorPrefix([]byte(accountPrefix))
486 defer accountIter.Release()
487 if !accountIter.Next() {
488 return nil, ErrFindAccount
491 account := &Account{}
492 if err := json.Unmarshal(accountIter.Value(), account); err != nil {
496 program, err := m.CreateAddress(account.ID, false)
501 rawCP, err := json.Marshal(program)
506 m.db.Set(miningAddressKey, rawCP)
510 // GetContractIndex return the current index
511 func (m *Manager) GetContractIndex(accountID string) uint64 {
513 if rawIndexBytes := m.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
514 index = common.BytesToUnit64(rawIndexBytes)
519 // GetBip44ContractIndex return the current bip44 contract index
520 func (m *Manager) GetBip44ContractIndex(accountID string, change bool) uint64 {
522 if rawIndexBytes := m.db.Get(bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil {
523 index = common.BytesToUnit64(rawIndexBytes)
528 // GetLocalCtrlProgramByAddress return CtrlProgram by given address
529 func (m *Manager) GetLocalCtrlProgramByAddress(address string) (*CtrlProgram, error) {
530 program, err := m.getProgramByAddress(address)
536 sha3pool.Sum256(hash[:], program)
537 rawProgram := m.db.Get(ContractKey(hash))
538 if rawProgram == nil {
539 return nil, ErrFindCtrlProgram
543 return cp, json.Unmarshal(rawProgram, cp)
546 // GetMiningAddress will return the mining address
547 func (m *Manager) GetMiningAddress() (string, error) {
548 cp, err := m.GetCoinbaseCtrlProgram()
552 return cp.Address, nil
555 // IsLocalControlProgram check is the input control program belong to local
556 func (m *Manager) IsLocalControlProgram(prog []byte) bool {
558 sha3pool.Sum256(hash[:], prog)
559 bytes := m.db.Get(ContractKey(hash))
563 // ListAccounts will return the accounts in the db
564 func (m *Manager) ListAccounts(id string) ([]*Account, error) {
565 accounts := []*Account{}
566 accountIter := m.db.IteratorPrefix(Key(strings.TrimSpace(id)))
567 defer accountIter.Release()
569 for accountIter.Next() {
570 account := &Account{}
571 if err := json.Unmarshal(accountIter.Value(), &account); err != nil {
574 accounts = append(accounts, account)
579 // ListControlProgram return all the local control program
580 func (m *Manager) ListControlProgram() ([]*CtrlProgram, error) {
581 cps := []*CtrlProgram{}
582 cpIter := m.db.IteratorPrefix(contractPrefix)
583 defer cpIter.Release()
587 if err := json.Unmarshal(cpIter.Value(), cp); err != nil {
590 cps = append(cps, cp)
595 func (m *Manager) ListUnconfirmedUtxo(accountID string, isSmartContract bool) []*UTXO {
596 utxos := m.utxoKeeper.ListUnconfirmed()
598 for _, utxo := range utxos {
599 if segwit.IsP2WScript(utxo.ControlProgram) != isSmartContract && (accountID == utxo.AccountID || accountID == "") {
600 result = append(result, utxo)
606 // RemoveUnconfirmedUtxo remove utxos from the utxoKeeper
607 func (m *Manager) RemoveUnconfirmedUtxo(hashes []*bc.Hash) {
608 m.utxoKeeper.RemoveUnconfirmedUtxo(hashes)
611 // SetMiningAddress will set the mining address
612 func (m *Manager) SetMiningAddress(miningAddress string) (string, error) {
613 program, err := m.getProgramByAddress(miningAddress)
619 Address: miningAddress,
620 ControlProgram: program,
622 rawCP, err := json.Marshal(cp)
627 m.db.Set(miningAddressKey, rawCP)
628 return m.GetMiningAddress()
631 func (m *Manager) SetCoinbaseArbitrary(arbitrary []byte) {
632 m.db.Set(CoinbaseAbKey, arbitrary)
635 // CreateCtrlProgram generate an address for the select account
636 func CreateCtrlProgram(account *Account, addrIdx uint64, change bool) (cp *CtrlProgram, err error) {
637 path, err := signers.Path(account.Signer, signers.AccountKeySpace, change, addrIdx)
642 if len(account.XPubs) == 1 {
643 cp, err = createP2PKH(account, path)
645 cp, err = createP2SH(account, path)
650 cp.KeyIndex, cp.Change = addrIdx, change
654 func createP2PKH(account *Account, path [][]byte) (*CtrlProgram, error) {
655 derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
656 derivedPK := derivedXPubs[0].PublicKey()
657 pubHash := crypto.Ripemd160(derivedPK)
659 address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
664 control, err := vmutil.P2WPKHProgram([]byte(pubHash))
670 AccountID: account.ID,
671 Address: address.EncodeAddress(),
672 ControlProgram: control,
676 func createP2SH(account *Account, path [][]byte) (*CtrlProgram, error) {
677 derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
678 derivedPKs := chainkd.XPubKeys(derivedXPubs)
679 signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, account.Quorum)
683 scriptHash := crypto.Sha256(signScript)
685 address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
690 control, err := vmutil.P2WSHProgram(scriptHash)
696 AccountID: account.ID,
697 Address: address.EncodeAddress(),
698 ControlProgram: control,
702 func GetAccountIndexKey(xpubs []chainkd.XPub) []byte {
705 cpy := append([]chainkd.XPub{}, xpubs[:]...)
706 sort.Sort(signers.SortKeys(cpy))
707 for _, xpub := range cpy {
708 xPubs = append(xPubs, xpub[:]...)
710 sha3pool.Sum256(hash[:], xPubs)
711 return append(accountIndexPrefix, hash[:]...)
714 func (m *Manager) getCurrentContractIndex(account *Account, change bool) (uint64, error) {
715 switch account.DeriveRule {
716 case signers.BIP0032:
717 return m.GetContractIndex(account.ID), nil
718 case signers.BIP0044:
719 return m.GetBip44ContractIndex(account.ID, change), nil
721 return 0, ErrDeriveRule
724 func (m *Manager) getProgramByAddress(address string) ([]byte, error) {
725 addr, err := common.DecodeAddress(address, &consensus.ActiveNetParams)
729 redeemContract := addr.ScriptAddress()
732 case *common.AddressWitnessPubKeyHash:
733 program, err = vmutil.P2WPKHProgram(redeemContract)
734 case *common.AddressWitnessScriptHash:
735 program, err = vmutil.P2WSHProgram(redeemContract)
737 return nil, ErrInvalidAddress
745 func (m *Manager) saveControlProgram(prog *CtrlProgram, updateIndex bool) error {
748 sha3pool.Sum256(hash[:], prog.ControlProgram)
749 acct, err := m.GetAccountByProgram(prog)
754 accountCP, err := json.Marshal(prog)
759 storeBatch := m.db.NewBatch()
760 storeBatch.Set(ContractKey(hash), accountCP)
762 switch acct.DeriveRule {
763 case signers.BIP0032:
764 storeBatch.Set(contractIndexKey(acct.ID), common.Unit64ToBytes(prog.KeyIndex))
765 case signers.BIP0044:
766 storeBatch.Set(bip44ContractIndexKey(acct.ID, prog.Change), common.Unit64ToBytes(prog.KeyIndex))
774 // SaveControlPrograms save account control programs
775 func (m *Manager) SaveControlPrograms(progs ...*CtrlProgram) error {
777 defer m.addressMu.Unlock()
779 for _, prog := range progs {
780 acct, err := m.GetAccountByProgram(prog)
785 currentIndex, err := m.getCurrentContractIndex(acct, prog.Change)
790 m.saveControlProgram(prog, prog.KeyIndex > currentIndex)
795 func (m *Manager) CreatePeginAddress(accountID string, change bool) (string, []byte, error) {
797 claimCtrlProg, _ := m.CreateAddress(accountID, change)
798 claimScript := claimCtrlProg.ControlProgram
800 federationRedeemScript := vmutil.CalculateContract(consensus.ActiveNetParams.FedpegXPubs, claimScript)
802 scriptHash := crypto.Sha256(federationRedeemScript)
804 address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
809 return address.EncodeAddress(), claimScript, nil
813 func (m *Manager) GetPeginControlPrograms(claimScript []byte) (string, []byte) {
814 federationRedeemScript := vmutil.CalculateContract(consensus.ActiveNetParams.FedpegXPubs, claimScript)
815 scriptHash := crypto.Sha256(federationRedeemScript)
817 address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
822 redeemContract := address.ScriptAddress()
825 program, err = vmutil.P2WSHProgram(redeemContract)
830 return address.EncodeAddress(), program
833 var lockWith2of3KeysFmt = `
834 contract LockWith3Keys(%s) locks amount of asset {
835 clause unlockWith2Sigs(%s) {
836 verify checkTxMultiSig(%s)
837 unlock amount of asset
842 func (m *Manager) CreatePeginContractAddress(accountID string, change bool) (string, []byte, error) {
844 claimCtrlProg, err := m.CreateAddress(accountID, change)
848 claimScript := claimCtrlProg.ControlProgram
850 peginContractPrograms, err := m.getPeginContractPrograms(claimScript)
855 scriptHash := crypto.Sha256(peginContractPrograms)
857 address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
862 return address.EncodeAddress(), claimScript, nil
866 func (m *Manager) GetPeginContractControlPrograms(claimScript []byte) (string, []byte) {
868 peginContractPrograms, err := m.getPeginContractPrograms(claimScript)
872 scriptHash := crypto.Sha256(peginContractPrograms)
874 address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
879 redeemContract := address.ScriptAddress()
882 program, err = vmutil.P2WSHProgram(redeemContract)
887 return address.EncodeAddress(), program
890 func (m *Manager) getPeginContractPrograms(claimScript []byte) ([]byte, error) {
892 pubkeys := getNewXpub(consensus.ActiveNetParams.FedpegXPubs, claimScript)
896 return nil, errors.New("Fedpeg's XPubs is empty")
902 for index := 0; index < num; index++ {
903 param := fmt.Sprintf("pubkey%d", index+1)
906 if (index + 1) < num {
911 params += ": PublicKey"
915 signNum := getNumberOfSignaturesRequired(pubkeys)
916 for index := 0; index < signNum; index++ {
917 param := fmt.Sprintf("sig%d", index+1)
918 unlockParams += param
920 if index+1 < signNum {
926 unlockParams += ": Signature"
929 lockWith2of3Keys := fmt.Sprintf(lockWith2of3KeysFmt, params, unlockParams, checkParams)
930 fmt.Println(lockWith2of3Keys)
931 r := strings.NewReader(lockWith2of3Keys)
932 compiled, err := compiler.Compile(r)
934 return nil, errors.New("Compile contract failed")
937 contract := compiled[len(compiled)-1]
939 if num < len(contract.Params) {
940 return nil, errors.New("Compile contract failed")
943 contractArgs, err := convertArguments(contract, pubkeys)
945 fmt.Println("Convert arguments into contract parameters error:", err)
946 return nil, errors.New("Convert arguments into contract parameters error")
949 instantProg, err := instantiateContract(contract, contractArgs)
951 fmt.Println("Instantiate contract error:", err)
952 return nil, errors.New("Instantiate contract error")
955 return instantProg, nil
958 func getNewXpub(federationRedeemXPub []chainkd.XPub, claimScript []byte) []ed25519.PublicKey {
960 var pubkeys []ed25519.PublicKey
961 for _, xpub := range federationRedeemXPub {
962 // pub + scriptPubKey 生成一个随机数A
964 h := hmac.New(sha256.New, xpub[:])
966 tweak := h.Sum(tmp[:])
967 // pub + A 生成一个新的公钥pub_new
968 chaildXPub := xpub.Child(tweak)
969 pubkeys = append(pubkeys, chaildXPub.PublicKey())
974 func getNumberOfSignaturesRequired(pubkeys []ed25519.PublicKey) int {
975 return len(pubkeys)/2 + 1
978 // InstantiateContract instantiate contract parameters
979 func instantiateContract(contract *compiler.Contract, args []compiler.ContractArg) ([]byte, error) {
980 program, err := compiler.Instantiate(contract.Body, contract.Params, contract.Recursive, args)
988 func convertArguments(contract *compiler.Contract, args []ed25519.PublicKey) ([]compiler.ContractArg, error) {
989 var contractArgs []compiler.ContractArg
990 for i, p := range contract.Params {
991 var argument compiler.ContractArg
994 if len(args[i]) != 64 {
995 return nil, errors.New("mismatch length for Asset/Hash/PublicKey argument")
997 argument.S = (*chainjson.HexBytes)(&args[i])
999 return nil, errors.New("Contract parameter type error")
1001 contractArgs = append(contractArgs, argument)
1004 return contractArgs, nil