// // Helper: create two dummy transactions, each with // two outputs. The first has 11 and 50 CENT outputs // paid to a TX_PUBKEY, the second 21 and 22 CENT outputs // paid to a TX_PUBKEYHASH. // static std::vector<CMutableTransaction> SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) { std::vector<CMutableTransaction> dummyTransactions; dummyTransactions.resize(2); // Add some keys to the keystore: CKey key[4]; for (int i = 0; i < 4; i++) { key[i].MakeNewKey(i % 2); keystoreRet.AddKey(key[i]); } // Create some dummy input transactions dummyTransactions[0].vout.resize(2); dummyTransactions[0].vout[0].nValue = 11*CENT; dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG; dummyTransactions[0].vout[1].nValue = 50*CENT; dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG; AddCoins(coinsRet, dummyTransactions[0], 0); dummyTransactions[1].vout.resize(2); dummyTransactions[1].vout[0].nValue = 21*CENT; dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID()); dummyTransactions[1].vout[1].nValue = 22*CENT; dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID()); AddCoins(coinsRet, dummyTransactions[1], 0); return dummyTransactions; }
// Microbenchmark for verification of a basic P2WPKH script. Can be easily // modified to measure performance of other types of scripts. static void VerifyScriptBench(benchmark::State& state) { const int flags = SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH; const int witnessversion = 0; // Keypair. CKey key; static const std::array<unsigned char, 32> vchKey = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }; key.Set(vchKey.begin(), vchKey.end(), false); CPubKey pubkey = key.GetPubKey(); uint160 pubkeyHash; CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(pubkeyHash.begin()); // Script. CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash); CScript scriptSig; CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG; const CMutableTransaction& txCredit = BuildCreditingTransaction(scriptPubKey); CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit); CScriptWitness& witness = txSpend.vin[0].scriptWitness; witness.stack.emplace_back(); key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0), witness.stack.back()); witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL)); witness.stack.push_back(ToByteVector(pubkey)); // Benchmark. while (state.KeepRunning()) { ScriptError err; bool success = VerifyScript( txSpend.vin[0].scriptSig, txCredit.vout[0].scriptPubKey, &txSpend.vin[0].scriptWitness, flags, MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue), &err); assert(err == SCRIPT_ERR_OK); assert(success); #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << txSpend; int csuccess = fujicoinconsensus_verify_script_with_amount( txCredit.vout[0].scriptPubKey.data(), txCredit.vout[0].scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)stream.data(), stream.size(), 0, flags, nullptr); assert(csuccess == 1); #endif } }
/** * Sign scriptPubKey using signature made with creator. * Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion) { CScript scriptRet; uint160 h160; ret.clear(); std::vector<valtype> vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) return false; CKeyID keyID; switch (whichTypeRet) { case TX_NONSTANDARD: case TX_NULL_DATA: case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); return Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (!Sign1(provider, keyID, creator, scriptPubKey, ret, sigversion)) return false; else { CPubKey vch; provider.GetPubKey(keyID, vch); ret.push_back(ToByteVector(vch)); } return true; case TX_SCRIPTHASH: if (provider.GetCScript(uint160(vSolutions[0]), scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } return false; case TX_MULTISIG: ret.push_back(valtype()); // workaround CHECKMULTISIG bug return (SignN(provider, vSolutions, creator, scriptPubKey, ret, sigversion)); case TX_WITNESS_V0_KEYHASH: ret.push_back(vSolutions[0]); return true; case TX_WITNESS_V0_SCRIPTHASH: CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin()); if (provider.GetCScript(h160, scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } return false; default: return false; } }
BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) { // Make sure skipping validation of transactions that were // validated going into the memory pool does not allow // double-spends in blocks to pass validation when they should not. CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; // Create a double-spend of mature coinbase txn: std::vector<CMutableTransaction> spends; spends.resize(2); for (int i = 0; i < 2; i++) { spends[i].nVersion = 1; spends[i].vin.resize(1); spends[i].vin[0].prevout.hash = m_coinbase_txns[0]->GetHash(); spends[i].vin[0].prevout.n = 0; spends[i].vout.resize(1); spends[i].vout[0].nValue = 11*CENT; spends[i].vout[0].scriptPubKey = scriptPubKey; // Sign: std::vector<unsigned char> vchSig; uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, SigVersion::BASE); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); spends[i].vin[0].scriptSig << vchSig; } CBlock block; // Test 1: block with both of those transactions should be rejected. block = CreateAndProcessBlock(spends, scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash()); // Test 2: ... and should be rejected if spend1 is in the memory pool BOOST_CHECK(ToMemPool(spends[0])); block = CreateAndProcessBlock(spends, scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash()); mempool.clear(); // Test 3: ... and should be rejected if spend2 is in the memory pool BOOST_CHECK(ToMemPool(spends[1])); block = CreateAndProcessBlock(spends, scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() != block.GetHash()); mempool.clear(); // Final sanity test: first spend in mempool, second in block, that's OK: std::vector<CMutableTransaction> oneSpend; oneSpend.push_back(spends[0]); BOOST_CHECK(ToMemPool(spends[1])); block = CreateAndProcessBlock(oneSpend, scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); // spends[1] should have been removed from the mempool when the // block with spends[0] is accepted: BOOST_CHECK_EQUAL(mempool.size(), 0U); }
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys) { CScript script; script << CScript::EncodeOP_N(nRequired); for (const CPubKey& key : keys) script << ToByteVector(key); script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; return script; }
bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) { if (sigdata.complete) return true; std::vector<valtype> result; txnouttype whichType; bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); if (solved && whichType == TX_SCRIPTHASH) { // Solver returns the subscript that needs to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: subscript = CScript(result[0].begin(), result[0].end()); sigdata.redeem_script = subscript; solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TX_SCRIPTHASH; P2SH = true; } if (solved && whichType == TX_WITNESS_V0_KEYHASH) { CScript witnessscript; witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG; txnouttype subType; solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata); sigdata.scriptWitness.stack = result; sigdata.witness = true; result.clear(); } else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH) { CScript witnessscript(result[0].begin(), result[0].end()); sigdata.witness_script = witnessscript; txnouttype subType; solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end())); sigdata.scriptWitness.stack = result; sigdata.witness = true; result.clear(); } else if (solved && whichType == TX_WITNESS_UNKNOWN) { sigdata.witness = true; } if (P2SH) { result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end())); } sigdata.scriptSig = PushAll(result); // Test solution sigdata.complete = solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); return sigdata.complete; }
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) { // Generate a 100-block chain: coinbaseKey.MakeNewKey(true); CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; for (int i = 0; i < COINBASE_MATURITY; i++) { std::vector<CMutableTransaction> noTxns; CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); coinbaseTxns.push_back(b.vtx[0]); } }
TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) { // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests. // TODO: fix the code to support SegWit blocks. UpdateVersionBitsParameters(Consensus::DEPLOYMENT_SEGWIT, 0, Consensus::BIP9Deployment::NO_TIMEOUT); // Generate a 100-block chain: coinbaseKey.MakeNewKey(true); CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; for (int i = 0; i < COINBASE_MATURITY; i++) { std::vector<CMutableTransaction> noTxns; CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); m_coinbase_txns.push_back(b.vtx[0]); } }
void TestChainForComputingMediansSetup::BuildBlocks() { CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; for (int i = 0; i < 10; ++i) { //it takes about a second to validate a large block such as this std::vector<CTransaction> txs; CTransaction tx; if(GenerateRandomTransaction(tx)) { txs.push_back(tx); CBlock b = CreateAndProcessBlock(txs, scriptPubKey); coinbaseTxns.push_back(b.vtx[0]); //this needs to be called because the code itself isn't calling it due to the fact that it is not super majority BlockSizeCalculator::ComputeBlockSize(chainActive.Tip(), 10); } nOutputs += 1000; } }
valtype CAuxpowBuilder::buildAuxpowChain (const uint256& hashAux, unsigned h, int index) { auxpowChainIndex = index; /* Just use "something" for the branch. Doesn't really matter. */ auxpowChainMerkleBranch.clear (); for (unsigned i = 0; i < h; ++i) auxpowChainMerkleBranch.push_back (ArithToUint256 (arith_uint256 (i))); const uint256 hash = CBlock::CheckMerkleBranch (hashAux, auxpowChainMerkleBranch, index); valtype res = ToByteVector (hash); std::reverse (res.begin (), res.end ()); return res; }
/** * Ensure that the mempool won't accept coinbase transactions. */ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup) { CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; CMutableTransaction coinbaseTx; coinbaseTx.nVersion = 1; coinbaseTx.vin.resize(1); coinbaseTx.vout.resize(1); coinbaseTx.vin[0].scriptSig = CScript() << OP_11 << OP_EQUAL; coinbaseTx.vout[0].nValue = 1 * CENT; coinbaseTx.vout[0].scriptPubKey = scriptPubKey; BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase()); CValidationState state; LOCK(cs_main); unsigned int initialPoolSize = mempool.size(); BOOST_CHECK_EQUAL( false, AcceptToMemoryPool(mempool, state, MakeTransactionRef(coinbaseTx), nullptr /* pfMissingInputs */, nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */)); // Check that the transaction hasn't been added to mempool. BOOST_CHECK_EQUAL(mempool.size(), initialPoolSize); // Check that the validation state reflects the unsuccessful attempt. BOOST_CHECK(state.IsInvalid()); BOOST_CHECK_EQUAL(state.GetRejectReason(), "coinbase"); int nDoS; BOOST_CHECK_EQUAL(state.IsInvalid(nDoS), true); BOOST_CHECK_EQUAL(nDoS, 100); }
CScript GetScriptForWitness(const CScript& redeemscript) { CScript ret; txnouttype typ; std::vector<std::vector<unsigned char> > vSolutions; if (Solver(redeemscript, typ, vSolutions)) { if (typ == TX_PUBKEY) { unsigned char h160[20]; CHash160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160); ret << OP_0 << std::vector<unsigned char>(&h160[0], &h160[20]); return ret; } else if (typ == TX_PUBKEYHASH) { ret << OP_0 << vSolutions[0]; return ret; } } uint256 hash; CSHA256().Write(&redeemscript[0], redeemscript.size()).Finalize(hash.begin()); ret << OP_0 << ToByteVector(hash); return ret; }
bool TestChainForComputingMediansSetup::GenerateRandomTransaction(CTransaction& txNew) { CAmount amountToSend = 5000; std::vector<CTransaction> res; CKey key; key.MakeNewKey(true); CScript scriptPubKey = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; CBasicKeyStore keystore; keystore.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); CTransaction utxo = coinbaseTxns[0]; coinbaseTxns.erase(coinbaseTxns.begin()); txNew.nLockTime = chainActive.Height(); txNew.vin.clear(); txNew.vout.clear(); for (int j = 0; j < nOutputs; ++j) { CTxOut txout(amountToSend, scriptPubKey); txNew.vout.push_back(txout); } //vin CTxIn vin = CTxIn(utxo.GetHash(), 0, CScript(), std::numeric_limits<unsigned int>::max() - 1); txNew.vin.push_back(vin); //previous tx's script pub key that we need to sign CScript& scriptSigRes = txNew.vin[0].scriptSig; CTransaction txNewConst(txNew); ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0), utxo.vout[0].scriptPubKey, scriptSigRes); res.push_back(txNew); return true; }
/** * Sign scriptPubKey using signature made with creator. * Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey, std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata) { CScript scriptRet; uint160 h160; ret.clear(); std::vector<unsigned char> sig; std::vector<valtype> vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) return false; switch (whichTypeRet) { case TX_NONSTANDARD: case TX_NULL_DATA: case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false; ret.push_back(std::move(sig)); return true; case TX_PUBKEYHASH: { CKeyID keyID = CKeyID(uint160(vSolutions[0])); CPubKey pubkey; GetPubKey(provider, sigdata, keyID, pubkey); if (!CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) return false; ret.push_back(std::move(sig)); ret.push_back(ToByteVector(pubkey)); return true; } case TX_SCRIPTHASH: if (GetCScript(provider, sigdata, uint160(vSolutions[0]), scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } return false; case TX_MULTISIG: { size_t required = vSolutions.front()[0]; ret.push_back(valtype()); // workaround CHECKMULTISIG bug for (size_t i = 1; i < vSolutions.size() - 1; ++i) { CPubKey pubkey = CPubKey(vSolutions[i]); if (ret.size() < required + 1 && CreateSig(creator, sigdata, provider, sig, pubkey, scriptPubKey, sigversion)) { ret.push_back(std::move(sig)); } } bool ok = ret.size() == required + 1; for (size_t i = 0; i + ret.size() < required + 1; ++i) { ret.push_back(valtype()); } return ok; } case TX_WITNESS_V0_KEYHASH: ret.push_back(vSolutions[0]); return true; case TX_WITNESS_V0_SCRIPTHASH: CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin()); if (GetCScript(provider, sigdata, h160, scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } return false; default: return false; } }
bool MultisigDialog::createRedeemScript(int m, vector<string> vKeys, CScript& redeemRet, string& errorRet) { try{ int n = vKeys.size(); //gather pub keys if (n < 1) throw runtime_error("a Multisignature address must require at least one key to redeem"); if (n < m) throw runtime_error( strprintf("not enough keys supplied " "(got %d keys, but need at least %d to redeem)", m, n)); if (n > 15) throw runtime_error("Number of addresses involved in the Multisignature address creation > 15\nReduce the number"); vector<CPubKey> pubkeys; pubkeys.resize(n); int i = 0; for(vector<string>::iterator it = vKeys.begin(); it != vKeys.end(); ++it) { string keyString = *it; #ifdef ENABLE_WALLET // Case 1: PIVX address and we have full public key: CBitcoinAddress address(keyString); if (pwalletMain && address.IsValid()) { CKeyID keyID; if (!address.GetKeyID(keyID)) { throw runtime_error( strprintf("%s does not refer to a key", keyString)); } CPubKey vchPubKey; if (!pwalletMain->GetPubKey(keyID, vchPubKey)) throw runtime_error( strprintf("no full public key for address %s", keyString)); if (!vchPubKey.IsFullyValid()){ string sKey = keyString.empty()?"(empty)":keyString; throw runtime_error(" Invalid public key: " + sKey ); } pubkeys[i++] = vchPubKey; } //case 2: hex pub key else #endif if (IsHex(keyString)) { CPubKey vchPubKey(ParseHex(keyString)); if (!vchPubKey.IsFullyValid()){ throw runtime_error(" Invalid public key: " + keyString); } pubkeys[i++] = vchPubKey; } else { throw runtime_error(" Invalid public key: " + keyString); } } //populate redeem script //OP_N for required signatures redeemRet << redeemRet.EncodeOP_N(m); //public keys for(CPubKey& key : pubkeys){ vector<unsigned char> vKey= ToByteVector(key); redeemRet << vKey; } //OP_N for total pubkeys redeemRet << redeemRet.EncodeOP_N(pubkeys.size()); redeemRet << OP_CHECKMULTISIG; return true; }catch(const runtime_error& e){ errorRet = string(e.what()); return false; } }
bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingConfirmations) const { strError = ""; fMissingConfirmations = false; CAmount nMinFee = GetMinCollateralFee(); uint256 nExpectedHash = GetHash(); CTransactionRef txCollateral; uint256 nBlockHash; // RETRIEVE TRANSACTION IN QUESTION if (!GetTransaction(nCollateralHash, txCollateral, Params().GetConsensus(), nBlockHash, true)) { strError = strprintf("Can't find collateral tx %s", nCollateralHash.ToString()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } if (nBlockHash == uint256()) { strError = strprintf("Collateral tx %s is not mined yet", txCollateral->ToString()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } if (txCollateral->vout.size() < 1) { strError = strprintf("tx vout size less than 1 | %d", txCollateral->vout.size()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } // LOOK FOR SPECIALIZED GOVERNANCE SCRIPT (PROOF OF BURN) CScript findScript; findScript << OP_RETURN << ToByteVector(nExpectedHash); LogPrint("gobject", "CGovernanceObject::IsCollateralValid -- txCollateral->vout.size() = %s, findScript = %s, nMinFee = %lld\n", txCollateral->vout.size(), ScriptToAsmStr(findScript, false), nMinFee); bool foundOpReturn = false; for (const auto& output : txCollateral->vout) { LogPrint("gobject", "CGovernanceObject::IsCollateralValid -- txout = %s, output.nValue = %lld, output.scriptPubKey = %s\n", output.ToString(), output.nValue, ScriptToAsmStr(output.scriptPubKey, false)); if (!output.scriptPubKey.IsPayToPublicKeyHash() && !output.scriptPubKey.IsUnspendable()) { strError = strprintf("Invalid Script %s", txCollateral->ToString()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } if (output.scriptPubKey == findScript && output.nValue >= nMinFee) { foundOpReturn = true; } } if (!foundOpReturn) { strError = strprintf("Couldn't find opReturn %s in %s", nExpectedHash.ToString(), txCollateral->ToString()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } // GET CONFIRMATIONS FOR TRANSACTION AssertLockHeld(cs_main); int nConfirmationsIn = 0; if (nBlockHash != uint256()) { BlockMap::iterator mi = mapBlockIndex.find(nBlockHash); if (mi != mapBlockIndex.end() && (*mi).second) { CBlockIndex* pindex = (*mi).second; if (chainActive.Contains(pindex)) { nConfirmationsIn += chainActive.Height() - pindex->nHeight + 1; } } } if ((nConfirmationsIn < GOVERNANCE_FEE_CONFIRMATIONS) && (!instantsend.IsLockedInstantSendTransaction(nCollateralHash) || llmq::quorumInstantSendManager->IsLocked(nCollateralHash))) { strError = strprintf("Collateral requires at least %d confirmations to be relayed throughout the network (it has only %d)", GOVERNANCE_FEE_CONFIRMATIONS, nConfirmationsIn); if (nConfirmationsIn >= GOVERNANCE_MIN_RELAY_FEE_CONFIRMATIONS) { fMissingConfirmations = true; strError += ", pre-accepted -- waiting for required confirmations"; } else { strError += ", rejected -- try again later"; } LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } strError = "valid"; return true; }
BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) { // Test that passing CheckInputs with one set of script flags doesn't imply // that we would pass again with a different set of flags. { LOCK(cs_main); InitScriptExecutionCache(); } CScript p2pk_scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; CScript p2sh_scriptPubKey = GetScriptForDestination(CScriptID(p2pk_scriptPubKey)); CScript p2pkh_scriptPubKey = GetScriptForDestination(coinbaseKey.GetPubKey().GetID()); CScript p2wpkh_scriptPubKey = GetScriptForWitness(p2pkh_scriptPubKey); CBasicKeyStore keystore; keystore.AddKey(coinbaseKey); keystore.AddCScript(p2pk_scriptPubKey); // flags to test: SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, SCRIPT_VERIFY_CHECKSEQUENCE_VERIFY, SCRIPT_VERIFY_NULLDUMMY, uncompressed pubkey thing // Create 2 outputs that match the three scripts above, spending the first // coinbase tx. CMutableTransaction spend_tx; spend_tx.nVersion = 1; spend_tx.vin.resize(1); spend_tx.vin[0].prevout.hash = m_coinbase_txns[0]->GetHash(); spend_tx.vin[0].prevout.n = 0; spend_tx.vout.resize(4); spend_tx.vout[0].nValue = 11*CENT; spend_tx.vout[0].scriptPubKey = p2sh_scriptPubKey; spend_tx.vout[1].nValue = 11*CENT; spend_tx.vout[1].scriptPubKey = p2wpkh_scriptPubKey; spend_tx.vout[2].nValue = 11*CENT; spend_tx.vout[2].scriptPubKey = CScript() << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; spend_tx.vout[3].nValue = 11*CENT; spend_tx.vout[3].scriptPubKey = CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; // Sign, with a non-DER signature { std::vector<unsigned char> vchSig; uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER vchSig.push_back((unsigned char)SIGHASH_ALL); spend_tx.vin[0].scriptSig << vchSig; } // Test that invalidity under a set of flags doesn't preclude validity // under other (eg consensus) flags. // spend_tx is invalid according to DERSIG { LOCK(cs_main); CValidationState state; PrecomputedTransactionData ptd_spend_tx(spend_tx); BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); // If we call again asking for scriptchecks (as happens in // ConnectBlock), we should add a script check object for this -- we're // not caching invalidity (if that changes, delete this test case). std::vector<CScriptCheck> scriptchecks; BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), 1U); // Test that CheckInputs returns true iff DERSIG-enforcing flags are // not present. Don't add these checks to the cache, so that we can // test later that block validation works fine in the absence of cached // successes. ValidateCheckInputsForAllFlags(spend_tx, SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_STRICTENC, false); } // And if we produce a block with this tx, it should be valid (DERSIG not // enabled yet), even though there's no cache entry. CBlock block; block = CreateAndProcessBlock({spend_tx}, p2pk_scriptPubKey); BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash()); BOOST_CHECK(pcoinsTip->GetBestBlock() == block.GetHash()); LOCK(cs_main); // Test P2SH: construct a transaction that is valid without P2SH, and // then test validity with P2SH. { CMutableTransaction invalid_under_p2sh_tx; invalid_under_p2sh_tx.nVersion = 1; invalid_under_p2sh_tx.vin.resize(1); invalid_under_p2sh_tx.vin[0].prevout.hash = spend_tx.GetHash(); invalid_under_p2sh_tx.vin[0].prevout.n = 0; invalid_under_p2sh_tx.vout.resize(1); invalid_under_p2sh_tx.vout[0].nValue = 11*CENT; invalid_under_p2sh_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end()); invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2; ValidateCheckInputsForAllFlags(invalid_under_p2sh_tx, SCRIPT_VERIFY_P2SH, true); } // Test CHECKLOCKTIMEVERIFY { CMutableTransaction invalid_with_cltv_tx; invalid_with_cltv_tx.nVersion = 1; invalid_with_cltv_tx.nLockTime = 100; invalid_with_cltv_tx.vin.resize(1); invalid_with_cltv_tx.vin[0].prevout.hash = spend_tx.GetHash(); invalid_with_cltv_tx.vin[0].prevout.n = 2; invalid_with_cltv_tx.vin[0].nSequence = 0; invalid_with_cltv_tx.vout.resize(1); invalid_with_cltv_tx.vout[0].nValue = 11*CENT; invalid_with_cltv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; // Sign std::vector<unsigned char> vchSig; uint256 hash = SignatureHash(spend_tx.vout[2].scriptPubKey, invalid_with_cltv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 101; ValidateCheckInputsForAllFlags(invalid_with_cltv_tx, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true); // Make it valid, and check again invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; PrecomputedTransactionData txdata(invalid_with_cltv_tx); BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); } // TEST CHECKSEQUENCEVERIFY { CMutableTransaction invalid_with_csv_tx; invalid_with_csv_tx.nVersion = 2; invalid_with_csv_tx.vin.resize(1); invalid_with_csv_tx.vin[0].prevout.hash = spend_tx.GetHash(); invalid_with_csv_tx.vin[0].prevout.n = 3; invalid_with_csv_tx.vin[0].nSequence = 100; invalid_with_csv_tx.vout.resize(1); invalid_with_csv_tx.vout[0].nValue = 11*CENT; invalid_with_csv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; // Sign std::vector<unsigned char> vchSig; uint256 hash = SignatureHash(spend_tx.vout[3].scriptPubKey, invalid_with_csv_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 101; ValidateCheckInputsForAllFlags(invalid_with_csv_tx, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true); // Make it valid, and check again invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; PrecomputedTransactionData txdata(invalid_with_csv_tx); BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); } // TODO: add tests for remaining script flags // Test that passing CheckInputs with a valid witness doesn't imply success // for the same tx with a different witness. { CMutableTransaction valid_with_witness_tx; valid_with_witness_tx.nVersion = 1; valid_with_witness_tx.vin.resize(1); valid_with_witness_tx.vin[0].prevout.hash = spend_tx.GetHash(); valid_with_witness_tx.vin[0].prevout.n = 1; valid_with_witness_tx.vout.resize(1); valid_with_witness_tx.vout[0].nValue = 11*CENT; valid_with_witness_tx.vout[0].scriptPubKey = p2pk_scriptPubKey; // Sign SignatureData sigdata; ProduceSignature(keystore, MutableTransactionSignatureCreator(&valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata); UpdateTransaction(valid_with_witness_tx, 0, sigdata); // This should be valid under all script flags. ValidateCheckInputsForAllFlags(valid_with_witness_tx, 0, true); // Remove the witness, and check that it is now invalid. valid_with_witness_tx.vin[0].scriptWitness.SetNull(); ValidateCheckInputsForAllFlags(valid_with_witness_tx, SCRIPT_VERIFY_WITNESS, true); } { // Test a transaction with multiple inputs. CMutableTransaction tx; tx.nVersion = 1; tx.vin.resize(2); tx.vin[0].prevout.hash = spend_tx.GetHash(); tx.vin[0].prevout.n = 0; tx.vin[1].prevout.hash = spend_tx.GetHash(); tx.vin[1].prevout.n = 1; tx.vout.resize(1); tx.vout[0].nValue = 22*CENT; tx.vout[0].scriptPubKey = p2pk_scriptPubKey; // Sign for (int i=0; i<2; ++i) { SignatureData sigdata; ProduceSignature(keystore, MutableTransactionSignatureCreator(&tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata); UpdateTransaction(tx, i, sigdata); } // This should be valid under all script flags ValidateCheckInputsForAllFlags(tx, 0, true); // Check that if the second input is invalid, but the first input is // valid, the transaction is not cached. // Invalidate vin[1] tx.vin[1].scriptWitness.SetNull(); CValidationState state; PrecomputedTransactionData txdata(tx); // This transaction is now invalid under segwit, because of the second input. BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr)); std::vector<CScriptCheck> scriptchecks; // Make sure this transaction was not cached (ie because the first // input was valid) BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks)); // Should get 2 script checks back -- caching is on a whole-transaction basis. BOOST_CHECK_EQUAL(scriptchecks.size(), 2U); } }