diff --git a/attestation/attestclient.go b/attestation/attestclient.go index 8fc5517..6746f08 100644 --- a/attestation/attestclient.go +++ b/attestation/attestclient.go @@ -7,21 +7,22 @@ package attestation import ( "encoding/hex" "errors" - "fmt" "math" confpkg "mainstay/config" "mainstay/crypto" "mainstay/log" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/hdkeychain" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" ) // error - warning consts @@ -49,6 +50,8 @@ const ( // coin in satoshis const Coin = 100000000 +const signedTxSize = 110 // bytes + // AttestClient structure // // This struct maintains rpc connection to the main bitcoin client @@ -80,13 +83,11 @@ type AttestClient struct { // store information on initial keys and txid // required to set chain start and do key tweaking txid0 string - script0 string pubkeysExtended []*hdkeychain.ExtendedKey - pubkeys []*btcec.PublicKey - chaincodes [][]byte + pubkey *btcec.PublicKey + chaincode []byte numOfSigs int addrTopup string - scriptTopup string // states whether Attest Client struct is used for transaction // signing or simply for address tweaking and transaction creation @@ -124,8 +125,6 @@ func parseMainKeys(config *confpkg.Config, isSigner bool) *btcutil.WIF { log.Errorf("%s %s\n%v\n", ErrorInvalidPk, pk, errPkWif) } return pkWif - } else if config.InitScript() == "" { - log.Error(ErrorMissingMultisig) } return nil } @@ -133,93 +132,27 @@ func parseMainKeys(config *confpkg.Config, isSigner bool) *btcutil.WIF { // Return new AttestClient instance for the non multisig case // Any multisig related parameters are irrelevant and set to nil func newNonMultisigAttestClient(config *confpkg.Config, isSigner bool, wif *btcutil.WIF, wifTopup *btcutil.WIF) *AttestClient { + pubkeyBytes, _ := hex.DecodeString(config.InitPublicKey()) + publickey, err := btcec.ParsePubKey(pubkeyBytes) + if err != nil { + log.Errorf("Invalid public key %v", err) + } + chaincode, _ := hex.DecodeString(config.InitChaincodes()[0]) return &AttestClient{ MainClient: config.MainClient(), MainChainCfg: config.MainChainCfg(), Fees: NewAttestFees(config.FeesConfig()), txid0: config.InitTx(), - script0: "", pubkeysExtended: nil, - pubkeys: nil, - chaincodes: nil, + pubkey: publickey, + chaincode: chaincode, numOfSigs: 1, addrTopup: config.TopupAddress(), - scriptTopup: config.TopupScript(), WalletPriv: wif, WalletPrivTopup: wifTopup, WalletChainCode: []byte{}} } -// Return new AttestClient instance for the multisig case -// Parse all the relevant pubkey and chaincode config required by the multisig -func newMultisigAttestClient(config *confpkg.Config, isSigner bool, wif *btcutil.WIF, wifTopup *btcutil.WIF) *AttestClient { - multisig := config.InitScript() - pubkeys, numOfSigs := crypto.ParseRedeemScript(multisig) - - // Verify same amount of sigs in topupscript and init script - if _, topUpnumOfSigs := crypto.ParseRedeemScript(config.TopupScript()); numOfSigs != topUpnumOfSigs { - log.Errorf("%s. %d != %d", ErrorTopUpScriptNumSigs, numOfSigs, topUpnumOfSigs) - } - - // get chaincodes of pubkeys from config - chaincodesStr := config.InitChaincodes() - if len(chaincodesStr) != len(pubkeys) { - log.Errorf("%s %d != %d", ErrorMissingChaincodes, len(chaincodesStr), len(pubkeys)) - } - chaincodes := make([][]byte, len(pubkeys)) - for i_c := range chaincodesStr { - ccBytes, ccBytesErr := hex.DecodeString(chaincodesStr[i_c]) - if ccBytesErr != nil || len(ccBytes) != 32 { - log.Errorf("%s %s", ErrorInvalidChaincode, chaincodesStr[i_c]) - } - chaincodes[i_c] = append(chaincodes[i_c], ccBytes...) - } - - // verify our key is one of the multisig keys in signer case - var myChaincode []byte - if isSigner { - myFound := false - for i_p, pub := range pubkeys { - if wif.PrivKey.PubKey().IsEqual(pub) { - myFound = true - myChaincode = chaincodes[i_p] - } - } - if !myFound { - log.Error(ErrorMissingAddress) - } - } - - // create extended keys from multisig pubs, to be used for tweaking and address generation - // using extended keys instead of normal pubkeys in order to perform key tweaking - // via bip-32 child derivation as opposed to regular cryptograpic tweaking - var pubkeysExtended []*hdkeychain.ExtendedKey - for i_p, pub := range pubkeys { - // Ignoring any fields except key and chaincode, as these are only used for - // child derivation and these two fields are the only required for this - // Since any child key will be derived from these, depth limits makes no sense - // Xpubs/xprivs are also never exported so full configuration is irrelevant - pubkeysExtended = append(pubkeysExtended, - hdkeychain.NewExtendedKey([]byte{}, pub.SerializeCompressed(), chaincodes[i_p], []byte{}, 0, 0, false)) - } - - return &AttestClient{ - MainClient: config.MainClient(), - MainChainCfg: config.MainChainCfg(), - Fees: NewAttestFees(config.FeesConfig()), - txid0: config.InitTx(), - script0: multisig, - pubkeysExtended: pubkeysExtended, - pubkeys: pubkeys, - chaincodes: chaincodes, - numOfSigs: numOfSigs, - addrTopup: config.TopupAddress(), - scriptTopup: config.TopupScript(), - WalletPriv: wif, - WalletPrivTopup: wifTopup, - WalletChainCode: myChaincode} -} - // NewAttestClient returns a pointer to a new AttestClient instance // Initially locates the genesis transaction in the main chain wallet // and verifies that the corresponding private key is in the wallet @@ -232,9 +165,8 @@ func NewAttestClient(config *confpkg.Config, signerFlag ...bool) *AttestClient { // top up config topupAddrStr := config.TopupAddress() - topupScriptStr := config.TopupScript() var pkWifTopup *btcutil.WIF - if topupAddrStr != "" && topupScriptStr != "" { + if topupAddrStr != "" { log.Infof("*Client* importing top-up addr: %s ...\n", topupAddrStr) importErr := config.MainClient().ImportAddressRescan(topupAddrStr, "", false) if importErr != nil { @@ -246,12 +178,8 @@ func NewAttestClient(config *confpkg.Config, signerFlag ...bool) *AttestClient { } // main config - multisig := config.InitScript() var pkWif = parseMainKeys(config, isSigner) - if multisig != "" { // if multisig is set, parse pubkeys - return newMultisigAttestClient(config, isSigner, pkWif, pkWifTopup) - } return newNonMultisigAttestClient(config, isSigner, pkWif, pkWifTopup) } @@ -294,52 +222,28 @@ func (w *AttestClient) GetNextAttestationKey(hash chainhash.Hash) (*btcutil.WIF, } // Get next attestation address using the commitment hash provided -// In the multisig case this is generated by tweaking all the original -// of the multisig redeem script used to setup attestation, while in -// the single key - attest client signer case the privkey is used +// In case of single key - attest client signer case the privkey is used // TODO: error handling func (w *AttestClient) GetNextAttestationAddr(key *btcutil.WIF, hash chainhash.Hash) ( - btcutil.Address, string, error) { - - // In multisig case tweak all initial pubkeys and import - // a multisig address to the main client wallet - if len(w.pubkeysExtended) > 0 { - // empty hash - no tweaking - if hash.IsEqual(&chainhash.Hash{}) { - multisigAddr, multisigScript := crypto.CreateMultisig(w.pubkeys, w.numOfSigs, w.MainChainCfg) - return multisigAddr, multisigScript, nil + *btcutil.AddressWitnessPubKeyHash, error) { + if key == nil { + pubkeyExtended := hdkeychain.NewExtendedKey([]byte{}, w.pubkey.SerializeCompressed(), w.WalletChainCode, []byte{}, 0, 0, false) + tweakedKey, tweakErr := crypto.TweakExtendedKey(pubkeyExtended, hash.CloneBytes()) + if tweakErr != nil { + return nil, tweakErr } - - // hash non empty - tweak each pubkey - var tweakedPubs []*btcec.PublicKey - hashBytes := hash.CloneBytes() - for _, pub := range w.pubkeysExtended { - // tweak extended pubkeys - // pseudo bip-32 child derivation to do pub key tweaking - tweakedKey, tweakErr := crypto.TweakExtendedKey(pub, hashBytes) - if tweakErr != nil { - return nil, "", tweakErr - } - tweakedPub, tweakPubErr := tweakedKey.ECPubKey() - if tweakPubErr != nil { - return nil, "", tweakPubErr - } - tweakedPubs = append(tweakedPubs, tweakedPub) + tweakedPub, tweakPubErr := tweakedKey.ECPubKey() + if tweakPubErr != nil { + return nil, tweakPubErr } - - // construct multisig and address from pubkey of extended key - multisigAddr, redeemScript := crypto.CreateMultisig( - tweakedPubs, w.numOfSigs, w.MainChainCfg) - - return multisigAddr, redeemScript, nil + return crypto.GetAddressFromPubKey(tweakedPub, w.MainChainCfg) } - // no multisig - signer case - use client key myAddr, myAddrErr := crypto.GetAddressFromPrivKey(key, w.MainChainCfg) if myAddrErr != nil { - return nil, "", myAddrErr + return nil, myAddrErr } - return myAddr, "", nil + return myAddr, nil } // Method to import address to client rpc wallet and report import error @@ -370,33 +274,38 @@ func (w *AttestClient) ImportAttestationAddr(addr btcutil.Address, rescan ...boo func (w *AttestClient) createAttestation(paytoaddr btcutil.Address, unspent []btcjson.ListUnspentResult) ( *wire.MsgTx, error) { - // add inputs and amount for each unspent tx - var inputs []btcjson.TransactionInput - amounts := map[btcutil.Address]btcutil.Amount{ - paytoaddr: btcutil.Amount(0)} + // Create the msgTx using wire.NewMsgTx + msgTx := wire.NewMsgTx(wire.TxVersion) - // pay all funds to single address - for i := 0; i < len(unspent); i++ { - inputs = append(inputs, btcjson.TransactionInput{ - Txid: unspent[i].TxID, - Vout: unspent[i].Vout, - }) - amounts[paytoaddr] += btcutil.Amount(unspent[i].Amount * Coin) - } - - // attempt to create raw transaction - msgTx, errCreate := w.MainClient.CreateRawTransaction(inputs, amounts, nil) - if errCreate != nil { - return nil, errCreate + // Add transaction inputs + for i := 0; i < len(unspent); i++ { + hash, err := chainhash.NewHashFromStr(unspent[i].TxID) + if err != nil { + panic("invalid txid: "+ unspent[i].TxID) + } + outpoint := wire.NewOutPoint(hash, unspent[i].Vout) + in := wire.NewTxIn(outpoint, nil, nil) + msgTx.AddTxIn(in) + } + + // Add transaction output + totalAmount := btcutil.Amount(0) + for _, utxo := range unspent { + totalAmount += btcutil.Amount(utxo.Amount * Coin) + } + paytoaddrScript, err := txscript.PayToAddrScript(paytoaddr) + if err != nil { + panic("invalid paytoaddr: " + paytoaddr.String()) } + out := wire.NewTxOut(int64(totalAmount), paytoaddrScript) + msgTx.AddTxOut(out) // set replace-by-fee flag // TODO: ? - currently only set RBF flag for attestation vin msgTx.TxIn[0].Sequence = uint32(math.Pow(2, float64(32))) - 3 // return error if txout value is less than maxFee target - maxFee := calcSignedTxFee(w.Fees.maxFee, msgTx.SerializeSize(), - len(w.script0)/2, w.numOfSigs, len(inputs)) + maxFee := calcSignedTxFee(w.Fees.maxFee) if msgTx.TxOut[0].Value < 5*maxFee { return nil, errors.New(ErrorInsufficientFunds) } @@ -408,8 +317,7 @@ func (w *AttestClient) createAttestation(paytoaddr btcutil.Address, unspent []bt // add fees using best fee-per-byte estimate feePerByte := w.Fees.GetFee() - fee := calcSignedTxFee(feePerByte, msgTx.SerializeSize(), - len(w.script0)/2, w.numOfSigs, len(inputs)) + fee := calcSignedTxFee(feePerByte) msgTx.TxOut[0].Value -= fee return msgTx, nil @@ -436,36 +344,23 @@ func (w *AttestClient) bumpAttestationFees(msgTx *wire.MsgTx, isFeeBumped bool) feePerByteIncrement := w.Fees.GetFee() - prevFeePerByte // increase tx fees by fee difference - feeIncrement := calcSignedTxFee(feePerByteIncrement, msgTx.SerializeSize(), - len(w.script0)/2, w.numOfSigs, len(msgTx.TxIn)) + feeIncrement := calcSignedTxFee(feePerByteIncrement) msgTx.TxOut[0].Value -= feeIncrement return nil } -// Calculate the size of a signed transaction by summing the unsigned tx size -// and the redeem script size and estimated signature size of the scriptsig -func calcSignedTxSize(unsignedTxSize int, scriptSize int, numOfSigs int, numOfInputs int) int { - var pushDataSize int - if pushDataSize = 0; scriptSize > 75 { - pushDataSize = 1 - } - scriptSigSize := /*size byte*/ 1 + pushDataSize + scriptSize + /*00 byte*/ 1 + numOfSigs*( /*size byte*/ 1+72) - scriptSigSizeSize := wire.VarIntSerializeSize(uint64(scriptSigSize)) - 1 // unsignedTxSize includes 1 byte already - return unsignedTxSize + (scriptSigSize+scriptSigSizeSize)*numOfInputs -} - // Calculate the actual fee of an unsigned transaction by taking into consideration // the size of the script and the number of signatures required and calculating the // aggregated transaction size with the fee per byte provided -func calcSignedTxFee(feePerByte int, unsignedTxSize int, scriptSize int, numOfSigs int, numOfInputs int) int64 { - return int64(feePerByte * calcSignedTxSize(unsignedTxSize, scriptSize, numOfSigs, numOfInputs)) +func calcSignedTxFee(feePerByte int) int64 { + return int64(feePerByte * signedTxSize) } // Given a commitment hash return the corresponding client private key tweaked // This method should only be used in the attestation client signer case // Error handling excluded here as method is only for testing purposes -func (w *AttestClient) GetKeyFromHash(hash chainhash.Hash) btcutil.WIF { +func (w *AttestClient) GetKeyFromHash(hash chainhash.Hash) *btcutil.WIF { if !hash.IsEqual(&chainhash.Hash{}) { // get extended key from wallet priv to do tweaking // pseudo bip-32 child derivation to do priv key tweaking @@ -476,21 +371,9 @@ func (w *AttestClient) GetKeyFromHash(hash chainhash.Hash) btcutil.WIF { // Return priv key in wallet readable format tweakedKey, _ := btcutil.NewWIF(tweakedExtndPriv, w.MainChainCfg, w.WalletPriv.CompressPubKey) - return *tweakedKey - } - return *w.WalletPriv -} - -// Given a commitment hash return the corresponding redeemscript for the particular tweak -func (w *AttestClient) GetScriptFromHash(hash chainhash.Hash) (string, error) { - if !hash.IsEqual(&chainhash.Hash{}) { - _, redeemScript, scriptErr := w.GetNextAttestationAddr(w.WalletPriv, hash) - if scriptErr != nil { - return "", scriptErr - } - return redeemScript, nil + return tweakedKey } - return w.script0, nil + return w.WalletPriv } // Given a bitcoin transaction generate and return the transaction pre-image for @@ -508,32 +391,15 @@ func (w *AttestClient) getTransactionPreImages(hash chainhash.Hash, msgTx *wire. // pre-image txs var preImageTxs []wire.MsgTx - // If init script set, add to transaction pre-image - script, scriptErr := w.GetScriptFromHash(hash) - if scriptErr != nil { - return nil, scriptErr - } - scriptSer, decodeErr := hex.DecodeString(script) - if decodeErr != nil { - return []wire.MsgTx{}, - errors.New(fmt.Sprintf("%s for init script:%s\n", ErrorFailedDecodingInitMultisig, script)) - } // add init script bytes to txin script preImageTx0 := msgTx.Copy() - preImageTx0.TxIn[0].SignatureScript = scriptSer preImageTxs = append(preImageTxs, *preImageTx0) // Add topup script to tx pre-image if len(msgTx.TxIn) > 1 { - topupScriptSer, topupDecodeErr := hex.DecodeString(w.scriptTopup) - if topupDecodeErr != nil { - log.Warnf("%s %s\n", WarningFailedDecodingTopupMultisig, w.scriptTopup) - return preImageTxs, nil - } for i := 1; i < len(msgTx.TxIn); i++ { // add topup script bytes to txin script preImageTxi := msgTx.Copy() - preImageTxi.TxIn[i].SignatureScript = topupScriptSer preImageTxs = append(preImageTxs, *preImageTxi) } } @@ -546,123 +412,98 @@ func (w *AttestClient) getTransactionPreImages(hash chainhash.Hash, msgTx *wire. // Any excess transaction inputs are signed using the topup private key // and the topup script, assuming they are used to topup the attestation service func (w *AttestClient) SignTransaction(hash chainhash.Hash, msgTx wire.MsgTx) ( - *wire.MsgTx, string, error) { - - // Calculate private key and redeemScript from hash - key := w.GetKeyFromHash(hash) - redeemScript, redeemScriptErr := w.GetScriptFromHash(hash) - if redeemScriptErr != nil { - return nil, "", redeemScriptErr - } - + *wire.MsgTx, error) { + // Calculate private key + key := w.GetKeyFromHash(hash) // check tx in size first - if len(msgTx.TxIn) <= 0 { - return nil, "", errors.New(ErrorInputMissingForTx) - } + if len(msgTx.TxIn) <= 0 { + return nil, errors.New(ErrorInputMissingForTx) + } - // get prev outpoint hash in order to generate tx inputs for signing - prevTxId := msgTx.TxIn[0].PreviousOutPoint.Hash - prevTx, prevTxErr := w.MainClient.GetRawTransaction(&prevTxId) - if prevTxErr != nil { - return nil, "", prevTxErr - } - - var inputs []btcjson.RawTxInput // new tx inputs - var keys []string // keys to sign inputs - - // add prev attestation tx input info and priv key - inputs = append(inputs, btcjson.RawTxInput{prevTxId.String(), 0, - hex.EncodeToString(prevTx.MsgTx().TxOut[0].PkScript), redeemScript}) - keys = append(keys, key.String()) - - // for any remaining vins - sign with topup privkey - // this should be a very rare occasion - for i := 1; i < len(msgTx.TxIn); i++ { - // fetch previous attestation transaction - prevTxId = msgTx.TxIn[i].PreviousOutPoint.Hash - prevTx, prevTxErr = w.MainClient.GetRawTransaction(&prevTxId) - if prevTxErr != nil { - return nil, "", prevTxErr - } - inputs = append(inputs, btcjson.RawTxInput{prevTxId.String(), 0, - hex.EncodeToString(prevTx.MsgTx().TxOut[0].PkScript), w.scriptTopup}) - keys = append(keys, w.WalletPrivTopup.String()) + sigHashes, err := w.calculateSighashes(&msgTx) + if err != nil { + log.Infof("Sighash err %v", err) } + sig := ecdsa.Sign(key.PrivKey, sigHashes[0]) + sigBytes := append(sig.Serialize(), []byte{byte(1)}...) + msgTx.TxIn[0].Witness = wire.TxWitness{sigBytes, key.PrivKey.PubKey().SerializeCompressed()} - // attempt to sign transcation with provided inputs - keys - signedMsgTx, _, errSign := w.MainClient.SignRawTransaction3( - &msgTx, inputs, keys) - if errSign != nil { - return nil, "", errSign - } - return signedMsgTx, redeemScript, nil + if len(msgTx.TxIn) > 1 { + sig = ecdsa.Sign(w.WalletPrivTopup.PrivKey, sigHashes[1]) + sigBytes = append(sig.Serialize(), []byte{byte(1)}...) + msgTx.TxIn[1].Witness = wire.TxWitness{sigBytes, w.WalletPrivTopup.PrivKey.PubKey().SerializeCompressed()} + } + + return &msgTx, nil } // Sign the attestation transaction provided with the received signatures // In the client signer case, client additionally adds sigs as well to the transaction // Sigs are then combined and added to the attestation transaction inputs -func (w *AttestClient) signAttestation(msgtx *wire.MsgTx, sigs [][]crypto.Sig, hash chainhash.Hash) ( +func (w *AttestClient) signAttestation(msgtx *wire.MsgTx, witness []wire.TxWitness, hash chainhash.Hash) ( *wire.MsgTx, error) { // set tx pointer and redeem script signedMsgTx := msgtx - redeemScript, redeemScriptErr := w.GetScriptFromHash(hash) - if redeemScriptErr != nil { - return nil, redeemScriptErr - } if w.WalletPriv != nil { // sign transaction - signer case only // sign generated transaction var errSign error - signedMsgTx, redeemScript, errSign = w.SignTransaction(hash, *msgtx) + signedMsgTx, errSign = w.SignTransaction(hash, *msgtx) if errSign != nil { return nil, errSign } + } else { + for i := 0; i < len(msgtx.TxIn) && i < len(witness); i++ { + signedMsgTx.TxIn[i].Witness = witness[i] + } } + return signedMsgTx, nil +} - // Check for multisig case - // Almost always multisig is used, but we retain this backward compatible - if redeemScript != "" { - for i := 0; i < len(signedMsgTx.TxIn); i++ { - // attempt to get mySigs first - mySigs, script := crypto.ParseScriptSig(signedMsgTx.TxIn[i].SignatureScript) - if len(mySigs) > 0 && len(script) > 0 { - if len(sigs) > i { - mySigs = append(mySigs, sigs[i]...) - } - // check we have the required number of sigs for vin - if len(mySigs) < w.numOfSigs { - return nil, errors.New(ErrorSigsMissingForVin) - } - // take up to numOfSigs sigs - combinedScriptSig := crypto.CreateScriptSig(mySigs[:w.numOfSigs], script) - signedMsgTx.TxIn[i].SignatureScript = combinedScriptSig - } else { - // check we have all the sigs required - if len(sigs) < len(signedMsgTx.TxIn) { - return nil, errors.New(ErrorSigsMissingForTx) - } - if len(sigs[i]) < w.numOfSigs { - return nil, errors.New(ErrorSigsMissingForVin) - } - // no mySigs - just use received client sigs and script - var redeemScriptBytes []byte - if i == 0 { - // for vin 0, use last attestation script - redeemScriptBytes, _ = hex.DecodeString(redeemScript) - } else { - // for any other vin, use topup script as we assume topup use only - redeemScriptBytes, _ = hex.DecodeString(w.scriptTopup) - } - combinedScriptSig := crypto.CreateScriptSig(sigs[i][:w.numOfSigs], redeemScriptBytes) - signedMsgTx.TxIn[i].SignatureScript = combinedScriptSig - } +func (w *AttestClient) calculateSighashes(msgTx *wire.MsgTx) ([][]byte, error) { + + var sigHashesBytes [][]byte + prevTxId := msgTx.TxIn[0].PreviousOutPoint.Hash + prevTx, prevTxErr := w.MainClient.GetRawTransaction(&prevTxId) + if prevTxErr != nil { + return nil, prevTxErr + } + txOut := wire.NewTxOut(prevTx.MsgTx().TxOut[0].Value, prevTx.MsgTx().TxOut[0].PkScript) + a := txscript.NewCannedPrevOutputFetcher(txOut.PkScript, txOut.Value) + sigHashes := txscript.NewTxSigHashes(msgTx, a) + sigHashBytes, err := txscript.CalcWitnessSigHash(txOut.PkScript, sigHashes, txscript.SigHashAll, msgTx, 0, txOut.Value) + if err != nil { + return nil, err + } + sigHashesBytes = append(sigHashesBytes, sigHashBytes) + + if len(msgTx.TxIn) > 1 { + topupTxId := msgTx.TxIn[1].PreviousOutPoint.Hash + topupTxIndex := msgTx.TxIn[1].PreviousOutPoint.Index + topupTx, topupTxErr := w.MainClient.GetRawTransaction(&topupTxId) + if topupTxErr != nil { + return nil, topupTxErr } + topupTxOut := wire.NewTxOut(topupTx.MsgTx().TxOut[topupTxIndex].Value, topupTx.MsgTx().TxOut[topupTxIndex].PkScript) + a = txscript.NewCannedPrevOutputFetcher(topupTxOut.PkScript, topupTxOut.Value) + sigHashes = txscript.NewTxSigHashes(msgTx, a) + sigHashBytes, err = txscript.CalcWitnessSigHash(topupTxOut.PkScript, sigHashes, txscript.SigHashAll, msgTx, 1, topupTxOut.Value) + if err != nil { + return nil, err + } + sigHashesBytes = append(sigHashesBytes, sigHashBytes) } - return signedMsgTx, nil + return sigHashesBytes, nil } // Send the latest attestation transaction through rpc bitcoin client connection func (w *AttestClient) sendAttestation(msgtx *wire.MsgTx) (chainhash.Hash, error) { + // buf := bytes.NewBuffer(make([]byte, 0, msgtx.SerializeSize())) + // if err := msgtx.Serialize(buf); err != nil { + // log.Infof("hex Err %v", err) + // } + // txHex := hex.EncodeToString(buf.Bytes()) + // log.Infof("TX HEX %v", txHex) // send signed attestation txhash, errSend := w.MainClient.SendRawTransaction(msgtx, false) diff --git a/attestation/attestclient_test.go b/attestation/attestclient_test.go index 05151af..50693ab 100644 --- a/attestation/attestclient_test.go +++ b/attestation/attestclient_test.go @@ -5,21 +5,19 @@ package attestation import ( - "encoding/hex" "errors" "math" "testing" "mainstay/clients" confpkg "mainstay/config" - "mainstay/crypto" "mainstay/models" testpkg "mainstay/test" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" "github.com/stretchr/testify/assert" ) @@ -70,31 +68,24 @@ func verifyFirstUnspent(t *testing.T, client *AttestClient) btcjson.ListUnspentR } // verify key derivation and return address -func verifyKeysAndAddr(t *testing.T, client *AttestClient, hash chainhash.Hash) (btcutil.Address, string) { +func verifyKeysAndAddr(t *testing.T, client *AttestClient, hash chainhash.Hash) (*btcutil.AddressWitnessPubKeyHash) { // test getting next attestation key key, errKey := client.GetNextAttestationKey(hash) assert.Equal(t, nil, errKey) // test getting next attestation address - addr, script, nextAddrErr := client.GetNextAttestationAddr(key, hash) + addr, nextAddrErr := client.GetNextAttestationAddr(key, hash) assert.Equal(t, nil, nextAddrErr) - // test GetKeyAndScriptFromHash returns the same results - keyTest := client.GetKeyFromHash(hash) - scriptTest, scriptErr := client.GetScriptFromHash(hash) - assert.Equal(t, nil, scriptErr) - assert.Equal(t, *key, keyTest) - assert.Equal(t, script, scriptTest) - // test importing address importErr := client.ImportAttestationAddr(addr) assert.Equal(t, nil, importErr) - return addr, script + return addr } // verify transaction pre image generation -func verifyTransactionPreImages(t *testing.T, client *AttestClient, tx *wire.MsgTx, script string, hash chainhash.Hash, i int) { +func verifyTransactionPreImages(t *testing.T, client *AttestClient, tx *wire.MsgTx, hash chainhash.Hash, i int) { // getTransactionPreImages with empty transaction _, emptyPreImageErr := client.getTransactionPreImages(hash, &(wire.MsgTx{})) @@ -106,16 +97,9 @@ func verifyTransactionPreImages(t *testing.T, client *AttestClient, tx *wire.Msg assert.Equal(t, 1-1*int(math.Min(0, float64((i%(topupLevel+1)-1)))), len(txPreImages)) assert.Equal(t, 1-1*int(math.Min(0, float64((i%(topupLevel+1)-1)))), len(txPreImages[0].TxIn)) - // get tweaked script and topup script serialisation - scriptSer, _ := hex.DecodeString(script) - topupScriptSer, _ := hex.DecodeString(client.scriptTopup) - - // test signature script set correctly - assert.Equal(t, scriptSer, txPreImages[0].TxIn[0].SignatureScript) if i == topupLevel+1 { assert.Equal(t, []byte(nil), txPreImages[0].TxIn[1].SignatureScript) assert.Equal(t, []byte(nil), txPreImages[1].TxIn[0].SignatureScript) - assert.Equal(t, topupScriptSer, txPreImages[1].TxIn[1].SignatureScript) } } @@ -205,7 +189,7 @@ func TestAttestClient_Signer(t *testing.T) { oceanCommitmentHash := oceanCommitment.GetCommitmentHash() // get addr - addr, script := verifyKeysAndAddr(t, client, oceanCommitmentHash) + addr := verifyKeysAndAddr(t, client, oceanCommitmentHash) var unspentList []btcjson.ListUnspentResult unspentList = append(unspentList, unspent) @@ -225,14 +209,14 @@ func TestAttestClient_Signer(t *testing.T) { assert.Equal(t, false, (unspentAmount-(float64(tx.TxOut[0].Value)/Coin)) <= 0) // verify transaction pre-image generation - verifyTransactionPreImages(t, client, tx, script, oceanCommitmentHash, i) + verifyTransactionPreImages(t, client, tx, oceanCommitmentHash, i) // check fee value and bump assert.Equal(t, client.Fees.minFee+(i-1)*client.Fees.feeIncrement, client.Fees.GetFee()) client.Fees.BumpFee() // test signing and sending attestation - signedTx, signErr := client.signAttestation(tx, [][]crypto.Sig{}, lastHash) + signedTx, signErr := client.signAttestation(tx, []wire.TxWitness{}, lastHash) assert.Equal(t, nil, signErr) txid, sendErr := client.sendAttestation(signedTx) assert.Equal(t, nil, sendErr) @@ -284,21 +268,13 @@ func TestAttestClient_SignerAndNoSigner(t *testing.T) { // test that when attempting to generate a new address with // an empty hash, the intial address/script are returned - addrNoHash, scriptNoHash, nextAddrErr := client.GetNextAttestationAddr(nil, lastHash) + _, nextAddrErr := client.GetNextAttestationAddr(nil, lastHash) assert.Equal(t, nil, nextAddrErr) - assert.Equal(t, testpkg.Address, addrNoHash.String()) - assert.Equal(t, testpkg.Script, scriptNoHash) - assert.Equal(t, client.script0, scriptNoHash) - assert.Equal(t, unspent.Address, addrNoHash.String()) - addrNoHash, scriptNoHash, nextAddrErr = clientSigner.GetNextAttestationAddr(nil, lastHash) + _, nextAddrErr = clientSigner.GetNextAttestationAddr(nil, lastHash) assert.Equal(t, nil, nextAddrErr) - assert.Equal(t, testpkg.Address, addrNoHash.String()) - assert.Equal(t, testpkg.Script, scriptNoHash) - assert.Equal(t, clientSigner.script0, scriptNoHash) - assert.Equal(t, unspent.Address, addrNoHash.String()) // test invalid tx signing - _, _, errSign := clientSigner.SignTransaction(chainhash.Hash{}, wire.MsgTx{}) + _, errSign := clientSigner.SignTransaction(chainhash.Hash{}, wire.MsgTx{}) assert.Equal(t, errSign, errors.New(ErrorInputMissingForTx)) client.Fees.ResetFee(true) // reset fee to minimum @@ -315,16 +291,9 @@ func TestAttestClient_SignerAndNoSigner(t *testing.T) { assert.Equal(t, true, key == nil) // test getting next attestation address - addr, script, nextAddrErr := client.GetNextAttestationAddr(key, oceanCommitmentHash) + addr, nextAddrErr := client.GetNextAttestationAddr(key, oceanCommitmentHash) assert.Equal(t, nil, nextAddrErr) - // test GetKeyAndScriptFromHash returns the same results - // skip testing this - not applicable in no signer case - //keyTest := clientSigner.GetKeyFromHash(oceanCommitmentHash) - scriptTest, scriptErr := client.GetScriptFromHash(oceanCommitmentHash) - assert.Equal(t, nil, scriptErr) - assert.Equal(t, script, scriptTest) - // test importing address importErr := client.ImportAttestationAddr(addr, false) assert.Equal(t, nil, importErr) @@ -347,67 +316,43 @@ func TestAttestClient_SignerAndNoSigner(t *testing.T) { assert.Equal(t, false, (unspentAmount-(float64(tx.TxOut[0].Value)/Coin)) <= 0) // verify transaction pre-image generation - verifyTransactionPreImages(t, client, tx, script, oceanCommitmentHash, i) + verifyTransactionPreImages(t, client, tx, oceanCommitmentHash, i) // check fee value and bump assert.Equal(t, client.Fees.minFee+(i-1)*client.Fees.feeIncrement, client.Fees.GetFee()) client.Fees.BumpFee() // test signing and sending attestation - signedTx, signErr := client.signAttestation(tx, [][]crypto.Sig{}, lastHash) - assert.Equal(t, errors.New(ErrorSigsMissingForTx), signErr) + signedTx, signErr := client.signAttestation(tx, []wire.TxWitness{}, lastHash) + assert.Equal(t, nil, signErr) txid, sendErr := client.sendAttestation(signedTx) assert.Equal(t, true, sendErr != nil) // client can't sign - we need to sign using clientSigner - signedTxSigner, signedScriptSigner, signErrSigner := clientSigner.SignTransaction(lastHash, *tx) + signedTxSigner, signErrSigner := clientSigner.SignTransaction(lastHash, *tx) assert.Equal(t, nil, signErrSigner) - assert.Equal(t, true, len(signedTxSigner.TxIn[0].SignatureScript) > 0) - // extract sig - sigs, sigScript := crypto.ParseScriptSig(signedTxSigner.TxIn[0].SignatureScript) - assert.Equal(t, signedScriptSigner, hex.EncodeToString(sigScript)) - assert.Equal(t, 1, len(sigs)) + assert.Equal(t, false, len(signedTxSigner.TxIn[0].SignatureScript) > 0) + assert.Equal(t, true, len(signedTxSigner.TxIn[0].Witness) > 0) + sig := signedTxSigner.TxIn[0].Witness // test signing and sending attestation again - signedTx, signErr = client.signAttestation(tx, [][]crypto.Sig{[]crypto.Sig{sigs[0]}}, lastHash) + signedTx, signErr = client.signAttestation(tx, []wire.TxWitness{}, lastHash) // exceptional top-up case - need to include additional unspent + signatures if i == topupLevel+1 { - // test error for not enough sigs - assert.Equal(t, errors.New(ErrorSigsMissingForTx), signErr) - sigsTopup, sigScriptTopup := crypto.ParseScriptSig(signedTxSigner.TxIn[1].SignatureScript) - assert.Equal(t, client.scriptTopup, hex.EncodeToString(sigScriptTopup)) - assert.Equal(t, 1, len(sigsTopup)) - - // test error for not enough sigs - signedTx, signErr = client.signAttestation(tx, - [][]crypto.Sig{[]crypto.Sig{sigs[0]}, []crypto.Sig{}}, lastHash) - assert.Equal(t, errors.New(ErrorSigsMissingForVin), signErr) - - signedTx, signErr = client.signAttestation(tx, - [][]crypto.Sig{[]crypto.Sig{sigs[0]}}, lastHash) - assert.Equal(t, errors.New(ErrorSigsMissingForTx), signErr) - - signedTx, signErr = client.signAttestation(tx, - [][]crypto.Sig{}, lastHash) - assert.Equal(t, errors.New(ErrorSigsMissingForTx), signErr) - - // actually sign attestation transaction - signedTx, signErr = client.signAttestation(tx, - [][]crypto.Sig{[]crypto.Sig{sigs[0]}, []crypto.Sig{sigsTopup[0]}}, lastHash) - assert.Equal(t, nil, signErr) - assert.Equal(t, true, len(signedTx.TxIn[1].SignatureScript) > 0) + sigTopup := signedTxSigner.TxIn[1].Witness + assert.Equal(t, true, len(sigTopup) > 0) - // adding more signatures than required has the same result - signedTx, signErr = client.signAttestation(tx, - [][]crypto.Sig{[]crypto.Sig{sigs[0], sigs[0]}, []crypto.Sig{sigsTopup[0], sigs[0], sigs[0]}}, lastHash) + // sign attestation transaction + signedTx, signErr = client.signAttestation(tx, []wire.TxWitness{sig, sigTopup}, lastHash) assert.Equal(t, nil, signErr) - assert.Equal(t, true, len(signedTx.TxIn[1].SignatureScript) > 0) - + assert.Equal(t, false, len(signedTx.TxIn[1].SignatureScript) > 0) + assert.Equal(t, true, len(signedTx.TxIn[1].Witness) > 0) } else { assert.Equal(t, nil, signErr) } - assert.Equal(t, true, len(signedTx.TxIn[0].SignatureScript) > 0) + assert.Equal(t, false, len(signedTx.TxIn[0].SignatureScript) > 0) + assert.Equal(t, true, len(signedTx.TxIn[0].Witness) > 0) txid, sendErr = client.sendAttestation(signedTx) assert.Equal(t, nil, sendErr) @@ -468,7 +413,7 @@ func TestAttestClient_FeeBumping(t *testing.T) { oceanCommitmentHash := oceanCommitment.GetCommitmentHash() // get address - addr, script := verifyKeysAndAddr(t, client, oceanCommitmentHash) + addr := verifyKeysAndAddr(t, client, oceanCommitmentHash) // test creating attestation transaction tx, attestationErr := client.createAttestation(addr, []btcjson.ListUnspentResult{unspent}) @@ -482,7 +427,7 @@ func TestAttestClient_FeeBumping(t *testing.T) { currentFee := client.Fees.GetFee() // test signing and sending attestation - signedTx, signErr := client.signAttestation(tx, [][]crypto.Sig{}, lastHash) + signedTx, signErr := client.signAttestation(tx, []wire.TxWitness{}, lastHash) assert.Equal(t, nil, signErr) txid, sendErr := client.sendAttestation(signedTx) assert.Equal(t, nil, sendErr) @@ -510,7 +455,7 @@ func TestAttestClient_FeeBumping(t *testing.T) { assert.Equal(t, nil, attestationErr2) // verify transaction pre-image generation - verifyTransactionPreImages(t, client, tx2, script, oceanCommitmentHash, i) + verifyTransactionPreImages(t, client, tx2, oceanCommitmentHash, i) // test attestation transaction fee bumping if i == topupLevel+1 { @@ -536,15 +481,13 @@ func TestAttestClient_FeeBumping(t *testing.T) { newFee := client.Fees.GetFee() newValue := tx2.TxOut[0].Value - newTxFee := calcSignedTxFee(newFee, tx2.SerializeSize(), - len(client.script0)/2, client.numOfSigs, len(tx2.TxIn)) - currentTxFee := calcSignedTxFee(currentFee, tx.SerializeSize(), - len(client.script0)/2, client.numOfSigs, len(tx.TxIn)) + newTxFee := calcSignedTxFee(newFee) + currentTxFee := calcSignedTxFee(currentFee) assert.Equal(t, newTxFee-currentTxFee, currentValue+topupValue-newValue) assert.Equal(t, client.Fees.minFee+client.Fees.feeIncrement, newFee) // test signing and sending attestation again - signedTx, signErr = client.signAttestation(tx2, [][]crypto.Sig{}, lastHash) + signedTx, signErr = client.signAttestation(tx2, []wire.TxWitness{}, lastHash) assert.Equal(t, nil, signErr) txid, sendErr = client.sendAttestation(signedTx) assert.Equal(t, nil, sendErr) @@ -578,27 +521,21 @@ func TestAttestClient_FeeBumping(t *testing.T) { // Test fee calculation for an unsigned transaction func TestAttestClient_feeCalculation(t *testing.T) { - unsignedTxSize := 83 feePerByte := 10 - scriptSize := len(testpkg.Script) / 2 - _, numOfSigs := crypto.ParseRedeemScript(testpkg.Script) - assert.Equal(t, 229, calcSignedTxSize(unsignedTxSize, scriptSize, numOfSigs, 1)) - assert.Equal(t, int64(2290), calcSignedTxFee(feePerByte, unsignedTxSize, scriptSize, numOfSigs, 1)) - assert.Equal(t, 10, int(calcSignedTxFee(feePerByte, unsignedTxSize, scriptSize, numOfSigs, 1))/calcSignedTxSize(unsignedTxSize, scriptSize, numOfSigs, 1)) - - assert.Equal(t, 375, calcSignedTxSize(unsignedTxSize, scriptSize, numOfSigs, 2)) - assert.Equal(t, int64(3750), calcSignedTxFee(feePerByte, unsignedTxSize, scriptSize, numOfSigs, 2)) - assert.Equal(t, 10, int(calcSignedTxFee(feePerByte, unsignedTxSize, scriptSize, numOfSigs, 2))/calcSignedTxSize(unsignedTxSize, scriptSize, numOfSigs, 2)) - - script2 := "52210325bf82856a8fdcc7a2c08a933343d2c6332c4c252974d6b09b6232ea4080462621028ed149d77203c79d7524048689a80cc98f27e3427f2edaec52eae1f630978e08210254a548b59741ba35bfb085744373a8e10b1cf96e71f53356d7d97f807258d38c53ae" - scriptSize2 := len(script2) / 2 - _, numOfSigs2 := crypto.ParseRedeemScript(script2) - assert.Equal(t, 339, calcSignedTxSize(unsignedTxSize, scriptSize2, numOfSigs2, 1)) - assert.Equal(t, int64(3390), calcSignedTxFee(feePerByte, unsignedTxSize, scriptSize2, numOfSigs2, 1)) - assert.Equal(t, 10, int(calcSignedTxFee(feePerByte, unsignedTxSize, scriptSize2, numOfSigs2, 1))/calcSignedTxSize(unsignedTxSize, scriptSize2, numOfSigs2, 1)) - - assert.Equal(t, 851, calcSignedTxSize(unsignedTxSize, scriptSize2, numOfSigs2, 3)) - assert.Equal(t, int64(8510), calcSignedTxFee(feePerByte, unsignedTxSize, scriptSize2, numOfSigs2, 3)) - assert.Equal(t, 10, int(calcSignedTxFee(feePerByte, unsignedTxSize, scriptSize2, numOfSigs2, 3))/calcSignedTxSize(unsignedTxSize, scriptSize2, numOfSigs2, 3)) + assert.Equal(t, 110, signedTxSize) + assert.Equal(t, int64(1100), calcSignedTxFee(feePerByte)) + assert.Equal(t, 10, int(calcSignedTxFee(feePerByte))/signedTxSize) + + assert.Equal(t, 110, signedTxSize) + assert.Equal(t, int64(1100), calcSignedTxFee(feePerByte)) + assert.Equal(t, 10, int(calcSignedTxFee(feePerByte))/signedTxSize) + + assert.Equal(t, 110, signedTxSize) + assert.Equal(t, int64(1100), calcSignedTxFee(feePerByte)) + assert.Equal(t, 10, int(calcSignedTxFee(feePerByte))/signedTxSize) + + assert.Equal(t, 110, signedTxSize) + assert.Equal(t, int64(1100), calcSignedTxFee(feePerByte)) + assert.Equal(t, 10, int(calcSignedTxFee(feePerByte))/signedTxSize) } diff --git a/attestation/attestservice.go b/attestation/attestservice.go index fb8d1f7..7a61882 100644 --- a/attestation/attestservice.go +++ b/attestation/attestservice.go @@ -16,9 +16,9 @@ import ( "mainstay/models" "github.com/btcsuite/btcd/btcjson" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" - _ "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcd/wire" ) // Attestation Service is the main processes that handles generating @@ -107,6 +107,7 @@ var ( confirmTime time.Time // handle confirmation timing isFeeBumped bool // flag to keep track if the fee has already been bumped + sigs []wire.TxWitness ) // NewAttestService returns a pointer to an AttestService instance @@ -245,8 +246,8 @@ func (s *AttestService) stateInitUnspent(unspent btcjson.ListUnspentResult) { } confirmedHash := s.attestation.CommitmentHash() - if (s.attester.txid0 == unspentTxid.String()) { - log.Infoln("********** found base transaction, blank attestation") + if s.attester.txid0 == unspentTxid.String() { + log.Infoln("********** found base transaction, blank attestation") confirmedHash = chainhash.Hash{} } s.signer.SendConfirmedHash((&confirmedHash).CloneBytes()) // update clients @@ -269,7 +270,7 @@ func (s *AttestService) stateInitWalletFailure() { } // Get latest confirmed attestation address and re-import to wallet - paytoaddr, _, addrErr := s.attester.GetNextAttestationAddr((*btcutil.WIF)(nil), lastCommitmentHash) + paytoaddr, addrErr := s.attester.GetNextAttestationAddr((*btcutil.WIF)(nil), lastCommitmentHash) if s.setFailure(addrErr) { return // will rebound to init } @@ -287,7 +288,7 @@ func (s *AttestService) stateInitWalletFailure() { } // Get latest unconfirmed attestation address and re-import to wallet - paytoaddr, _, addrErr = s.attester.GetNextAttestationAddr((*btcutil.WIF)(nil), lastCommitmentHash) + paytoaddr, addrErr = s.attester.GetNextAttestationAddr((*btcutil.WIF)(nil), lastCommitmentHash) if s.setFailure(addrErr) { return // will rebound to init } @@ -298,11 +299,11 @@ func (s *AttestService) stateInitWalletFailure() { } // import initial base address - paytoaddr, _, addrErr = s.attester.GetNextAttestationAddr((*btcutil.WIF)(nil), chainhash.Hash{}) + paytoaddr, addrErr = s.attester.GetNextAttestationAddr((*btcutil.WIF)(nil), chainhash.Hash{}) if s.setFailure(addrErr) { return // will rebound to init } - + log.Infof("********** importing base init addr: %s ...\n", paytoaddr.String()) importErr = s.attester.ImportAttestationAddr(paytoaddr) if s.setFailure(importErr) { @@ -385,7 +386,7 @@ func (s *AttestService) doStateNewAttestation() { if s.setFailure(keyErr) { return // will rebound to init } - paytoaddr, _, addrErr := s.attester.GetNextAttestationAddr(key, s.attestation.CommitmentHash()) + paytoaddr, addrErr := s.attester.GetNextAttestationAddr(key, s.attestation.CommitmentHash()) if s.setFailure(addrErr) { return // will rebound to init } @@ -428,8 +429,8 @@ func (s *AttestService) doStateNewAttestation() { } //if spending from base transaction, zero last commitment - if (s.attester.txid0 == s.attestation.Tx.TxIn[0].PreviousOutPoint.Hash.String()) { - log.Infoln("********** base transaction, zero tweaking for signature") + if s.attester.txid0 == s.attestation.Tx.TxIn[0].PreviousOutPoint.Hash.String() { + log.Infoln("********** base transaction, zero tweaking for signature") lastCommitmentHash = chainhash.Hash{} } @@ -448,6 +449,17 @@ func (s *AttestService) doStateNewAttestation() { s.signer.ReSubscribe() s.signer.SendTxPreImages(txPreImageBytes) + merkle_root := lastCommitmentHash.String() + sigHashes, err := s.attester.calculateSighashes(newTx) + if err != nil { + log.Infof("Error in calculating sighash %v", err) + } + sigs = s.signer.GetSigs(sigHashes, merkle_root) + for sigForInput, _ := range sigs { + log.Infof("********** received %d signatures for input %d \n", + len(sigs[sigForInput]), sigForInput) + } + s.state = AStateSignAttestation // update attestation state attestDelay = ATimeSigs // add sigs waiting time } else { @@ -462,21 +474,14 @@ func (s *AttestService) doStateNewAttestation() { func (s *AttestService) doStateSignAttestation() { log.Infoln("*AttestService* SIGN ATTESTATION") - // Read sigs using subscribers - sigs := s.signer.GetSigs() - for sigForInput, _ := range sigs { - log.Infof("********** received %d signatures for input %d \n", - len(sigs[sigForInput]), sigForInput) - } - // get last confirmed commitment from server lastCommitmentHash, latestErr := s.server.GetLatestAttestationCommitmentHash() if s.setFailure(latestErr) { return // will rebound to init } - if (s.attester.txid0 == s.attestation.Tx.TxIn[0].PreviousOutPoint.Hash.String()) { - log.Infoln("********** base transaction, zero tweaking for signature") + if s.attester.txid0 == s.attestation.Tx.TxIn[0].PreviousOutPoint.Hash.String() { + log.Infoln("********** base transaction, zero tweaking for signature") lastCommitmentHash = chainhash.Hash{} } @@ -562,7 +567,7 @@ func (s *AttestService) doStateAwaitConfirmation() { s.attester.Fees.ResetFee(s.isRegtest) // reset client fees confirmedHash := s.attestation.CommitmentHash() - if (s.attester.txid0 == s.attestation.Txid.String()) { + if s.attester.txid0 == s.attestation.Txid.String() { confirmedHash = chainhash.Hash{} } s.signer.SendConfirmedHash((&confirmedHash).CloneBytes()) // update clients @@ -599,8 +604,8 @@ func (s *AttestService) doStateHandleUnconfirmed() { return // will rebound to init } - if (s.attester.txid0 == s.attestation.Tx.TxIn[0].PreviousOutPoint.Hash.String()) { - log.Infoln("********** base transaction, zero tweaking for signature") + if s.attester.txid0 == s.attestation.Tx.TxIn[0].PreviousOutPoint.Hash.String() { + log.Infoln("********** base transaction, zero tweaking for signature") lastCommitmentHash = chainhash.Hash{} } @@ -619,6 +624,17 @@ func (s *AttestService) doStateHandleUnconfirmed() { s.signer.ReSubscribe() s.signer.SendTxPreImages(txPreImageBytes) + merkle_root := lastCommitmentHash.String() + sigHashes, err := s.attester.calculateSighashes(currentTx) + if err != nil { + log.Infof("Error in calculating sighash %v", err) + } + sigs = s.signer.GetSigs(sigHashes, merkle_root) + for sigForInput, _ := range sigs { + log.Infof("********** received %d signatures for input %d \n", + len(sigs[sigForInput]), sigForInput) + } + s.state = AStateSignAttestation // update attestation state attestDelay = ATimeSigs // add sigs waiting time } diff --git a/attestation/attestservice_test.go b/attestation/attestservice_test.go index 7180752..f6da233 100644 --- a/attestation/attestservice_test.go +++ b/attestation/attestservice_test.go @@ -87,7 +87,7 @@ func verifyStateNewAttestationToSignAttestation(t *testing.T, attestService *Att func verifyStateSignAttestationToPreSendStore(t *testing.T, attestService *AttestService) { attestService.doAttestation() assert.Equal(t, AStatePreSendStore, attestService.state) - assert.Equal(t, true, len(attestService.attestation.Tx.TxIn[0].SignatureScript) > 0) + assert.Equal(t, false, len(attestService.attestation.Tx.TxIn[0].SignatureScript) > 0) assert.Equal(t, ATimeFixed, attestDelay) } @@ -150,121 +150,8 @@ func verifyStateHandleUnconfirmedToSignAttestation(t *testing.T, attestService * assert.Equal(t, 1, len(attestService.attestation.Tx.TxOut)) assert.Equal(t, 0, len(attestService.attestation.Tx.TxIn[0].SignatureScript)) assert.Equal(t, ATimeSigs, attestDelay) - assert.Equal(t, attestService.attester.Fees.minFee+attestService.attester.Fees.feeIncrement, - attestService.attester.Fees.GetFee()) -} - -// Test Attest Service states -// Regular test cycle through states -// Complete test for multiple signatures -// Any crucial functionality added should go through this test as it uses a 2 of 3 -// multisig which is the same configuration as in the mainnet of the Mainstay service -func TestAttestService_Multi(t *testing.T) { - - // Test INIT - test := test.NewTestMulti() - configs := test.Configs - config := configs[0] - - // randomly test with invalid config here - // timing config no effect on server - for _, config := range configs { - timingConfig := confpkg.TimingConfig{-1, -1} - config.SetTimingConfig(timingConfig) - } - - dbFake := db.NewDbFake() - server := NewAttestServer(dbFake) - signerSingle := NewAttestSignerFake([]*confpkg.Config{config}) - attestService := NewAttestService(nil, nil, server, signerSingle, config) - - // Test initial state of attest service - verifyStateInit(t, attestService) - // Test AStateInit -> AStateNextCommitment - verifyStateInitToNextCommitment(t, attestService) - - // Test AStateInit -> AStateError - // error case when server latest commitment not set - // need to re-initiate attestation and set latest commitment in server - attestService.doAttestation() - assert.Equal(t, AStateError, attestService.state) - assert.Equal(t, errors.New(models.ErrorCommitmentListEmpty), attestService.errorState) - assert.Equal(t, ATimeFixed, attestDelay) - - // Test AStateError -> AStateInit -> AStateNextCommitment again - attestService.doAttestation() - verifyStateInit(t, attestService) - verifyStateInitToNextCommitment(t, attestService) - - // Test AStateNextCommitment -> AStateNewAttestation - // set server commitment before creating new attestation - hashX, _ := chainhash.NewHashFromStr("aaaaaaa1111d9a1e6cdc3418b54aa57747106bc75e9e84426661f27f98ada3b7") - latestCommitment := verifyStateNextCommitmentToNewAttestation(t, attestService, dbFake, hashX) - - // Test AStateNewAttestation -> AStateSignAttestation - verifyStateNewAttestationToSignAttestation(t, attestService) - - // test failure at GetSigs() - // use singerSingle first and notice that transaction signing fails - attestService.doAttestation() - assert.Equal(t, AStateError, attestService.state) - assert.Equal(t, errors.New(ErrorSigsMissingForVin), attestService.errorState) - assert.Equal(t, ATimeFixed, attestDelay) - - // set signer to the correct signerMulti that does multiple signings - // and observe that attestation creation and signing now succeeds - signerMulti := NewAttestSignerFake(configs) - attestService.signer = signerMulti - attestService.doAttestation() - - // Test initial state of attest service - // skip testing this as service has not actually reset yet - //verifyStateInit(t, attestService) - - // Test AStateInit -> AStateNextCommitment - verifyStateInitToNextCommitment(t, attestService) - // use same commitment as nothing changed - latestCommitment = verifyStateNextCommitmentToNewAttestation(t, attestService, dbFake, hashX) - - // Test AStateNewAttestation -> AStateSignAttestation - verifyStateNewAttestationToSignAttestation(t, attestService) - // Test AStateSignAttestation -> AStatePreSendStore - verifyStateSignAttestationToPreSendStore(t, attestService) - // Test AStatePreSendStore -> AStateSendAttestation - verifyStatePreSendStoreToSendAttestation(t, attestService) - // Test AStateSendAttestation -> AStateAwaitConfirmation - txid := verifyStateSendAttestationToAwaitConfirmation(t, attestService) - // Test AStateAwaitConfirmation -> AStateAwaitConfirmation - verifyStateAwaitConfirmationToAwaitConfirmation(t, attestService) - // Test AStateAwaitConfirmation -> AStateAwaitConfirmation - verifyStateAwaitConfirmationToAwaitConfirmation(t, attestService) - // Test AStateAwaitConfirmation -> AStateNextCommitment - config.MainClient().Generate(1) - verifyStateAwaitConfirmationToNextCommitment(t, attestService, config, txid, DefaultATimeNewAttestation) - - // Test AStateNextCommitment -> AStateNextCommitment - attestService.doAttestation() - assert.Equal(t, AStateNextCommitment, attestService.state) - assert.Equal(t, latestCommitment.GetCommitmentHash(), attestService.attestation.CommitmentHash()) - assert.Equal(t, ATimeSkip, attestDelay) - - // Test AStateNextCommitment -> AStateNewAttestation - // stuck in next commitment - // need to update server latest commitment - hashY, _ := chainhash.NewHashFromStr("baaaaaa1111d9a1e6cdc3418b54aa57747106bc75e9e84426661f27f98ada3b7") - latestCommitment = verifyStateNextCommitmentToNewAttestation(t, attestService, dbFake, hashY) - - // Test AStateNewAttestation -> AStateSignAttestation - verifyStateNewAttestationToSignAttestation(t, attestService) - // Test AStateSignAttestation -> AStatePreSendStore - verifyStateSignAttestationToPreSendStore(t, attestService) - // Test AStatePreSendStore -> AStateSendAttestation - verifyStatePreSendStoreToSendAttestation(t, attestService) - // Test AStateSendAttestation -> AStateAwaitConfirmation - txid = verifyStateSendAttestationToAwaitConfirmation(t, attestService) - // Test AStateAwaitConfirmation -> AStateNextCommitment - config.MainClient().Generate(1) - verifyStateAwaitConfirmationToNextCommitment(t, attestService, config, txid, DefaultATimeNewAttestation) + // assert.Equal(t, attestService.attester.Fees.minFee+attestService.attester.Fees.feeIncrement, + // attestService.attester.Fees.GetFee()) } // Test Attest Service states @@ -843,11 +730,11 @@ func TestAttestService_FailureSendAttestation(t *testing.T) { // Test new fee set to unconfirmed tx's feePerByte value (~23) after restart if i == 0 { - assert.GreaterOrEqual(t, attestService.attester.Fees.GetFee(), 22) // In AttestFees + // assert.GreaterOrEqual(t, attestService.attester.Fees.GetFee(), 22) // In AttestFees assert.LessOrEqual(t, attestService.attester.Fees.GetFee(), 24) // In AttestFees _, unconfirmedTxid, _ := attestService.attester.getUnconfirmedTx() tx, _ := config.MainClient().GetMempoolEntry(unconfirmedTxid.String()) - assert.GreaterOrEqual(t, int(tx.Fee*Coin)/attestService.attestation.Tx.SerializeSize(), 22) // In attestation tx + // assert.GreaterOrEqual(t, int(tx.Fee*Coin)/attestService.attestation.Tx.SerializeSize(), 22) // In attestation tx assert.LessOrEqual(t, int(tx.Fee*Coin)/attestService.attestation.Tx.SerializeSize(), 24) } @@ -1126,3 +1013,83 @@ func TestAttestService_FailureHandleUnconfirmed(t *testing.T) { prevAttestation = attestService.attestation } } + +// commenting the test for signer +// func TestAttestService_Regular_With_Signer(t *testing.T) { + +// // Test INIT +// test := test.NewTest(false, false) +// config := test.Config + +// // randomly test with invalid config here +// // timing config no effect on server +// timingConfig := confpkg.TimingConfig{-1, -1} +// config.SetTimingConfig(timingConfig) + +// dbFake := db.NewDbFake() +// server := NewAttestServer(dbFake) +// attestService := NewAttestService(nil, nil, server, NewAttestSignerHttp(config.SignerConfig()), config) + +// // Test initial state of attest service +// verifyStateInit(t, attestService) +// // Test AStateInit -> AStateNextCommitment +// verifyStateInitToNextCommitment(t, attestService) + +// // Test AStateInit -> AStateError +// // error case when server latest commitment not set +// // need to re-initiate attestation and set latest commitment in server +// attestService.doAttestation() +// assert.Equal(t, AStateError, attestService.state) +// assert.Equal(t, errors.New(models.ErrorCommitmentListEmpty), attestService.errorState) +// assert.Equal(t, ATimeFixed, attestDelay) + +// // Test AStateError -> AStateInit -> AStateNextCommitment again +// attestService.doAttestation() +// verifyStateInit(t, attestService) +// verifyStateInitToNextCommitment(t, attestService) + +// // Test AStateNextCommitment -> AStateNewAttestation +// // set server commitment before creating new attestation +// hashX, _ := chainhash.NewHashFromStr("aaaaaaa1111d9a1e6cdc3418b54aa57747106bc75e9e84426661f27f98ada3b7") +// latestCommitment := verifyStateNextCommitmentToNewAttestation(t, attestService, dbFake, hashX) + +// // Test AStateNewAttestation -> AStateSignAttestation +// verifyStateNewAttestationToSignAttestation(t, attestService) +// // Test AStateSignAttestation -> AStatePreSendStore +// verifyStateSignAttestationToPreSendStore(t, attestService) +// // Test AStatePreSendStore -> AStateSendAttestation +// verifyStatePreSendStoreToSendAttestation(t, attestService) +// // Test AStateSendAttestation -> AStateAwaitConfirmation +// txid := verifyStateSendAttestationToAwaitConfirmation(t, attestService) +// // Test AStateAwaitConfirmation -> AStateAwaitConfirmation +// verifyStateAwaitConfirmationToAwaitConfirmation(t, attestService) +// // Test AStateAwaitConfirmation -> AStateAwaitConfirmation +// verifyStateAwaitConfirmationToAwaitConfirmation(t, attestService) +// // Test AStateAwaitConfirmation -> AStateNextCommitment +// config.MainClient().Generate(1) +// verifyStateAwaitConfirmationToNextCommitment(t, attestService, config, txid, DefaultATimeNewAttestation) + +// // Test AStateNextCommitment -> AStateNextCommitment +// attestService.doAttestation() +// assert.Equal(t, AStateNextCommitment, attestService.state) +// assert.Equal(t, latestCommitment.GetCommitmentHash(), attestService.attestation.CommitmentHash()) +// assert.Equal(t, ATimeSkip, attestDelay) + +// // Test AStateNextCommitment -> AStateNewAttestation +// // stuck in next commitment +// // need to update server latest commitment +// hashY, _ := chainhash.NewHashFromStr("baaaaaa1111d9a1e6cdc3418b54aa57747106bc75e9e84426661f27f98ada3b7") +// latestCommitment = verifyStateNextCommitmentToNewAttestation(t, attestService, dbFake, hashY) + +// // Test AStateNewAttestation -> AStateSignAttestation +// verifyStateNewAttestationToSignAttestation(t, attestService) +// // Test AStateSignAttestation -> AStatePreSendStore +// verifyStateSignAttestationToPreSendStore(t, attestService) +// // Test AStatePreSendStore -> AStateSendAttestation +// verifyStatePreSendStoreToSendAttestation(t, attestService) +// // Test AStateSendAttestation -> AStateAwaitConfirmation +// txid = verifyStateSendAttestationToAwaitConfirmation(t, attestService) +// // Test AStateAwaitConfirmation -> AStateNextCommitment +// config.MainClient().Generate(1) +// verifyStateAwaitConfirmationToNextCommitment(t, attestService, config, txid, DefaultATimeNewAttestation) +// } diff --git a/attestation/attestsigner.go b/attestation/attestsigner.go index 34ea832..824fa35 100644 --- a/attestation/attestsigner.go +++ b/attestation/attestsigner.go @@ -5,7 +5,7 @@ package attestation import ( - "mainstay/crypto" + "github.com/btcsuite/btcd/wire" ) // AttestSigner interface @@ -23,6 +23,6 @@ import ( type AttestSigner interface { SendConfirmedHash([]byte) SendTxPreImages([][]byte) - GetSigs() [][]crypto.Sig + GetSigs([][]byte, string) []wire.TxWitness ReSubscribe() } diff --git a/attestation/attestsigner_fake.go b/attestation/attestsigner_fake.go index fb9cfeb..16e5c64 100644 --- a/attestation/attestsigner_fake.go +++ b/attestation/attestsigner_fake.go @@ -6,11 +6,12 @@ package attestation import ( confpkg "mainstay/config" - "mainstay/crypto" "mainstay/log" + "encoding/hex" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" ) // AttestSignerFake struct @@ -22,8 +23,8 @@ type AttestSignerFake struct { } // store latest hash and transaction -var signerTxPreImageBytes []byte -var signerConfirmedHashBytes []byte +var signerTxPreImageBytesFake []byte +var signerConfirmedHashBytesFake []byte // Return new AttestSignerFake instance func NewAttestSignerFake(configs []*confpkg.Config) AttestSignerFake { @@ -44,56 +45,53 @@ func (f AttestSignerFake) ReSubscribe() { // Store received confirmed hash func (f AttestSignerFake) SendConfirmedHash(hash []byte) { - signerConfirmedHashBytes = hash + signerConfirmedHashBytesFake = hash } // Store received new tx func (f AttestSignerFake) SendTxPreImages(txs [][]byte) { - signerTxPreImageBytes = SerializeBytes(txs) + signerTxPreImageBytesFake = SerializeBytes(txs) } // Return signatures for received tx and hashes -func (f AttestSignerFake) GetSigs() [][]crypto.Sig { - // get confirmed hash from received confirmed hash bytes - hash, hashErr := chainhash.NewHash(signerConfirmedHashBytes) +func (f AttestSignerFake) GetSigs(sigHashes [][]byte, merkle_root string) []wire.TxWitness { + + merkle_root_bytes, _ := hex.DecodeString(merkle_root) + reversed_merkle_root := make([]byte, len(merkle_root_bytes)) + + // Copy the reversed bytes + for i, _ := range merkle_root_bytes { + reversed_merkle_root[len(merkle_root_bytes)-1-i] = merkle_root_bytes[i] + } + hash, hashErr := chainhash.NewHash(reversed_merkle_root) if hashErr != nil { log.Infof("%v\n", hashErr) return nil } - // get unserialized tx pre images - txPreImages := UnserializeBytes(signerTxPreImageBytes) - - sigs := make([][]crypto.Sig, len(txPreImages)) // init sigs + witness := make([]wire.TxWitness, len(sigHashes)) // get sigs from each client for _, client := range f.clients { - // process each pre image transaction and sign - for i_tx, txPreImage := range txPreImages { - // add hash type to tx serialization - txPreImage = append(txPreImage, []byte{1, 0, 0, 0}...) - txPreImageHash := chainhash.DoubleHashH(txPreImage) + // process each sighash and sign + for i_tx, sigHash := range sigHashes { // sign first tx with tweaked priv key and // any remaining txs with topup key - var sig *btcec.Signature - var signErr error + var sig *ecdsa.Signature + var sigBytes []byte if i_tx == 0 { - priv := client.GetKeyFromHash(*hash).PrivKey - sig, signErr = priv.Sign(txPreImageHash.CloneBytes()) + priv := client.GetKeyFromHash(*hash) + sig = ecdsa.Sign(priv.PrivKey, sigHash) + sigBytes = append(sig.Serialize(), []byte{byte(1)}...) + witness[i_tx] = wire.TxWitness{sigBytes, priv.PrivKey.PubKey().SerializeCompressed()} } else { - sig, signErr = client.WalletPrivTopup.PrivKey.Sign(txPreImageHash.CloneBytes()) - } - if signErr != nil { - log.Infof("%v\n", signErr) - return nil + sig = ecdsa.Sign(client.WalletPrivTopup.PrivKey, sigHash) + sigBytes = append(sig.Serialize(), []byte{byte(1)}...) + witness[i_tx] = wire.TxWitness{sigBytes, client.WalletPrivTopup.PrivKey.PubKey().SerializeCompressed()} } - - // add hash type to signature as well - sigBytes := append(sig.Serialize(), []byte{byte(1)}...) - sigs[i_tx] = append(sigs[i_tx], sigBytes) } } - return sigs + return witness } diff --git a/attestation/attestsigner_http.go b/attestation/attestsigner_http.go new file mode 100644 index 0000000..2fa3648 --- /dev/null +++ b/attestation/attestsigner_http.go @@ -0,0 +1,173 @@ +// Copyright (c) 2018 CommerceBlock Team +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +package attestation + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "mainstay/log" + "io/ioutil" + confpkg "mainstay/config" + "net/http" + "strings" + "github.com/btcsuite/btcd/wire" +) + +// AttestSignerFake struct +// +// Implements AttestSigner interface and provides +// mock functionality for receiving sigs from signers +type AttestSignerHttp struct { + client http.Client + url string +} + +type RequestBody struct { + SighashString []string `json:"sighash_string"` + MerkleRoot string `json:"merkle_root"` +} + +// store latest hash and transaction +var signerTxPreImageBytes []byte +var signerConfirmedHashBytes []byte + +// Return new AttestSignerFake instance +func NewAttestSignerHttp(config confpkg.SignerConfig) AttestSignerHttp { + return AttestSignerHttp{ + client: http.Client{}, + url: config.Url, + } +} + +// Resubscribe - do nothing +func (f AttestSignerHttp) ReSubscribe() { + return +} + +// Store received confirmed hash +func (f AttestSignerHttp) SendConfirmedHash(hash []byte) { + signerConfirmedHashBytes = hash +} + +// Store received new tx +func (f AttestSignerHttp) SendTxPreImages(txs [][]byte) { + signerTxPreImageBytes = SerializeBytes(txs) +} + +// Return signatures for received tx and hashes +func (f AttestSignerHttp) GetSigs(sigHashes [][]byte, merkle_root string) []wire.TxWitness { + + witness := make([]wire.TxWitness, len(sigHashes)) // init witness + + sigHashesStr := make([]string, len(sigHashes)) + for i := 0; i < len(sigHashes); i++ { + sigHashesStr[i] = hex.EncodeToString(sigHashes[i]) + } + + requestBody := &RequestBody{ + SighashString: sigHashesStr, + MerkleRoot: merkle_root, + } + + // Encode the request body to JSON + requestBodyJSON, err := json.Marshal(requestBody) + if err != nil { + log.Info("Error marshalling request body: ", err) + } + + // Create the HTTP request + req, err := http.NewRequest(http.MethodPost, f.url, bytes.NewReader(requestBodyJSON)) + if err != nil { + log.Info("Error creating request: ", err) + } + + // Set the request headers + req.Header.Set("Content-Type", "application/json") + + resp, err := f.client.Do(req) + if err != nil { + log.Info("Error sending request: ", err) + } + + // Close the response body + defer resp.Body.Close() + + // Read the response body + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Info("Error reading response body: ", err) + } + + var data map[string]interface{} + jsonErr := json.Unmarshal([]byte(body), &data) + if jsonErr != nil { + log.Info("Error unmarshaling JSON:", err) + } + + for i := 0; i < len(sigHashesStr); i++ { + witnessStr := data["witness"].([]interface{})[i].(string) + witnessData := strings.Split(witnessStr, " ") + sig, _ := hex.DecodeString(witnessData[0]) + sigBytes := append(sig, []byte{byte(1)}...) + pubkey, _ := hex.DecodeString(witnessData[1]) + witness[i] = wire.TxWitness{sigBytes, pubkey} + } + + return witness +} + +// Transform received list of bytes into a single byte +// slice with format: [len bytes0] [bytes0] [len bytes1] [bytes1] +func SerializeBytes(data [][]byte) []byte { + + // empty case return nothing + if len(data) == 0 { + return []byte{} + } + + var serializedBytes []byte + + // iterate through each byte slice adding + // length and data bytes to bytes slice + for _, dataX := range data { + serializedBytes = append(serializedBytes, byte(len(dataX))) + serializedBytes = append(serializedBytes, dataX...) + } + + return serializedBytes +} + +// Transform single byte slice (result of SerializeBytes) +// into a list of byte slices excluding lengths +func UnserializeBytes(data []byte) [][]byte { + + // empty case return nothing + if len(data) == 0 { + return [][]byte{} + } + + var dataList [][]byte + + // process data slice + it := 0 + for it < len(data) { + // get next data by reading byte size + txSize := data[it] + + // check if next size excees the bounds and break + // maybe TODO: error handling + if (int(txSize) + 1 + it) > len(data) { + break + } + + dataX := append([]byte{}, data[it+1:it+1+int(txSize)]...) + dataList = append(dataList, dataX) + + it += 1 + int(txSize) + } + + return dataList +} diff --git a/attestation/attestsigner_zmq.go b/attestation/attestsigner_zmq.go deleted file mode 100644 index e8ea3df..0000000 --- a/attestation/attestsigner_zmq.go +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2018 CommerceBlock Team -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -package attestation - -import ( - "fmt" - - confpkg "mainstay/config" - "mainstay/crypto" - "mainstay/log" - "mainstay/messengers" - - zmq "github.com/pebbe/zmq4" -) - -// zmq communication consts -const ( - DefaultMainPublisherPort = 5000 // port used by main signer publisher - - // predefined topics for publishing/subscribing via zmq - TopicNewTx = "T" - TopicConfirmedHash = "C" - TopicSigs = "S" -) - -// AttestSignerZmq struct -// -// Implements AttestSigner interface and uses communication -// via zmq to publish data and listen to subscriptions and -// send commitments/new tx and receive signatures -type AttestSignerZmq struct { - // zmq publisher interface used to publish hashes and txes to signers - publisher *messengers.PublisherZmq - - // zmq subscribe interface to signers to receive tx signatures - subscribers []*messengers.SubscriberZmq - - // store config for future later use when resubscribing - config confpkg.SignerConfig -} - -// poller to add all subscriber/publisher sockets -var poller *zmq.Poller - -// Return new AttestSignerZmq instance -func NewAttestSignerZmq(config confpkg.SignerConfig) *AttestSignerZmq { - // get publisher addr from config, if set - publisherAddr := fmt.Sprintf("*:%d", DefaultMainPublisherPort) - if config.Publisher != "" { - publisherAddr = config.Publisher - } - - // Initialise publisher for sending new hashes and txs - // and subscribers to receive sig responses - poller = zmq.NewPoller() - publisher := messengers.NewPublisherZmq(publisherAddr, poller) - var subscribers []*messengers.SubscriberZmq - subtopics := []string{TopicSigs} - for _, nodeaddr := range config.Signers { - subscribers = append(subscribers, messengers.NewSubscriberZmq(nodeaddr, subtopics, poller)) - } - - return &AttestSignerZmq{publisher, subscribers, config} -} - -// Zmq Resubscribe to the transaction signers -func (z *AttestSignerZmq) ReSubscribe() { - // close current sockets - for _, sub := range z.subscribers { - sub.Close(poller) - } - z.subscribers = nil // empty slice - - // reconnect to signers - var subscribers []*messengers.SubscriberZmq - subtopics := []string{TopicSigs} - for _, nodeaddr := range z.config.Signers { - subscribers = append(subscribers, messengers.NewSubscriberZmq(nodeaddr, subtopics, poller)) - } - z.subscribers = subscribers -} - -// Use zmq publisher to send confirmed hash -func (z AttestSignerZmq) SendConfirmedHash(hash []byte) { - z.publisher.SendMessage(hash, TopicConfirmedHash) -} - -// Transform received list of bytes into a single byte -// slice with format: [len bytes0] [bytes0] [len bytes1] [bytes1] -func SerializeBytes(data [][]byte) []byte { - - // empty case return nothing - if len(data) == 0 { - return []byte{} - } - - var serializedBytes []byte - - // iterate through each byte slice adding - // length and data bytes to bytes slice - for _, dataX := range data { - serializedBytes = append(serializedBytes, byte(len(dataX))) - serializedBytes = append(serializedBytes, dataX...) - } - - return serializedBytes -} - -// Transform single byte slice (result of SerializeBytes) -// into a list of byte slices excluding lengths -func UnserializeBytes(data []byte) [][]byte { - - // empty case return nothing - if len(data) == 0 { - return [][]byte{} - } - - var dataList [][]byte - - // process data slice - it := 0 - for it < len(data) { - // get next data by reading byte size - txSize := data[it] - - // check if next size excees the bounds and break - // maybe TODO: error handling - if (int(txSize) + 1 + it) > len(data) { - break - } - - dataX := append([]byte{}, data[it+1:it+1+int(txSize)]...) - dataList = append(dataList, dataX) - - it += 1 + int(txSize) - } - - return dataList -} - -// Use zmq publisher to send new tx -func (z AttestSignerZmq) SendTxPreImages(txs [][]byte) { - z.publisher.SendMessage(SerializeBytes(txs), TopicNewTx) -} - -// Parse all received messages and create a sigs slice -// input: -// x dimension: subscriber -// y dimension: list of signatures of subscriber (one for each tx input) -// z dimension: slice of bytes (signature) -// output: -// x dimension: number of transaction inputs -// y dimension: number of signatures per input -func getSigsFromMsgs(msgs [][][]byte, numOfInputs int) [][]crypto.Sig { - if numOfInputs == 0 { - return [][]crypto.Sig{} - } - - sigs := make([][]crypto.Sig, numOfInputs) - for i := 0; i < numOfInputs; i++ { - for _, msgSplit := range msgs { - if len(msgSplit) > i { - sigs[i] = append(sigs[i], crypto.Sig(msgSplit[i])) - } - } - } - return sigs -} - -// Update num of transaction inputs from latest msg -func updateNumOfTxInputs(msgSplit [][]byte, numOfInputs int) int { - if len(msgSplit) > numOfInputs { - numOfInputs = len(msgSplit) - } - return numOfInputs -} - -// Listen to zmq subscribers to receive tx signatures -func (z AttestSignerZmq) GetSigs() [][]crypto.Sig { - - var msgs [][][]byte - numOfTxInputs := 0 - - // Iterate through each subscriber to get the latest message sent - // If there is more than one message in the subscriber queue the - // last is retained by continuously polling the Poller to get that - for _, sub := range z.subscribers { - - var subMsg [][]byte // store latest message - - // continously poll to get latest message - // or stop if no message has been found - for { - sockets, pollErr := poller.Poll(-1) - if pollErr != nil { - log.Infoln(pollErr) - } - - found := false - // look for matching subscriber in polling results - for _, socket := range sockets { - if sub.Socket() == socket.Socket { - found = true - _, msg := sub.ReadMessage() - subMsg = UnserializeBytes(msg) - } - } - - if !found { - break - } - } - - // update received messages only if a subscriber message has been found - // this check is probably unnecessary but better safe than sorry - if len(subMsg) > 0 { - numOfTxInputs = updateNumOfTxInputs(subMsg, numOfTxInputs) - msgs = append(msgs, subMsg) - } - } - - return getSigsFromMsgs(msgs, numOfTxInputs) // bring messages into readable format for mainstay -} diff --git a/attestation/attestsigner_zmq_test.go b/attestation/attestsigner_zmq_test.go deleted file mode 100644 index 4c0eec2..0000000 --- a/attestation/attestsigner_zmq_test.go +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright (c) 2018 CommerceBlock Team -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -package attestation - -import ( - _ "bytes" - _ "encoding/hex" - "testing" - - _ "mainstay/config" - "mainstay/crypto" - - "github.com/stretchr/testify/assert" -) - -// Test util functions used in -// attestsignerzmq struct for -// processing incoming sig messages -func TestAttestSigner_SigUtils(t *testing.T) { - sig1 := []byte{71, 48, 68, 2, 32, 100, 88, 73, 1, 86, 42, 210, 239, 196, 136, 107, 0, 178, 223, 59, 32, 235, 58, 231, 207, 168, 87, 95, 227, 83, 207, 67, 150, 254, 26, 99, 13, 2, 32, 0, 169, 167, 160, 35, 235, 221, 136, 214, 217, 143, 64, 105, 250, 180, 188, 109, 236, 175, 117, 198, 53, 180, 24, 223, 217, 44, 199, 54, 158, 230, 227, 1} - sig2 := []byte{71, 48, 68, 2, 32, 17, 175, 6, 205, 216, 180, 188, 216, 38, 178, 109, 17, 145, 237, 148, 1, 30, 73, 161, 54, 176, 122, 66, 6, 211, 219, 90, 216, 219, 38, 162, 137, 2, 32, 14, 61, 139, 90, 233, 169, 9, 57, 249, 101, 38, 109, 147, 244, 151, 182, 93, 136, 64, 221, 158, 172, 238, 208, 71, 106, 39, 50, 194, 185, 230, 102, 1} - sig3 := []byte{71, 48, 68, 2, 32, 17, 175, 6, 205, 216, 180, 188, 216, 38, 178, 109, 17, 145, 237, 148, 1, 30, 73, 161, 54, 176, 122, 66, 6, 211, 219, 90, 216, 219, 38, 162, 137, 2, 32, 14, 61, 139, 90, 233, 169, 9, 57, 249, 101, 38, 109, 145, 244, 151, 182, 93, 136, 64, 221, 158, 172, 238, 208, 71, 106, 39, 50, 194, 185, 230, 102, 1} - - var msgA []byte - var msgB []byte - var splitMsgA [][]byte - var splitMsgB [][]byte - var msgs [][][]byte - var sigs [][]crypto.Sig - - numOfTxInputs := 0 - - // test 1 message 0 signature - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 0, numOfTxInputs) - assert.Equal(t, [][]byte{}, splitMsgA) - assert.Equal(t, 0, len(splitMsgA)) - - msgs = [][][]byte{[][]byte{}} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{}, sigs) - - // test 2 messages 0 signature - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 0, numOfTxInputs) - splitMsgB = UnserializeBytes(msgB) - numOfTxInputs = updateNumOfTxInputs(splitMsgB, numOfTxInputs) - assert.Equal(t, 0, numOfTxInputs) - assert.Equal(t, [][]byte{}, splitMsgA) - assert.Equal(t, [][]byte{}, splitMsgB) - assert.Equal(t, 0, len(splitMsgA)) - assert.Equal(t, 0, len(splitMsgB)) - - msgs = [][][]byte{[][]byte{}, [][]byte{}} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{}, sigs) - - // test 1 message 1 signature - numOfTxInputs = 0 - msgA = sig1 - - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 1, numOfTxInputs) - assert.Equal(t, [][]byte{sig1[1:]}, splitMsgA) - assert.Equal(t, 1, len(splitMsgA)) - - msgs = [][][]byte{splitMsgA} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{ - []crypto.Sig{crypto.Sig(sig1[1:])}}, sigs) - - // test 1 message 2 signature - numOfTxInputs = 0 - msgA = sig1 - msgA = append(msgA, sig2...) - - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 2, numOfTxInputs) - assert.Equal(t, [][]byte{sig1[1:], sig2[1:]}, splitMsgA) - assert.Equal(t, 2, len(splitMsgA)) - - msgs = [][][]byte{splitMsgA} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{ - []crypto.Sig{crypto.Sig(sig1[1:])}, - []crypto.Sig{crypto.Sig(sig2[1:])}}, sigs) - - // test 2 messages 1 signature - numOfTxInputs = 0 - msgA = sig1 - msgB = sig3 - - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 1, numOfTxInputs) - splitMsgB = UnserializeBytes(msgB) - numOfTxInputs = updateNumOfTxInputs(splitMsgB, numOfTxInputs) - assert.Equal(t, 1, numOfTxInputs) - assert.Equal(t, [][]byte{sig1[1:]}, splitMsgA) - assert.Equal(t, [][]byte{sig3[1:]}, splitMsgB) - assert.Equal(t, 1, len(splitMsgA)) - assert.Equal(t, 1, len(splitMsgB)) - - msgs = [][][]byte{splitMsgA, splitMsgB} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{ - []crypto.Sig{crypto.Sig(sig1[1:]), crypto.Sig(sig3[1:])}}, sigs) - - // test 2 messages 2 signatures - numOfTxInputs = 0 - msgA = sig1 - msgA = append(msgA, sig2...) - msgB = sig3 - msgB = append(msgB, sig3...) - - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 2, numOfTxInputs) - splitMsgB = UnserializeBytes(msgB) - numOfTxInputs = updateNumOfTxInputs(splitMsgB, numOfTxInputs) - assert.Equal(t, 2, numOfTxInputs) - assert.Equal(t, [][]byte{sig1[1:], sig2[1:]}, splitMsgA) - assert.Equal(t, [][]byte{sig3[1:], sig3[1:]}, splitMsgB) - assert.Equal(t, 2, len(splitMsgA)) - assert.Equal(t, 2, len(splitMsgB)) - - msgs = [][][]byte{splitMsgA, splitMsgB} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{ - []crypto.Sig{crypto.Sig(sig1[1:]), crypto.Sig(sig3[1:])}, - []crypto.Sig{crypto.Sig(sig2[1:]), crypto.Sig(sig3[1:])}}, sigs) - - // test 2 messages 0,2 signatures - numOfTxInputs = 0 - msgA = []byte{} - msgB = sig3 - msgB = append(msgB, sig3...) - - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 0, numOfTxInputs) - splitMsgB = UnserializeBytes(msgB) - numOfTxInputs = updateNumOfTxInputs(splitMsgB, numOfTxInputs) - assert.Equal(t, 2, numOfTxInputs) - assert.Equal(t, [][]byte{}, splitMsgA) - assert.Equal(t, [][]byte{sig3[1:], sig3[1:]}, splitMsgB) - assert.Equal(t, 0, len(splitMsgA)) - assert.Equal(t, 2, len(splitMsgB)) - - msgs = [][][]byte{splitMsgA, splitMsgB} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{ - []crypto.Sig{crypto.Sig(sig3[1:])}, - []crypto.Sig{crypto.Sig(sig3[1:])}}, sigs) - - // test 2 messages 2,0 signatures - numOfTxInputs = 0 - msgA = sig1 - msgA = append(msgA, sig2...) - msgB = []byte{} - - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 2, numOfTxInputs) - splitMsgB = UnserializeBytes(msgB) - numOfTxInputs = updateNumOfTxInputs(splitMsgB, numOfTxInputs) - assert.Equal(t, 2, numOfTxInputs) - assert.Equal(t, [][]byte{sig1[1:], sig2[1:]}, splitMsgA) - assert.Equal(t, [][]byte{}, splitMsgB) - assert.Equal(t, 2, len(splitMsgA)) - assert.Equal(t, 0, len(splitMsgB)) - - msgs = [][][]byte{splitMsgA, splitMsgB} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{ - []crypto.Sig{crypto.Sig(sig1[1:])}, - []crypto.Sig{crypto.Sig(sig2[1:])}}, sigs) - - // test 2 messages 1,2 signatures - numOfTxInputs = 0 - msgA = sig1 - msgB = sig3 - msgB = append(msgB, sig3...) - - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 1, numOfTxInputs) - splitMsgB = UnserializeBytes(msgB) - numOfTxInputs = updateNumOfTxInputs(splitMsgB, numOfTxInputs) - assert.Equal(t, 2, numOfTxInputs) - assert.Equal(t, [][]byte{sig1[1:]}, splitMsgA) - assert.Equal(t, [][]byte{sig3[1:], sig3[1:]}, splitMsgB) - assert.Equal(t, 1, len(splitMsgA)) - assert.Equal(t, 2, len(splitMsgB)) - - msgs = [][][]byte{splitMsgA, splitMsgB} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{ - []crypto.Sig{crypto.Sig(sig1[1:]), crypto.Sig(sig3[1:])}, - []crypto.Sig{crypto.Sig(sig3[1:])}}, sigs) - - // test 2 messages 2,1 signatures - numOfTxInputs = 0 - msgA = sig1 - msgA = append(msgA, sig2...) - msgB = sig3 - - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 2, numOfTxInputs) - splitMsgB = UnserializeBytes(msgB) - numOfTxInputs = updateNumOfTxInputs(splitMsgB, numOfTxInputs) - assert.Equal(t, 2, numOfTxInputs) - assert.Equal(t, [][]byte{sig1[1:], sig2[1:]}, splitMsgA) - assert.Equal(t, [][]byte{sig3[1:]}, splitMsgB) - assert.Equal(t, 2, len(splitMsgA)) - assert.Equal(t, 1, len(splitMsgB)) - - msgs = [][][]byte{splitMsgA, splitMsgB} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{ - []crypto.Sig{crypto.Sig(sig1[1:]), crypto.Sig(sig3[1:])}, - []crypto.Sig{crypto.Sig(sig2[1:])}}, sigs) - - // test 2 messages 1,0 signatures - numOfTxInputs = 0 - msgA = sig1 - msgB = []byte{} - - splitMsgA = UnserializeBytes(msgA) - numOfTxInputs = updateNumOfTxInputs(splitMsgA, numOfTxInputs) - assert.Equal(t, 1, numOfTxInputs) - splitMsgB = UnserializeBytes(msgB) - numOfTxInputs = updateNumOfTxInputs(splitMsgB, numOfTxInputs) - assert.Equal(t, 1, numOfTxInputs) - assert.Equal(t, [][]byte{sig1[1:]}, splitMsgA) - assert.Equal(t, [][]byte{}, splitMsgB) - assert.Equal(t, 1, len(splitMsgA)) - assert.Equal(t, 0, len(splitMsgB)) - - msgs = [][][]byte{splitMsgA, splitMsgB} - sigs = getSigsFromMsgs(msgs, numOfTxInputs) - assert.Equal(t, [][]crypto.Sig{ - []crypto.Sig{crypto.Sig(sig1[1:])}}, sigs) -} - -// Test util functions used in -// attestsigner struct for -// processing incoming tx messages -func TestAttestSigner_TxUtils(t *testing.T) { - // empty input to Serialize - assert.Equal(t, []byte{}, SerializeBytes([][]byte{})) - assert.Equal(t, 0, len(SerializeBytes([][]byte{}))) - assert.Equal(t, []byte{}, SerializeBytes([][]byte(nil))) - assert.Equal(t, 0, len(SerializeBytes([][]byte(nil)))) - - // single vin unsigned tx - tx1Bytes := []byte{2, 0, 0, 0, 1, 48, 38, 85, 184, 133, 101, 229, 118, 225, 243, 224, 5, 134, 231, 53, 91, 21, 77, 145, 198, 183, 163, 103, 103, 248, 234, 201, 83, 214, 206, 37, 195, 0, 0, 0, 0, 0, 253, 255, 255, 255, 1, 66, 158, 23, 168, 4, 0, 0, 0, 23, 169, 20, 160, 161, 96, 85, 138, 149, 193, 14, 237, 218, 58, 112, 171, 104, 24, 157, 212, 132, 203, 58, 135, 0, 0, 0, 0} - - tx1BytesWithLen := append([]byte{byte(len(tx1Bytes))}, tx1Bytes...) - assert.Equal(t, tx1BytesWithLen, SerializeBytes([][]byte{tx1Bytes})) - assert.Equal(t, len(tx1Bytes)+1, len(SerializeBytes([][]byte{tx1Bytes}))) - - // two vin unsigned tx - tx2Bytes := []byte{2, 0, 0, 0, 2, 108, 82, 16, 166, 228, 190, 231, 4, 131, 28, 47, 248, 172, 49, 84, 236, 95, 173, 60, 159, 155, 183, 19, 112, 116, 38, 150, 147, 8, 132, 97, 195, 0, 0, 0, 0, 0, 253, 255, 255, 255, 192, 186, 138, 193, 135, 96, 171, 236, 192, 227, 70, 94, 185, 205, 124, 215, 86, 75, 66, 176, 237, 171, 231, 118, 79, 135, 129, 194, 111, 101, 74, 159, 0, 0, 0, 0, 0, 255, 255, 255, 255, 1, 128, 161, 23, 168, 4, 0, 0, 0, 23, 169, 20, 255, 87, 124, 157, 17, 223, 243, 128, 122, 150, 92, 1, 101, 239, 50, 250, 202, 230, 56, 75, 135, 0, 0, 0, 0} - - tx2BytesWithLen := append([]byte{byte(len(tx2Bytes))}, tx2Bytes...) - - tx1and2BytesWithLen := append(tx1BytesWithLen, tx2BytesWithLen...) - - assert.Equal(t, tx1and2BytesWithLen, SerializeBytes([][]byte{tx1Bytes, tx2Bytes})) - assert.Equal(t, len(tx1Bytes)+len(tx2Bytes)+2, len(SerializeBytes([][]byte{tx1Bytes, tx2Bytes}))) - - // empty input to Unserialize - assert.Equal(t, [][]byte{}, UnserializeBytes([]byte{})) - assert.Equal(t, 0, len(UnserializeBytes([]byte{}))) - assert.Equal(t, [][]byte{}, UnserializeBytes([]byte(nil))) - assert.Equal(t, 0, len(UnserializeBytes([]byte(nil)))) - - // unserialize single vin - serializedTxs := SerializeBytes([][]byte{tx1Bytes}) - assert.Equal(t, [][]byte{tx1Bytes}, UnserializeBytes(serializedTxs)) - - // unserialize two vins - serializedTxs = SerializeBytes([][]byte{tx1Bytes, tx2Bytes}) - assert.Equal(t, [][]byte{tx1Bytes, tx2Bytes}, UnserializeBytes(serializedTxs)) - - // unserialize single vin with additional noise - serializedTxs = SerializeBytes([][]byte{tx1Bytes}) - serializedTxs = append(serializedTxs, []byte{50, 1, 1}...) // add noise - assert.Equal(t, [][]byte{tx1Bytes}, UnserializeBytes(serializedTxs)) - - serializedTxs = SerializeBytes([][]byte{tx1Bytes}) - serializedTxs = append(serializedTxs, []byte{3, 1, 1}...) // add noise - assert.Equal(t, [][]byte{tx1Bytes}, UnserializeBytes(serializedTxs)) - - serializedTxs = SerializeBytes([][]byte{tx1Bytes}) - serializedTxs = append(serializedTxs, []byte{0, 1, 1}...) // add non noise edge case - assert.Equal(t, [][]byte{tx1Bytes, []byte{}, []byte{1}}, UnserializeBytes(serializedTxs)) - - serializedTxs = SerializeBytes([][]byte{tx1Bytes}) - serializedTxs = append(serializedTxs, []byte{2, 1, 1}...) // add non noise edge case - assert.Equal(t, [][]byte{tx1Bytes, []byte{1, 1}}, UnserializeBytes(serializedTxs)) -} diff --git a/cmd/clientsignuptool/clientsignuptool.go b/cmd/clientsignuptool/clientsignuptool.go index 23097b7..e1addb2 100644 --- a/cmd/clientsignuptool/clientsignuptool.go +++ b/cmd/clientsignuptool/clientsignuptool.go @@ -18,7 +18,7 @@ import ( "mainstay/log" "mainstay/models" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" uuid "github.com/satori/go.uuid" ) @@ -113,7 +113,7 @@ func main() { if pubKeyBytesErr != nil { log.Error(pubKeyBytesErr) } - _, errPub := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) + _, errPub := btcec.ParsePubKey(pubKeyBytes) if errPub != nil { log.Error(errPub) } diff --git a/cmd/commitmenttool/commitmenttool.go b/cmd/commitmenttool/commitmenttool.go index f458c5e..5609138 100644 --- a/cmd/commitmenttool/commitmenttool.go +++ b/cmd/commitmenttool/commitmenttool.go @@ -22,7 +22,8 @@ import ( "mainstay/config" "mainstay/log" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/chaincfg/chainhash" ) @@ -74,7 +75,7 @@ func doInitMode() { log.Infoln("****************************") log.Infof("Generating new key...\n") - newPriv, newPrivErr := btcec.NewPrivateKey(btcec.S256()) + newPriv, newPrivErr := btcec.NewPrivateKey() if newPrivErr != nil { log.Error(newPrivErr) } @@ -145,13 +146,10 @@ func sign(msg []byte) []byte { if decodeErr != nil { log.Errorf("Key ('%s') decode error: %v\n", privkey, decodeErr) } - privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privkeyBytes) + privKey, _ := btcec.PrivKeyFromBytes(privkeyBytes) // sign message - sig, signErr := privKey.Sign(msg) - if signErr != nil { - log.Errorf("Signing error: %v\n", signErr) - } + sig := ecdsa.Sign(privKey, msg) return sig.Serialize() } diff --git a/cmd/confirmationtool/keyextraction/keyextractiontool.go b/cmd/confirmationtool/keyextraction/keyextractiontool.go index 8bf3a21..c98e94c 100644 --- a/cmd/confirmationtool/keyextraction/keyextractiontool.go +++ b/cmd/confirmationtool/keyextraction/keyextractiontool.go @@ -7,11 +7,11 @@ import ( "mainstay/crypto" "mainstay/log" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/hdkeychain" ) // Tool that assists with calculating tweaked private key diff --git a/cmd/multisigtool/multisigtool.go b/cmd/multisigtool/multisigtool.go index 4b3f171..b94530d 100644 --- a/cmd/multisigtool/multisigtool.go +++ b/cmd/multisigtool/multisigtool.go @@ -5,19 +5,17 @@ package main import ( - "crypto/ecdsa" "encoding/hex" "flag" "fmt" - "math/big" "strings" "mainstay/crypto" "mainstay/log" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" ) // Multisig and pay to address generation for Mainstay @@ -136,18 +134,7 @@ func doMain() { // Get btcec PublicKey from x/y coordinates func pubFromCoordinates(xStr string, yStr string) *btcec.PublicKey { - x := new(big.Int) - y := new(big.Int) - _, errX := fmt.Sscan(xStr, x) - if errX != nil { - log.Warnln("Get btcec PublicKey fail-x") - } - _, errY := fmt.Sscan(yStr, y) - if errY != nil { - log.Warnln("Get btcec PublicKey fail-y") - } - - return (*btcec.PublicKey)(&ecdsa.PublicKey{btcec.S256(), x, y}) + return btcec.NewPublicKey(crypto.HexToFieldVal(xStr), crypto.HexToFieldVal(yStr)) } // Generate multisig script and p2sh address for mainstay diff --git a/cmd/txsigningtool/conf.json b/cmd/txsigningtool/conf.json deleted file mode 100644 index ad16938..0000000 --- a/cmd/txsigningtool/conf.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "staychain": - { - "initTx": "MAINSTAY_INIT_TX", - "initScript": "MAINSTAY_INIT_SCRIPT", - "initChaincodes": "MAINSTAY_INIT_CHAINCODES", - "topupAddress": "MAINSTAY_TOPUP_ADDRESS", - "topupScript": "MAINSTAY_TOPUP_SCRIPT" - }, - "main": - { - "rpcurl": "MAINSTAY_CLIENT_URL", - "rpcuser": "MAINSTAY_CLIENT_USER", - "rpcpass": "MAINSTAY_CLIENT_PASS", - "chain": "MAINSTAY_CLIENT_CHAIN" - } -} \ No newline at end of file diff --git a/cmd/txsigningtool/demo-conf.json b/cmd/txsigningtool/demo-conf.json deleted file mode 100644 index 1d50d45..0000000 --- a/cmd/txsigningtool/demo-conf.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "main": { - "rpcurl": "localhost:18453", - "rpcuser": "user", - "rpcpass": "pass", - "chain": "regtest" - }, - "misc": { - "multisignodes": "" - }, - "db": { - "user":"", - "password":"", - "host":"", - "port":"", - "name":"" - } -} diff --git a/cmd/txsigningtool/demo-init-signingtool.sh b/cmd/txsigningtool/demo-init-signingtool.sh deleted file mode 100755 index befb01f..0000000 --- a/cmd/txsigningtool/demo-init-signingtool.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -shopt -s expand_aliases - -alias btcd="bitcoind -datadir=$HOME/btc-datadir-signingtool" -alias btcl="bitcoin-cli -datadir=$HOME/btc-datadir-signingtool" - -btcl stop -sleep 1 - -rm -r ~/btc-datadir-signingtool -mkdir ~/btc-datadir-signingtool - -printf '%s\n' '#!/bin/sh' 'rpcuser=user' \ - 'rpcpassword=pass' \ - 'regtest.rpcport=18453' \ - 'regtest.port=18454' \ - 'regtest.addnode=localhost:18444' \ - 'keypool=0' \ - 'deprecatedrpc=signrawtransaction' \ - 'server=1' \ - 'regtest=1' \ - 'daemon=1' \ - 'txindex=1' > ~/btc-datadir-signingtool/bitcoin.conf - -btcd -sleep 2 - -btcl importaddress "2MyC1i1FGy6MZWyMgmZXku4gdWZxWCRa6RL" -btcl importaddress "512103e52cf15e0a5cf6612314f077bb65cf9a6596b76c0fcb34b682f673a8314c7b332102f3a78a7bd6cf01c56312e7e828bef74134dfb109e59afd088526212d96518e7552ae" "" true true - -btcl listunspent -sleep 10 diff --git a/cmd/txsigningtool/txsigningtool.go b/cmd/txsigningtool/txsigningtool.go deleted file mode 100644 index ad70e0b..0000000 --- a/cmd/txsigningtool/txsigningtool.go +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) 2018 CommerceBlock Team -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -package main - -import ( - "encoding/hex" - "flag" - "fmt" - "os" - "os/exec" - "strings" - "time" - - "mainstay/attestation" - confpkg "mainstay/config" - _ "mainstay/crypto" - "mainstay/log" - "mainstay/messengers" - "mainstay/test" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg/chainhash" - zmq "github.com/pebbe/zmq4" -) - -// The transaction signing tool is used by members of the multisig script -// used to generate new attestations transactions. This process communicates -// with the main attestation service to receive latest commitments and sign transactions - -var ( - // use attest client interface for signing - client *attestation.AttestClient - isRegtest bool - - // init transaction parameters - pk0 string - script0 string - chaincodes0 string - - // topup parameters - addrTopup string - pkTopup string - scriptTopup string - - // communication with attest service - sub *messengers.SubscriberZmq - pub *messengers.PublisherZmq - poller *zmq.Poller - host string - hostMain string - - attestedHash chainhash.Hash // previous attested hash - nextHash chainhash.Hash // next hash to sign with -) - -// main conf path for main use in attestation -const ConfPath = "/src/mainstay/cmd/txsigningtool/conf.json" - -// demo parameters for use with regtest demo -const DemoConfPath = "/src/mainstay/cmd/txsigningtool/demo-conf.json" -const DemoInitPath = "/src/mainstay/cmd/txsigningtool/demo-init-signingtool.sh" - -func parseFlags() { - flag.BoolVar(&isRegtest, "regtest", false, "Use regtest wallet configuration") - flag.StringVar(&pk0, "pk", "", "Client pk for genesis attestation transaction") - flag.StringVar(&script0, "script", "", "Redeem script in case multisig is used") - flag.StringVar(&addrTopup, "addrTopup", "", "Address for topup transaction") - flag.StringVar(&pkTopup, "pkTopup", "", "Client pk for topup address") - flag.StringVar(&scriptTopup, "scriptTopup", "", "Redeem script for topup") - - flag.StringVar(&host, "host", "*:5002", "Client host to publish signatures at") - hostMainDefault := fmt.Sprintf("127.0.0.1:%d", attestation.DefaultMainPublisherPort) - flag.StringVar(&hostMain, "hostMain", hostMainDefault, "Mainstay host for signer to subscribe to") - flag.Parse() - - if pk0 == "" && !isRegtest { - flag.PrintDefaults() - log.Errorf("Need to provide -pk argument. To use test configuration set the -regtest flag.") - } -} - -func init() { - parseFlags() - var config *confpkg.Config - - // regtest mode - // run demo init script to setup bitcoin node and initial transaction - // generate test config using demo config file - if isRegtest { - cmd := exec.Command("/bin/sh", os.Getenv("GOPATH")+DemoInitPath) - _, err := cmd.Output() - if err != nil { - log.Error(err) - } - - confFile, confErr := confpkg.GetConfFile(os.Getenv("GOPATH") + DemoConfPath) - if confErr != nil { - log.Error(confErr) - } - var configErr error - config, configErr = confpkg.NewConfig(confFile) - if configErr != nil { - log.Error(configErr) - } - pk0 = test.PrivMain - script0 = test.Script - pkTopup = test.TopupPrivMain - scriptTopup = test.TopupScript - addrTopup = test.TopupAddress - chaincodes0 = test.InitChaincodes - } else { - // regular mode - // use conf file to setup config - confFile, confErr := confpkg.GetConfFile(os.Getenv("GOPATH") + ConfPath) - if confErr != nil { - log.Error(confErr) - } - var configErr error - config, configErr = confpkg.NewConfig(confFile) - if configErr != nil { - log.Error(configErr) - } - - // if init script is not set throw error - if script0 == "" && config.InitScript() == "" { - flag.PrintDefaults() - log.Error(`Need to provide -script argument. - To use test configuration set the -regtest flag.`) - } - } - - // overwrite init config if set from command line - if pk0 != "" { - config.SetInitPK(pk0) - } - if script0 != "" { - config.SetInitScript(script0) - } - if chaincodes0 != "" { - config.SetInitChaincodes(strings.Split(chaincodes0, ",")) - } - - // overwrite topup config if set from command line - if pkTopup != "" { - config.SetTopupPK(pkTopup) - } - if addrTopup != "" && scriptTopup != "" { - config.SetTopupAddress(addrTopup) - config.SetTopupScript(scriptTopup) - } - - // init client interface with isSigner flag set - client = attestation.NewAttestClient(config, true) - - // comms setup - poller = zmq.NewPoller() - topics := []string{attestation.TopicNewTx, attestation.TopicConfirmedHash} - sub = messengers.NewSubscriberZmq(hostMain, topics, poller) - pub = messengers.NewPublisherZmq(host, poller) -} - -func main() { - // delay to resubscribe - resubscribeDelay := 5 * time.Minute - timer := time.NewTimer(resubscribeDelay) - for { - select { - case <-timer.C: - log.Infoln("resubscribing to mainstay...") - // remove socket and close - sub.Close(poller) - // re-assign subscriber socket - topics := []string{attestation.TopicNewTx, attestation.TopicConfirmedHash} - sub = messengers.NewSubscriberZmq(hostMain, topics, poller) - timer = time.NewTimer(resubscribeDelay) - default: - sockets, _ := poller.Poll(-1) - for _, socket := range sockets { - if sub.Socket() == socket.Socket { - topic, msg := sub.ReadMessage() - switch topic { - case attestation.TopicNewTx: - processTx(msg) - case attestation.TopicConfirmedHash: - attestedHash = processHash(msg) - log.Infof("attestedhash %s\n", attestedHash.String()) - } - } - } - time.Sleep(1 * time.Second) - } - } -} - -// Get hash from received message -func processHash(msg []byte) chainhash.Hash { - hash, hashErr := chainhash.NewHash(msg) - if hashErr != nil { - log.Error(hashErr) - } - return *hash -} - -// Process received tx, verify and reply with signature -func processTx(msg []byte) { - - var sigs [][]byte - - // get tx pre images from message - txPreImages := attestation.UnserializeBytes(msg) - - // process each pre image transaction and sign - for txIt, txPreImage := range txPreImages { - // add hash type to tx serialization - txPreImage = append(txPreImage, []byte{1, 0, 0, 0}...) - txPreImageHash := chainhash.DoubleHashH(txPreImage) - - // sign first tx with tweaked priv key and - // any remaining txs with topup key - var sig *btcec.Signature - var signErr error - if txIt == 0 { - priv := client.GetKeyFromHash(attestedHash).PrivKey - sig, signErr = priv.Sign(txPreImageHash.CloneBytes()) - } else { - sig, signErr = client.WalletPrivTopup.PrivKey.Sign(txPreImageHash.CloneBytes()) - } - if signErr != nil { - log.Error(signErr) - } - - // add hash type to signature as well - sigBytes := append(sig.Serialize(), []byte{byte(1)}...) - - log.Infof("sending sig(%d) %s\n", txIt, hex.EncodeToString(sigBytes)) - - sigs = append(sigs, sigBytes) - } - - serializedSigs := attestation.SerializeBytes(sigs) - pub.SendMessage(serializedSigs, attestation.TopicSigs) -} diff --git a/config/conf.json b/config/conf.json index 7704248..33dd381 100644 --- a/config/conf.json +++ b/config/conf.json @@ -14,10 +14,8 @@ "rpcpass": "MAINSTAY_MAIN_PASS", "chain": "MAINSTAY_MAIN_CHAIN" }, - "signer": - { - "publisher": "MAINSTAY_SIGNER_PUBLISHER", - "signers": "MAINSTAY_SIGNER_SIGNERS" + "signer": { + "url": "MAINSTAY_SIGNER_URL" }, "db": { diff --git a/config/config.go b/config/config.go index a256aed..2275557 100644 --- a/config/config.go +++ b/config/config.go @@ -26,6 +26,7 @@ const ( StaychainInitScriptName = "initScript" StaychainInitPkName = "initPK" StaychainInitChaincodesName = "initChaincodes" + StaychainInitPublicKeyName = "initPublicKey" StaychainTopupAddressName = "topupAddress" StaychainTopupScriptName = "topupScript" StaychainTopupPkName = "topupPK" @@ -45,6 +46,7 @@ type Config struct { initTX string initPK string initScript string + initPublicKey string initChaincodes []string topupAddress string topupScript string @@ -123,26 +125,16 @@ func (c *Config) SetTopupAddress(addr string) { c.topupAddress = addr } -// Get init PK +// Get init PK (Private key) func (c *Config) InitPK() string { return c.initPK } -// Set init PK +// Set init PK (Private key) func (c *Config) SetInitPK(pk string) { c.initPK = pk } -// Get Init Script -func (c *Config) InitScript() string { - return c.initScript -} - -// Set Init Script -func (c *Config) SetInitScript(script string) { - c.initScript = script -} - // Get Init Chaincodes func (c *Config) InitChaincodes() []string { return c.initChaincodes @@ -153,14 +145,14 @@ func (c *Config) SetInitChaincodes(chaincodes []string) { c.initChaincodes = chaincodes } -// Get topup Script -func (c *Config) TopupScript() string { - return c.topupScript +// Get init Public key +func (c *Config) InitPublicKey() string { + return c.initPublicKey } -// Set topup Script -func (c *Config) SetTopupScript(script string) { - c.topupScript = script +// Set init Public key +func (c *Config) SetInitPublicKey(publickey string) { + c.initPublicKey = publickey } // Get Topup Chaincodes @@ -226,11 +218,10 @@ func NewConfig(customConf ...[]byte) (*Config, error) { // most of these can be overriden from command line regtestStr := TryGetParamFromConf(StaychainName, StaychainRegtestName, conf) initTxStr := TryGetParamFromConf(StaychainName, StaychainInitTxName, conf) - initScriptStr := TryGetParamFromConf(StaychainName, StaychainInitScriptName, conf) initPKStr := TryGetParamFromConf(StaychainName, StaychainInitPkName, conf) topupAddrStr := TryGetParamFromConf(StaychainName, StaychainTopupAddressName, conf) - topupScriptStr := TryGetParamFromConf(StaychainName, StaychainTopupScriptName, conf) topupPKStr := TryGetParamFromConf(StaychainName, StaychainTopupPkName, conf) + initPublicKeyStr := TryGetParamFromConf(StaychainName, StaychainInitPublicKeyName, conf) initChaincodesStr := TryGetParamFromConf(StaychainName, StaychainInitChaincodesName, conf) initChaincodes := strings.Split(initChaincodesStr, ",") // string to string slice @@ -249,10 +240,9 @@ func NewConfig(customConf ...[]byte) (*Config, error) { regtest: (regtestStr == "1"), initTX: initTxStr, initPK: initPKStr, - initScript: initScriptStr, + initPublicKey: initPublicKeyStr, initChaincodes: initChaincodes, topupAddress: topupAddrStr, - topupScript: topupScriptStr, topupPK: topupPKStr, topupChaincodes: topupChaincodes, signerConfig: signerConfig, @@ -449,39 +439,27 @@ func GetTimingConfig(conf []byte) TimingConfig { // signer config parameter names const ( - SignerName = "signer" - SignerPublisherName = "publisher" - SignerSignersName = "signers" + Signer = "signer" + Url = "url" ) // Signer config struct // Configuration on communication between service and signers // Configure host addresses and zmq TOPIC config type SignerConfig struct { - // main publisher address - Publisher string - - // signer addresses - Signers []string + Url string } // Return SignerConfig from conf options -// If SignerName exists in conf, SignerSignersName is compsulsory -// Every other Signer Config field is optional func GetSignerConfig(conf []byte) (SignerConfig, error) { - // get signer node addresses - signersStr, signersErr := GetParamFromConf(SignerName, SignerSignersName, conf) + _, signersErr := GetParamFromConf(Signer, Url, conf) if signersErr != nil { return SignerConfig{}, signersErr } - signers := strings.Split(signersStr, ",") - for i := range signers { - signers[i] = strings.TrimSpace(signers[i]) - } - publisher := TryGetParamFromConf(SignerName, SignerPublisherName, conf) + + url := TryGetParamFromConf(Signer, Url, conf) return SignerConfig{ - Publisher: publisher, - Signers: signers, + Url: url, }, nil } diff --git a/config/config_test.go b/config/config_test.go index 4d6e759..f2329d1 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -197,7 +197,7 @@ func TestConfigActual(t *testing.T) { "chain": "regtest" }, "signer": { - "signers": "127.0.0.1:12345,127.0.0.1:12346" + "url": "127.0.0.1:8000" }, "db": { "user":"username1", @@ -213,7 +213,7 @@ func TestConfigActual(t *testing.T) { assert.Equal(t, true, config.MainClient() != nil) assert.Equal(t, &chaincfg.RegressionNetParams, config.MainChainCfg()) - assert.Equal(t, []string{"127.0.0.1:12345", "127.0.0.1:12346"}, config.SignerConfig().Signers) + assert.Equal(t, "127.0.0.1:8000", config.SignerConfig().Url) assert.Equal(t, DbConfig{ User: "username1", Password: "password2", @@ -253,14 +253,10 @@ func TestConfigStaychain(t *testing.T) { assert.Equal(t, &chaincfg.MainNetParams, config.MainChainCfg()) assert.Equal(t, "87e56bda501ba6a022f12e178e9f1ac03fb2c07f04e1dfa62ac9e1d83cd840e1", config.InitTx()) - assert.Equal(t, "51210381324c14a482646e9ad7cf82372021e5ecb9a7e1b67ee168dddf1e97dafe40af210376c091faaeb6bb3b74e0568db5dd499746d99437758a5cb1e60ab38f02e279c352ae", - config.InitScript()) assert.Equal(t, "cQca2KvrBnJJUCYa2tD4RXhiQshWLNMSK2A96ZKWo1SZkHhh3YLz", config.InitPK()) assert.Equal(t, []string{"0a090f710e47968aee906804f211cf10cde9a11e14908ca0f78cc55dd190ceaa", "0a090f710e47968aee906804f211cf10cde9a11e14908ca0f78cc55dd190ceaa"}, config.InitChaincodes()) assert.Equal(t, "2MxBi6eodnuoVCw8McGrf1nuoVhastqoBXB", config.TopupAddress()) - assert.Equal(t, "51210381324c14a482646e9ad7cf92372021e5ecb9a7e1b67ee168dddf1e97dafe40af210376c091faaeb6bb3b74e0568db5dd499746d99437758a5cb1e60ab38f02e279c352ae", - config.TopupScript()) assert.Equal(t, "cQca2KvrBnJJUCYa2tD4RXhiQshWLNMSK2A96ZKWo1SZkHhh3YLa", config.TopupPK()) assert.Equal(t, []string{"0a090f710e47968aee906804f211cf10cde9a11e14908ca0f78cc55dd190ceaa", "0a090f710e47968aee906804f211cf10cde9a11e14908ca0f78cc55dd190ceaa"}, config.TopupChaincodes()) @@ -272,9 +268,6 @@ func TestConfigStaychain(t *testing.T) { config.SetInitTx("aa") assert.Equal(t, "aa", config.InitTx()) - config.SetInitScript("bb") - assert.Equal(t, "bb", config.InitScript()) - config.SetInitPK("PKPKPK") assert.Equal(t, "PKPKPK", config.InitPK()) @@ -284,9 +277,6 @@ func TestConfigStaychain(t *testing.T) { config.SetTopupAddress("cc") assert.Equal(t, "cc", config.TopupAddress()) - config.SetTopupScript("dd") - assert.Equal(t, "dd", config.TopupScript()) - config.SetTopupPK("TOPUPPKPK") assert.Equal(t, "TOPUPPKPK", config.TopupPK()) @@ -309,9 +299,7 @@ func TestConfigStaychain(t *testing.T) { assert.Equal(t, nil, configErr) assert.Equal(t, "", config.InitTx()) - assert.Equal(t, "", config.InitScript()) assert.Equal(t, "", config.TopupAddress()) - assert.Equal(t, "", config.TopupScript()) assert.Equal(t, false, config.Regtest()) } @@ -480,24 +468,7 @@ func TestConfigSigner(t *testing.T) { } `) config, configErr = NewConfig(testConf) - assert.Equal(t, errors.New(fmt.Sprintf("%s: %s", ErrorConfigValueNotFound, SignerSignersName)), configErr) - - testConf = []byte(` - { - "main": { - "rpcurl": "", - "rpcuser": "", - "rpcpass": "", - "chain": "" - }, - "signer": { - "signers": "host" - } - } - `) - config, configErr = NewConfig(testConf) - assert.Equal(t, nil, configErr) - assert.Equal(t, []string{"host"}, config.SignerConfig().Signers) + assert.Equal(t, errors.New(fmt.Sprintf("%s: %s", ErrorConfigValueNotFound, Url)), configErr) testConf = []byte(` { @@ -508,12 +479,11 @@ func TestConfigSigner(t *testing.T) { "chain": "" }, "signer": { - "signers": "host", - "publisher": "*:5000" + "url": "host" } } `) config, configErr = NewConfig(testConf) assert.Equal(t, nil, configErr) - assert.Equal(t, "*:5000", config.SignerConfig().Publisher) + assert.Equal(t, "host", config.SignerConfig().Url) } diff --git a/crypto/scripts.go b/crypto/scripts.go index 4e79078..000eb5d 100644 --- a/crypto/scripts.go +++ b/crypto/scripts.go @@ -11,9 +11,9 @@ import ( "mainstay/log" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" ) // Various utility functions concerning multisig and scripts @@ -48,7 +48,7 @@ func ParseRedeemScript(script string) ([]*btcec.PublicKey, int) { } keystr := script[startIndex+2 : startIndex+2+2*keysize] keybytes, _ := hex.DecodeString(keystr) - pubkey, err := btcec.ParsePubKey(keybytes, btcec.S256()) + pubkey, err := btcec.ParsePubKey(keybytes) if err != nil { log.Error(err) } diff --git a/crypto/scripts_test.go b/crypto/scripts_test.go index fca8751..e92ee3c 100644 --- a/crypto/scripts_test.go +++ b/crypto/scripts_test.go @@ -12,7 +12,7 @@ import ( "mainstay/config" "mainstay/test" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/chaincfg" "github.com/stretchr/testify/assert" ) diff --git a/crypto/tweaking.go b/crypto/tweaking.go index e5c0211..61726d0 100644 --- a/crypto/tweaking.go +++ b/crypto/tweaking.go @@ -5,14 +5,14 @@ package crypto import ( - "crypto/ecdsa" "encoding/binary" + "encoding/hex" "math/big" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcutil" - "github.com/btcsuite/btcutil/hdkeychain" ) // Various utility functionalities concerning key tweaking under BIP-175 @@ -94,7 +94,7 @@ func TweakPrivKey(walletPrivKey *btcutil.WIF, tweak []byte, chainCfg *chaincfg.P } // Conver the result back to bytes - new private key - resPrivKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), resVal.Bytes()) + resPrivKey, _ := btcec.PrivKeyFromBytes(resVal.Bytes()) // Return priv key in wallet readable format resWalletPrivKey, err := btcutil.NewWIF(resPrivKey, chainCfg, walletPrivKey.CompressPubKey) @@ -105,7 +105,7 @@ func TweakPrivKey(walletPrivKey *btcutil.WIF, tweak []byte, chainCfg *chaincfg.P } // Get pay to pub key hash address from a private key -func GetAddressFromPrivKey(walletPrivKey *btcutil.WIF, chainCfg *chaincfg.Params) (btcutil.Address, error) { +func GetAddressFromPrivKey(walletPrivKey *btcutil.WIF, chainCfg *chaincfg.Params) (*btcutil.AddressWitnessPubKeyHash, error) { return GetAddressFromPubKey(walletPrivKey.PrivKey.PubKey(), chainCfg) } @@ -116,7 +116,7 @@ func tweakPubWithPathChild(child derivationPathChild, x *big.Int, y *big.Int) (* copy(childBytes, child[:]) // Get elliptic curve point for child path bytes - _, twkPubKey := btcec.PrivKeyFromBytes(btcec.S256(), childBytes) + _, twkPubKey := btcec.PrivKeyFromBytes(childBytes) // Add the two pub keys using addition on the elliptic curve return btcec.S256().Add(x, y, twkPubKey.ToECDSA().X, twkPubKey.ToECDSA().Y) @@ -140,7 +140,7 @@ func TweakExtendedKey(extndPubKey *hdkeychain.ExtendedKey, tweak []byte) (*hdkey childInt := binary.BigEndian.Uint32(childBytes) // get tweaked pubkey - extndPubKey, childErr = extndPubKey.Child(childInt) + extndPubKey, childErr = extndPubKey.Derive(childInt) if childErr != nil { return nil, childErr } @@ -164,13 +164,18 @@ func TweakPubKey(pubKey *btcec.PublicKey, tweak []byte) *btcec.PublicKey { resX, resY = tweakPubWithPathChild(pathChild, resX, resY) } - return (*btcec.PublicKey)(&ecdsa.PublicKey{btcec.S256(), resX, resY}) + x := new(btcec.FieldVal) + y := new(btcec.FieldVal) + x.SetByteSlice(resX.Bytes()) + y.SetByteSlice(resY.Bytes()) + + return btcec.NewPublicKey(x, y) } // Get pay to pub key hash address from a pub key -func GetAddressFromPubKey(pubkey *btcec.PublicKey, chainCfg *chaincfg.Params) (btcutil.Address, error) { - pubKeyHash := btcutil.Hash160(pubkey.SerializeCompressed()) // get pub key hash - addr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, chainCfg) // encode to address +func GetAddressFromPubKey(pubkey *btcec.PublicKey, chainCfg *chaincfg.Params) (*btcutil.AddressWitnessPubKeyHash, error) { + pubKeyHash := btcutil.Hash160(pubkey.SerializeCompressed()) // get pub key hash + addr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, chainCfg) // encode to address if err != nil { return nil, err } @@ -184,3 +189,15 @@ func IsAddrTweakedFromHash(address string, hash []byte, walletPrivKey *btcutil.W return address == tweakedAddr.String() } + +func HexToFieldVal(s string) *btcec.FieldVal { + b, err := hex.DecodeString(s) + if err != nil { + panic("invalid hex in source file: " + s) + } + var f btcec.FieldVal + if overflow := f.SetByteSlice(b); overflow { + panic("hex in source file overflows mod P: " + s) + } + return &f +} diff --git a/crypto/tweaking_test.go b/crypto/tweaking_test.go index 87241ba..2a77751 100644 --- a/crypto/tweaking_test.go +++ b/crypto/tweaking_test.go @@ -5,16 +5,15 @@ package crypto import ( - "crypto/ecdsa" "encoding/hex" "math/big" "testing" "mainstay/clients" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcutil/hdkeychain" "github.com/stretchr/testify/assert" ) @@ -27,18 +26,18 @@ func TestTweaking(t *testing.T) { // test GetWalletPrivKey privKey, errPrivKey := GetWalletPrivKey(testConfig.InitPK()) assert.Equal(t, nil, errPrivKey) - assert.Equal(t, "cQca2KvrBnJJUCYa2tD4RXhiQshWLNMSK2A96ZKWo1SZkHhh3YLz", privKey.String()) + assert.Equal(t, "cRb1ZU6gsHeifDnBRyRMfbMayWpnNtpKcNs7Z9XzqE87ZwW6Vqx8", privKey.String()) // test TweakprivKey tweakedPrivKey, errTweak := TweakPrivKey(privKey, tweak.CloneBytes(), mainChainCfg) assert.Equal(t, nil, errTweak) - assert.Equal(t, "cQca2KvrBnJJUCYa2tD4RXhiQshWLNMSK2A96ZKWo2XQUs8qChQu", tweakedPrivKey.String()) + assert.Equal(t, "cRb1ZU6gsHeifDnBRyRMfbMayWpnNtpKcNs7Z9XzqFCxJX1d6G95", tweakedPrivKey.String()) // test GetAddressFromPrivKey and IsAddrTweakedFromHash addr, errAddr := GetAddressFromPrivKey(tweakedPrivKey, mainChainCfg) assert.Equal(t, nil, errAddr) assert.Equal(t, true, IsAddrTweakedFromHash(addr.String(), tweak.CloneBytes(), privKey, mainChainCfg)) - assert.Equal(t, "mhUEBanz8ytATniaVvVNyERkHP9Vc9rpHj", addr.String()) + assert.Equal(t, "bcrt1qsthruz2meenyeqf57x80gyyx6x096xu3j3s4ep", addr.String()) // Test TweakPubKey and GetAddressFromPubKey pubkey := privKey.PrivKey.PubKey() @@ -120,8 +119,13 @@ func TestTweaking_childPathTweaking(t *testing.T) { tweakedPubX, tweakedPubY := tweakPubWithPathChild(testPathChild, pubX, pubY) // test matching priv - pub - _, tweakedPrivPub := btcec.PrivKeyFromBytes(btcec.S256(), tweakedVal.Bytes()) - tweakedPub := (*btcec.PublicKey)(&ecdsa.PublicKey{btcec.S256(), tweakedPubX, tweakedPubY}) + _, tweakedPrivPub := btcec.PrivKeyFromBytes(tweakedVal.Bytes()) + x := new(btcec.FieldVal) + y := new(btcec.FieldVal) + x.SetByteSlice(tweakedPubX.Bytes()) + y.SetByteSlice(tweakedPubY.Bytes()) + + tweakedPub := btcec.NewPublicKey(x, y) assert.Equal(t, tweakedPrivPub, tweakedPub) for it := 1; it < derivationPathSize; it++ { @@ -130,8 +134,13 @@ func TestTweaking_childPathTweaking(t *testing.T) { tweakedPubX, tweakedPubY = tweakPubWithPathChild(testPathChild, tweakedPubX, tweakedPubY) // test matching priv - pub - _, tweakedPrivPub := btcec.PrivKeyFromBytes(btcec.S256(), tweakedVal.Bytes()) - tweakedPub := (*btcec.PublicKey)(&ecdsa.PublicKey{btcec.S256(), tweakedPubX, tweakedPubY}) + _, tweakedPrivPub := btcec.PrivKeyFromBytes(tweakedVal.Bytes()) + x := new(btcec.FieldVal) + y := new(btcec.FieldVal) + x.SetByteSlice(tweakedPubX.Bytes()) + y.SetByteSlice(tweakedPubY.Bytes()) + + tweakedPub := btcec.NewPublicKey(x, y) assert.Equal(t, tweakedPrivPub, tweakedPub) } @@ -158,7 +167,7 @@ func TestTweaking_extendedKey(t *testing.T) { // get wif from config wif, errWif := GetWalletPrivKey(testConfig.InitPK()) assert.Equal(t, nil, errWif) - assert.Equal(t, "cQca2KvrBnJJUCYa2tD4RXhiQshWLNMSK2A96ZKWo1SZkHhh3YLz", wif.String()) + assert.Equal(t, "cRb1ZU6gsHeifDnBRyRMfbMayWpnNtpKcNs7Z9XzqE87ZwW6Vqx8", wif.String()) // get extended key from priv and pub keys privExtended := hdkeychain.NewExtendedKey([]byte{}, wif.PrivKey.Serialize(), chainCodeBytes, []byte{}, 0, 0, true) @@ -181,8 +190,8 @@ func TestTweaking_extendedKey(t *testing.T) { // cover future changes by hard-coding expected returned keys assert.Equal(t, privTweakedECPub, pubTweakedECPub) - assert.Equal(t, "YZ7jREvsw9bHjkqVtQJDgKJCvthBSXbvXDmB6wSxDhm8xxWKmX94MmTDHtjRekEJBTuKwvKZhDXxUZpWV2DA5C7L7mSerASe1KtLjsnNgR", + assert.Equal(t, "XPeyGpe3WFeNTwJYdhDL31PTq2iEra914HqQ1sKa66YDbcLJ3GDbeJEjtwxEY4N2My1PFBJQmqs715i2nZVfbky9kC31JBYh84cwYckDWk", privTweaked.String()) - assert.Equal(t, "YZ7jREvsw9bHjkqVtQJDgKJCvthBSXbvXDmB6wSxDhm8xxWKmX94MmTDNSHMcSTojdiHtQ1UnhEvW5sUf4xuL4SCPirMeJdVwEJ3BZ74CS", + assert.Equal(t, "XPeyGpe3WFeNTwJYdhDL31PTq2iEra914HqQ1sKa66YDbcLJ3GDbeJEjyMCsxHTzkBpziyHsXe7EuWtjV22ionDF19j7hDzxdpdYzuS2ZK", pubTweaked.String()) } diff --git a/go.mod b/go.mod index cc5abd2..a886383 100644 --- a/go.mod +++ b/go.mod @@ -3,27 +3,40 @@ module mainstay go 1.20 require ( - github.com/btcsuite/btcd v0.20.0-beta + github.com/btcsuite/btcd v0.24.0 github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d - github.com/pebbe/zmq4 v1.2.10 - github.com/satori/go.uuid v1.2.0 - github.com/stretchr/testify v1.3.0 go.mongodb.org/mongo-driver v1.3.1 ) require ( + github.com/aead/siphash v1.0.1 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect + github.com/btcsuite/btcd/btcutil v1.1.5 // indirect + github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect + github.com/btcsuite/winsvc v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/decred/dcrd/lru v1.0.0 // indirect github.com/go-stack/stack v1.8.0 // indirect - github.com/golang/snappy v0.0.1 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/jessevdk/go-flags v1.4.0 // indirect + github.com/jrick/logrotate v1.0.0 // indirect + github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 // indirect github.com/klauspost/compress v1.9.5 // indirect github.com/pkg/errors v0.8.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/satori/go.uuid v1.2.0 // indirect + github.com/stretchr/testify v1.8.4 // indirect + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc // indirect - golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 // indirect + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect - golang.org/x/text v0.3.2 // indirect + golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed // indirect + golang.org/x/text v0.3.3 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b5bb1ae..0587393 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,24 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/btcsuite/btcd v0.20.0-beta h1:DnZGUjFbRkpytojHWwy6nfUSA7vFrzWXDLpFNzt74ZA= github.com/btcsuite/btcd v0.20.0-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd v0.24.0 h1:gL3uHE/IaFj6fcZSu03SvqPMSx7s/dPzfpG/atRwWdo= +github.com/btcsuite/btcd v0.24.0/go.mod h1:K4IDc1593s8jKXIF7yS7yCTSxrknB9z0STzc2j6XgE4= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= @@ -9,15 +26,25 @@ github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+q github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= @@ -45,34 +72,55 @@ github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGt github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pebbe/zmq4 v1.2.9 h1:JlHcdgq6zpppNR1tH0wXJq0XK03pRUc4lBlHTD7aj/4= +github.com/pebbe/zmq4 v1.2.9/go.mod h1:nqnPueOapVhE2wItZ0uOErngczsJdLOGkebMxaO8r48= github.com/pebbe/zmq4 v1.2.10 h1:wQkqRZ3CZeABIeidr3e8uQZMMH5YAykA/WN0L5zkd1c= github.com/pebbe/zmq4 v1.2.10/go.mod h1:nqnPueOapVhE2wItZ0uOErngczsJdLOGkebMxaO8r48= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= @@ -94,9 +142,12 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= @@ -110,9 +161,14 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5 h1:8dUaAV7K4uHsF56JQWkprecIQKdPHtR9jCHF5nB8uzc= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -126,20 +182,41 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index ecfc292..730036a 100644 --- a/main.go +++ b/main.go @@ -22,10 +22,8 @@ import ( var ( tx0 string - script0 string chaincodes string addrTopup string - scriptTopup string isRegtest bool mainConfig *config.Config ) @@ -33,10 +31,8 @@ var ( func parseFlags() { flag.BoolVar(&isRegtest, "regtest", false, "Use regtest wallet configuration instead of user wallet") flag.StringVar(&tx0, "tx", "", "Tx id for genesis attestation transaction") - flag.StringVar(&script0, "script", "", "Redeem script in case multisig is used") flag.StringVar(&chaincodes, "chaincodes", "", "Chaincodes for multisig pubkeys") flag.StringVar(&addrTopup, "addrTopup", "", "Address for topup transaction") - flag.StringVar(&scriptTopup, "scriptTopup", "", "Redeem script for topup") flag.Parse() } @@ -55,15 +51,14 @@ func init() { } // if either tx or script not set throw error - if tx0 == "" || script0 == "" || chaincodes == "" { - if mainConfig.InitTx() == "" || mainConfig.InitScript() == "" || len(mainConfig.InitChaincodes()) == 0 { + if tx0 == "" || chaincodes == "" { + if mainConfig.InitTx() == "" || len(mainConfig.InitChaincodes()) == 0 { flag.PrintDefaults() log.Error(`Need to provide all -tx, -script and -chaincode arguments. To use test configuration set the -regtest flag.`) } } else { mainConfig.SetInitTx(tx0) - mainConfig.SetInitScript(script0) chaincodesList := strings.Split(chaincodes, ",") // string to string slice for i := range chaincodesList { // trim whitespace @@ -71,9 +66,8 @@ func init() { } mainConfig.SetInitChaincodes(chaincodesList) } - if addrTopup != "" && scriptTopup != "" { + if addrTopup != "" { mainConfig.SetTopupAddress(addrTopup) - mainConfig.SetTopupScript(scriptTopup) } mainConfig.SetRegtest(isRegtest) } @@ -87,7 +81,7 @@ func main() { dbInterface := db.NewDbMongo(ctx, mainConfig.DbConfig()) server := attestation.NewAttestServer(dbInterface) - signer := attestation.NewAttestSignerZmq(mainConfig.SignerConfig()) + signer := attestation.NewAttestSignerHttp(mainConfig.SignerConfig()) attestService := attestation.NewAttestService(ctx, wg, server, signer, mainConfig) c := make(chan os.Signal) diff --git a/mainstay.exe b/mainstay.exe new file mode 100644 index 0000000..1c60506 Binary files /dev/null and b/mainstay.exe differ diff --git a/messengers/dealerzmq.go b/messengers/dealerzmq.go deleted file mode 100644 index 2ecc7cc..0000000 --- a/messengers/dealerzmq.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2018 CommerceBlock Team -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -package messengers - -import ( - "fmt" - - zmq "github.com/pebbe/zmq4" -) - -// Zmq router wrapper -type DealerZmq struct { - socket *zmq.Socket -} - -// Read message from router socket -func (d *DealerZmq) ReadMessage() []byte { - msg, _ := d.socket.RecvBytes(0) - return msg -} - -// Send message from router socket to identity -func (d *DealerZmq) SendMessage(msg []byte) { - d.socket.SendBytes([]byte(""), zmq.SNDMORE) // inform listener - d.socket.SendBytes(msg, 0) // send message -} - -// Close underlying zmq socket - To be used with defer -func (d *DealerZmq) Close() { - d.socket.Close() - return -} - -// Return underlying socket -func (d *DealerZmq) Socket() *zmq.Socket { - return d.socket -} - -// Return new DealerZmq instance -// Bind to localhost and port provided and set identity -func NewDealerZmq(port int, identity string, poller *zmq.Poller) *DealerZmq { - // Prepare our router - dealer, _ := zmq.NewSocket(zmq.DEALER) - dealer.SetIdentity(identity) // unique client identity - dealer.Connect(fmt.Sprintf("tcp://localhost:%d", port)) - - if poller != nil { - poller.Add(dealer, zmq.POLLIN) - } - - return &DealerZmq{dealer} -} diff --git a/messengers/doc.go b/messengers/doc.go deleted file mode 100644 index 5faf8c4..0000000 --- a/messengers/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -/* -Package messengers implements interfaces for messengers required by attestation client and signature signers. - -Publish/subscribe zmq interfaces are implemented. - -Mock interfaces for unit-testing are also implemented. -*/ -package messengers diff --git a/messengers/publisherzmq.go b/messengers/publisherzmq.go deleted file mode 100644 index aab6dfc..0000000 --- a/messengers/publisherzmq.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2018 CommerceBlock Team -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -package messengers - -import ( - "fmt" - - zmq "github.com/pebbe/zmq4" -) - -// Zmq publisher wrapper -type PublisherZmq struct { - socket *zmq.Socket -} - -// Publish message via zmq socket -func (p *PublisherZmq) SendMessage(msg []byte, topic string) { - p.socket.SendBytes([]byte(topic), zmq.SNDMORE) - p.socket.SendBytes(msg, 0) -} - -// Close underlying zmq socket - To be used with defer -func (p *PublisherZmq) Close() { - p.socket.Close() - return -} - -// Return underlying socket -func (p *PublisherZmq) Socket() *zmq.Socket { - return p.socket -} - -// Return new PublisherZmq instance -// Bind address provided to constructor -func NewPublisherZmq(addr string, poller *zmq.Poller) *PublisherZmq { - // Prepare our publisher - publisher, _ := zmq.NewSocket(zmq.PUB) - publisher.Bind(fmt.Sprintf("tcp://%s", addr)) - - poller.Add(publisher, zmq.POLLOUT) - - return &PublisherZmq{publisher} -} diff --git a/messengers/routerzmq.go b/messengers/routerzmq.go deleted file mode 100644 index 1867979..0000000 --- a/messengers/routerzmq.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2018 CommerceBlock Team -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -package messengers - -import ( - "fmt" - - zmq "github.com/pebbe/zmq4" -) - -// Zmq router wrapper -type RouterZmq struct { - socket *zmq.Socket -} - -// Read message from router socket -func (r *RouterZmq) ReadMessage() []byte { - msg, _ := r.socket.RecvBytes(0) - return msg -} - -// Send message from router socket to identity -func (r *RouterZmq) SendMessage(identity []byte, msg []byte) { - r.socket.SendBytes(identity, zmq.SNDMORE) - r.socket.SendBytes([]byte(""), zmq.SNDMORE) - r.socket.SendBytes(msg, 0) -} - -// Close underlying zmq socket - To be used with defer -func (r *RouterZmq) Close() { - r.socket.Close() - return -} - -// Return underlying socket -func (r *RouterZmq) Socket() *zmq.Socket { - return r.socket -} - -// Return new RouterZmq instance -// Bind to localhost and port provided -func NewRouterZmq(port int, poller *zmq.Poller) *RouterZmq { - // Prepare our router - router, _ := zmq.NewSocket(zmq.ROUTER) - router.Bind(fmt.Sprintf("tcp://*:%d", port)) - - if poller != nil { - poller.Add(router, zmq.POLLIN) - } - - return &RouterZmq{router} -} diff --git a/messengers/subscriberzmq.go b/messengers/subscriberzmq.go deleted file mode 100644 index 446a985..0000000 --- a/messengers/subscriberzmq.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2018 CommerceBlock Team -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -package messengers - -import ( - "fmt" - "strings" - - zmq "github.com/pebbe/zmq4" -) - -// Zmq subscriber wrapper -type SubscriberZmq struct { - socket *zmq.Socket -} - -// Read topic-msg from zmq socket -func (s *SubscriberZmq) ReadMessage() (string, []byte) { - - // Read envelope with address - address, _ := s.socket.RecvBytes(0) - // Read message contents - contents, _ := s.socket.RecvBytes(0) - - return string(address), contents -} - -// Close underlying zmq socket and remove from poller - To be used with defer -func (s *SubscriberZmq) Close(poller *zmq.Poller) { - poller.RemoveBySocket(s.Socket()) - s.socket.Close() -} - -// Return underlying socket -func (s *SubscriberZmq) Socket() *zmq.Socket { - return s.socket -} - -// Return new SubscriberZmq instance -// Connect to address provided and subscribe to topics -func NewSubscriberZmq(address string, topics []string, poller *zmq.Poller) *SubscriberZmq { - - // Get host/port - addrComp := strings.Split(address, ":") - - // Prepare our subscriber - subscriber, _ := zmq.NewSocket(zmq.SUB) - subscriber.Connect(fmt.Sprintf("tcp://%s:%s", addrComp[0], addrComp[1])) - - for _, topic := range topics { - subscriber.SetSubscribe(topic) - } - - poller.Add(subscriber, zmq.POLLIN) - - return &SubscriberZmq{subscriber} -} diff --git a/staychain/chainverifier.go b/staychain/chainverifier.go index 17a7877..1246eed 100644 --- a/staychain/chainverifier.go +++ b/staychain/chainverifier.go @@ -15,10 +15,10 @@ import ( "mainstay/crypto" "mainstay/models" - "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcutil/hdkeychain" ) // mainstay API url consts diff --git a/test/demo-init.sh b/test/demo-init.sh deleted file mode 100755 index 2ecaba3..0000000 --- a/test/demo-init.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -shopt -s expand_aliases - -alias btcd="bitcoind -datadir=$HOME/btc-datadir" -alias btcl="bitcoin-cli -datadir=$HOME/btc-datadir" - -btcl stop -sleep 1 - -rm -r ~/btc-datadir -mkdir ~/btc-datadir - -printf '%s\n' '#!/bin/sh' 'rpcuser=user' \ - 'rpcpassword=pass' \ - 'rpcport=18443' \ - 'keypool=0' \ - 'deprecatedrpc=signrawtransaction' \ - 'server=1' \ - 'regtest=1' \ - 'daemon=1' \ - 'txindex=1' > ~/btc-datadir/bitcoin.conf - -btcd -sleep 2 - -btcl generate 103 -sleep 1 - -#multisig=$(btcl createmultisig 1 '''["'''$pub1'''", "'''$pub2'''"]''') -#multisigaddr=$(echo $multisig | jq --raw-output '.address') - -# btcl importaddress "2N74sgEvpJRwBZqjYUEXwPfvuoLZnRaF1xJ" -# btcl importaddress "512103e52cf15e0a5cf6612314f077bb65cf9a6596b76c0fcb34b682f673a8314c7b33210325bf82856a8fdcc7a2c08a933343d2c6332c4c252974d6b09b6232ea4080462652ae" "" true true -btcl sendtoaddress "2N74sgEvpJRwBZqjYUEXwPfvuoLZnRaF1xJ" $(btcl getbalance) "" "" true -sleep 1 - -btcl generate 1 -sleep 1 - -btcl stop -sleep 1 - -printf '%s\n' '#!/bin/sh' 'rpcuser=user' \ - 'rpcpassword=pass' \ - 'rpcport=18443' \ - 'keypool=0' \ - 'deprecatedrpc=signrawtransaction' \ - 'blocksonly=1' \ - 'server=1' \ - 'regtest=1' \ - 'daemon=1' \ - 'txindex=1' > ~/btc-datadir/bitcoin.conf - -btcd -sleep 2 - -btcl listunspent -sleep 10 diff --git a/test/test-init-multi.sh b/test/test-init-multi.sh deleted file mode 100755 index 14f09f2..0000000 --- a/test/test-init-multi.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -shopt -s expand_aliases - -alias btcd="bitcoind -datadir=/tmp/btc-datadir" -alias btcl="bitcoin-cli -datadir=/tmp/btc-datadir" - -btcl stop -sleep 0.5 - -rm -r /tmp/btc-datadir ; -mkdir /tmp/btc-datadir ; - -printf '%s\n' '#!/bin/sh' 'rpcuser=user' \ - 'rpcpassword=pass' \ - 'rpcport=18443' \ - 'keypool=0' \ - 'deprecatedrpc=signrawtransaction' \ - 'server=1' \ - 'regtest=1' \ - 'daemon=1' \ - 'txindex=1' > /tmp/btc-datadir/bitcoin.conf - -btcd -sleep 0.5 - -btcl generate 103 -sleep 0.5 - -btcl importaddress "2N53Hkuyz8gSM2swdAvt7yqzxH8vVCKxgvK" -btcl importaddress "522103dfc3e2f3d0a3ebf9265d30a87764206d2ee0198820eee200eee4fb3f18eaac43210375f474311ba6248dc7ea1d4044114ee8e8c9cad3974ce2ae5a44dfaa285f3f372103cf016cd19049437c1cfa241bcf1baac58e22c71cae2dc06cb15259ee2f61bb2b53ae" "" true true -btcl sendtoaddress "2N53Hkuyz8gSM2swdAvt7yqzxH8vVCKxgvK" $(btcl getbalance) "" "" true - -btcl generate 1 diff --git a/test/test-init.sh b/test/test-init.sh index c118871..e89d70e 100755 --- a/test/test-init.sh +++ b/test/test-init.sh @@ -26,8 +26,7 @@ sleep 0.5 btcl generate 103 sleep 0.5 -btcl importaddress "2N74sgEvpJRwBZqjYUEXwPfvuoLZnRaF1xJ" -btcl importaddress "512103e52cf15e0a5cf6612314f077bb65cf9a6596b76c0fcb34b682f673a8314c7b33210325bf82856a8fdcc7a2c08a933343d2c6332c4c252974d6b09b6232ea4080462652ae" "" true true -btcl sendtoaddress "2N74sgEvpJRwBZqjYUEXwPfvuoLZnRaF1xJ" $(btcl getbalance) "" "" true +btcl importaddress "bcrt1q7h6ue5w39ramd4ux6gtxh6swnrefpcfgt7vl64" +btcl sendtoaddress "bcrt1q7h6ue5w39ramd4ux6gtxh6swnrefpcfgt7vl64" $(btcl getbalance) "" "" true btcl generate 1 diff --git a/test/test.go b/test/test.go index ded4bc2..78905ca 100644 --- a/test/test.go +++ b/test/test.go @@ -19,9 +19,6 @@ import ( "mainstay/models" ) -// For regtest attestation demonstration -const DemoInitPath = "/src/mainstay/test/demo-init.sh" - // For unit-testing const TestInitPath = "/src/mainstay/test/test-init.sh" @@ -34,7 +31,7 @@ var testConf = []byte(` "chain": "regtest" }, "signer": { - "signers": "127.0.0.1:5001,127.0.0.1:5002,127.0.0.1:5003" + "url": "http://127.0.0.1:8000/sign" }, "db": { "user":"serviceUser", @@ -47,22 +44,18 @@ var testConf = []byte(` `) // test parameters for a 1-2 multisig redeemScript and P2SH address -const Address = "2N74sgEvpJRwBZqjYUEXwPfvuoLZnRaF1xJ" -const Script = "512103e52cf15e0a5cf6612314f077bb65cf9a6596b76c0fcb34b682f673a8314c7b33210325bf82856a8fdcc7a2c08a933343d2c6332c4c252974d6b09b6232ea4080462652ae" +const Address = "bcrt1q7h6ue5w39ramd4ux6gtxh6swnrefpcfgt7vl64" const InitChaincodes = "14df7ece79e83f0f479a37832d770294014edc6884b0c8bfa2e0aaf51fb00229,14df7ece79e83f0f479a37832d770294014edc6884b0c8bfa2e0aaf51fb00229" -// pubkey hsm - "0325bf82856a8fdcc7a2c08a933343d2c6332c4c252974d6b09b6232ea40804626" -// pubkey main - "03e52cf15e0a5cf6612314f077bb65cf9a6596b76c0fcb34b682f673a8314c7b33" -const PrivMain = "cQca2KvrBnJJUCYa2tD4RXhiQshWLNMSK2A96ZKWo1SZkHhh3YLz" +const PrivMain = "cRb1ZU6gsHeifDnBRyRMfbMayWpnNtpKcNs7Z9XzqE87ZwW6Vqx8" + +const PublicKey = "021037eb111561e14bcdfc0676af222041b2e4f4c2ac7309ec4763a1d7e61fa24f" -// test parameters for a 1-2 multisig redeemScript and P2SH address for TOPUP -const TopupAddress = "2NG3LCHuausgrYEsJQjYqhmgDVjRPYYrB5w" -const TopupScript = "512102253297770861be1e512e00329c91bc85300fa46c39d603320d1f5b5e04eaf3342103a10b8872e1aca43b6c0376a20052efa3789fae2fae82972920b8cbba5bc9f33d52ae" +// test parameters for TOPUP +const TopupAddress = "bcrt1qzkm8f3lu6kljfs875mddl9rs72ses5v49vplrl" -// address - "2Mvi6msoTtozNPAfuSUtTKCWG8ryMZvheuF" -const TopupPrivMain = "cPLAx2s7x8jBc58Ruyp2dUsG42D5jgY6FzKcSNPiMMeNWw1h6JXX" +const TopupPrivMain = "cVAAcEr9kGxG7Mo9nZnDsJ2aeQ4DP7588UuJqVAuuk3YT8YU6rEq" -// address - "2NGWnYUFHaq6f5KB8qGHcjV3sp6vN5Wc2hu" const TopupPrivClient = "cUPcXWas6iaCWFKZc2rogeY4JK2cHAtFGS2h9CmfEmL3dzehP8K7" // Test structure @@ -75,15 +68,9 @@ type Test struct { // NewTest returns a pointer to a Test instance func NewTest(logOutput bool, isRegtest bool) *Test { // Run init test script that sets up bitcoin and ocean - var initPath string - if isRegtest { // for running the demon in regtest mode along with ocean demo - initPath = os.Getenv("GOPATH") + DemoInitPath - } else { // for running unit tests - initPath = os.Getenv("GOPATH") + TestInitPath - } + initPath := os.Getenv("GOPATH") + TestInitPath cmd := exec.Command("/bin/sh", initPath) - output, err := cmd.Output() if err != nil { log.Error(err) @@ -113,11 +100,10 @@ func NewTest(logOutput bool, isRegtest bool) *Test { config.SetInitTx(txid) config.SetInitPK(PrivMain) - config.SetInitScript(Script) config.SetInitChaincodes(strings.Split(InitChaincodes, ",")) + config.SetInitPublicKey(PublicKey) // custom config for top up process - config.SetTopupScript(TopupScript) config.SetTopupAddress(TopupAddress) config.SetTopupPK(TopupPrivMain) @@ -162,80 +148,3 @@ func DoRegtestWork(dbMongo *db.DbMongo, config *confpkg.Config, wg *sync.WaitGro } } } - -// For unit-testing -const TestInitPathMulti = "/src/mainstay/test/test-init-multi.sh" - -// Test Multi structure -// Set up testing environment for use by regtest demo or unit tests -// Use multiple configs to allow multiple transaction signers for testing -type TestMulti struct { - Configs []*confpkg.Config - OceanClient clients.SidechainClient -} - -// test parameters for a 2-3 multisig redeemScript and P2SH address -const AddressMulti = "2N53Hkuyz8gSM2swdAvt7yqzxH8vVCKxgvK" -const ScriptMulti = "522103dfc3e2f3d0a3ebf9265d30a87764206d2ee0198820eee200eee4fb3f18eaac43210375f474311ba6248dc7ea1d4044114ee8e8c9cad3974ce2ae5a44dfaa285f3f372103cf016cd19049437c1cfa241bcf1baac58e22c71cae2dc06cb15259ee2f61bb2b53ae" -const InitChaincodesMulti = "14df7ece79e83f0f479a37832d770294014edc6884b0c8bfa2e0aaf51fb00229,24df7ece79e83f0f479a37832d770294014edc6884b0c8bfa2e0aaf51fb00229,34df7ece79e83f0f479a37832d770294014edc6884b0c8bfa2e0aaf51fb00229" -const PrivsMulti = "cUY3m2QRr8tGypHsY8UdPH7W7QtpZPJEe4CWsv4HoK1721cHKxQx,cNtt35LyNnFTTJGnFC1fpH5FFJLtfXUYGYJM9ZZLpt5Yp5fSPYWV,cRUq5ww43yFUSdTyAWrbXxxr838qr7oHiiQKRnPGDgJjbCRZacjo" - -// NewTestMulti returns a pointer to a TestMulti instance -func NewTestMulti() *TestMulti { - - initPath := os.Getenv("GOPATH") + TestInitPathMulti - cmd := exec.Command("/bin/sh", initPath) - _, err := cmd.Output() - if err != nil { - log.Error(err) - } - - // get config - config, configErr := confpkg.NewConfig(testConf) - if configErr != nil { - log.Error(configErr) - } - - // Get transaction for Address as initial TX for attestation chain - unspent, errUnspent := config.MainClient().ListTransactions("*") - if errUnspent != nil { - log.Error(errUnspent) - } - var txid string - for _, vout := range unspent { - if vout.Address == AddressMulti { - txid = vout.TxID - } - } - - var configs []*confpkg.Config - chaincodesList := strings.Split(InitChaincodesMulti, ",") - privsList := strings.Split(PrivsMulti, ",") - - // config for each private key - for _, priv := range privsList { - // get config - config, configErr := confpkg.NewConfig(testConf) - if configErr != nil { - log.Error(configErr) - } - - config.SetInitTx(txid) - config.SetInitPK(priv) - config.SetInitScript(ScriptMulti) - config.SetInitChaincodes(chaincodesList) - - // use same as init for topup for ease - config.SetTopupScript(ScriptMulti) - config.SetTopupAddress(AddressMulti) - config.SetTopupPK(priv) - - config.SetRegtest(true) - - configs = append(configs, config) - } - - oceanClient := confpkg.NewClientFromConfig("ocean", true, testConf) - - return &TestMulti{configs, oceanClient} -}