1 // Package account stores and tracks accounts within a Bytom Core.
15 "github.com/golang/groupcache/lru"
16 log "github.com/sirupsen/logrus"
17 dbm "github.com/tendermint/tmlibs/db"
19 "github.com/vapor/blockchain/signers"
20 "github.com/vapor/blockchain/txbuilder"
21 "github.com/vapor/common"
22 "github.com/vapor/consensus"
23 "github.com/vapor/consensus/segwit"
24 "github.com/vapor/crypto"
25 "github.com/vapor/crypto/ed25519"
26 "github.com/vapor/crypto/ed25519/chainkd"
27 "github.com/vapor/crypto/sha3pool"
28 chainjson "github.com/vapor/encoding/json"
29 "github.com/vapor/equity/compiler"
30 "github.com/vapor/errors"
31 "github.com/vapor/protocol"
32 "github.com/vapor/protocol/bc"
33 "github.com/vapor/protocol/vm/vmutil"
37 maxAccountCache = 1000
39 // HardenedKeyStart bip32 hierarchical deterministic wallets
40 // keys with index ≥ 0x80000000 are hardened keys
41 HardenedKeyStart = 0x80000000
45 accountIndexPrefix = []byte("AccountIndex:")
46 accountPrefix = []byte("Account:")
47 aliasPrefix = []byte("AccountAlias:")
48 contractIndexPrefix = []byte("ContractIndex")
49 contractPrefix = []byte("Contract:")
50 miningAddressKey = []byte("MiningAddress")
51 CoinbaseAbKey = []byte("CoinbaseArbitrary")
54 // pre-define errors for supporting bytom errorFormatter
56 ErrDuplicateAlias = errors.New("duplicate account alias")
57 ErrDuplicateIndex = errors.New("duplicate account with same xPubs and index")
58 ErrFindAccount = errors.New("fail to find account")
59 ErrMarshalAccount = errors.New("failed marshal account")
60 ErrInvalidAddress = errors.New("invalid address")
61 ErrFindCtrlProgram = errors.New("fail to find account control program")
62 ErrDeriveRule = errors.New("invalid key derive rule")
63 ErrContractIndex = errors.New("exceed the maximum addresses per account")
64 ErrAccountIndex = errors.New("exceed the maximum accounts per xpub")
65 ErrFindTransaction = errors.New("no transaction")
68 // ContractKey account control promgram store prefix
69 func ContractKey(hash common.Hash) []byte {
70 return append(contractPrefix, hash[:]...)
73 // Key account store prefix
74 func Key(name string) []byte {
75 return append(accountPrefix, []byte(name)...)
78 func aliasKey(name string) []byte {
79 return append(aliasPrefix, []byte(name)...)
82 func bip44ContractIndexKey(accountID string, change bool) []byte {
83 key := append(contractIndexPrefix, accountID...)
85 return append(key, []byte{1}...)
87 return append(key, []byte{0}...)
90 func contractIndexKey(accountID string) []byte {
91 return append(contractIndexPrefix, []byte(accountID)...)
94 // Account is structure of Bytom account
98 Alias string `json:"alias"`
101 //CtrlProgram is structure of account control program
102 type CtrlProgram struct {
106 ControlProgram []byte
107 Change bool // Mark whether this control program is for UTXO change
110 // Manager stores accounts and their associated control programs.
111 type Manager struct {
113 chain *protocol.Chain
114 utxoKeeper *utxoKeeper
118 aliasCache *lru.Cache
120 delayedACPsMu sync.Mutex
121 delayedACPs map[*txbuilder.TemplateBuilder][]*CtrlProgram
127 // NewManager creates a new account manager
128 func NewManager(walletDB dbm.DB, chain *protocol.Chain) *Manager {
132 utxoKeeper: newUtxoKeeper(chain.BestBlockHeight, walletDB),
133 cache: lru.New(maxAccountCache),
134 aliasCache: lru.New(maxAccountCache),
135 delayedACPs: make(map[*txbuilder.TemplateBuilder][]*CtrlProgram),
139 // AddUnconfirmedUtxo add untxo list to utxoKeeper
140 func (m *Manager) AddUnconfirmedUtxo(utxos []*UTXO) {
141 m.utxoKeeper.AddUnconfirmedUtxo(utxos)
144 // CreateAccount creates a new Account.
145 func CreateAccount(xpubs []chainkd.XPub, quorum int, alias string, acctIndex uint64, deriveRule uint8) (*Account, error) {
146 if acctIndex >= HardenedKeyStart {
147 return nil, ErrAccountIndex
150 signer, err := signers.Create("account", xpubs, quorum, acctIndex, deriveRule)
152 return nil, errors.Wrap(err)
155 id := signers.IDGenerate()
156 return &Account{Signer: signer, ID: id, Alias: strings.ToLower(strings.TrimSpace(alias))}, nil
159 func (m *Manager) saveAccount(account *Account, updateIndex bool) error {
160 rawAccount, err := json.Marshal(account)
162 return ErrMarshalAccount
165 storeBatch := m.db.NewBatch()
166 storeBatch.Set(Key(account.ID), rawAccount)
167 storeBatch.Set(aliasKey(account.Alias), []byte(account.ID))
169 storeBatch.Set(GetAccountIndexKey(account.XPubs), common.Unit64ToBytes(account.KeyIndex))
175 // SaveAccount save a new account.
176 func (m *Manager) SaveAccount(account *Account) error {
178 defer m.accountMu.Unlock()
180 if existed := m.db.Get(aliasKey(account.Alias)); existed != nil {
181 return ErrDuplicateAlias
184 acct, err := m.GetAccountByXPubsIndex(account.XPubs, account.KeyIndex)
190 return ErrDuplicateIndex
193 currentIndex := uint64(0)
194 if rawIndexBytes := m.db.Get(GetAccountIndexKey(account.XPubs)); rawIndexBytes != nil {
195 currentIndex = common.BytesToUnit64(rawIndexBytes)
197 return m.saveAccount(account, account.KeyIndex > currentIndex)
200 // Create creates and save a new Account.
201 func (m *Manager) Create(xpubs []chainkd.XPub, quorum int, alias string, deriveRule uint8) (*Account, error) {
203 defer m.accountMu.Unlock()
205 if existed := m.db.Get(aliasKey(alias)); existed != nil {
206 return nil, ErrDuplicateAlias
209 acctIndex := uint64(1)
210 if rawIndexBytes := m.db.Get(GetAccountIndexKey(xpubs)); rawIndexBytes != nil {
211 acctIndex = common.BytesToUnit64(rawIndexBytes) + 1
213 account, err := CreateAccount(xpubs, quorum, alias, acctIndex, deriveRule)
218 if err := m.saveAccount(account, true); err != nil {
225 func (m *Manager) UpdateAccountAlias(accountID string, newAlias string) (err error) {
227 defer m.accountMu.Unlock()
229 account, err := m.FindByID(accountID)
233 oldAlias := account.Alias
235 normalizedAlias := strings.ToLower(strings.TrimSpace(newAlias))
236 if existed := m.db.Get(aliasKey(normalizedAlias)); existed != nil {
237 return ErrDuplicateAlias
241 m.aliasCache.Remove(oldAlias)
244 account.Alias = normalizedAlias
245 rawAccount, err := json.Marshal(account)
247 return ErrMarshalAccount
250 storeBatch := m.db.NewBatch()
251 storeBatch.Delete(aliasKey(oldAlias))
252 storeBatch.Set(Key(accountID), rawAccount)
253 storeBatch.Set(aliasKey(normalizedAlias), []byte(accountID))
258 // CreateAddress generate an address for the select account
259 func (m *Manager) CreateAddress(accountID string, change bool) (cp *CtrlProgram, err error) {
261 defer m.addressMu.Unlock()
263 account, err := m.FindByID(accountID)
268 currentIdx, err := m.getCurrentContractIndex(account, change)
273 cp, err = CreateCtrlProgram(account, currentIdx+1, change)
278 return cp, m.saveControlProgram(cp, true)
281 // CreateBatchAddresses generate a batch of addresses for the select account
282 func (m *Manager) CreateBatchAddresses(accountID string, change bool, stopIndex uint64) error {
284 defer m.addressMu.Unlock()
286 account, err := m.FindByID(accountID)
291 currentIndex, err := m.getCurrentContractIndex(account, change)
296 for currentIndex++; currentIndex <= stopIndex; currentIndex++ {
297 cp, err := CreateCtrlProgram(account, currentIndex, change)
302 if err := m.saveControlProgram(cp, true); err != nil {
310 // deleteAccountControlPrograms deletes control program matching accountID
311 func (m *Manager) deleteAccountControlPrograms(accountID string) error {
312 cps, err := m.ListControlProgram()
318 for _, cp := range cps {
319 if cp.AccountID == accountID {
320 sha3pool.Sum256(hash[:], cp.ControlProgram)
321 m.db.Delete(ContractKey(hash))
327 // deleteAccountUtxos deletes utxos matching accountID
328 func (m *Manager) deleteAccountUtxos(accountID string) error {
329 accountUtxoIter := m.db.IteratorPrefix([]byte(UTXOPreFix))
330 defer accountUtxoIter.Release()
331 for accountUtxoIter.Next() {
332 accountUtxo := &UTXO{}
333 if err := json.Unmarshal(accountUtxoIter.Value(), accountUtxo); err != nil {
337 if accountID == accountUtxo.AccountID {
338 m.db.Delete(StandardUTXOKey(accountUtxo.OutputID))
344 // DeleteAccount deletes the account's ID or alias matching account ID.
345 func (m *Manager) DeleteAccount(accountID string) (err error) {
347 defer m.accountMu.Unlock()
349 account, err := m.FindByID(accountID)
354 if err := m.deleteAccountControlPrograms(accountID); err != nil {
357 if err := m.deleteAccountUtxos(accountID); err != nil {
362 m.aliasCache.Remove(account.Alias)
365 storeBatch := m.db.NewBatch()
366 storeBatch.Delete(aliasKey(account.Alias))
367 storeBatch.Delete(Key(account.ID))
372 // FindByAlias retrieves an account's Signer record by its alias
373 func (m *Manager) FindByAlias(alias string) (*Account, error) {
375 cachedID, ok := m.aliasCache.Get(alias)
378 return m.FindByID(cachedID.(string))
381 rawID := m.db.Get(aliasKey(alias))
383 return nil, ErrFindAccount
386 accountID := string(rawID)
388 m.aliasCache.Add(alias, accountID)
390 return m.FindByID(accountID)
393 // FindByID returns an account's Signer record by its ID.
394 func (m *Manager) FindByID(id string) (*Account, error) {
396 cachedAccount, ok := m.cache.Get(id)
399 return cachedAccount.(*Account), nil
402 rawAccount := m.db.Get(Key(id))
403 if rawAccount == nil {
404 return nil, ErrFindAccount
407 account := &Account{}
408 if err := json.Unmarshal(rawAccount, account); err != nil {
413 m.cache.Add(id, account)
418 // GetAccountByProgram return Account by given CtrlProgram
419 func (m *Manager) GetAccountByProgram(program *CtrlProgram) (*Account, error) {
420 rawAccount := m.db.Get(Key(program.AccountID))
421 if rawAccount == nil {
422 return nil, ErrFindAccount
425 account := &Account{}
426 return account, json.Unmarshal(rawAccount, account)
429 // GetAccountByXPubsIndex get account by xPubs and index
430 func (m *Manager) GetAccountByXPubsIndex(xPubs []chainkd.XPub, index uint64) (*Account, error) {
431 accounts, err := m.ListAccounts("")
436 for _, account := range accounts {
437 if reflect.DeepEqual(account.XPubs, xPubs) && account.KeyIndex == index {
444 // GetAliasByID return the account alias by given ID
445 func (m *Manager) GetAliasByID(id string) string {
446 rawAccount := m.db.Get(Key(id))
447 if rawAccount == nil {
448 log.Warn("GetAliasByID fail to find account")
452 account := &Account{}
453 if err := json.Unmarshal(rawAccount, account); err != nil {
459 func (m *Manager) GetCoinbaseArbitrary() []byte {
460 if arbitrary := m.db.Get(CoinbaseAbKey); arbitrary != nil {
466 // GetCoinbaseControlProgram will return a coinbase script
467 func (m *Manager) GetCoinbaseControlProgram() ([]byte, error) {
468 cp, err := m.GetCoinbaseCtrlProgram()
469 if err == ErrFindAccount {
470 log.Warningf("GetCoinbaseControlProgram: can't find any account in db")
471 return vmutil.DefaultCoinbaseProgram()
476 return cp.ControlProgram, nil
479 // GetCoinbaseCtrlProgram will return the coinbase CtrlProgram
480 func (m *Manager) GetCoinbaseCtrlProgram() (*CtrlProgram, error) {
481 if data := m.db.Get(miningAddressKey); data != nil {
483 return cp, json.Unmarshal(data, cp)
486 accountIter := m.db.IteratorPrefix([]byte(accountPrefix))
487 defer accountIter.Release()
488 if !accountIter.Next() {
489 return nil, ErrFindAccount
492 account := &Account{}
493 if err := json.Unmarshal(accountIter.Value(), account); err != nil {
497 program, err := m.CreateAddress(account.ID, false)
502 rawCP, err := json.Marshal(program)
507 m.db.Set(miningAddressKey, rawCP)
511 // GetContractIndex return the current index
512 func (m *Manager) GetContractIndex(accountID string) uint64 {
514 if rawIndexBytes := m.db.Get(contractIndexKey(accountID)); rawIndexBytes != nil {
515 index = common.BytesToUnit64(rawIndexBytes)
520 // GetBip44ContractIndex return the current bip44 contract index
521 func (m *Manager) GetBip44ContractIndex(accountID string, change bool) uint64 {
523 if rawIndexBytes := m.db.Get(bip44ContractIndexKey(accountID, change)); rawIndexBytes != nil {
524 index = common.BytesToUnit64(rawIndexBytes)
529 // GetLocalCtrlProgramByAddress return CtrlProgram by given address
530 func (m *Manager) GetLocalCtrlProgramByAddress(address string) (*CtrlProgram, error) {
531 program, err := m.getProgramByAddress(address)
537 sha3pool.Sum256(hash[:], program)
538 rawProgram := m.db.Get(ContractKey(hash))
539 if rawProgram == nil {
540 return nil, ErrFindCtrlProgram
544 return cp, json.Unmarshal(rawProgram, cp)
547 // GetMiningAddress will return the mining address
548 func (m *Manager) GetMiningAddress() (string, error) {
549 cp, err := m.GetCoinbaseCtrlProgram()
553 return cp.Address, nil
556 // IsLocalControlProgram check is the input control program belong to local
557 func (m *Manager) IsLocalControlProgram(prog []byte) bool {
559 sha3pool.Sum256(hash[:], prog)
560 bytes := m.db.Get(ContractKey(hash))
564 // ListAccounts will return the accounts in the db
565 func (m *Manager) ListAccounts(id string) ([]*Account, error) {
566 accounts := []*Account{}
567 accountIter := m.db.IteratorPrefix(Key(strings.TrimSpace(id)))
568 defer accountIter.Release()
570 for accountIter.Next() {
571 account := &Account{}
572 if err := json.Unmarshal(accountIter.Value(), &account); err != nil {
575 accounts = append(accounts, account)
580 // ListControlProgram return all the local control program
581 func (m *Manager) ListControlProgram() ([]*CtrlProgram, error) {
582 cps := []*CtrlProgram{}
583 cpIter := m.db.IteratorPrefix(contractPrefix)
584 defer cpIter.Release()
588 if err := json.Unmarshal(cpIter.Value(), cp); err != nil {
591 cps = append(cps, cp)
596 func (m *Manager) ListUnconfirmedUtxo(accountID string, isSmartContract bool) []*UTXO {
597 utxos := m.utxoKeeper.ListUnconfirmed()
599 for _, utxo := range utxos {
600 if segwit.IsP2WScript(utxo.ControlProgram) != isSmartContract && (accountID == utxo.AccountID || accountID == "") {
601 result = append(result, utxo)
607 // RemoveUnconfirmedUtxo remove utxos from the utxoKeeper
608 func (m *Manager) RemoveUnconfirmedUtxo(hashes []*bc.Hash) {
609 m.utxoKeeper.RemoveUnconfirmedUtxo(hashes)
612 // SetMiningAddress will set the mining address
613 func (m *Manager) SetMiningAddress(miningAddress string) (string, error) {
614 program, err := m.getProgramByAddress(miningAddress)
620 Address: miningAddress,
621 ControlProgram: program,
623 rawCP, err := json.Marshal(cp)
628 m.db.Set(miningAddressKey, rawCP)
629 return m.GetMiningAddress()
632 func (m *Manager) SetCoinbaseArbitrary(arbitrary []byte) {
633 m.db.Set(CoinbaseAbKey, arbitrary)
636 // CreateCtrlProgram generate an address for the select account
637 func CreateCtrlProgram(account *Account, addrIdx uint64, change bool) (cp *CtrlProgram, err error) {
638 path, err := signers.Path(account.Signer, signers.AccountKeySpace, change, addrIdx)
643 if len(account.XPubs) == 1 {
644 cp, err = createP2PKH(account, path)
646 cp, err = createP2SH(account, path)
651 cp.KeyIndex, cp.Change = addrIdx, change
655 func createP2PKH(account *Account, path [][]byte) (*CtrlProgram, error) {
656 derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
657 derivedPK := derivedXPubs[0].PublicKey()
658 pubHash := crypto.Ripemd160(derivedPK)
660 address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
665 control, err := vmutil.P2WPKHProgram([]byte(pubHash))
671 AccountID: account.ID,
672 Address: address.EncodeAddress(),
673 ControlProgram: control,
677 func createP2SH(account *Account, path [][]byte) (*CtrlProgram, error) {
678 derivedXPubs := chainkd.DeriveXPubs(account.XPubs, path)
679 derivedPKs := chainkd.XPubKeys(derivedXPubs)
680 signScript, err := vmutil.P2SPMultiSigProgram(derivedPKs, account.Quorum)
684 scriptHash := crypto.Sha256(signScript)
686 address, err := common.NewAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
691 control, err := vmutil.P2WSHProgram(scriptHash)
697 AccountID: account.ID,
698 Address: address.EncodeAddress(),
699 ControlProgram: control,
703 func GetAccountIndexKey(xpubs []chainkd.XPub) []byte {
706 cpy := append([]chainkd.XPub{}, xpubs[:]...)
707 sort.Sort(signers.SortKeys(cpy))
708 for _, xpub := range cpy {
709 xPubs = append(xPubs, xpub[:]...)
711 sha3pool.Sum256(hash[:], xPubs)
712 return append(accountIndexPrefix, hash[:]...)
715 func (m *Manager) getCurrentContractIndex(account *Account, change bool) (uint64, error) {
716 switch account.DeriveRule {
717 case signers.BIP0032:
718 return m.GetContractIndex(account.ID), nil
719 case signers.BIP0044:
720 return m.GetBip44ContractIndex(account.ID, change), nil
722 return 0, ErrDeriveRule
725 func (m *Manager) getProgramByAddress(address string) ([]byte, error) {
726 addr, err := common.DecodeAddress(address, &consensus.ActiveNetParams)
730 redeemContract := addr.ScriptAddress()
733 case *common.AddressWitnessPubKeyHash:
734 program, err = vmutil.P2WPKHProgram(redeemContract)
735 case *common.AddressWitnessScriptHash:
736 program, err = vmutil.P2WSHProgram(redeemContract)
738 return nil, ErrInvalidAddress
746 func (m *Manager) saveControlProgram(prog *CtrlProgram, updateIndex bool) error {
749 sha3pool.Sum256(hash[:], prog.ControlProgram)
750 acct, err := m.GetAccountByProgram(prog)
755 accountCP, err := json.Marshal(prog)
760 storeBatch := m.db.NewBatch()
761 storeBatch.Set(ContractKey(hash), accountCP)
763 switch acct.DeriveRule {
764 case signers.BIP0032:
765 storeBatch.Set(contractIndexKey(acct.ID), common.Unit64ToBytes(prog.KeyIndex))
766 case signers.BIP0044:
767 storeBatch.Set(bip44ContractIndexKey(acct.ID, prog.Change), common.Unit64ToBytes(prog.KeyIndex))
775 // SaveControlPrograms save account control programs
776 func (m *Manager) SaveControlPrograms(progs ...*CtrlProgram) error {
778 defer m.addressMu.Unlock()
780 for _, prog := range progs {
781 acct, err := m.GetAccountByProgram(prog)
786 currentIndex, err := m.getCurrentContractIndex(acct, prog.Change)
791 m.saveControlProgram(prog, prog.KeyIndex > currentIndex)
796 func (m *Manager) CreatePeginAddress(accountID string, change bool) (string, []byte, error) {
798 claimCtrlProg, _ := m.CreateAddress(accountID, change)
799 claimScript := claimCtrlProg.ControlProgram
801 federationRedeemScript := vmutil.CalculateContract(consensus.ActiveNetParams.FedpegXPubs, claimScript)
803 scriptHash := crypto.Sha256(federationRedeemScript)
805 address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
810 return address.EncodeAddress(), claimScript, nil
814 func (m *Manager) GetPeginControlPrograms(claimScript []byte) (string, []byte) {
815 federationRedeemScript := vmutil.CalculateContract(consensus.ActiveNetParams.FedpegXPubs, claimScript)
816 scriptHash := crypto.Sha256(federationRedeemScript)
818 address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
823 redeemContract := address.ScriptAddress()
826 program, err = vmutil.P2WSHProgram(redeemContract)
831 return address.EncodeAddress(), program
834 var lockWith2of3KeysFmt = `
835 contract LockWith2of3Keys(%s) locks amount of asset {
836 clause unlockWith2Sigs(%s) {
837 verify checkTxMultiSig(%s)
838 unlock amount of asset
843 func (m *Manager) CreatePeginContractPrograms(accountID string, change bool) (string, []byte, error) {
845 claimCtrlProg, err := m.CreateAddress(accountID, change)
849 claimScript := claimCtrlProg.ControlProgram
851 peginContractPrograms, err := m.GetPeginContractPrograms(claimScript)
855 return hex.EncodeToString(peginContractPrograms), claimScript, nil
859 func (m *Manager) CreatePeginContractAddress(accountID string, change bool) (string, []byte, error) {
861 claimCtrlProg, err := m.CreateAddress(accountID, change)
865 claimScript := claimCtrlProg.ControlProgram
867 peginContractPrograms, err := m.GetPeginContractPrograms(claimScript)
872 scriptHash := crypto.Sha256(peginContractPrograms)
874 address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
879 return address.EncodeAddress(), claimScript, nil
883 func (m *Manager) GetPeginContractControlPrograms(claimScript []byte) (string, []byte) {
885 peginContractPrograms, err := m.GetPeginContractPrograms(claimScript)
889 scriptHash := crypto.Sha256(peginContractPrograms)
891 address, err := common.NewPeginAddressWitnessScriptHash(scriptHash, &consensus.ActiveNetParams)
896 redeemContract := address.ScriptAddress()
899 program, err = vmutil.P2WSHProgram(redeemContract)
904 return address.EncodeAddress(), program
907 func (m *Manager) GetPeginContractPrograms(claimScript []byte) ([]byte, error) {
909 pubkeys := getNewXpub(consensus.ActiveNetParams.FedpegXPubs, claimScript)
912 return nil, errors.New("Fedpeg's XPubs is empty")
918 for index := 0; index < num; index++ {
919 param := fmt.Sprintf("pubkey%d", index+1)
922 if (index + 1) < num {
927 params += ": PublicKey"
930 signNum := getNumberOfSignaturesRequired(pubkeys)
931 for index := 0; index < signNum; index++ {
932 param := fmt.Sprintf("sig%d", index+1)
933 unlockParams += param
935 if index+1 < signNum {
941 unlockParams += ": Signature"
944 lockWith2of3Keys := fmt.Sprintf(lockWith2of3KeysFmt, params, unlockParams, checkParams)
945 r := strings.NewReader(lockWith2of3Keys)
946 compiled, err := compiler.Compile(r)
948 return nil, errors.New("Compile contract failed")
951 contract := compiled[len(compiled)-1]
953 if num < len(contract.Params) {
954 return nil, errors.New("Compile contract failed")
957 contractArgs, err := convertArguments(contract, pubkeys)
959 fmt.Println("Convert arguments into contract parameters error:", err)
960 return nil, errors.New("Convert arguments into contract parameters error")
963 instantProg, err := instantiateContract(contract, contractArgs)
965 fmt.Println("Instantiate contract error:", err)
966 return nil, errors.New("Instantiate contract error")
969 return instantProg, nil
972 func getNewXpub(federationRedeemXPub []chainkd.XPub, claimScript []byte) []ed25519.PublicKey {
974 var pubkeys []ed25519.PublicKey
975 for _, xpub := range federationRedeemXPub {
976 // pub + scriptPubKey 生成一个随机数A
978 h := hmac.New(sha256.New, xpub[:])
980 tweak := h.Sum(tmp[:])
981 // pub + A 生成一个新的公钥pub_new
982 chaildXPub := xpub.Child(tweak)
983 pubkeys = append(pubkeys, chaildXPub.PublicKey())
988 func getNumberOfSignaturesRequired(pubkeys []ed25519.PublicKey) int {
989 return len(pubkeys)/2 + 1
992 // InstantiateContract instantiate contract parameters
993 func instantiateContract(contract *compiler.Contract, args []compiler.ContractArg) ([]byte, error) {
994 program, err := compiler.Instantiate(contract.Body, contract.Params, contract.Recursive, args)
1002 func convertArguments(contract *compiler.Contract, args []ed25519.PublicKey) ([]compiler.ContractArg, error) {
1003 var contractArgs []compiler.ContractArg
1004 for i, p := range contract.Params {
1005 var argument compiler.ContractArg
1008 argument.S = (*chainjson.HexBytes)(&args[i])
1010 return nil, errors.New("Contract parameter type error")
1012 contractArgs = append(contractArgs, argument)
1015 return contractArgs, nil