CTxDestination COFCCoinAddress::Get() const { if (!IsValid()) return CNoDestination(); uint160 id; memcpy(&id, &vchData[0], 20); if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) return CKeyID(id); else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) return CScriptID(id); else return CNoDestination(); }
CTxDestination CBitcoinAddress::Get() const { if (!IsValid()) return CNoDestination(); uint160 id; memcpy(&id, &vchData[0], 20); if (vchVersion == Params().Base58Prefix(pubkey_address)) return CKeyID(id); else if (vchVersion == Params().Base58Prefix(script_address)) return CScriptID(id); else return CNoDestination(); }
UniValue decodescript(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "decodescript \"hexstring\"\n" "\nDecode a hex-encoded script.\n" "\nArguments:\n" "1. \"hexstring\" (string) the hex encoded script\n" "\nResult:\n" "{\n" " \"asm\":\"asm\", (string) Script public key\n" " \"hex\":\"hex\", (string) hex encoded public key\n" " \"type\":\"type\", (string) The output type\n" " \"reqSigs\": n, (numeric) The required signatures\n" " \"addresses\": [ (json array of string)\n" " \"address\" (string) bitcoin address\n" " ,...\n" " ],\n" " \"p2sh\",\"address\" (string) address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH).\n" "}\n" "\nExamples:\n" + HelpExampleCli("decodescript", "\"hexstring\"") + HelpExampleRpc("decodescript", "\"hexstring\"") ); RPCTypeCheck(request.params, {UniValue::VSTR}); UniValue r(UniValue::VOBJ); CScript script; if (request.params[0].get_str().size() > 0){ std::vector<unsigned char> scriptData(ParseHexV(request.params[0], "argument")); script = CScript(scriptData.begin(), scriptData.end()); } else { // Empty scripts are valid } ScriptPubKeyToUniv(script, r, false); UniValue type; type = find_value(r, "type"); if (type.isStr() && type.get_str() != "scripthash") { // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script)))); } return r; }
UniValue operator()(const WitnessV0ScriptHash& id) const { UniValue obj(UniValue::VOBJ); CScript subscript; obj.push_back(Pair("isscript", true)); obj.push_back(Pair("iswitness", true)); obj.push_back(Pair("witness_version", 0)); obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); CRIPEMD160 hasher; uint160 hash; hasher.Write(id.begin(), 32).Finalize(hash.begin()); if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); } return obj; }
void CMasternode::UpdateLastPaid(const CBlockIndex* pindex, int nMaxBlocksToScanBack) { if (!pindex) return; const CBlockIndex* pindexActive = chainActive.Tip(); assert(pindexActive); CDiskBlockPos blockPos = pindexActive->GetBlockPos(); CScript mnpayee = GetScriptForDestination(CScriptID(GetScriptForDestination(WitnessV0KeyHash(pubKeyCollateralAddress.GetID())))); //LogPrint(MCLog::MN, "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s\n", outpoint.ToStringShort()); LOCK(cs_mapMasternodeBlocks); for (int i = 0; pindexActive->nHeight > nBlockLastPaid && i < nMaxBlocksToScanBack; i++) { if (mnpayments.mapMasternodeBlocks.count(pindexActive->nHeight) && mnpayments.mapMasternodeBlocks[pindexActive->nHeight].HasPayeeWithVotes(mnpayee, 2)) { if (blockPos.IsNull() == true) { return; } CBlock block; if (!ReadBlockFromDisk(block, blockPos, Params().GetConsensus())) continue; CAmount nMasternodePayment = GetMasternodePayment(pindexActive->nHeight, block.vtx[0]->GetValueOut()); for (const auto& txout : block.vtx[0]->vout) if (mnpayee == txout.scriptPubKey && nMasternodePayment == txout.nValue) { nBlockLastPaid = pindexActive->nHeight; nTimeLastPaid = pindexActive->nTime; LogPrint(MCLog::MN, "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- found new %d\n", outpoint.ToStringShort(), nBlockLastPaid); return; } } if (pindexActive->pprev == nullptr) { assert(pindexActive); break; } pindexActive = pindexActive->pprev; } // Last payment for this masternode wasn't found in latest mnpayments blocks // or it was found in mnpayments blocks but wasn't found in the blockchain. LogPrint(MCLog::MN, "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- keeping old %d\n", outpoint.ToStringShort(), nBlockLastPaid); }
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { std::vector<valtype> vSolutions; txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; if (whichType == TX_PUBKEY) { CPubKey pubKey(vSolutions[0]); if (!pubKey.IsValid()) return false; addressRet = pubKey.GetID(); return true; } else if (whichType == TX_PUBKEYHASH) { addressRet = CKeyID(uint160(vSolutions[0])); return true; } else if (whichType == TX_SCRIPTHASH) { addressRet = CScriptID(uint160(vSolutions[0])); return true; } else if (whichType == TX_WITNESS_V0_KEYHASH) { WitnessV0KeyHash hash; std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); addressRet = hash; return true; } else if (whichType == TX_WITNESS_V0_SCRIPTHASH) { WitnessV0ScriptHash hash; std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); addressRet = hash; return true; } else if (whichType == TX_WITNESS_UNKNOWN) { WitnessUnknown unk; unk.version = vSolutions[0][0]; std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program); unk.length = vSolutions[1].size(); addressRet = unk; return true; } // Multisig txns have more than one address... return false; }
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type) { switch (type) { case OutputType::LEGACY: return key.GetID(); case OutputType::P2SH_SEGWIT: case OutputType::BECH32: { if (!key.IsCompressed()) return key.GetID(); CTxDestination witdest = WitnessV0KeyHash(key.GetID()); CScript witprog = GetScriptForDestination(witdest); if (type == OutputType::P2SH_SEGWIT) { return CScriptID(witprog); } else { return witdest; } } default: assert(false); } }
static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput) { // separate VALUE:SCRIPT[:FLAGS] std::vector<std::string> vStrInputParts; boost::split(vStrInputParts, strInput, boost::is_any_of(":")); if (vStrInputParts.size() < 2) throw std::runtime_error("TX output missing separator"); // Extract and validate VALUE CAmount value = ExtractAndValidateValue(vStrInputParts[0]); // extract and validate script std::string strScript = vStrInputParts[1]; CScript scriptPubKey = ParseScript(strScript); // Extract FLAGS bool bSegWit = false; bool bScriptHash = false; if (vStrInputParts.size() == 3) { std::string flags = vStrInputParts.back(); bSegWit = (flags.find('W') != std::string::npos); bScriptHash = (flags.find('S') != std::string::npos); } if (scriptPubKey.size() > MAX_SCRIPT_SIZE) { throw std::runtime_error(strprintf( "script exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_SIZE)); } if (bSegWit) { scriptPubKey = GetScriptForWitness(scriptPubKey); } if (bScriptHash) { if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) { throw std::runtime_error(strprintf( "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE)); } scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); tx.vout.push_back(txout); }
static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& strInput) { // Separate into VALUE:PUBKEY[:FLAGS] std::vector<std::string> vStrInputParts; boost::split(vStrInputParts, strInput, boost::is_any_of(":")); if (vStrInputParts.size() < 2 || vStrInputParts.size() > 3) throw std::runtime_error("TX output missing or too many separators"); // Extract and validate VALUE CAmount value = ExtractAndValidateValue(vStrInputParts[0]); // Extract and validate PUBKEY CPubKey pubkey(ParseHex(vStrInputParts[1])); if (!pubkey.IsFullyValid()) throw std::runtime_error("invalid TX output pubkey"); CScript scriptPubKey = GetScriptForRawPubKey(pubkey); // Extract and validate FLAGS bool bSegWit = false; bool bScriptHash = false; if (vStrInputParts.size() == 3) { std::string flags = vStrInputParts[2]; bSegWit = (flags.find('W') != std::string::npos); bScriptHash = (flags.find('S') != std::string::npos); } if (bSegWit) { if (!pubkey.IsCompressed()) { throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } if (bScriptHash) { // Get the ID for the script, and then construct a P2SH destination for it. scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); tx.vout.push_back(txout); }
CTxDestination CBitcoinAddress::Get() const { if (!IsValid()) return CNoDestination(); switch (nVersion) { case PUBKEY_ADDRESS: case PUBKEY_ADDRESS_TEST: { uint160 id; memcpy(&id, &vchData[0], 20); return CKeyID(id); } case SCRIPT_ADDRESS: case SCRIPT_ADDRESS_TEST: { uint160 id; memcpy(&id, &vchData[0], 20); return CScriptID(id); } } return CNoDestination(); }
bool CBitcoinAddress::GetScriptID(CScriptID &scriptID) const { /* if (!IsValid()) return false; switch (vchVersion) { case Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS): case Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS_TEST): { uint160 id; memcpy(&id, &vchData[0], 20); scriptID = CScriptID(id); return true; } default: return false; }*/ if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) return false; uint160 id; memcpy(&id, &vchData[0], 20); scriptID = CScriptID(id); return true; }
CMasternode::CollateralStatus CMasternode::CheckCollateral(const COutPoint& outpoint, const CPubKey& pubkey, int& nHeightRet) { AssertLockHeld(cs_main); Coin coin; if (!GetUTXOCoin(outpoint, coin)) { return COLLATERAL_UTXO_NOT_FOUND; } if (coin.out.nValue != 25000 * COIN) { return COLLATERAL_INVALID_AMOUNT; } if (pubkey == CPubKey() || coin.out.scriptPubKey != GetScriptForDestination(CScriptID(GetScriptForDestination(WitnessV0KeyHash(pubkey.GetID()))))) { return COLLATERAL_INVALID_PUBKEY; } nHeightRet = coin.nHeight; return COLLATERAL_OK; }
CTxDestination CBitcoinAddress::Get() const { if (!IsValid()) return CNoDestination(); if (vchData.size() == 20) { uint160 id; memcpy(&id, &vchData[0], 20); if (vchVersion == Params().Base58Prefix(CBaseParams::PUBKEY_ADDRESS)) return CKeyID(id); else if (vchVersion == Params().Base58Prefix(CBaseParams::SCRIPT_ADDRESS)) return CScriptID(id); else return CNoDestination(); } else { CAccountID id; memcpy(&id, &vchData[0], 26); return id; } }
UniValue validateaddress(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "validateaddress \"address\"\n" "\nReturn information about the given bitcoin address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to validate\n" "\nResult:\n" "{\n" " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" " \"address\" : \"address\", (string) The bitcoin address validated\n" " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" " \"ismine\" : true|false, (boolean) If the address is yours or not\n" " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" " \"isscript\" : true|false, (boolean) If the key is a script\n" " \"script\" : \"type\" (string, optional) The output script type. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash\n" " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n" " \"addresses\" (string, optional) Array of addresses associated with the known redeemscript\n" " [\n" " \"address\"\n" " ,...\n" " ]\n" " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output\n" " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n" " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" " \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n" " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n" " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n" " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n" "}\n" "\nExamples:\n" + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") ); #ifdef ENABLE_WALLET CWallet * const pwallet = GetWalletForJSONRPCRequest(request); LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : NULL); #else LOCK(cs_main); #endif CBitcoinAddress address(request.params[0].get_str()); bool isValid = address.IsValid(); UniValue ret(UniValue::VOBJ); ret.push_back(Pair("isvalid", isValid)); if (isValid) { CTxDestination dest = address.Get(); std::string currentAddress = address.ToString(); ret.push_back(Pair("address", currentAddress)); CScript scriptPubKey = GetScriptForDestination(dest); ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); #ifdef ENABLE_WALLET isminetype mine = pwallet ? IsMine(*pwallet, dest) : ISMINE_NO; ret.push_back(Pair("ismine", bool(mine & ISMINE_SPENDABLE))); ret.push_back(Pair("iswatchonly", bool(mine & ISMINE_WATCH_ONLY))); UniValue detail = boost::apply_visitor(DescribeAddressVisitor(pwallet), dest); ret.pushKVs(detail); if (pwallet && pwallet->mapAddressBook.count(dest)) { ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name)); } CKeyID keyID; if (pwallet) { const auto& meta = pwallet->mapKeyMetadata; auto it = address.GetKeyID(keyID) ? meta.find(keyID) : meta.end(); if (it == meta.end()) { it = meta.find(CScriptID(scriptPubKey)); } if (it != meta.end()) { ret.push_back(Pair("timestamp", it->second.nCreateTime)); if (!it->second.hdKeypath.empty()) { ret.push_back(Pair("hdkeypath", it->second.hdKeypath)); ret.push_back(Pair("hdmasterkeyid", it->second.hdMasterKeyID.GetHex())); } } } #endif } return ret; }
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion) { std::vector<valtype> vSolutions; txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) { if (keystore.HaveWatchOnly(scriptPubKey)) return ISMINE_WATCH_UNSOLVABLE; return ISMINE_NO; } CKeyID keyID; switch (whichType) { case TX_NONSTANDARD: case TX_NULL_DATA: break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); if (sigversion != SIGVERSION_BASE && vSolutions[0].size() != 33) { isInvalid = true; return ISMINE_NO; } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; case TX_WITNESS_V0_KEYHASH: { if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { // We do not support bare witness outputs unless the P2SH version of it would be // acceptable as well. This protects against matching before segwit activates. // This also applies to the P2WSH case. break; } isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SIGVERSION_WITNESS_V0); if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; break; } case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (sigversion != SIGVERSION_BASE) { CPubKey pubkey; if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { isInvalid = true; return ISMINE_NO; } } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; case TX_SCRIPTHASH: { CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { isminetype ret = IsMine(keystore, subscript, isInvalid); if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; } case TX_WITNESS_V0_SCRIPTHASH: { if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { break; } uint160 hash; CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin()); CScriptID scriptID = CScriptID(hash); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { isminetype ret = IsMine(keystore, subscript, isInvalid, SIGVERSION_WITNESS_V0); if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; } case TX_MULTISIG: { // Only consider transactions "mine" if we own ALL the // keys involved. Multi-signature transactions that are // partially owned (somebody else has a key that can spend // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); if (sigversion != SIGVERSION_BASE) { for (size_t i = 0; i < keys.size(); i++) { if (keys[i].size() != 33) { isInvalid = true; return ISMINE_NO; } } } if (HaveKeys(keys, keystore) == keys.size()) return ISMINE_SPENDABLE; break; } } if (keystore.HaveWatchOnly(scriptPubKey)) { // TODO: This could be optimized some by doing some work after the above solver SignatureData sigs; return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; } return ISMINE_NO; }
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); } }
static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& strInput) { // Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS] std::vector<std::string> vStrInputParts; boost::split(vStrInputParts, strInput, boost::is_any_of(":")); // Check that there are enough parameters if (vStrInputParts.size()<3) throw std::runtime_error("Not enough multisig parameters"); // Extract and validate VALUE CAmount value = ExtractAndValidateValue(vStrInputParts[0]); // Extract REQUIRED uint32_t required = stoul(vStrInputParts[1]); // Extract NUMKEYS uint32_t numkeys = stoul(vStrInputParts[2]); // Validate there are the correct number of pubkeys if (vStrInputParts.size() < numkeys + 3) throw std::runtime_error("incorrect number of multisig pubkeys"); if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required) throw std::runtime_error("multisig parameter mismatch. Required " \ + std::to_string(required) + " of " + std::to_string(numkeys) + "signatures."); // extract and validate PUBKEYs std::vector<CPubKey> pubkeys; for(int pos = 1; pos <= int(numkeys); pos++) { CPubKey pubkey(ParseHex(vStrInputParts[pos + 2])); if (!pubkey.IsFullyValid()) throw std::runtime_error("invalid TX output pubkey"); pubkeys.push_back(pubkey); } // Extract FLAGS bool bSegWit = false; bool bScriptHash = false; if (vStrInputParts.size() == numkeys + 4) { std::string flags = vStrInputParts.back(); bSegWit = (flags.find('W') != std::string::npos); bScriptHash = (flags.find('S') != std::string::npos); } else if (vStrInputParts.size() > numkeys + 4) { // Validate that there were no more parameters passed throw std::runtime_error("Too many parameters"); } CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); if (bSegWit) { for (CPubKey& pubkey : pubkeys) { if (!pubkey.IsCompressed()) { throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); } } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } if (bScriptHash) { if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) { throw std::runtime_error(strprintf( "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE)); } // Get the ID for the script, and then construct a P2SH destination for it. scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); tx.vout.push_back(txout); }