diff --git a/src/validation.cpp b/src/validation.cpp index 8bdb9f28dd..2eacdf3be1 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2294,6 +2294,72 @@ bool BitcoindRPCCheck(const bool init) return true; } +/* Takes federation redeeem script and adds HMAC_SHA256(pubkey, witnessProgram) as a tweak to each pubkey */ +CScript calculate_contract(const CScript& federationRedeemScript, const CScript& witnessProgram) { + CScript scriptDestination(federationRedeemScript); + int version; + std::vector program; + if (!witnessProgram.IsWitnessProgram(version, program)) { + assert(false); + } + txnouttype type; + std::vector > solutions; + // Sanity check fedRedeemScript + if (!Solver(federationRedeemScript, type, solutions) || (type != TX_MULTISIG && type != TX_TRUE)) { + assert(false); + } + + { + CScript::iterator sdpc = scriptDestination.begin(); + std::vector vch; + opcodetype opcodeTmp; + while (scriptDestination.GetOp(sdpc, opcodeTmp, vch)) + { + if (vch.size() == 33) + { + + unsigned char tweak[32]; + size_t pub_len = 33; + int ret; + unsigned char *pub_start = &(*(sdpc - pub_len)); + CHMAC_SHA256(pub_start, pub_len).Write(witnessProgram.data(), witnessProgram.size()).Finalize(tweak); + secp256k1_pubkey watchman; + secp256k1_pubkey tweaked; + ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_verify_amounts, &watchman, pub_start, pub_len); + assert(ret == 1); + ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_verify_amounts, &tweaked, pub_start, pub_len); + assert(ret == 1); + // If someone creates a tweak that makes this fail, they broke SHA256 + ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx_verify_amounts, &tweaked, tweak); + assert(ret == 1); + ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx_verify_amounts, pub_start, &pub_len, &tweaked, SECP256K1_EC_COMPRESSED); + assert(ret == 1); + assert(pub_len == 33); + + // Sanity checks to reduce pegin risk. If the tweaked + // value flips a bit, we may lose pegin funds irretrievably. + // We take the tweak, derive its pubkey and check that + // `tweaked - watchman = tweak` to check the computation + // two different ways + secp256k1_pubkey tweaked2; + ret = secp256k1_ec_pubkey_create(secp256k1_ctx_verify_amounts, &tweaked2, tweak); + assert(ret); + ret = secp256k1_ec_pubkey_negate(secp256k1_ctx_verify_amounts, &watchman); + assert(ret); + secp256k1_pubkey* pubkey_combined[2]; + pubkey_combined[0] = &watchman; + pubkey_combined[1] = &tweaked; + secp256k1_pubkey maybe_tweaked2; + ret = secp256k1_ec_pubkey_combine(secp256k1_ctx_verify_amounts, &maybe_tweaked2, pubkey_combined, 2); + assert(ret); + assert(!memcmp(&maybe_tweaked2, &tweaked2, 64)); + } + } + } + + return scriptDestination; +} + bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& prevout) { // Format on stack is as follows: @@ -2395,45 +2461,10 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p } // Check that the witness program matches the p2ch on the transaction output - opcodetype opcodeTmp; - CScript fedpegscript = Params().GetConsensus().fedpegScript; - - // fedpegscript should be multisig or OP_TRUE - // When set to OP_TRUE, the witness program can be any valid witness program - // as there are no pubkeys to tweak. This means typically in regtest there - // is no spending authorization for peg-in inputs. - txnouttype type; - std::vector > solutions; - if (!Solver(fedpegscript, type, solutions) || (type != TX_MULTISIG && type != TX_TRUE)) { - assert(false); - } - { - CScript::iterator sdpc = fedpegscript.begin(); - std::vector vch; - while (fedpegscript.GetOp(sdpc, opcodeTmp, vch)) - { - if (vch.size() == 33) - { - unsigned char tweak[32]; - size_t pub_len = 33; - int ret; - unsigned char *pub_start = &(*(sdpc - pub_len)); - CHMAC_SHA256(pub_start, pub_len).Write(witness_program.data(), witness_program.size()).Finalize(tweak); - secp256k1_pubkey pubkey; - ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_verify_amounts, &pubkey, pub_start, pub_len); - assert(ret == 1); - // If someone creates a tweak that makes this fail, they broke SHA256 - ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx_verify_amounts, &pubkey, tweak); - assert(ret == 1); - ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx_verify_amounts, pub_start, &pub_len, &pubkey, SECP256K1_EC_COMPRESSED); - assert(ret == 1); - assert(pub_len == 33); - } - } - } + CScript tweaked_fedpegscript = calculate_contract(Params().GetConsensus().fedpegScript, witness_program); // Check against expected p2sh output - CScriptID expectedP2SH(fedpegscript); + CScriptID expectedP2SH(tweaked_fedpegscript); CScript expected_script(CScript() << OP_HASH160 << ToByteVector(expectedP2SH) << OP_EQUAL); if (pegtx->vout[prevout.n].scriptPubKey != expected_script) { return false; diff --git a/src/validation.h b/src/validation.h index 48e6ea7778..3a424b479a 100644 --- a/src/validation.h +++ b/src/validation.h @@ -542,6 +542,9 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr /** Produce the necessary coinbase commitment for a block (modifies the hash, don't call for mined blocks). */ std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); +/** Calculates script necessary for p2ch peg-in transactions */ +CScript calculate_contract(const CScript& federationRedeemScript, const CScript& witnessProgram); + /** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */ class CVerifyDB { public: diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 467fadd4d0..9b3146bd89 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3389,72 +3389,6 @@ public: static CSecp256k1Init instance_of_csecp256k1; } -/* Takes federation redeeem script and adds SHA2(witnessProgram) as a tweak to each pubkey */ -CScriptID calculate_contract(const CScript& federationRedeemScript, const CScript& witnessProgram) { - CScript scriptDestination(federationRedeemScript); - int version; - std::vector program; - if (!witnessProgram.IsWitnessProgram(version, program)) { - assert(false); - } - txnouttype type; - std::vector > solutions; - // Sanity check fedRedeemScript - if (!Solver(federationRedeemScript, type, solutions) || (type != TX_MULTISIG && type != TX_TRUE)) { - assert(false); - } - - { - CScript::iterator sdpc = scriptDestination.begin(); - vector vch; - opcodetype opcodeTmp; - while (scriptDestination.GetOp(sdpc, opcodeTmp, vch)) - { - if (vch.size() == 33) - { - - unsigned char tweak[32]; - size_t pub_len = 33; - int ret; - unsigned char *pub_start = &(*(sdpc - pub_len)); - CHMAC_SHA256(pub_start, pub_len).Write(witnessProgram.data(), witnessProgram.size()).Finalize(tweak); - secp256k1_pubkey watchman; - secp256k1_pubkey tweaked; - ret = secp256k1_ec_pubkey_parse(secp256k1_ctx, &watchman, pub_start, pub_len); - assert(ret == 1); - ret = secp256k1_ec_pubkey_parse(secp256k1_ctx, &tweaked, pub_start, pub_len); - assert(ret == 1); - // If someone creates a tweak that makes this fail, they broke SHA256 - ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx, &tweaked, tweak); - assert(ret == 1); - ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx, pub_start, &pub_len, &tweaked, SECP256K1_EC_COMPRESSED); - assert(ret == 1); - assert(pub_len == 33); - - // Sanity checks to reduce pegin risk. If the tweaked - // value flips a bit, we may lose pegin funds irretrievably. - // We take the tweak, derive its pubkey and check that - // `tweaked - watchman = tweak` to check the computation - // two different ways - secp256k1_pubkey tweaked2; - ret = secp256k1_ec_pubkey_create(secp256k1_ctx, &tweaked2, tweak); - assert(ret); - ret = secp256k1_ec_pubkey_negate(secp256k1_ctx, &watchman); - assert(ret); - secp256k1_pubkey* pubkey_combined[2]; - pubkey_combined[0] = &watchman; - pubkey_combined[1] = &tweaked; - secp256k1_pubkey maybe_tweaked2; - ret = secp256k1_ec_pubkey_combine(secp256k1_ctx, &maybe_tweaked2, pubkey_combined, 2); - assert(ret); - assert(!memcmp(&maybe_tweaked2, &tweaked2, 64)); - } - } - } - - return CScriptID(scriptDestination); -} - UniValue getpeginaddress(const JSONRPCRequest& request) { if (!EnsureWalletIsAvailable(request.fHelp)) @@ -3492,7 +3426,7 @@ UniValue getpeginaddress(const JSONRPCRequest& request) CScript witProg = GetScriptForWitness(destScript); //Call contracthashtool, get deposit address on mainchain. - CParentBitcoinAddress destAddr(calculate_contract(Params().GetConsensus().fedpegScript, witProg)); + CParentBitcoinAddress destAddr(CScriptID(calculate_contract(Params().GetConsensus().fedpegScript, witProg))); UniValue fundinginfo(UniValue::VOBJ); @@ -3568,7 +3502,7 @@ unsigned int GetPeginTxnOutputIndex(const Sidechain::Bitcoin::CTransaction& txn, { unsigned int nOut = 0; //Call contracthashtool - CScript mainchain_script = GetScriptForDestination(calculate_contract(Params().GetConsensus().fedpegScript, witnessProgram)); + CScript mainchain_script = GetScriptForDestination(CScriptID(calculate_contract(Params().GetConsensus().fedpegScript, witnessProgram))); for (; nOut < txn.vout.size(); nOut++) if (txn.vout[nOut].scriptPubKey == mainchain_script) break;