From efa12a943ae20e0fcee3096ee33ab06b56d38dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Tim=C3=B3n?= Date: Fri, 3 Jul 2015 16:11:06 +0200 Subject: [PATCH 01/32] Testchains: Don't check the genesis block Rebased by Pieter Wuille. Cleanup by Matt Corallo. --- src/main.cpp | 2 +- src/pow.cpp | 3 +++ src/txdb.cpp | 4 ++-- src/txdb.h | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 5b27698d8b96a..e0ae37809ce78 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3424,7 +3424,7 @@ CBlockIndex * InsertBlockIndex(uint256 hash) bool static LoadBlockIndexDB() { const CChainParams& chainparams = Params(); - if (!pblocktree->LoadBlockIndexGuts()) + if (!pblocktree->LoadBlockIndexGuts(chainparams.GetConsensus())) return false; boost::this_thread::interruption_point(); diff --git a/src/pow.cpp b/src/pow.cpp index 7392defe64b82..dad64350f8a0d 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -90,6 +90,9 @@ bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& bool fOverflow; arith_uint256 bnTarget; + if (hash == params.hashGenesisBlock) + return true; + bnTarget.SetCompact(nBits, &fNegative, &fOverflow); // Check range diff --git a/src/txdb.cpp b/src/txdb.cpp index f99e11f26e3f4..52dfd8635186f 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -175,7 +175,7 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { return true; } -bool CBlockTreeDB::LoadBlockIndexGuts() +bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams) { boost::scoped_ptr pcursor(NewIterator()); @@ -203,7 +203,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nStatus = diskindex.nStatus; pindexNew->nTx = diskindex.nTx; - if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus())) + if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams)) return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); pcursor->Next(); diff --git a/src/txdb.h b/src/txdb.h index 22e0c5704cb26..196ef8a19a37e 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -18,6 +18,7 @@ class CBlockFileInfo; class CBlockIndex; struct CDiskTxPos; class uint256; +namespace Consensus { struct Params; } //! -dbcache default (MiB) static const int64_t nDefaultDbCache = 100; @@ -59,7 +60,7 @@ class CBlockTreeDB : public CDBWrapper bool WriteTxIndex(const std::vector > &list); bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); - bool LoadBlockIndexGuts(); + bool LoadBlockIndexGuts(const Consensus::Params& consensusParams); }; #endif // BITCOIN_TXDB_H From b3f9afded8ddc709efa4a91b0337bfa38732db08 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 31 Dec 2015 03:49:58 +0100 Subject: [PATCH 02/32] Create segnet --- src/chainparams.cpp | 54 +++++++++++++++++++++++++++++++++++++++++ src/chainparamsbase.cpp | 24 ++++++++++++++++-- src/chainparamsbase.h | 1 + 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 9cf99492c912e..0962ee7c1dd58 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -209,6 +209,58 @@ class CTestNetParams : public CChainParams { }; static CTestNetParams testNetParams; +/** + * Segnet + */ +class CSegNetParams : public CChainParams { +public: + CSegNetParams() { + strNetworkID = "segnet"; + consensus.nSubsidyHalvingInterval = 210000; + consensus.nMajorityEnforceBlockUpgrade = 7; + consensus.nMajorityRejectBlockOutdated = 9; + consensus.nMajorityWindow = 10; + consensus.BIP34Height = -1; + consensus.BIP34Hash = uint256(); + consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = true; + consensus.fPowNoRetargeting = false; + pchMessageStart[0] = 0x2e; + pchMessageStart[1] = 0x96; + pchMessageStart[2] = 0xea; + pchMessageStart[3] = 0xca; + vAlertPubKey = ParseHex("0300000000000000000000003b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63"); + nDefaultPort = 28333; + nMaxTipAge = 0x7fffffff; + nPruneAfterHeight = 1000; + + genesis = CreateGenesisBlock(1452831101, 0, 0x1d00ffff, 1, 50 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + + vFixedSeeds.clear(); + vSeeds.clear(); + + base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,30); + base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,50); + base58Prefixes[SECRET_KEY] = std::vector(1,158); + base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x05)(0x35)(0x87)(0xCF).convert_to_container >(); + base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x05)(0x35)(0x83)(0x94).convert_to_container >(); + + vFixedSeeds.clear(); + + fMiningRequiresPeers = true; + fDefaultConsistencyChecks = false; + fRequireStandard = false; + fMineBlocksOnDemand = false; + fTestnetToBeDeprecatedFieldRPC = true; + + // checkpointData is empty + } +}; +static CSegNetParams segNetParams; + /** * Regression test */ @@ -279,6 +331,8 @@ CChainParams& Params(const std::string& chain) return mainParams; else if (chain == CBaseChainParams::TESTNET) return testNetParams; + else if (chain == CBaseChainParams::SEGNET) + return segNetParams; else if (chain == CBaseChainParams::REGTEST) return regTestParams; else diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index cb71a8b550c75..c63be2c96bc52 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -13,6 +13,7 @@ const std::string CBaseChainParams::MAIN = "main"; const std::string CBaseChainParams::TESTNET = "test"; const std::string CBaseChainParams::REGTEST = "regtest"; +const std::string CBaseChainParams::SEGNET = "segnet"; void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp) { @@ -51,6 +52,20 @@ class CBaseTestNetParams : public CBaseChainParams }; static CBaseTestNetParams testNetParams; +/** + * Segnet + */ +class CBaseSegNetParams : public CBaseChainParams +{ +public: + CBaseSegNetParams() + { + nRPCPort = 28332; + strDataDir = "segnet"; + } +}; +static CBaseSegNetParams segNetParams; + /* * Regression test */ @@ -79,6 +94,8 @@ CBaseChainParams& BaseParams(const std::string& chain) return mainParams; else if (chain == CBaseChainParams::TESTNET) return testNetParams; + else if (chain == CBaseChainParams::SEGNET) + return segNetParams; else if (chain == CBaseChainParams::REGTEST) return regTestParams; else @@ -94,13 +111,16 @@ std::string ChainNameFromCommandLine() { bool fRegTest = GetBoolArg("-regtest", false); bool fTestNet = GetBoolArg("-testnet", false); + bool fSegNet = GetBoolArg("-segnet", false); - if (fTestNet && fRegTest) - throw std::runtime_error("Invalid combination of -regtest and -testnet."); + if ((int)fRegTest + (int)fTestNet + (int)fSegNet > 1) + throw std::runtime_error("Invalid combination of -regtest, -testnet, -segnet."); if (fRegTest) return CBaseChainParams::REGTEST; if (fTestNet) return CBaseChainParams::TESTNET; + if (fSegNet) + return CBaseChainParams::SEGNET; return CBaseChainParams::MAIN; } diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 59493afb9b65e..dea94820ae5b7 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -19,6 +19,7 @@ class CBaseChainParams static const std::string MAIN; static const std::string TESTNET; static const std::string REGTEST; + static const std::string SEGNET; const std::string& DataDir() const { return strDataDir; } int RPCPort() const { return nRPCPort; } From 0bee617f2ba085ca709a8c8c8ee87063d73ca0ff Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Thu, 21 Jan 2016 17:21:46 +0100 Subject: [PATCH 03/32] Add segnet seed nodes --- contrib/seeds/generate-seeds.py | 3 +++ contrib/seeds/nodes_seg.txt | 6 ++++++ src/chainparams.cpp | 2 +- src/chainparamsseeds.h | 7 +++++++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 contrib/seeds/nodes_seg.txt diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index a3d035218789b..38d52de3af496 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -129,6 +129,9 @@ def main(): with open(os.path.join(indir,'nodes_main.txt'),'r') as f: process_nodes(g, f, 'pnSeed6_main', 8333) g.write('\n') + with open(os.path.join(indir,'nodes_seg.txt'),'r') as f: + process_nodes(g, f, 'pnSeed6_seg', 28333) + g.write('\n') with open(os.path.join(indir,'nodes_test.txt'),'r') as f: process_nodes(g, f, 'pnSeed6_test', 18333) g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n') diff --git a/contrib/seeds/nodes_seg.txt b/contrib/seeds/nodes_seg.txt new file mode 100644 index 0000000000000..4fb46bf5dcaed --- /dev/null +++ b/contrib/seeds/nodes_seg.txt @@ -0,0 +1,6 @@ +# List of fixed seed nodes for segnet + +104.243.38.34 +104.155.1.158 +119.246.245.241 +46.101.235.82 diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 0962ee7c1dd58..522ea977894e9 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -248,7 +248,7 @@ class CSegNetParams : public CChainParams { base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x05)(0x35)(0x87)(0xCF).convert_to_container >(); base58Prefixes[EXT_SECRET_KEY] = boost::assign::list_of(0x05)(0x35)(0x83)(0x94).convert_to_container >(); - vFixedSeeds.clear(); + vFixedSeeds = std::vector(pnSeed6_seg, pnSeed6_seg + ARRAYLEN(pnSeed6_seg)); fMiningRequiresPeers = true; fDefaultConsistencyChecks = false; diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index 1406e86805d67..b9c20f6562df8 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -947,6 +947,13 @@ static SeedSpec6 pnSeed6_main[] = { {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0xb3,0xd1,0xf8,0xbe,0xa7,0x6b,0x46,0xbe,0xe8,0x84}, 8333} }; +static SeedSpec6 pnSeed6_seg[] = { + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0xf3,0x26,0x22}, 28333}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0x9b,0x01,0x9e}, 28333}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x77,0xf6,0xf5,0xf1}, 28333}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x65,0xeb,0x52}, 28333} +}; + static SeedSpec6 pnSeed6_test[] = { {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x99,0xcb,0x26,0x31,0xba,0x48,0x51,0x31,0x39,0x0d}, 18333}, {{0xfd,0x87,0xd8,0x7e,0xeb,0x43,0x44,0xf4,0xf4,0xf0,0xbf,0xf7,0x7e,0x6d,0xc4,0xe8}, 18333}, From 0b6c2e42b8fc9be0e4bc5cd4b5d31b24a28274f5 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Fri, 15 Jan 2016 19:26:25 -0500 Subject: [PATCH 04/32] qt: Work (don't crash) with -segnet --- src/qt/guiconstants.h | 1 + src/qt/networkstyle.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 5ceffcd70af58..c96097e5ffcae 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -50,5 +50,6 @@ static const int MAX_URI_LENGTH = 255; #define QAPP_ORG_DOMAIN "bitcoin.org" #define QAPP_APP_NAME_DEFAULT "Bitcoin-Qt" #define QAPP_APP_NAME_TESTNET "Bitcoin-Qt-testnet" +#define QAPP_APP_NAME_SEGNET "Bitcoin-Qt-segnet" #endif // BITCOIN_QT_GUICONSTANTS_H diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index 5f31f49372724..b7e833761c71b 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -17,6 +17,7 @@ static const struct { } network_styles[] = { {"main", QAPP_APP_NAME_DEFAULT, 0, 0, ""}, {"test", QAPP_APP_NAME_TESTNET, 70, 30, QT_TRANSLATE_NOOP("SplashScreen", "[testnet]")}, + {"segnet", QAPP_APP_NAME_SEGNET, 30, 30, QT_TRANSLATE_NOOP("SplashScreen", "[segnet]")}, {"regtest", QAPP_APP_NAME_TESTNET, 160, 30, "[regtest]"} }; static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles); From 1c5149b8e3d55c6c79b68580b3f0a0191508c5cb Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 Nov 2015 01:32:04 +0100 Subject: [PATCH 05/32] Add segregated witness transaction serialization Contains refactorings by Eric Lombrozo. Contains fixup by Nicolas Dorier. --- src/core_read.cpp | 4 +- src/core_write.cpp | 2 +- src/main.cpp | 51 ++++++------ src/net.cpp | 26 ++---- src/net.h | 26 ++++-- src/primitives/block.h | 2 - src/primitives/transaction.cpp | 16 +++- src/primitives/transaction.h | 148 ++++++++++++++++++++++++++++++--- src/protocol.cpp | 7 +- src/protocol.h | 9 +- src/rpcblockchain.cpp | 2 +- src/rpcrawtransaction.cpp | 2 +- src/script/script.cpp | 12 +++ src/script/script.h | 12 +++ src/streams.h | 33 ++++++++ src/test/data/tx_invalid.json | 4 - src/wallet/db.cpp | 2 +- src/wallet/db.h | 15 ++-- src/wallet/walletdb.cpp | 8 +- src/wallet/walletdb.h | 3 +- 20 files changed, 294 insertions(+), 90 deletions(-) diff --git a/src/core_read.cpp b/src/core_read.cpp index 444a4c7eba195..85bd52b7f66ae 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -96,7 +96,7 @@ bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx) return false; vector txData(ParseHex(strHexTx)); - CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS); try { ssData >> tx; } @@ -113,7 +113,7 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk) return false; std::vector blockData(ParseHex(strHexBlk)); - CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION); + CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS); try { ssBlock >> block; } diff --git a/src/core_write.cpp b/src/core_write.cpp index b660e86c30b5d..17e206e455af6 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -118,7 +118,7 @@ string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode) string EncodeHexTx(const CTransaction& tx) { - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS); ssTx << tx; return HexStr(ssTx.begin(), ssTx.end()); } diff --git a/src/main.cpp b/src/main.cpp index e0ae37809ce78..fc46573cffa6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1247,7 +1247,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P if (fTxIndex) { CDiskTxPos postx; if (pblocktree->ReadTxIndex(hash, postx)) { - CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION); + CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS); if (file.IsNull()) return error("%s: OpenBlockFile failed", __func__); CBlockHeader header; @@ -1306,7 +1306,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart) { // Open history file to append - CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); + CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS); if (fileout.IsNull()) return error("WriteBlockToDisk: OpenBlockFile failed"); @@ -1329,7 +1329,7 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus: block.SetNull(); // Open history file to read - CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); + CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS); if (filein.IsNull()) return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); @@ -1703,7 +1703,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint fileout << blockundo; // calculate & write checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS); hasher << hashBlock; hasher << blockundo; fileout << hasher.GetHash(); @@ -1729,7 +1729,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin } // Verify checksum - CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS); hasher << hashBlock; hasher << blockundo; if (hashChecksum != hasher.GetHash()) @@ -2107,7 +2107,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); vPos.push_back(std::make_pair(tx.GetHash(), pos)); - pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); + pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS); } int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001); @@ -3159,7 +3159,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha // Write block to history file try { - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS); CDiskBlockPos blockPos; if (dbp != NULL) blockPos = *dbp; @@ -3679,7 +3679,7 @@ bool InitBlockIndex(const CChainParams& chainparams) try { CBlock &block = const_cast(chainparams.GenesisBlock()); // Start new block file - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS); CDiskBlockPos blockPos; CValidationState state; if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.GetBlockTime())) @@ -3710,7 +3710,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor - CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); + CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS); uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); @@ -4069,6 +4069,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) switch (inv.type) { case MSG_TX: + case MSG_WITNESS_TX: { assert(recentRejects); if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) @@ -4087,6 +4088,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) pcoinsTip->HaveCoins(inv.hash); } case MSG_BLOCK: + case MSG_WITNESS_BLOCK: return mapBlockIndex.count(inv.hash); } // Don't know what it is, just say we already got one @@ -4111,7 +4113,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam boost::this_thread::interruption_point(); it++; - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_WITNESS_BLOCK) { bool send = false; BlockMap::iterator mi = mapBlockIndex.find(inv.hash); @@ -4153,6 +4155,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam assert(!"cannot load block from disk"); if (inv.type == MSG_BLOCK) pfrom->PushMessage(NetMsgType::BLOCK, block); + if (inv.type == MSG_WITNESS_BLOCK) + pfrom->PushMessageWithFlag(SERIALIZE_TRANSACTION_WITNESS, NetMsgType::BLOCK, block); else // MSG_FILTERED_BLOCK) { LOCK(pfrom->cs_filter); @@ -4187,25 +4191,22 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } } - else if (inv.IsKnownType()) + else if (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX) { // Send stream from relay memory bool pushed = false; { - LOCK(cs_mapRelay); - map::iterator mi = mapRelay.find(inv); - if (mi != mapRelay.end()) { - pfrom->PushMessage(inv.GetCommand(), (*mi).second); + LOCK(cs_mapRelayTx); + map::iterator mi = mapRelayTx.find(inv.hash); + if (mi != mapRelayTx.end()) { + pfrom->PushMessageWithFlag(inv.type == MSG_WITNESS_TX ? SERIALIZE_TRANSACTION_WITNESS : 0, NetMsgType::TX, (*mi).second); pushed = true; } } - if (!pushed && inv.type == MSG_TX) { + if (!pushed && (inv.type == MSG_TX || inv.type == MSG_WITNESS_TX)) { CTransaction tx; if (mempool.lookup(inv.hash, tx)) { - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(1000); - ss << tx; - pfrom->PushMessage(NetMsgType::TX, ss); + pfrom->PushMessageWithFlag(inv.type == MSG_WITNESS_TX ? SERIALIZE_TRANSACTION_WITNESS : 0, NetMsgType::TX, tx); pushed = true; } } @@ -4217,7 +4218,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // Track requests for our stuff. GetMainSignals().Inventory(inv.hash); - if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK) + if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_WITNESS_BLOCK) break; } } @@ -4689,7 +4690,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vector vWorkQueue; vector vEraseQueue; CTransaction tx; - vRecv >> tx; + WithOrVersion(&vRecv, SERIALIZE_TRANSACTION_WITNESS) >> tx; CInv inv(MSG_TX, tx.GetHash()); pfrom->AddInventoryKnown(inv); @@ -4918,7 +4919,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing { CBlock block; - vRecv >> block; + WithOrVersion(&vRecv, SERIALIZE_TRANSACTION_WITNESS) >> block; CInv inv(MSG_BLOCK, block.GetHash()); LogPrint("net", "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id); @@ -5543,11 +5544,11 @@ bool SendMessages(CNode* pto) vInvWait.reserve(pto->vInventoryToSend.size()); BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend) { - if (inv.type == MSG_TX && pto->filterInventoryKnown.contains(inv.hash)) + if ((inv.type == MSG_TX || inv.type == MSG_WITNESS_TX) && pto->filterInventoryKnown.contains(inv.hash)) continue; // trickle out tx inv to protect privacy - if (inv.type == MSG_TX && !fSendTrickle) + if ((inv.type == MSG_TX || inv.type == MSG_WITNESS_TX) && !fSendTrickle) { // 1/4 of tx invs blast to all immediately static uint256 hashSalt; diff --git a/src/net.cpp b/src/net.cpp index 2f60cfa1ca11b..c8d6c1ce6f649 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -89,9 +89,9 @@ std::string strSubVersion; vector vNodes; CCriticalSection cs_vNodes; -map mapRelay; -deque > vRelayExpiration; -CCriticalSection cs_mapRelay; +map mapRelayTx; +deque > vRelayTxExpiration; +CCriticalSection cs_mapRelayTx; limitedmap mapAlreadyAskedFor(MAX_INV_SZ); static deque vOneShots; @@ -2049,28 +2049,20 @@ instance_of_cnetcleanup; void RelayTransaction(const CTransaction& tx) -{ - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - ss.reserve(10000); - ss << tx; - RelayTransaction(tx, ss); -} - -void RelayTransaction(const CTransaction& tx, const CDataStream& ss) { CInv inv(MSG_TX, tx.GetHash()); { - LOCK(cs_mapRelay); + LOCK(cs_mapRelayTx); // Expire old relay messages - while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) + while (!vRelayTxExpiration.empty() && vRelayTxExpiration.front().first < GetTime()) { - mapRelay.erase(vRelayExpiration.front().second); - vRelayExpiration.pop_front(); + mapRelayTx.erase(vRelayTxExpiration.front().second); + vRelayTxExpiration.pop_front(); } // Save original serialized message so newer versions are preserved - mapRelay.insert(std::make_pair(inv, ss)); - vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv)); + mapRelayTx.insert(std::make_pair(inv.hash, tx)); + vRelayTxExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv.hash)); } LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) diff --git a/src/net.h b/src/net.h index 07083341de4bd..bda8d6ba149bc 100644 --- a/src/net.h +++ b/src/net.h @@ -161,9 +161,9 @@ extern int nMaxConnections; extern std::vector vNodes; extern CCriticalSection cs_vNodes; -extern std::map mapRelay; -extern std::deque > vRelayExpiration; -extern CCriticalSection cs_mapRelay; +extern std::map mapRelayTx; +extern std::deque > vRelayTxExpiration; +extern CCriticalSection cs_mapRelayTx; extern limitedmap mapAlreadyAskedFor; extern std::vector vAddedNodes; @@ -507,7 +507,7 @@ class CNode { { LOCK(cs_inventory); - if (inv.type == MSG_TX && filterInventoryKnown.contains(inv.hash)) + if ((inv.type == MSG_TX || inv.type == MSG_WITNESS_TX) && filterInventoryKnown.contains(inv.hash)) return; vInventoryToSend.push_back(inv); } @@ -563,6 +563,23 @@ class CNode } } + /** Send a message containing a1, serialized with flag flag. */ + template + void PushMessageWithFlag(int flag, const char* pszCommand, const T1& a1) + { + try + { + BeginMessage(pszCommand); + WithOrVersion(&ssSend, flag) << a1; + EndMessage(); + } + catch (...) + { + AbortMessage(); + throw; + } + } + template void PushMessage(const char* pszCommand, const T1& a1, const T2& a2) { @@ -762,7 +779,6 @@ class CNode class CTransaction; void RelayTransaction(const CTransaction& tx); -void RelayTransaction(const CTransaction& tx, const CDataStream& ss); /** Access to the (IP) address database (peers.dat) */ class CAddrDB diff --git a/src/primitives/block.h b/src/primitives/block.h index 0e93399c08e47..221a6da1c6020 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -39,7 +39,6 @@ class CBlockHeader template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(this->nVersion); - nVersion = this->nVersion; READWRITE(hashPrevBlock); READWRITE(hashMerkleRoot); READWRITE(nTime); @@ -121,7 +120,6 @@ class CBlock : public CBlockHeader std::string ToString() const; }; - /** Describes a place in the block chain to another node such that if the * other node doesn't have the same branch, it can find a recent common trunk. * The further back it is, the further before the fork it may be. diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index aea96d8a124ce..514e206c71761 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -60,21 +60,26 @@ std::string CTxOut::ToString() const } CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} -CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) {} uint256 CMutableTransaction::GetHash() const { - return SerializeHash(*this); + return SerializeHash(*this, SER_GETHASH, 0); } void CTransaction::UpdateHash() const { - *const_cast(&hash) = SerializeHash(*this); + *const_cast(&hash) = SerializeHash(*this, SER_GETHASH, 0); +} + +uint256 CTransaction::GetWitnessHash() const +{ + return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_WITNESS); } CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { } -CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) { +CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), wit(tx.wit), nLockTime(tx.nLockTime) { UpdateHash(); } @@ -82,6 +87,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) { *const_cast(&nVersion) = tx.nVersion; *const_cast*>(&vin) = tx.vin; *const_cast*>(&vout) = tx.vout; + *const_cast(&wit) = tx.wit; *const_cast(&nLockTime) = tx.nLockTime; *const_cast(&hash) = tx.hash; return *this; @@ -136,6 +142,8 @@ std::string CTransaction::ToString() const nLockTime); for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; + for (unsigned int i = 0; i < wit.vtxinwit.size(); i++) + str += " " + wit.vtxinwit[i].scriptWitness.ToString() + "\n"; for (unsigned int i = 0; i < vout.size(); i++) str += " " + vout[i].ToString() + "\n"; return str; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 8bd6d00e2eba2..58c1bb8a6382f 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -11,6 +11,8 @@ #include "serialize.h" #include "uint256.h" +static const int SERIALIZE_TRANSACTION_WITNESS = 0x40000000; + /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -171,8 +173,134 @@ class CTxOut std::string ToString() const; }; +class CTxinWitness +{ +public: + CScriptWitness scriptWitness; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + READWRITE(scriptWitness.stack); + } + + bool IsNull() const { return scriptWitness.IsNull(); } + + CTxinWitness() { } +}; + +class CTxWitness +{ +public: + /** In case vtxinwit is missing, all entries are treated as if they were empty CTxInWitnesses */ + std::vector vtxinwit; + + ADD_SERIALIZE_METHODS; + + bool IsEmpty() const { return vtxinwit.empty(); } + + bool IsNull() const + { + for (size_t n = 0; n < vtxinwit.size(); n++) { + if (!vtxinwit[n].IsNull()) { + return false; + } + } + return true; + } + + void SetNull() + { + vtxinwit.clear(); + } + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) + { + for (size_t n = 0; n < vtxinwit.size(); n++) { + READWRITE(vtxinwit[n]); + } + if (IsNull()) { + /* It's illegal to encode a witness when all vtxinwit entries are empty. */ + throw std::ios_base::failure("Superfluous witness record"); + } + } +}; + struct CMutableTransaction; +/** + * Basic transaction serialization format: + * - int32_t nVersion + * - std::vector vin + * - std::vector vout + * - uint32_t nLockTime + * + * Extended transaction serialization format: + * - int32_t nVersion + * - unsigned char dummy = 0x00 + * - unsigned char flags (!= 0) + * - std::vector vin + * - std::vector vout + * - if (flags & 1): + * - CTxWitness wit; + * - uint32_t nLockTime + */ +template +inline void SerializeTransaction(TxType& tx, Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(*const_cast(&tx.nVersion)); + unsigned char flags = 0; + if (ser_action.ForRead()) { + /* Try to read the vin. In case the dummy is there, this will be read as an empty vector. */ + READWRITE(*const_cast*>(&tx.vin)); + if (tx.vin.size() == 0 && (nVersion & SERIALIZE_TRANSACTION_WITNESS)) { + /* We read a dummy or an empty vin. */ + READWRITE(flags); + if (flags != 0) { + /* Assume we read a dummy and a flag. */ + READWRITE(*const_cast*>(&tx.vin)); + READWRITE(*const_cast*>(&tx.vout)); + } + } else { + /* We read a non-empty vin. Assume a normal vout follows. */ + READWRITE(*const_cast*>(&tx.vout)); + } + const_cast(&tx.wit)->SetNull(); + if ((flags & 1) && (nVersion & SERIALIZE_TRANSACTION_WITNESS)) { + /* The witness flag is present, and we support witnesses. */ + flags ^= 1; + const_cast(&tx.wit)->vtxinwit.resize(tx.vin.size()); + READWRITE(tx.wit); + } + if (flags) { + /* Unknown flag in the serialization */ + throw std::ios_base::failure("Unknown transaction optional data"); + } + } else { + if (nVersion & SERIALIZE_TRANSACTION_WITNESS) { + /* Check whether witnesses need to be serialized. */ + if (!tx.wit.IsNull()) { + flags |= 1; + } + } + if (flags) { + /* Use extended format in case witnesses are to be serialized. */ + std::vector vinDummy; + READWRITE(vinDummy); + READWRITE(flags); + } + READWRITE(*const_cast*>(&tx.vin)); + READWRITE(*const_cast*>(&tx.vout)); + if (flags & 1) { + const_cast(&tx.wit)->vtxinwit.resize(tx.vin.size()); + READWRITE(tx.wit); + } + } + READWRITE(*const_cast(&tx.nLockTime)); +} + /** The basic transaction that is broadcasted on the network and contained in * blocks. A transaction can contain multiple inputs and outputs. */ @@ -194,6 +322,7 @@ class CTransaction const int32_t nVersion; const std::vector vin; const std::vector vout; + CTxWitness wit; const uint32_t nLockTime; /** Construct a CTransaction that qualifies as IsNull() */ @@ -208,13 +337,10 @@ class CTransaction template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(*const_cast(&this->nVersion)); - nVersion = this->nVersion; - READWRITE(*const_cast*>(&vin)); - READWRITE(*const_cast*>(&vout)); - READWRITE(*const_cast(&nLockTime)); - if (ser_action.ForRead()) + SerializeTransaction(*this, s, ser_action, nType, nVersion); + if (ser_action.ForRead()) { UpdateHash(); + } } bool IsNull() const { @@ -225,6 +351,9 @@ class CTransaction return hash; } + // Compute a hash that includes both transaction and witness data + uint256 GetWitnessHash() const; + // Return sum of txouts. CAmount GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because @@ -260,6 +389,7 @@ struct CMutableTransaction int32_t nVersion; std::vector vin; std::vector vout; + CTxWitness wit; uint32_t nLockTime; CMutableTransaction(); @@ -269,11 +399,7 @@ struct CMutableTransaction template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(vin); - READWRITE(vout); - READWRITE(nLockTime); + SerializeTransaction(*this, s, ser_action, nType, nVersion); } /** Compute the hash of this CMutableTransaction. This is computed on the diff --git a/src/protocol.cpp b/src/protocol.cpp index c1c7c0b96bf7c..468d8668dac5c 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -42,7 +42,9 @@ static const char* ppszTypeName[] = "ERROR", // Should never occur NetMsgType::TX, NetMsgType::BLOCK, - "filtered block" // Should never occur + "filtered block", // Should never occur + "witness block", + "witness tx", }; /** All known message types. Keep this in the same order as the list of @@ -180,7 +182,8 @@ bool operator<(const CInv& a, const CInv& b) bool CInv::IsKnownType() const { - return (type >= 1 && type < (int)ARRAYLEN(ppszTypeName)); + int masked = type & MSG_TYPE_MASK; + return (masked >= 1 && masked <= MSG_TYPE_MAX); } const char* CInv::GetCommand() const diff --git a/src/protocol.h b/src/protocol.h index c8b8d20eaddd5..5d7544b9b5fcc 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -310,12 +310,17 @@ class CInv uint256 hash; }; +const uint32_t MSG_WITNESS_FLAG = 1 << 30; +const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2; enum { MSG_TX = 1, MSG_BLOCK, - // Nodes may always request a MSG_FILTERED_BLOCK in a getdata, however, - // MSG_FILTERED_BLOCK should not appear in any invs except as a part of getdata. + // The following can only occur in getdata. Invs always use TX or BLOCK. MSG_FILTERED_BLOCK, + MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, + MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG, }; +const int MSG_TYPE_MAX = MSG_FILTERED_BLOCK; + #endif // BITCOIN_PROTOCOL_H diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index f5b16bc7c49b1..7ce7de48b8eab 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -419,7 +419,7 @@ UniValue getblock(const UniValue& params, bool fHelp) if (!fVerbose) { - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS); ssBlock << block; std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()); return strHex; diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index bd51aa0ab018d..c1c25dfd6ad06 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -605,7 +605,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); vector txData(ParseHexV(params[0], "argument 1")); - CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); + CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS); vector txVariants; while (!ssData.empty()) { try { diff --git a/src/script/script.cpp b/src/script/script.cpp index 9f2809e593756..6b1eb52bbf8c2 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -231,3 +231,15 @@ bool CScript::IsPushOnly() const { return this->IsPushOnly(begin()); } + +std::string CScriptWitness::ToString() const +{ + std::string ret = "CScriptWitness("; + for (unsigned int i = 0; i < stack.size(); i++) { + if (i) { + ret += ", "; + } + ret += HexStr(stack[i]); + } + return ret + ")"; +} diff --git a/src/script/script.h b/src/script/script.h index 6551eea30d340..be8f01a70a941 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -619,6 +619,18 @@ class CScript : public CScriptBase } }; +struct CScriptWitness +{ + std::vector > stack; + + // Some compilers complain without a default constructor + CScriptWitness() { } + + bool IsNull() const { return stack.empty(); } + + std::string ToString() const; +}; + class CReserveScript { public: diff --git a/src/streams.h b/src/streams.h index 0fc6135a6a793..e5b0f06cde475 100644 --- a/src/streams.h +++ b/src/streams.h @@ -22,6 +22,39 @@ #include #include +template +class OverrideStream +{ + Stream* stream; +public: + const int nType; + const int nVersion; + + OverrideStream(Stream* stream_, int nType_, int nVersion_) : stream(stream_), nType(nType_), nVersion(nVersion_) {} + + template + OverrideStream& operator<<(const T& obj) + { + // Serialize to this stream + ::Serialize(*this->stream, obj, nType, nVersion); + return (*this); + } + + template + OverrideStream& operator>>(T& obj) + { + // Unserialize from this stream + ::Unserialize(*this->stream, obj, nType, nVersion); + return (*this); + } +}; + +template +OverrideStream WithOrVersion(S* s, int nVersionFlag) +{ + return OverrideStream(s, s->GetType(), s->GetVersion() | nVersionFlag); +} + /** Double ended buffer combining vector and stream-like interfaces. * * >> and << read and write unformatted data using the above serialization templates. diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index 902584194907b..b36c99c180a32 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -30,10 +30,6 @@ "010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH"], ["Tests for CheckTransaction()"], -["No inputs"], -[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]], -"0100000000010000000000000000015100000000", "P2SH"], - ["No outputs"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]], "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "P2SH"], diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 50b0f40a6aefd..ff7482fb99475 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -224,7 +224,7 @@ void CDBEnv::CheckpointLSN(const std::string& strFile) } -CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnCloseIn) : pdb(NULL), activeTxn(NULL) +CDB::CDB(const std::string& strFilename, const char* pszMode, bool fFlushOnCloseIn, int nSerVersionIn) : pdb(NULL), activeTxn(NULL), nSerVersion(nSerVersionIn) { int ret; fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); diff --git a/src/wallet/db.h b/src/wallet/db.h index 01b8c71a04bbb..ef1fff1be34f9 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -99,8 +99,9 @@ class CDB DbTxn* activeTxn; bool fReadOnly; bool fFlushOnClose; + int nSerVersion; - explicit CDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnCloseIn=true); + explicit CDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnCloseIn=true, int nSerVersion = CLIENT_VERSION); ~CDB() { Close(); } public: @@ -119,7 +120,7 @@ class CDB return false; // Key - CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssKey(SER_DISK, nSerVersion); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); @@ -134,7 +135,7 @@ class CDB // Unserialize value try { - CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); + CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, nSerVersion); ssValue >> value; } catch (const std::exception&) { return false; @@ -155,13 +156,13 @@ class CDB assert(!"Write called on database in read-only mode"); // Key - CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssKey(SER_DISK, nSerVersion); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Value - CDataStream ssValue(SER_DISK, CLIENT_VERSION); + CDataStream ssValue(SER_DISK, nSerVersion); ssValue.reserve(10000); ssValue << value; Dbt datValue(&ssValue[0], ssValue.size()); @@ -184,7 +185,7 @@ class CDB assert(!"Erase called on database in read-only mode"); // Key - CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssKey(SER_DISK, nSerVersion); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); @@ -204,7 +205,7 @@ class CDB return false; // Key - CDataStream ssKey(SER_DISK, CLIENT_VERSION); + CDataStream ssKey(SER_DISK, nSerVersion); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index b1b9d0c235bc2..036a8b2567aaa 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -639,8 +639,8 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) while (true) { // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); + CDataStream ssKey(SER_DISK, nSerVersion); + CDataStream ssValue(SER_DISK, nSerVersion); int ret = ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; @@ -745,8 +745,8 @@ DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector& vTxHash, vec while (true) { // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); + CDataStream ssKey(SER_DISK, nSerVersion); + CDataStream ssValue(SER_DISK, nSerVersion); int ret = ReadAtCursor(pcursor, ssKey, ssValue); if (ret == DB_NOTFOUND) break; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 8da33dead20c2..160bca6cba1f3 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -7,6 +7,7 @@ #define BITCOIN_WALLET_WALLETDB_H #include "amount.h" +#include "primitives/transaction.h" #include "wallet/db.h" #include "key.h" @@ -77,7 +78,7 @@ class CKeyMetadata class CWalletDB : public CDB { public: - CWalletDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnClose = true) : CDB(strFilename, pszMode, fFlushOnClose) + CWalletDB(const std::string& strFilename, const char* pszMode = "r+", bool fFlushOnClose = true) : CDB(strFilename, pszMode, fFlushOnClose, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS) { } From af13f1319d91d5ba0cc8d8b504921d7f5b9dc463 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Sun, 10 Jan 2016 23:55:11 -0800 Subject: [PATCH 06/32] Removed ppszTypeName from protocol.cpp --- src/protocol.cpp | 35 +++++++++++++---------------------- src/protocol.h | 34 +++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/protocol.cpp b/src/protocol.cpp index 468d8668dac5c..04bee3ddf0871 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -37,16 +37,6 @@ const char *REJECT="reject"; const char *SENDHEADERS="sendheaders"; }; -static const char* ppszTypeName[] = -{ - "ERROR", // Should never occur - NetMsgType::TX, - NetMsgType::BLOCK, - "filtered block", // Should never occur - "witness block", - "witness tx", -}; - /** All known message types. Keep this in the same order as the list of * messages above and in protocol.h. */ @@ -161,17 +151,13 @@ CInv::CInv(int typeIn, const uint256& hashIn) CInv::CInv(const std::string& strType, const uint256& hashIn) { - unsigned int i; - for (i = 1; i < ARRAYLEN(ppszTypeName); i++) - { - if (strType == ppszTypeName[i]) - { - type = i; - break; - } - } - if (i == ARRAYLEN(ppszTypeName)) + if (strType == NetMsgType::TX) + type = MSG_TX; + else if (strType == NetMsgType::BLOCK) + type = MSG_BLOCK; + else throw std::out_of_range(strprintf("CInv::CInv(string, uint256): unknown type '%s'", strType)); + hash = hashIn; } @@ -188,9 +174,14 @@ bool CInv::IsKnownType() const const char* CInv::GetCommand() const { - if (!IsKnownType()) + int masked = type & MSG_TYPE_MASK; + switch (masked) + { + case MSG_TX: return NetMsgType::TX; + case MSG_BLOCK: return NetMsgType::BLOCK; + default: throw std::out_of_range(strprintf("CInv::GetCommand(): type=%d unknown type", type)); - return ppszTypeName[type]; + } } std::string CInv::ToString() const diff --git a/src/protocol.h b/src/protocol.h index 5d7544b9b5fcc..3ef569bf527e4 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -281,6 +281,27 @@ class CAddress : public CService unsigned int nTime; }; +/** getdata message types */ +enum GetDataMsg +{ + MSG_TX = 1, + MSG_BLOCK, + MSG_TYPE_MAX = MSG_BLOCK, + // The following can only occur in getdata. Invs always use TX or BLOCK. + MSG_FILTERED_BLOCK, + UNDEFINED, +}; + +const uint32_t MSG_WITNESS_FLAG = 1 << 30; +const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2; + +enum GetDataMsgWithFlags +{ + MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, + MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG, + MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG, +}; + /** inv message data */ class CInv { @@ -310,17 +331,4 @@ class CInv uint256 hash; }; -const uint32_t MSG_WITNESS_FLAG = 1 << 30; -const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2; -enum { - MSG_TX = 1, - MSG_BLOCK, - // The following can only occur in getdata. Invs always use TX or BLOCK. - MSG_FILTERED_BLOCK, - MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, - MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG, -}; - -const int MSG_TYPE_MAX = MSG_FILTERED_BLOCK; - #endif // BITCOIN_PROTOCOL_H From 70b54b6a4c7a74939f0dc03cea72a88a00d4b664 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Fri, 15 Jan 2016 20:29:35 -0500 Subject: [PATCH 07/32] getdata enum issue fix --- src/protocol.h | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/protocol.h b/src/protocol.h index 3ef569bf527e4..e333b166406de 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -282,21 +282,16 @@ class CAddress : public CService }; /** getdata message types */ +const uint32_t MSG_WITNESS_FLAG = 1 << 30; +const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2; enum GetDataMsg { - MSG_TX = 1, + UNDEFINED = 0, + MSG_TX, MSG_BLOCK, MSG_TYPE_MAX = MSG_BLOCK, // The following can only occur in getdata. Invs always use TX or BLOCK. MSG_FILTERED_BLOCK, - UNDEFINED, -}; - -const uint32_t MSG_WITNESS_FLAG = 1 << 30; -const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2; - -enum GetDataMsgWithFlags -{ MSG_WITNESS_BLOCK = MSG_BLOCK | MSG_WITNESS_FLAG, MSG_WITNESS_TX = MSG_TX | MSG_WITNESS_FLAG, MSG_FILTERED_WITNESS_BLOCK = MSG_FILTERED_BLOCK | MSG_WITNESS_FLAG, From 33489d6b7425e7f1d78157c6946830324cd1bae8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 17 Nov 2015 00:20:49 +0100 Subject: [PATCH 08/32] Negotiate witness fetching via 'havewitness' --- src/main.cpp | 25 +++++++++++++++++++++++-- src/protocol.cpp | 4 +++- src/protocol.h | 5 +++++ src/version.h | 3 +++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index fc46573cffa6b..a4299d40add7a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -262,6 +262,8 @@ struct CNodeState { bool fPreferredDownload; //! Whether this peer wants invs or headers (when possible) for block announcements. bool fPreferHeaders; + //! Whether this peer can give us witnesses + bool fHaveWitness; CNodeState() { fCurrentlyConnected = false; @@ -277,6 +279,7 @@ struct CNodeState { nBlocksInFlightValidHeaders = 0; fPreferredDownload = false; fPreferHeaders = false; + fHaveWitness = false; } }; @@ -4412,6 +4415,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // nodes) pfrom->PushMessage(NetMsgType::SENDHEADERS); } + + if (pfrom->nVersion >= WITNESS_VERSION) { + pfrom->PushMessage(NetMsgType::HAVEWITNESS); + } } @@ -4488,6 +4495,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } + else if (strCommand == NetMsgType::HAVEWITNESS) + { + LOCK(cs_main); + State(pfrom->GetId())->fHaveWitness = true; + } + + else if (strCommand == NetMsgType::INV) { vector vInv; @@ -4510,7 +4524,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { - const CInv &inv = vInv[nInv]; + CInv &inv = vInv[nInv]; boost::this_thread::interruption_point(); pfrom->AddInventoryKnown(inv); @@ -4518,6 +4532,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool fAlreadyHave = AlreadyHave(inv); LogPrint("net", "got inv: %s %s peer=%d\n", inv.ToString(), fAlreadyHave ? "have" : "new", pfrom->id); + if (inv.type == MSG_TX && State(pfrom->GetId())->fHaveWitness) { + inv.type = MSG_WITNESS_TX; + } + if (inv.type == MSG_BLOCK) { UpdateBlockAvailability(pfrom->GetId(), inv.hash); if (!fAlreadyHave && !fImporting && !fReindex && !mapBlocksInFlight.count(inv.hash)) { @@ -4533,6 +4551,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CNodeState *nodestate = State(pfrom->GetId()); if (CanDirectFetch(chainparams.GetConsensus()) && nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + if (State(pfrom->GetId())->fHaveWitness) { + inv.type = MSG_WITNESS_BLOCK; + } vToFetch.push_back(inv); // Mark block as in flight already, even though the actual "getdata" message only goes out // later (within the same cs_main lock, though). @@ -5620,7 +5641,7 @@ bool SendMessages(CNode* pto) NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { - vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); + vGetData.push_back(CInv(State(staller)->fHaveWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), pindex->nHeight, pto->id); diff --git a/src/protocol.cpp b/src/protocol.cpp index 04bee3ddf0871..c91817a86934c 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -35,6 +35,7 @@ const char *FILTERADD="filteradd"; const char *FILTERCLEAR="filterclear"; const char *REJECT="reject"; const char *SENDHEADERS="sendheaders"; +const char *HAVEWITNESS="havewitness"; }; /** All known message types. Keep this in the same order as the list of @@ -62,7 +63,8 @@ const static std::string allNetMessageTypes[] = { NetMsgType::FILTERADD, NetMsgType::FILTERCLEAR, NetMsgType::REJECT, - NetMsgType::SENDHEADERS + NetMsgType::SENDHEADERS, + NetMsgType::HAVEWITNESS, }; const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index e333b166406de..22879a5dc0504 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -218,6 +218,11 @@ extern const char *REJECT; * @see https://bitcoin.org/en/developer-reference#sendheaders */ extern const char *SENDHEADERS; +/** + * Indicates that a node can be asked for blocks and transactions including + * witness data. + */ +extern const char *HAVEWITNESS; }; diff --git a/src/version.h b/src/version.h index f7cf18d0b68ca..d74e477580ba2 100644 --- a/src/version.h +++ b/src/version.h @@ -40,4 +40,7 @@ static const int NO_BLOOM_VERSION = 70011; //! "sendheaders" command and announcing blocks with headers starts with this version static const int SENDHEADERS_VERSION = 70012; +//! Version after which witness support potentially exists +static const int WITNESS_VERSION = 70012; + #endif // BITCOIN_VERSION_H From 9b180e7f322b19b30d2140f394e561b6daa2ea4e Mon Sep 17 00:00:00 2001 From: Nicolas DORIER Date: Fri, 29 Jan 2016 19:13:16 +0900 Subject: [PATCH 09/32] Negotiate witness fetching via service bit 'NODE_WITNESS' --- src/main.cpp | 17 ++++++----------- src/net.cpp | 2 +- src/protocol.cpp | 2 -- src/protocol.h | 8 +++----- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a4299d40add7a..188d250e1b50d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4327,6 +4327,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fClient = !(pfrom->nServices & NODE_NETWORK); + if((pfrom->nServices & NODE_WITNESS)) + { + LOCK(cs_main); + State(pfrom->GetId())->fHaveWitness = true; + } + // Potentially mark this peer as a preferred download peer. UpdatePreferredDownload(pfrom, State(pfrom->GetId())); @@ -4416,9 +4422,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage(NetMsgType::SENDHEADERS); } - if (pfrom->nVersion >= WITNESS_VERSION) { - pfrom->PushMessage(NetMsgType::HAVEWITNESS); - } } @@ -4494,14 +4497,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, State(pfrom->GetId())->fPreferHeaders = true; } - - else if (strCommand == NetMsgType::HAVEWITNESS) - { - LOCK(cs_main); - State(pfrom->GetId())->fHaveWitness = true; - } - - else if (strCommand == NetMsgType::INV) { vector vInv; diff --git a/src/net.cpp b/src/net.cpp index c8d6c1ce6f649..fe6ceff0c736b 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -74,7 +74,7 @@ namespace { // bool fDiscover = true; bool fListen = true; -uint64_t nLocalServices = NODE_NETWORK; +uint64_t nLocalServices = NODE_NETWORK | NODE_WITNESS; CCriticalSection cs_mapLocalHost; map mapLocalHost; static bool vfReachable[NET_MAX] = {}; diff --git a/src/protocol.cpp b/src/protocol.cpp index c91817a86934c..267450f752384 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -35,7 +35,6 @@ const char *FILTERADD="filteradd"; const char *FILTERCLEAR="filterclear"; const char *REJECT="reject"; const char *SENDHEADERS="sendheaders"; -const char *HAVEWITNESS="havewitness"; }; /** All known message types. Keep this in the same order as the list of @@ -64,7 +63,6 @@ const static std::string allNetMessageTypes[] = { NetMsgType::FILTERCLEAR, NetMsgType::REJECT, NetMsgType::SENDHEADERS, - NetMsgType::HAVEWITNESS, }; const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index 22879a5dc0504..05f939f17fd60 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -218,11 +218,6 @@ extern const char *REJECT; * @see https://bitcoin.org/en/developer-reference#sendheaders */ extern const char *SENDHEADERS; -/** - * Indicates that a node can be asked for blocks and transactions including - * witness data. - */ -extern const char *HAVEWITNESS; }; @@ -243,6 +238,9 @@ enum { // Bitcoin Core nodes used to support this by default, without advertising this bit, // but no longer do as of protocol version 70011 (= NO_BLOOM_VERSION) NODE_BLOOM = (1 << 2), + // Indicates that a node can be asked for blocks and transactions including + // witness data. + NODE_WITNESS = (1 << 3), // Bits 24-31 are reserved for temporary experiments. Just pick a bit that // isn't getting used, or one not being used much, and notify the From 0caed3f13556a9ec9d5750c8f03a0acbe15d960b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 6 Nov 2015 01:42:38 +0100 Subject: [PATCH 10/32] Witness commitment validation --- src/chainparams.cpp | 4 ++ src/consensus/merkle.cpp | 11 ++++ src/consensus/merkle.h | 6 ++ src/consensus/params.h | 1 + src/main.cpp | 111 +++++++++++++++++++++++++++++++++++ src/main.h | 11 ++++ src/miner.cpp | 17 ++++-- src/miner.h | 3 +- src/primitives/block.h | 2 +- src/primitives/transaction.h | 3 +- src/rpcmining.cpp | 20 +++++-- src/test/test_bitcoin.cpp | 2 +- 12 files changed, 179 insertions(+), 12 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 522ea977894e9..e389bc4d4fa82 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -77,6 +77,7 @@ class CMainParams : public CChainParams { consensus.BIP34Height = 227931; consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.SegWitHeight = 2000000000; consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = false; @@ -159,6 +160,7 @@ class CTestNetParams : public CChainParams { consensus.BIP34Height = 21111; consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.SegWitHeight = 2000000000; consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; @@ -223,6 +225,7 @@ class CSegNetParams : public CChainParams { consensus.BIP34Height = -1; consensus.BIP34Hash = uint256(); consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.SegWitHeight = 0; consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; @@ -275,6 +278,7 @@ class CRegTestParams : public CChainParams { consensus.BIP34Height = -1; // BIP34 has not necessarily activated on regtest consensus.BIP34Hash = uint256(); consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.SegWitHeight = 0; consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 9a8afa8a33f3b..b10f1bae51adf 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -161,6 +161,17 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated) return ComputeMerkleRoot(leaves, mutated); } +uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated) +{ + std::vector leaves; + leaves.resize(block.vtx.size()); + leaves[0].SetNull(); // The witness hash of the coinbase is 0. + for (size_t s = 1; s < block.vtx.size(); s++) { + leaves[s] = block.vtx[s].GetWitnessHash(); + } + return ComputeMerkleRoot(leaves, mutated); +} + std::vector BlockMerkleBranch(const CBlock& block, uint32_t position) { std::vector leaves; diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h index 6ef59745ac653..194aea9b75dc3 100644 --- a/src/consensus/merkle.h +++ b/src/consensus/merkle.h @@ -22,6 +22,12 @@ uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::vectornHeight + 1 >= Params().GetConsensus().SegWitHeight && IsSuperMajority(5, chainActive.Tip(), Params().GetConsensus().nMajorityRejectBlockOutdated, Params().GetConsensus()))) { + return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet"); + } + // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. @@ -3011,6 +3016,68 @@ static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidati return true; } +bool IsWitnessEnabled(const CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + return (block.nVersion >= 5 && pindexPrev->nHeight + 1 >= params.SegWitHeight && IsSuperMajority(5, pindexPrev, params.nMajorityEnforceBlockUpgrade, params)); +} + + +void UpdateUncommitedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +{ + int commitpos = -1; + for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { + if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) { + commitpos = o; + } + } + static const std::vector nonce(32, 0x00); + if (commitpos != -1 && IsWitnessEnabled(block, pindexPrev, consensusParams) && block.vtx[0].wit.IsEmpty()) { + block.vtx[0].wit.vtxinwit.resize(1); + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.resize(1); + block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0] = nonce; + } +} + +std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +{ + std::vector commitment; + int commitpos = -1; + for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { + if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) { + commitpos = o; + } + } + bool fHaveWitness = false; + for (size_t t = 1; t < block.vtx.size(); t++) { + if (!block.vtx[t].wit.IsNull()) { + fHaveWitness = true; + break; + } + } + std::vector ret(32, 0x00); + if (fHaveWitness && IsWitnessEnabled(block, pindexPrev, consensusParams)) { + if (commitpos == -1) { + uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL); + CHash256().Write(witnessroot.begin(), 32).Write(&ret[0], 32).Finalize(witnessroot.begin()); + CTxOut out; + out.nValue = 0; + out.scriptPubKey.resize(38); + out.scriptPubKey[0] = OP_RETURN; + out.scriptPubKey[1] = 0x24; + out.scriptPubKey[2] = 0xaa; + out.scriptPubKey[3] = 0x21; + out.scriptPubKey[4] = 0xa9; + out.scriptPubKey[5] = 0xed; + memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32); + commitment = std::vector(out.scriptPubKey.begin(), out.scriptPubKey.end()); + const_cast*>(&block.vtx[0].vout)->push_back(out); + block.vtx[0].UpdateHash(); + } + } + UpdateUncommitedBlockStructures(block, pindexPrev, consensusParams); + return commitment; +} + bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) { const Consensus::Params& consensusParams = Params().GetConsensus(); @@ -3039,6 +3106,12 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.Invalid(error("%s : rejected nVersion=3 block", __func__), REJECT_OBSOLETE, "bad-version"); + // Reject block.nVersion=4 blocks when 95% (75% on testnet) of the network has upgraded: + if (block.nVersion < 5 && pindexPrev->nHeight + 1 >= consensusParams.SegWitHeight && IsSuperMajority(5, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) + return state.Invalid(error("%s : rejected nVersion=4 block", __func__), + REJECT_OBSOLETE, "bad-version"); + + return true; } @@ -3069,6 +3142,44 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn } } + // Validation for witness commitments. + // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the + // coinbase (where 0x0000....0000 is used instead). + // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained). + // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header). + // * The must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are + // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256(witness root, witness nonce). In case there are + // multiple, the last one is used. + if (IsWitnessEnabled(block, pindexPrev, consensusParams)) { + int commitpos = -1; + for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { + if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) { + commitpos = o; + } + } + if (commitpos != -1) { + bool malleated = false; + uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated); + // The malleation check is ignored; as the transaction tree itself + // already does not permit it, it is impossible to trigger in the + // witness tree. + if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) { + return state.DoS(100, error("%s : invalid witness commitment size", __func__), REJECT_INVALID, "bad-witness-merkle-size", true); + } + CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin()); + if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) { + return state.DoS(100, error("%s : witness merkle commitment mismatch", __func__), REJECT_INVALID, "bad-witness-merkle-match", true); + } + return true; + } + } + // No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room from spam. + for (size_t i = 0; i < block.vtx.size(); i++) { + if (!block.vtx[i].wit.IsNull()) { + return state.DoS(100, error("%s : unexpected witness data found", __func__), REJECT_INVALID, "unexpected-witness", true); + } + } + return true; } diff --git a/src/main.h b/src/main.h index 9fd97d2126823..b71d3389f8367 100644 --- a/src/main.h +++ b/src/main.h @@ -415,6 +415,17 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn /** Check a block is completely valid from start to finish (only works on top of our current best block, with cs_main held) */ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true); +/** Check whether witness commitments are required for block. */ +bool IsWitnessEnabled(const CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& params); + +void UpdateUncommitedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); + +/** Update uncommitted block structures (currently: only the witness nonce). This is safe for submitted blocks. */ +void UpdateUncommitedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); + +/** 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); + class CBlockFileInfo { diff --git a/src/miner.cpp b/src/miner.cpp index c454c0279c200..7f3cef5d814ab 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -137,6 +137,9 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); + // Decide whether to include witness transactions (temporary) + bool fIncludeWitness = IsWitnessEnabled(*pblock, pindexPrev, chainparams.GetConsensus()); + int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) ? nMedianTimePast : pblock->GetBlockTime(); @@ -182,6 +185,9 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s const CTransaction& tx = iter->GetTx(); + if (!fIncludeWitness && !tx.wit.IsNull()) + continue; // cannot accept witness transactions into a non-witness block + bool fOrphan = false; BOOST_FOREACH(CTxMemPool::txiter parent, mempool.GetMemPoolParents(iter)) { @@ -275,10 +281,13 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s nLastBlockSize = nBlockSize; LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOps); + // Compute final coinbase transaction. txNew.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); - txNew.vin[0].scriptSig = CScript() << nHeight << OP_0; + txNew.vin[0].scriptSig = CScript() << nHeight; + txNew.vin[0].scriptSig << OP_0; pblock->vtx[0] = txNew; + pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); pblocktemplate->vTxFees[0] = -nFees; // Fill in header @@ -297,7 +306,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s return pblocktemplate.release(); } -void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce) +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce, const std::vector vchCoinbaseCommitment) { // Update nExtraNonce static uint256 hashPrevBlock; @@ -309,7 +318,7 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned ++nExtraNonce; unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 CMutableTransaction txCoinbase(pblock->vtx[0]); - txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; + txCoinbase.vin[0].scriptSig = (CScript() << nHeight << vchCoinbaseCommitment << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; assert(txCoinbase.vin[0].scriptSig.size() <= 100); pblock->vtx[0] = txCoinbase; @@ -424,7 +433,7 @@ void static BitcoinMiner(const CChainParams& chainparams) return; } CBlock *pblock = &pblocktemplate->block; - IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce, pblocktemplate->vchCoinbaseCommitment); LogPrintf("Running BitcoinMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(), ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); diff --git a/src/miner.h b/src/miner.h index 512494198b7c1..7e6fd41e73624 100644 --- a/src/miner.h +++ b/src/miner.h @@ -27,6 +27,7 @@ struct CBlockTemplate CBlock block; std::vector vTxFees; std::vector vTxSigOps; + std::vector vchCoinbaseCommitment; }; /** Run the miner threads */ @@ -34,7 +35,7 @@ void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainpar /** Generate a new block, without valid proof-of-work */ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn); /** Modify the extranonce in a block */ -void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce); +void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce, const std::vector vchCoinbaseCommitment); int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev); #endif // BITCOIN_MINER_H diff --git a/src/primitives/block.h b/src/primitives/block.h index 221a6da1c6020..353e47c6b7665 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -21,7 +21,7 @@ class CBlockHeader { public: // header - static const int32_t CURRENT_VERSION=4; + static const int32_t CURRENT_VERSION=5; int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 58c1bb8a6382f..327bdcd66876b 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -309,7 +309,6 @@ class CTransaction private: /** Memory only. */ const uint256 hash; - void UpdateHash() const; public: static const int32_t CURRENT_VERSION=1; @@ -381,6 +380,8 @@ class CTransaction } std::string ToString() const; + + void UpdateHash() const; }; /** A mutable version of CTransaction. */ diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 1048344e00920..96e74a94308fc 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -163,7 +163,7 @@ UniValue generate(const UniValue& params, bool fHelp) CBlock *pblock = &pblocktemplate->block; { LOCK(cs_main); - IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce); + IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce, pblocktemplate->vchCoinbaseCommitment); } while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { // Yes, there is a chance every nonce could fail to satisfy the -regtest @@ -348,6 +348,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) " {\n" " \"data\" : \"xxxx\", (string) transaction data encoded in hexadecimal (byte-for-byte)\n" " \"hash\" : \"xxxx\", (string) hash/id encoded in little-endian hexadecimal\n" + " \"withash\" : \"xxxx\", (string) witness hash encoded in little-endian hexadecimal\n" " \"depends\" : [ (array) array of numbers \n" " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" " ,...\n" @@ -528,7 +529,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) UniValue transactions(UniValue::VARR); map setTxIndex; int i = 0; - BOOST_FOREACH (const CTransaction& tx, pblock->vtx) { + BOOST_FOREACH (CTransaction& tx, pblock->vtx) { uint256 txHash = tx.GetHash(); setTxIndex[txHash] = i++; @@ -538,8 +539,8 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) UniValue entry(UniValue::VOBJ); entry.push_back(Pair("data", EncodeHexTx(tx))); - - entry.push_back(Pair("hash", txHash.GetHex())); + entry.push_back(Pair("txid", txHash.GetHex())); + entry.push_back(Pair("hash", tx.GetWitnessHash().GetHex())); UniValue deps(UniValue::VARR); BOOST_FOREACH (const CTxIn &in, tx.vin) @@ -586,6 +587,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) result.push_back(Pair("curtime", pblock->GetBlockTime())); result.push_back(Pair("bits", strprintf("%08x", pblock->nBits))); result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1))); + if (!pblocktemplate->vchCoinbaseCommitment.empty()) { + result.push_back(Pair("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()))); + } return result; } @@ -649,6 +653,14 @@ UniValue submitblock(const UniValue& params, bool fHelp) } } + { + LOCK(cs_main); + BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); + if (mi != mapBlockIndex.end()) { + UpdateUncommitedBlockStructures(block, mi->second, Params().GetConsensus()); + } + } + CValidationState state; submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index f81050b15d742..61968d9822ae9 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -126,7 +126,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector& block.vtx.push_back(tx); // IncrementExtraNonce creates a valid coinbase and merkleRoot unsigned int extraNonce = 0; - IncrementExtraNonce(&block, chainActive.Tip(), extraNonce); + IncrementExtraNonce(&block, chainActive.Tip(), extraNonce, pblocktemplate->vchCoinbaseCommitment); while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; From 778d1e44c64e8edca419af0f5da9a08ea16a403b Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 8 Nov 2015 01:16:45 +0100 Subject: [PATCH 11/32] Script validation logic for witnesses --- src/bitcoin-tx.cpp | 2 +- src/main.cpp | 3 +- src/policy/policy.h | 4 +- src/rpcrawtransaction.cpp | 2 +- src/script/bitcoinconsensus.cpp | 2 +- src/script/interpreter.cpp | 110 +++++++++++++++++++++++++++++++- src/script/interpreter.h | 12 +++- src/script/script.cpp | 18 ++++++ src/script/script.h | 1 + src/script/script_error.cpp | 14 ++++ src/script/script_error.h | 9 +++ src/script/sign.cpp | 2 +- src/test/multisig_tests.cpp | 16 ++--- src/test/script_P2SH_tests.cpp | 2 +- src/test/script_tests.cpp | 36 ++++++----- src/test/transaction_tests.cpp | 4 +- 16 files changed, 200 insertions(+), 37 deletions(-) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 2c502ead315f8..238b6a394593d 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -463,7 +463,7 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) BOOST_FOREACH(const CTransaction& txv, txVariants) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) fComplete = false; } diff --git a/src/main.cpp b/src/main.cpp index 9941409a4c0dd..59abe1e549478 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1574,7 +1574,8 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { + const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL; + if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { return false; } return true; diff --git a/src/policy/policy.h b/src/policy/policy.h index 726864f1902ba..80043a1901a47 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -40,7 +40,9 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS | SCRIPT_VERIFY_CLEANSTACK | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | - SCRIPT_VERIFY_LOW_S; + SCRIPT_VERIFY_LOW_S | + SCRIPT_VERIFY_WITNESS | + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM; /** For convenience, standard but not mandatory verify flags. */ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index c1c25dfd6ad06..476f6aaa04204 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -763,7 +763,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 47ad1d08073b9..5c9e7c0a571a5 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -85,7 +85,7 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i // Regardless of the verification result, the tx did not error. set_error(err, bitcoinconsensus_ERR_OK); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), flags, TransactionSignatureChecker(&tx, nIn), NULL); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 265131ae0d581..36c4380c0a3b5 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1156,9 +1156,67 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con return true; } +static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +{ + vector > stack; + CScript scriptPubKey; + + if (witversion == 0) { + if (program.size() == 32) { + // Version 0 segregated witness program: SHA256(CScript) inside the program, CScript + inputs in witness + if (witness.stack.size() == 0) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY); + } + scriptPubKey = CScript(witness.stack.back().begin(), witness.stack.back().end()); + stack = std::vector >(witness.stack.begin(), witness.stack.end() - 1); + uint256 hashScriptPubKey; + CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin()); + if (memcmp(hashScriptPubKey.begin(), &program[0], 32)) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); + } + } else if (program.size() == 20) { + // Special case for pay-to-pubkeyhash; signature + pubkey in witness + if (witness.stack.size() != 2) { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); // 2 items in witness + } + scriptPubKey << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG; + stack = witness.stack; + } else { + return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH); + } + } else if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM); + } else { + // Higher version witness scripts return true for future softfork compatibility + return set_success(serror); + } + + // Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack + for (unsigned int i = 0; i < stack.size(); i++) { + if (stack.at(i).size() > MAX_SCRIPT_ELEMENT_SIZE) + return set_error(serror, SCRIPT_ERR_PUSH_SIZE); + } + + if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) { + return false; + } + + // Scripts inside witness implicitly require cleanstack behaviour + if (stack.size() != 1) + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + if (!CastToBool(stack.back())) + return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + return true; +} -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { + static const CScriptWitness emptyWitness; + if (witness == NULL) { + witness = &emptyWitness; + } + bool hadWitness = false; + set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); if ((flags & SCRIPT_VERIFY_SIGPUSHONLY) != 0 && !scriptSig.IsPushOnly()) { @@ -1179,6 +1237,25 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne if (CastToBool(stack.back()) == false) return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + // Bare witness programs + int witnessversion; + std::vector witnessprogram; + if (flags & SCRIPT_VERIFY_WITNESS) { + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + hadWitness = true; + if (scriptSig.size() != 0) { + // The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability. + return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED); + } + if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) { + return false; + } + // Bypass the cleanstack check at the end. The actual stack is obviously not clean + // for witness programs. + stack.resize(1); + } + } + // Additional validation for spend-to-script-hash transactions: if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { @@ -1205,19 +1282,48 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne return set_error(serror, SCRIPT_ERR_EVAL_FALSE); if (!CastToBool(stack.back())) return set_error(serror, SCRIPT_ERR_EVAL_FALSE); + + // P2SH witness program + if (flags & SCRIPT_VERIFY_WITNESS) { + if (pubKey2.IsWitnessProgram(witnessversion, witnessprogram)) { + hadWitness = true; + if (scriptSig != CScript() << std::vector(pubKey2.begin(), pubKey2.end())) { + // The scriptSig must be _exactly_ a single push of the redeemScript. Otherwise we + // reintroduce malleability. + return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED_P2SH); + } + if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) { + return false; + } + // Bypass the cleanstack check at the end. The actual stack is obviously not clean + // for witness programs. + stack.resize(1); + } + } } // The CLEANSTACK check is only performed after potential P2SH evaluation, // as the non-P2SH evaluation of a P2SH script will obviously not result in - // a clean stack (the P2SH inputs remain). + // a clean stack (the P2SH inputs remain). The same holds for witness evaluation. if ((flags & SCRIPT_VERIFY_CLEANSTACK) != 0) { // Disallow CLEANSTACK without P2SH, as otherwise a switch CLEANSTACK->P2SH+CLEANSTACK // would be possible, which is not a softfork (and P2SH should be one). assert((flags & SCRIPT_VERIFY_P2SH) != 0); + assert((flags & SCRIPT_VERIFY_WITNESS) != 0); if (stack.size() != 1) { return set_error(serror, SCRIPT_ERR_CLEANSTACK); } } + if (flags & SCRIPT_VERIFY_WITNESS) { + // We can't check for correct unexpected witness data if P2SH was off, so require + // that WITNESS implies P2SH. Otherwise, going from WITNESS->P2SH+WITNESS would be + // possible, which is not a softfork. + assert((flags & SCRIPT_VERIFY_P2SH) != 0); + if (!hadWitness && !witness->IsNull()) { + return set_error(serror, SCRIPT_ERR_WITNESS_UNEXPECTED); + } + } + return set_success(serror); } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 7b34547ffbd7d..5646db4934cbb 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -74,13 +74,21 @@ enum // "At least one stack element must remain, and when interpreted as a boolean, it must be true" to // "Exactly one stack element must remain, and when interpreted as a boolean, it must be true". // (softfork safe, BIP62 rule 6) - // Note: CLEANSTACK should never be used without P2SH. + // Note: CLEANSTACK should never be used without P2SH or WITNESS. SCRIPT_VERIFY_CLEANSTACK = (1U << 8), // Verify CHECKLOCKTIMEVERIFY // // See BIP65 for details. SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), + + // Support segregated witness + // + SCRIPT_VERIFY_WITNESS = (1U << 10), + + // Making v2-v16 witness program non-standard + // + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1U << 11), }; bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); @@ -128,6 +136,6 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker }; bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL); #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/script.cpp b/src/script/script.cpp index 6b1eb52bbf8c2..cabee7dc979c7 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -210,6 +210,24 @@ bool CScript::IsPayToScriptHash() const (*this)[22] == OP_EQUAL); } +// A witness program is any valid CScript that consists of a 1-byte push opcode +// followed by a data push between 2 and 32 bytes. +bool CScript::IsWitnessProgram(int& version, std::vector& program) const +{ + if (this->size() < 4 || this->size() > 34) { + return false; + } + if ((*this)[0] != OP_0 && ((*this)[0] < OP_1 || (*this)[0] > OP_16)) { + return false; + } + if ((size_t)((*this)[1] + 2) == this->size()) { + version = DecodeOP_N((opcodetype)(*this)[0]); + program = std::vector(this->begin() + 2, this->end()); + return true; + } + return false; +} + bool CScript::IsPushOnly(const_iterator pc) const { while (pc < end()) diff --git a/src/script/script.h b/src/script/script.h index be8f01a70a941..d516a78c9a845 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -597,6 +597,7 @@ class CScript : public CScriptBase unsigned int GetSigOpCount(const CScript& scriptSig) const; bool IsPayToScriptHash() const; + bool IsWitnessProgram(int& version, std::vector& program) const; /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ bool IsPushOnly(const_iterator pc) const; diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index f1aa1fb408ae5..cef807edcf475 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -65,8 +65,22 @@ const char* ScriptErrorString(const ScriptError serror) return "Dummy CHECKMULTISIG argument must be zero"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS: return "NOPx reserved for soft-fork upgrades"; + case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: + return "Witness version reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: return "Public key is neither compressed or uncompressed"; + case SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH: + return "Witness program has incorrect length"; + case SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY: + return "Witness program was passed an empty witness"; + case SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH: + return "Witness program hash mismatch"; + case SCRIPT_ERR_WITNESS_MALLEATED: + return "Witness requires empty scriptSig"; + case SCRIPT_ERR_WITNESS_MALLEATED_P2SH: + return "Witness requires only-redeemscript scriptSig"; + case SCRIPT_ERR_WITNESS_UNEXPECTED: + return "Witness provided for non-witness script"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index bb10b8a2932a5..1dc50e72a8d75 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -51,6 +51,15 @@ typedef enum ScriptError_t /* softfork safeness */ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, + SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM, + + /* segregated witness */ + SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH, + SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY, + SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH, + SCRIPT_ERR_WITNESS_MALLEATED, + SCRIPT_ERR_WITNESS_MALLEATED_P2SH, + SCRIPT_ERR_WITNESS_UNEXPECTED, SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 2f4111f786420..37b702de17cb6 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -123,7 +123,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu } // Test solution - return VerifyScript(scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); + return VerifyScript(scriptSig, fromPubKey, NULL, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); } bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index b65c299adcbaa..8dedb23e43d21 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -83,20 +83,20 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.assign(1,key[0]); keys.push_back(key[1]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err)); + BOOST_CHECK(VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); for (int i = 0; i < 4; i++) { keys.assign(1,key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 1: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); keys.assign(1,key[1]); keys.push_back(key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 2: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } @@ -107,18 +107,18 @@ BOOST_AUTO_TEST_CASE(multisig_verify) s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) { - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err)); + BOOST_CHECK(!VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_DER, ScriptErrorString(err)); @@ -130,12 +130,12 @@ BOOST_AUTO_TEST_CASE(multisig_verify) s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) { - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, NULL, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 1: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, NULL, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 2: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 28b85e8d290a7..ed31271626215 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -45,7 +45,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err); + return VerifyScript(scriptSig, scriptPubKey, NULL, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 10175ebe8e580..fb7509e41917f 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -89,10 +89,14 @@ CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CMu void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, bool expect, const std::string& message) { + if (flags & SCRIPT_VERIFY_CLEANSTACK) { + flags |= SCRIPT_VERIFY_P2SH; + flags |= SCRIPT_VERIFY_WITNESS; + } ScriptError err; CMutableTransaction tx = BuildSpendingTransaction(scriptSig, BuildCreditingTransaction(scriptPubKey)); CMutableTransaction tx2 = tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, flags, MutableTransactionSignatureChecker(&tx, 0), &err) == expect, message); + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, NULL, flags, MutableTransactionSignatureChecker(&tx, 0), &err) == expect, message); BOOST_CHECK_MESSAGE(expect == (err == SCRIPT_ERR_OK), std::string(ScriptErrorString(err)) + ": " + message); #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); @@ -758,18 +762,18 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), txFrom12); CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } @@ -791,54 +795,54 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); } @@ -958,7 +962,7 @@ BOOST_AUTO_TEST_CASE(script_standard_push) CScript script; script << i; BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Number " << i << " is not pure push."); - BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Number " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, NULL, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Number " << i << " push is not minimal data."); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } @@ -967,7 +971,7 @@ BOOST_AUTO_TEST_CASE(script_standard_push) CScript script; script << data; BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Length " << i << " is not pure push."); - BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Length " << i << " push is not minimal data."); + BOOST_CHECK_MESSAGE(VerifyScript(script, CScript() << OP_1, NULL, SCRIPT_VERIFY_MINIMALDATA, BaseSignatureChecker(), &err), "Length " << i << " push is not minimal data."); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index c27f194b551bb..2846366154fab 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -150,7 +150,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - verify_flags, TransactionSignatureChecker(&tx, i), &err), + NULL, verify_flags, TransactionSignatureChecker(&tx, i), &err), strTest); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } @@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - verify_flags, TransactionSignatureChecker(&tx, i), &err); + NULL, verify_flags, TransactionSignatureChecker(&tx, i), &err); } BOOST_CHECK_MESSAGE(!fValid, strTest); BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err)); From 712da91e5df2d4bbb13a6ac4db000686131108f6 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 18 Nov 2015 01:04:56 +0100 Subject: [PATCH 12/32] Enable SCRIPT_VERIFY_WITNESS for mempool transactions --- src/consensus/validation.h | 3 +++ src/main.cpp | 19 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/consensus/validation.h b/src/consensus/validation.h index d7e57f5b5ebdd..000b197270271 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -77,6 +77,9 @@ class CValidationState { bool CorruptionPossible() const { return corruptionPossible; } + void SetCorruptionPossible() { + corruptionPossible = true; + } unsigned int GetRejectCode() const { return chRejectCode; } std::string GetRejectReason() const { return strRejectReason; } std::string GetDebugMessage() const { return strDebugMessage; } diff --git a/src/main.cpp b/src/main.cpp index 59abe1e549478..7a2557fd4c8f9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1183,8 +1183,14 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) + if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { + if (CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) && + !CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~SCRIPT_VERIFY_CLEANSTACK, true)) { + // Only the witness is wrong, so the transaction itself may be fine. + state.SetCorruptionPossible(); + } return false; + } // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause @@ -4906,8 +4912,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (nEvicted > 0) LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); } else { - assert(recentRejects); - recentRejects->insert(tx.GetHash()); + if (!state.CorruptionPossible()) { + assert(recentRejects); + recentRejects->insert(tx.GetHash()); + } if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { // Always relay transactions received from whitelisted peers, even @@ -4936,8 +4944,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P pfrom->PushMessage(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); - if (nDoS > 0) + if (nDoS > 0 && (!state.CorruptionPossible() || State(pfrom->id)->fHaveWitness)) { + // When a non-witness-supporting peer gives us a transaction that would + // be accepted if witness validation was off, we can't blame them for it. Misbehaving(pfrom->GetId(), nDoS); + } } FlushStateToDisk(state, FLUSH_STATE_PERIODIC); } From 4013d1e818f79414e6e5cfc4fc8449cf9a70768e Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 18 Nov 2015 16:27:00 +0100 Subject: [PATCH 13/32] Activate script consensus rules in v5 blocks --- src/main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 7a2557fd4c8f9..96a244f2cb51b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2064,6 +2064,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; } + // Start enforcing WITNESS rules, for block.nVersion=5 + // blocks, when 75% of the network has upgraded: + if (block.nVersion >= 5 && pindex->nHeight >= chainparams.GetConsensus().SegWitHeight && IsSuperMajority(5, pindex->pprev, chainparams.GetConsensus().nMajorityEnforceBlockUpgrade, chainparams.GetConsensus())) { + flags |= SCRIPT_VERIFY_WITNESS; + } + int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; LogPrint("bench", " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); From ce1766dac953a2aedabfc023845b4e03a2edbe4a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 29 Dec 2015 02:37:54 +0100 Subject: [PATCH 14/32] Only download blocks from witness peers after fork --- src/main.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 96a244f2cb51b..2ff379de56eae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4364,6 +4364,11 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } +bool static RequireWitness(CBlockIndex* pindex, const Consensus::Params& consensusParams) +{ + return (pindex->nVersion >= 5 && pindex->nHeight >= consensusParams.SegWitHeight && IsSuperMajority(5, pindex->pprev, consensusParams.nMajorityEnforceBlockUpgrade, consensusParams)); +} + bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) { const CChainParams& chainparams = Params(); @@ -4669,7 +4674,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash); CNodeState *nodestate = State(pfrom->GetId()); if (CanDirectFetch(chainparams.GetConsensus()) && - nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER) { + nodestate->nBlocksInFlight < MAX_BLOCKS_IN_TRANSIT_PER_PEER && + (!RequireWitness(chainActive.Tip(), chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { if (State(pfrom->GetId())->fHaveWitness) { inv.type = MSG_WITNESS_BLOCK; } @@ -5021,7 +5027,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Calculate all the blocks we'd need to switch to pindexLast, up to a limit. while (pindexWalk && !chainActive.Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && - !mapBlocksInFlight.count(pindexWalk->GetBlockHash())) { + !mapBlocksInFlight.count(pindexWalk->GetBlockHash()) && + (!RequireWitness(chainActive.Tip(), chainparams.GetConsensus()) || State(pfrom->GetId())->fHaveWitness)) { // We don't have this block, and it's not yet in flight. vToFetch.push_back(pindexWalk); } @@ -5043,7 +5050,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Can't download any more from this peer break; } - vGetData.push_back(CInv(MSG_BLOCK, pindex->GetBlockHash())); + vGetData.push_back(CInv(State(pfrom->GetId())->fHaveWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK, pindex->GetBlockHash())); MarkBlockAsInFlight(pfrom->GetId(), pindex->GetBlockHash(), chainparams.GetConsensus(), pindex); LogPrint("net", "Requesting block %s from peer=%d\n", pindex->GetBlockHash().ToString(), pfrom->id); @@ -5765,10 +5772,11 @@ bool SendMessages(CNode* pto) NodeId staller = -1; FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller); BOOST_FOREACH(CBlockIndex *pindex, vToDownload) { - vGetData.push_back(CInv(State(staller)->fHaveWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK, pindex->GetBlockHash())); - MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); - LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), - pindex->nHeight, pto->id); + if (State(pto->GetId())->fHaveWitness || !RequireWitness(pindex, consensusParams)) { + vGetData.push_back(CInv(State(pto->GetId())->fHaveWitness ? MSG_WITNESS_BLOCK : MSG_BLOCK, pindex->GetBlockHash())); + MarkBlockAsInFlight(pto->GetId(), pindex->GetBlockHash(), consensusParams, pindex); + LogPrint("net", "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(), pindex->nHeight, pto->id); + } } if (state.nBlocksInFlight == 0 && staller != -1) { if (State(staller)->nStallingSince == 0) { From f93a5ba0cb16c8bcedeec2a4842f9bd8ad11bbd8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 29 Dec 2015 02:38:07 +0100 Subject: [PATCH 15/32] Witness script signing --- src/bitcoin-tx.cpp | 11 +- src/rpcrawtransaction.cpp | 9 +- src/script/sign.cpp | 261 +++++++++++++++++++++++---------- src/script/sign.h | 26 +++- src/script/standard.cpp | 41 ++++++ src/script/standard.h | 3 + src/test/script_P2SH_tests.cpp | 1 + src/test/script_tests.cpp | 80 +++++----- src/wallet/wallet.cpp | 12 +- src/wallet/wallet_ismine.cpp | 19 ++- 10 files changed, 329 insertions(+), 134 deletions(-) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 238b6a394593d..86528c6ce76da 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -454,15 +454,16 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; - txin.scriptSig.clear(); + SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) - SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, nHashType), prevPubKey, sigdata); // ... and merge in other signatures: - BOOST_FOREACH(const CTransaction& txv, txVariants) { - txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); - } + BOOST_FOREACH(const CTransaction& txv, txVariants) + sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i), sigdata, DataFromTransaction(txv, i)); + UpdateTransaction(mergedTx, i, sigdata); + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) fComplete = false; } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 476f6aaa04204..a04b0f1d4d16a 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -753,15 +753,18 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; - txin.scriptSig.clear(); + SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) - SignSignature(keystore, prevPubKey, mergedTx, i, nHashType); + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, nHashType), prevPubKey, sigdata); // ... and merge in other signatures: BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { - txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); + sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i), sigdata, DataFromTransaction(txv, i)); } + + UpdateTransaction(mergedTx, i, sigdata); + ScriptError serror = SCRIPT_ERR_OK; if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 37b702de17cb6..55ab40d696775 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -33,16 +33,16 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, return true; } -static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) +static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret) { vector vchSig; if (!creator.CreateSig(vchSig, address, scriptCode)) return false; - scriptSigRet << vchSig; + ret.push_back(vchSig); return true; } -static bool SignN(const vector& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, CScript& scriptSigRet) +static bool SignN(const vector& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret) { int nSigned = 0; int nRequired = multisigdata.front()[0]; @@ -50,7 +50,7 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato { const valtype& pubkey = multisigdata[i]; CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(keyID, creator, scriptCode, scriptSigRet)) + if (Sign1(keyID, creator, scriptCode, ret)) ++nSigned; } return nSigned==nRequired; @@ -63,9 +63,11 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, - CScript& scriptSigRet, txnouttype& whichTypeRet) + std::vector& ret, txnouttype& whichTypeRet) { - scriptSigRet.clear(); + CScript scriptRet; + uint160 h160; + ret.clear(); vector vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) @@ -79,62 +81,142 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, creator, scriptPubKey, scriptSigRet); + return Sign1(keyID, creator, scriptPubKey, ret); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) + if (!Sign1(keyID, creator, scriptPubKey, ret)) return false; else { CPubKey vch; creator.KeyStore().GetPubKey(keyID, vch); - scriptSigRet << ToByteVector(vch); + ret.push_back(ToByteVector(vch)); } return true; case TX_SCRIPTHASH: - return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet); + if (creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptRet)) { + ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + return true; + } + return false; case TX_MULTISIG: - scriptSigRet << OP_0; // workaround CHECKMULTISIG bug - return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet)); + ret.push_back(valtype()); // workaround CHECKMULTISIG bug + return (SignN(vSolutions, creator, scriptPubKey, ret)); + + 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 (creator.KeyStore().GetCScript(h160, scriptRet)) { + ret.push_back(std::vector(scriptRet.begin(), scriptRet.end())); + return true; + } + return false; + + default: + return false; } - return false; } -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, CScript& scriptSig) +static CScript PushAll(const vector& values) { + CScript result; + BOOST_FOREACH(const valtype& v, values) { + if (v.size() == 0) { + result << OP_0; + } else if (v.size() == 1 && v[0] >= 1 && v[0] <= 16) { + result << CScript::EncodeOP_N(v[0]); + } else { + result << v; + } + } + return result; +} + +bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata) +{ + CScript script = fromPubKey; + bool solved = true; + std::vector result; txnouttype whichType; - if (!SignStep(creator, fromPubKey, scriptSig, whichType)) - return false; + solved = SignStep(creator, script, result, whichType); + bool P2SH = false; + CScript subscript; + sigdata.scriptWitness.stack.clear(); - if (whichType == TX_SCRIPTHASH) + if (solved && whichType == TX_SCRIPTHASH) { - // Solver returns the subscript that need to be evaluated; + // Solver returns the subscript that needs to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: - CScript subscript = scriptSig; + script = subscript = CScript(result[0].begin(), result[0].end()); + solved = solved && SignStep(creator, script, result, whichType) && 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; - bool fSolved = - SignStep(creator, subscript, scriptSig, subType) && subType != TX_SCRIPTHASH; - // Append serialized subscript whether or not it is completely signed: - scriptSig << valtype(subscript.begin(), subscript.end()); - if (!fSolved) return false; + solved = solved && SignStep(creator, witnessscript, result, subType, 1); + sigdata.scriptWitness.stack = result; + result.clear(); + } + else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH) + { + CScript witnessscript(result[0].begin(), result[0].end()); + txnouttype subType; + solved = solved && SignStep(creator, witnessscript, result, subType) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; + result.push_back(std::vector(witnessscript.begin(), witnessscript.end())); + sigdata.scriptWitness.stack = result; + result.clear(); } + if (P2SH) { + result.push_back(std::vector(subscript.begin(), subscript.end())); + } + sigdata.scriptSig = PushAll(result); + // Test solution - return VerifyScript(scriptSig, fromPubKey, NULL, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); + return solved && VerifyScript(sigdata.scriptSig, fromPubKey, &sigdata.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, creator.Checker()); +} + +SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn) +{ + SignatureData data; + assert(tx.vin.size() > nIn); + data.scriptSig = tx.vin[nIn].scriptSig; + if (tx.wit.vtxinwit.size() > nIn) { + data.scriptWitness = tx.wit.vtxinwit[nIn].scriptWitness; + } + return data; +} + +void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data) +{ + assert(tx.vin.size() > nIn); + tx.vin[nIn].scriptSig = data.scriptSig; + if (!data.scriptWitness.IsNull() || tx.wit.vtxinwit.size() > nIn) { + tx.wit.vtxinwit.resize(tx.vin.size()); + tx.wit.vtxinwit[nIn].scriptWitness = data.scriptWitness; + } } bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) { assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; CTransaction txToConst(txTo); TransactionSignatureCreator creator(&keystore, &txToConst, nIn, nHashType); - return ProduceSignature(creator, fromPubKey, txin.scriptSig); + SignatureData sigdata; + bool ret = ProduceSignature(creator, fromPubKey, sigdata); + UpdateTransaction(txTo, nIn, sigdata); + return ret; } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) @@ -147,15 +229,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutab return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); } -static CScript PushAll(const vector& values) -{ - CScript result; - BOOST_FOREACH(const valtype& v, values) - result << v; - return result; -} - -static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, +static vector CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const vector& vSolutions, const vector& sigs1, const vector& sigs2) { @@ -194,87 +268,126 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureC } // Now build a merged CScript: unsigned int nSigsHave = 0; - CScript result; result << OP_0; // pop-one-too-many workaround + std::vector result; result.push_back(valtype()); // pop-one-too-many workaround for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) { if (sigs.count(vSolutions[i+1])) { - result << sigs[vSolutions[i+1]]; + result.push_back(sigs[vSolutions[i+1]]); ++nSigsHave; } } // Fill any missing with OP_0: for (unsigned int i = nSigsHave; i < nSigsRequired; i++) - result << OP_0; + result.push_back(valtype()); return result; } -static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, +namespace +{ +struct Stacks +{ + std::vector script; + std::vector witness; + + Stacks() {} + explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_), witness() {} + explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) { + EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); + } + + SignatureData Output() const { + SignatureData result; + result.scriptSig = PushAll(script); + result.scriptWitness.stack = witness; + return result; + } +}; +} + +static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const txnouttype txType, const vector& vSolutions, - vector& sigs1, vector& sigs2) + Stacks sigs1, Stacks sigs2) { switch (txType) { case TX_NONSTANDARD: case TX_NULL_DATA: // Don't know anything about this, assume bigger one is correct: - if (sigs1.size() >= sigs2.size()) - return PushAll(sigs1); - return PushAll(sigs2); + if (sigs1.script.size() >= sigs2.script.size()) + return sigs1; + return sigs2; case TX_PUBKEY: case TX_PUBKEYHASH: // Signatures are bigger than placeholders or empty scripts: - if (sigs1.empty() || sigs1[0].empty()) - return PushAll(sigs2); - return PushAll(sigs1); + if (sigs1.script.empty() || sigs1.script[0].empty()) + return sigs2; + return sigs1; + case TX_WITNESS_V0_KEYHASH: + // Signatures are bigger than placeholders or empty scripts: + if (sigs1.witness.empty() || sigs1.witness[0].empty()) + return sigs2; + return sigs1; case TX_SCRIPTHASH: - if (sigs1.empty() || sigs1.back().empty()) - return PushAll(sigs2); - else if (sigs2.empty() || sigs2.back().empty()) - return PushAll(sigs1); + if (sigs1.script.empty() || sigs1.script.back().empty()) + return sigs2; + else if (sigs2.script.empty() || sigs2.script.back().empty()) + return sigs1; else { // Recur to combine: - valtype spk = sigs1.back(); + valtype spk = sigs1.script.back(); CScript pubKey2(spk.begin(), spk.end()); txnouttype txType2; vector > vSolutions2; Solver(pubKey2, txType2, vSolutions2); - sigs1.pop_back(); - sigs2.pop_back(); - CScript result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2); - result << spk; + sigs1.script.pop_back(); + sigs2.script.pop_back(); + Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2); + result.script.push_back(spk); return result; } case TX_MULTISIG: - return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2); + return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script)); + case TX_WITNESS_V0_SCRIPTHASH: + if (sigs1.witness.empty() || sigs1.witness.back().empty()) + return sigs2; + else if (sigs2.witness.empty() || sigs2.witness.back().empty()) + return sigs1; + else + { + // Recur to combine: + CScript pubKey2(sigs1.witness.back().begin(), sigs1.witness.back().end()); + txnouttype txType2; + vector vSolutions2; + Solver(pubKey2, txType2, vSolutions2); + sigs1.witness.pop_back(); + sigs1.script = sigs1.witness; + sigs1.witness.clear(); + sigs2.witness.pop_back(); + sigs2.script = sigs2.witness; + sigs2.witness.clear(); + Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2); + result.witness = result.script; + result.script.clear(); + result.witness.push_back(valtype(pubKey2.begin(), pubKey2.end())); + return result; + } + default: + return Stacks(); } - - return CScript(); } -CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - const CScript& scriptSig1, const CScript& scriptSig2) -{ - TransactionSignatureChecker checker(&txTo, nIn); - return CombineSignatures(scriptPubKey, checker, scriptSig1, scriptSig2); -} - -CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, - const CScript& scriptSig1, const CScript& scriptSig2) +SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, + const SignatureData& scriptSig1, const SignatureData& scriptSig2) { txnouttype txType; vector > vSolutions; Solver(scriptPubKey, txType, vSolutions); - vector stack1; - EvalScript(stack1, scriptSig1, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); - vector stack2; - EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); - - return CombineSignatures(scriptPubKey, checker, txType, vSolutions, stack1, stack2); + return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2)).Output(); } namespace { diff --git a/src/script/sign.h b/src/script/sign.h index 47a9cde7f4304..8496f8711545d 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -43,6 +43,13 @@ class TransactionSignatureCreator : public BaseSignatureCreator { bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; }; +class MutableTransactionSignatureCreator : public TransactionSignatureCreator { + CTransaction tx; + +public: + MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, int nHashTypeIn=SIGHASH_ALL) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, nHashTypeIn), tx(*txToIn) {} +}; + /** A signature creator that just produces 72-byte empty signatyres. */ class DummySignatureCreator : public BaseSignatureCreator { public: @@ -51,17 +58,26 @@ class DummySignatureCreator : public BaseSignatureCreator { bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; }; +struct SignatureData { + CScript scriptSig; + CScriptWitness scriptWitness; + + SignatureData() {} + explicit SignatureData(const CScript& script) : scriptSig(script) {} +}; + /** Produce a script signature using a generic signature creator. */ -bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, CScript& scriptSig); +bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); /** Produce a script signature for a transaction. */ -bool SignSignature(const CKeyStore& keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); /** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */ -CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const CScript& scriptSig1, const CScript& scriptSig2); +SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const SignatureData& scriptSig1, const SignatureData& scriptSig2); -/** Combine two script signatures on transactions. */ -CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, const CScript& scriptSig1, const CScript& scriptSig2); +/** Extract signature data from a transaction, and insert it. */ +SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn); +void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data); #endif // BITCOIN_SCRIPT_SIGN_H diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 67b6af327ae37..bb178f49fe9f2 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -31,6 +31,8 @@ const char* GetTxnOutputType(txnouttype t) case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; case TX_NULL_DATA: return "nulldata"; + case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; + case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; } return NULL; } @@ -66,6 +68,22 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector witnessprogram; + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + if (witnessversion == 0 && witnessprogram.size() == 20) { + typeRet = TX_WITNESS_V0_KEYHASH; + vSolutionsRet.push_back(witnessprogram); + return true; + } + if (witnessversion == 0 && witnessprogram.size() == 32) { + typeRet = TX_WITNESS_V0_SCRIPTHASH; + vSolutionsRet.push_back(witnessprogram); + return true; + } + return false; + } + // Provably prunable, data-carrying output // // So long as script passes the IsUnspendable() test and all but the first @@ -282,3 +300,26 @@ CScript GetScriptForMultisig(int nRequired, const std::vector& keys) script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; return script; } + +CScript GetScriptForWitness(const CScript& redeemscript) +{ + CScript ret; + + txnouttype typ; + std::vector > 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(&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; +} diff --git a/src/script/standard.h b/src/script/standard.h index 64bf010ec199e..7684cf25c3146 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -51,6 +51,8 @@ enum txnouttype TX_SCRIPTHASH, TX_MULTISIG, TX_NULL_DATA, + TX_WITNESS_V0_SCRIPTHASH, + TX_WITNESS_V0_KEYHASH, }; class CNoDestination { @@ -77,5 +79,6 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std:: CScript GetScriptForDestination(const CTxDestination& dest); CScript GetScriptForRawPubKey(const CPubKey& pubkey); CScript GetScriptForMultisig(int nRequired, const std::vector& keys); +CScript GetScriptForWitness(const CScript& redeemscript); #endif // BITCOIN_SCRIPT_STANDARD_H diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index ed31271626215..b026ce5ba9c59 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "core_io.h" #include "key.h" #include "keystore.h" #include "main.h" diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index fb7509e41917f..92e5945ddf124 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -866,50 +866,50 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) CScript& scriptPubKey = txFrom.vout[0].scriptPubKey; CScript& scriptSig = txTo.vin[0].scriptSig; - CScript empty; - CScript combined = CombineSignatures(scriptPubKey, txTo, 0, empty, empty); - BOOST_CHECK(combined.empty()); + SignatureData empty; + SignatureData combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), empty, empty); + BOOST_CHECK(combined.scriptSig.empty()); // Single signature case: SignSignature(keystore, txFrom, txTo, 0); // changes scriptSig - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); - BOOST_CHECK(combined == scriptSig); - combined = CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); - BOOST_CHECK(combined == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSig), empty); + BOOST_CHECK(combined.scriptSig == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), empty, SignatureData(scriptSig)); + BOOST_CHECK(combined.scriptSig == scriptSig); CScript scriptSigCopy = scriptSig; // Signing again will give a different, valid signature: SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); - BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSigCopy), SignatureData(scriptSig)); + BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig); // P2SH, single-signature case: CScript pkSingle; pkSingle << ToByteVector(keys[0].GetPubKey()) << OP_CHECKSIG; keystore.AddCScript(pkSingle); scriptPubKey = GetScriptForDestination(CScriptID(pkSingle)); SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); - BOOST_CHECK(combined == scriptSig); - combined = CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); - BOOST_CHECK(combined == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSig), empty); + BOOST_CHECK(combined.scriptSig == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), empty, SignatureData(scriptSig)); + BOOST_CHECK(combined.scriptSig == scriptSig); scriptSigCopy = scriptSig; SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); - BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSigCopy), SignatureData(scriptSig)); + BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig); // dummy scriptSigCopy with placeholder, should always choose non-placeholder: - scriptSigCopy = CScript() << OP_0 << vector(pkSingle.begin(), pkSingle.end()); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSigCopy, scriptSig); - BOOST_CHECK(combined == scriptSig); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, scriptSigCopy); - BOOST_CHECK(combined == scriptSig); + scriptSigCopy = CScript() << OP_0 << std::vector(pkSingle.begin(), pkSingle.end()); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSigCopy), SignatureData(scriptSig)); + BOOST_CHECK(combined.scriptSig == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSig), SignatureData(scriptSigCopy)); + BOOST_CHECK(combined.scriptSig == scriptSig); // Hardest case: Multisig 2-of-3 scriptPubKey = GetScriptForMultisig(2, pubkeys); keystore.AddCScript(scriptPubKey); SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, txTo, 0, scriptSig, empty); - BOOST_CHECK(combined == scriptSig); - combined = CombineSignatures(scriptPubKey, txTo, 0, empty, scriptSig); - BOOST_CHECK(combined == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSig), empty); + BOOST_CHECK(combined.scriptSig == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), empty, SignatureData(scriptSig)); + BOOST_CHECK(combined.scriptSig == scriptSig); // A couple of partially-signed versions: vector sig1; @@ -937,22 +937,22 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) CScript complete13 = CScript() << OP_0 << sig1 << sig3; CScript complete23 = CScript() << OP_0 << sig2 << sig3; - combined = CombineSignatures(scriptPubKey, txTo, 0, partial1a, partial1b); - BOOST_CHECK(combined == partial1a); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial1a, partial2a); - BOOST_CHECK(combined == complete12); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial2a, partial1a); - BOOST_CHECK(combined == complete12); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial1b, partial2b); - BOOST_CHECK(combined == complete12); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial1b); - BOOST_CHECK(combined == complete13); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial2a, partial3a); - BOOST_CHECK(combined == complete23); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial2b); - BOOST_CHECK(combined == complete23); - combined = CombineSignatures(scriptPubKey, txTo, 0, partial3b, partial3a); - BOOST_CHECK(combined == partial3c); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial1a), SignatureData(partial1b)); + BOOST_CHECK(combined.scriptSig == partial1a); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial1a), SignatureData(partial2a)); + BOOST_CHECK(combined.scriptSig == complete12); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial2a), SignatureData(partial1a)); + BOOST_CHECK(combined.scriptSig == complete12); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial1b), SignatureData(partial2b)); + BOOST_CHECK(combined.scriptSig == complete12); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial3b), SignatureData(partial1b)); + BOOST_CHECK(combined.scriptSig == complete13); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial2a), SignatureData(partial3a)); + BOOST_CHECK(combined.scriptSig == complete23); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial3b), SignatureData(partial2b)); + BOOST_CHECK(combined.scriptSig == complete23); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial3b), SignatureData(partial3a)); + BOOST_CHECK(combined.scriptSig == partial3c); } BOOST_AUTO_TEST_CASE(script_standard_push) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index be70240a6cbae..7b9df1c85d365 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2163,11 +2163,12 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt { bool signSuccess; const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey; - CScript& scriptSigRes = txNew.vin[nIn].scriptSig; - if (sign) - signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, scriptSigRes); - else - signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, scriptSigRes); + SignatureData sigdata; + if (sign) { + signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, sigdata); + UpdateTransaction(txNew, nIn, sigdata); + } else + signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata); if (!signSuccess) { @@ -2183,6 +2184,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt if (!sign) { BOOST_FOREACH (CTxIn& vin, txNew.vin) vin.scriptSig = CScript(); + txNew.wit.SetNull(); } // Embed the constructed transaction data in wtxNew. diff --git a/src/wallet/wallet_ismine.cpp b/src/wallet/wallet_ismine.cpp index ebda5cc53df76..447346da09f5a 100644 --- a/src/wallet/wallet_ismine.cpp +++ b/src/wallet/wallet_ismine.cpp @@ -57,6 +57,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) return ISMINE_SPENDABLE; break; case TX_PUBKEYHASH: + case TX_WITNESS_V0_KEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; @@ -72,6 +73,20 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) } break; } + case TX_WITNESS_V0_SCRIPTHASH: + { + 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); + if (ret == ISMINE_SPENDABLE) + return ret; + } + break; + } + case TX_MULTISIG: { // Only consider transactions "mine" if we own ALL the @@ -88,8 +103,8 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) if (keystore.HaveWatchOnly(scriptPubKey)) { // TODO: This could be optimized some by doing some work after the above solver - CScript scriptSig; - return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, scriptSig) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; + SignatureData sigs; + return ProduceSignature(DummySignatureCreator(&keystore), scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; } return ISMINE_NO; } From 58625a72413a8918aa0911cc9e381f62ec3e698d Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 20 Nov 2015 16:22:47 +0100 Subject: [PATCH 16/32] Signing tests --- src/test/transaction_tests.cpp | 277 +++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 2846366154fab..53a717b240d34 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -14,7 +14,9 @@ #include "main.h" // For CheckTransaction #include "policy/policy.h" #include "script/script.h" +#include "script/sign.h" #include "script/script_error.h" +#include "script/standard.h" #include "utilstrencodings.h" #include @@ -25,11 +27,14 @@ #include #include #include +#include #include using namespace std; +typedef vector valtype; + // In script_tests.cpp extern UniValue read_json(const std::string& jsondata); @@ -312,6 +317,278 @@ BOOST_AUTO_TEST_CASE(test_Get) BOOST_CHECK_EQUAL(coins.GetValueIn(t1), (50+21+22)*CENT); } +void CreateCreditAndSpend(const CKeyStore& keystore, const CScript& outscript, CTransaction& output, CMutableTransaction& input, bool success = true) +{ + CMutableTransaction outputm; + outputm.nVersion = 1; + outputm.vin.resize(1); + outputm.vin[0].prevout.SetNull(); + outputm.vin[0].scriptSig = CScript(); + outputm.wit.vtxinwit.resize(1); + outputm.vout.resize(1); + outputm.vout[0].nValue = 1; + outputm.vout[0].scriptPubKey = outscript; + CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION); + WithOrVersion(&ssout, SERIALIZE_TRANSACTION_WITNESS) << outputm; + WithOrVersion(&ssout, SERIALIZE_TRANSACTION_WITNESS) >> output; + assert(output.vin.size() == 1); + assert(output.vin[0] == outputm.vin[0]); + assert(output.vout.size() == 1); + assert(output.vout[0] == outputm.vout[0]); + assert(output.wit.vtxinwit.size() == 0); + + CMutableTransaction inputm; + inputm.nVersion = 1; + inputm.vin.resize(1); + inputm.vin[0].prevout.hash = output.GetHash(); + inputm.vin[0].prevout.n = 0; + inputm.wit.vtxinwit.resize(1); + inputm.vout.resize(1); + inputm.vout[0].nValue = 1; + inputm.vout[0].scriptPubKey = CScript(); + bool ret = SignSignature(keystore, output, inputm, 0); + assert(ret == success); + CDataStream ssin(SER_NETWORK, PROTOCOL_VERSION); + WithOrVersion(&ssin, SERIALIZE_TRANSACTION_WITNESS) << inputm; + WithOrVersion(&ssin, SERIALIZE_TRANSACTION_WITNESS) >> input; + assert(input.vin.size() == 1); + assert(input.vin[0] == inputm.vin[0]); + assert(input.vout.size() == 1); + assert(input.vout[0] == inputm.vout[0]); + if (inputm.wit.IsNull()) { + assert(input.wit.IsNull()); + } else { + assert(!input.wit.IsNull()); + assert(input.wit.vtxinwit.size() == 1); + assert(input.wit.vtxinwit[0].scriptWitness.stack == inputm.wit.vtxinwit[0].scriptWitness.stack); + } +} + +void CheckWithFlag(const CTransaction& output, const CMutableTransaction& input, int flags, bool success) +{ + ScriptError error; + CTransaction inputi(input); + bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, inputi.wit.vtxinwit.size() > 0 ? &inputi.wit.vtxinwit[0].scriptWitness : NULL, flags, TransactionSignatureChecker(&inputi, 0), &error); + assert(ret == success); +} + +static CScript PushAll(const vector& values) +{ + CScript result; + BOOST_FOREACH(const valtype& v, values) { + if (v.size() == 0) { + result << OP_0; + } else if (v.size() == 1 && v[0] >= 1 && v[0] <= 16) { + result << CScript::EncodeOP_N(v[0]); + } else { + result << v; + } + } + return result; +} + +void ReplaceRedeemScript(CScript& script, const CScript& redeemScript) +{ + vector stack; + EvalScript(stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); + assert(stack.size() > 0); + stack.back() = std::vector(redeemScript.begin(), redeemScript.end()); + script = PushAll(stack); +} + +BOOST_AUTO_TEST_CASE(test_witness) +{ + ScriptError serror; + CBasicKeyStore keystore, keystore2; + CKey key1, key2, key3, key1L, key2L; + CPubKey pubkey1, pubkey2, pubkey3, pubkey1L, pubkey2L; + key1.MakeNewKey(true); + key2.MakeNewKey(true); + key3.MakeNewKey(true); + key1L.MakeNewKey(false); + key2L.MakeNewKey(false); + pubkey1 = key1.GetPubKey(); + pubkey2 = key2.GetPubKey(); + pubkey3 = key3.GetPubKey(); + pubkey1L = key1L.GetPubKey(); + pubkey2L = key2L.GetPubKey(); + keystore.AddKeyPubKey(key1, pubkey1); + keystore.AddKeyPubKey(key2, pubkey2); + keystore.AddKeyPubKey(key1L, pubkey1L); + keystore.AddKeyPubKey(key2L, pubkey2L); + CScript scriptPubkey1, scriptPubkey2, scriptPubkey1L, scriptPubkey2L, scriptMulti; + scriptPubkey1 << ToByteVector(pubkey1) << OP_CHECKSIG; + scriptPubkey2 << ToByteVector(pubkey2) << OP_CHECKSIG; + scriptPubkey1L << ToByteVector(pubkey1L) << OP_CHECKSIG; + scriptPubkey2L << ToByteVector(pubkey2L) << OP_CHECKSIG; + std::vector oneandthree; + oneandthree.push_back(pubkey1); + oneandthree.push_back(pubkey3); + scriptMulti = GetScriptForMultisig(2, oneandthree); + keystore.AddCScript(scriptPubkey1); + keystore.AddCScript(scriptPubkey2); + keystore.AddCScript(scriptPubkey1L); + keystore.AddCScript(scriptPubkey2L); + keystore.AddCScript(scriptMulti); + keystore.AddCScript(GetScriptForWitness(scriptPubkey1)); + keystore.AddCScript(GetScriptForWitness(scriptPubkey2)); + keystore.AddCScript(GetScriptForWitness(scriptPubkey1L)); + keystore.AddCScript(GetScriptForWitness(scriptPubkey2L)); + keystore.AddCScript(GetScriptForWitness(scriptMulti)); + keystore2.AddCScript(scriptMulti); + keystore2.AddCScript(GetScriptForWitness(scriptMulti)); + keystore2.AddKeyPubKey(key3, pubkey3); + + CTransaction output1, output2; + CMutableTransaction input1, input2; + SignatureData sigdata; + + // Normal pay-to-compressed-pubkey. + CreateCreditAndSpend(keystore, scriptPubkey1, output1, input1); + CreateCreditAndSpend(keystore, scriptPubkey2, output2, input2); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input2, 0, false); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + + // P2SH pay-to-compressed-pubkey. + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1)), output1, input1); + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey2)), output2, input2); + ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input2, 0, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + + // Witness pay-to-compressed-pubkey (v0). + CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1), output1, input1); + CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2), output2, input2); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input2, 0, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + + // P2SH witness pay-to-compressed-pubkey (v0). + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1))), output1, input1); + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2))), output2, input2); + ReplaceRedeemScript(input2.vin[0].scriptSig, GetScriptForWitness(scriptPubkey1)); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input2, 0, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + + // Normal pay-to-uncompressed-pubkey. + CreateCreditAndSpend(keystore, scriptPubkey1L, output1, input1); + CreateCreditAndSpend(keystore, scriptPubkey2L, output2, input2); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input2, 0, false); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + + // P2SH pay-to-uncompressed-pubkey. + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey1L)), output1, input1); + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptPubkey2L)), output2, input2); + ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1L); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input2, 0, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + + // Witness pay-to-uncompressed-pubkey (v1). + CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey1L), output1, input1); + CreateCreditAndSpend(keystore, GetScriptForWitness(scriptPubkey2L), output2, input2); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input2, 0, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + + // P2SH witness pay-to-uncompressed-pubkey (v1). + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey1L))), output1, input1); + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptPubkey2L))), output2, input2); + ReplaceRedeemScript(input2.vin[0].scriptSig, GetScriptForWitness(scriptPubkey1L)); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + CheckWithFlag(output1, input2, 0, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false); + CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false); + + // Normal 2-of-2 multisig + CreateCreditAndSpend(keystore, scriptMulti, output1, input1, false); + CheckWithFlag(output1, input1, 0, false); + CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false); + CheckWithFlag(output2, input2, 0, false); + BOOST_CHECK(output1 == output2); + UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + + // P2SH 2-of-2 multisig + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(scriptMulti)), output1, input1, false); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, false); + CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(scriptMulti)), output2, input2, false); + CheckWithFlag(output2, input2, 0, true); + CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false); + BOOST_CHECK(output1 == output2); + UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + + // Witness 2-of-2 multisig + CreateCreditAndSpend(keystore, GetScriptForWitness(scriptMulti), output1, input1, false); + CheckWithFlag(output1, input1, 0, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); + CreateCreditAndSpend(keystore2, GetScriptForWitness(scriptMulti), output2, input2, false); + CheckWithFlag(output2, input2, 0, true); + CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); + BOOST_CHECK(output1 == output2); + UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); + + // P2SH witness 2-of-2 multisig + CreateCreditAndSpend(keystore, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output1, input1, false); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); + CreateCreditAndSpend(keystore2, GetScriptForDestination(CScriptID(GetScriptForWitness(scriptMulti))), output2, input2, false); + CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true); + CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); + BOOST_CHECK(output1 == output2); + UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true); + CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); +} + BOOST_AUTO_TEST_CASE(test_IsStandard) { LOCK(cs_main); From ab9ef689e8e5c9fef6b34e3c2eb1ad10cdba4704 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 26 Dec 2015 17:12:36 +0100 Subject: [PATCH 17/32] Make script validation observe input amounts --- src/bitcoin-tx.cpp | 24 +++++++++-- src/main.cpp | 2 +- src/main.h | 6 ++- src/rpcrawtransaction.cpp | 15 ++++--- src/script/bitcoinconsensus.cpp | 5 ++- src/script/bitcoinconsensus.h | 4 +- src/script/interpreter.h | 4 +- src/script/sigcache.h | 2 +- src/script/sign.cpp | 8 ++-- src/script/sign.h | 6 +-- src/test/multisig_tests.cpp | 17 ++++---- src/test/script_P2SH_tests.cpp | 2 +- src/test/script_tests.cpp | 72 +++++++++++++++++---------------- src/test/transaction_tests.cpp | 16 ++++---- src/wallet/wallet.cpp | 2 +- 15 files changed, 108 insertions(+), 77 deletions(-) diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 86528c6ce76da..026e87950f3d0 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -354,6 +354,18 @@ vector ParseHexUO(map& o, string strKey) return ParseHexUV(o[strKey], strKey); } +static CAmount AmountFromValue(const UniValue& value) +{ + if (!value.isNum() && !value.isStr()) + throw runtime_error("Amount is not a number or string"); + CAmount amount; + if (!ParseFixedPoint(value.getValStr(), 8, &amount)) + throw runtime_error("Invalid amount"); + if (!MoneyRange(amount)) + throw runtime_error("Amount out of range"); + return amount; +} + static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) { int nHashType = SIGHASH_ALL; @@ -425,7 +437,10 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) if ((unsigned int)nOut >= coins->vout.size()) coins->vout.resize(nOut+1); coins->vout[nOut].scriptPubKey = scriptPubKey; - coins->vout[nOut].nValue = 0; // we don't know the actual output value + coins->vout[nOut].nValue = 0; + if (prevOut.exists("amount")) { + coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]); + } } // if redeemScript given and private keys given, @@ -453,18 +468,19 @@ static void MutateTxSign(CMutableTransaction& tx, const string& flagStr) continue; } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; + const CAmount& amount = coins->vout[txin.prevout.n].nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, nHashType), prevPubKey, sigdata); + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata); // ... and merge in other signatures: BOOST_FOREACH(const CTransaction& txv, txVariants) - sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i), sigdata, DataFromTransaction(txv, i)); + sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); UpdateTransaction(mergedTx, i, sigdata); - if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount))) fComplete = false; } diff --git a/src/main.cpp b/src/main.cpp index 2ff379de56eae..e72ac2b7e3219 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1581,7 +1581,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = (nIn < ptxTo->wit.vtxinwit.size()) ? &ptxTo->wit.vtxinwit[nIn].scriptWitness : NULL; - if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { + if (!VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore), &error)) { return false; } return true; diff --git a/src/main.h b/src/main.h index b71d3389f8367..a7458f64fd982 100644 --- a/src/main.h +++ b/src/main.h @@ -361,6 +361,7 @@ class CScriptCheck { private: CScript scriptPubKey; + CAmount amount; const CTransaction *ptxTo; unsigned int nIn; unsigned int nFlags; @@ -368,9 +369,9 @@ class CScriptCheck ScriptError error; public: - CScriptCheck(): ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} + CScriptCheck(): amount(0), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) : - scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), + scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR) { } bool operator()(); @@ -378,6 +379,7 @@ class CScriptCheck void swap(CScriptCheck &check) { scriptPubKey.swap(check.scriptPubKey); std::swap(ptxTo, check.ptxTo); + std::swap(amount, check.amount); std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index a04b0f1d4d16a..ffcfa2820681a 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -559,7 +559,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n, (numeric, required) The output number\n" " \"scriptPubKey\": \"hex\", (string, required) script key\n" - " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n" + " \"redeemScript\": \"hex\", (string, required for P2SH) redeem script\n" + " \"amount\": value (numeric, required) The amount spent\n" " }\n" " ,...\n" " ]\n" @@ -697,7 +698,10 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) if ((unsigned int)nOut >= coins->vout.size()) coins->vout.resize(nOut+1); coins->vout[nOut].scriptPubKey = scriptPubKey; - coins->vout[nOut].nValue = 0; // we don't know the actual output value + coins->vout[nOut].nValue = 0; + if (prevOut.exists("amount")) { + coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount")); + } } // if redeemScript given and not using the local wallet (private keys @@ -752,21 +756,22 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) continue; } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; + const CAmount& amount = coins->vout[txin.prevout.n].nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if (!fHashSingle || (i < mergedTx.vout.size())) - ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, nHashType), prevPubKey, sigdata); + ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata); // ... and merge in other signatures: BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { - sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i), sigdata, DataFromTransaction(txv, i)); + sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); } UpdateTransaction(mergedTx, i, sigdata); ScriptError serror = SCRIPT_ERR_OK; - if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i), &serror)) { + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx.wit.vtxinwit.size() > i ? &mergedTx.wit.vtxinwit[i].scriptWitness : NULL, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount), &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 5c9e7c0a571a5..7c0cc29017981 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -69,7 +69,7 @@ struct ECCryptoClosure ECCryptoClosure instance_of_eccryptoclosure; } -int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, +int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, uint64_t amount, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { @@ -85,7 +85,8 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i // Regardless of the verification result, the tx did not error. set_error(err, bitcoinconsensus_ERR_OK); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn), NULL); + CAmount am(amount); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, am), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 5b8c33c6bf42e..40b396a880bbd 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -6,6 +6,8 @@ #ifndef BITCOIN_BITCOINCONSENSUS_H #define BITCOIN_BITCOINCONSENSUS_H +#include + #if defined(BUILD_BITCOIN_INTERNAL) && defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #if defined(_WIN32) @@ -54,7 +56,7 @@ enum /// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under /// the additional constraints specified by flags. /// If not NULL, err will contain an error/success code for the operation -EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, +EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, uint64_t amount, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 5646db4934cbb..e263c7f7cd834 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -121,7 +121,7 @@ class TransactionSignatureChecker : public BaseSignatureChecker virtual bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : txTo(txToIn), nIn(nInIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const; bool CheckLockTime(const CScriptNum& nLockTime) const; }; @@ -132,7 +132,7 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker const CTransaction txTo; public: - MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn) : TransactionSignatureChecker(&txTo, nInIn), txTo(*txToIn) {} + MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : TransactionSignatureChecker(&txTo, nInIn, amount), txTo(*txToIn) {} }; bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); diff --git a/src/script/sigcache.h b/src/script/sigcache.h index be1df09c2ade2..050bf8cc42290 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -22,7 +22,7 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker bool store; public: - CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, bool storeIn=true) : TransactionSignatureChecker(txToIn, nInIn), store(storeIn) {} + CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, bool storeIn) : TransactionSignatureChecker(txToIn, nInIn, amount), store(storeIn) {} bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 55ab40d696775..76bf9f27318e6 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -18,7 +18,7 @@ using namespace std; typedef std::vector valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn) {} +TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn, amount) {} bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode) const { @@ -206,12 +206,12 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur } } -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) { assert(nIn < txTo.vin.size()); CTransaction txToConst(txTo); - TransactionSignatureCreator creator(&keystore, &txToConst, nIn, nHashType); + TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, nHashType); SignatureData sigdata; bool ret = ProduceSignature(creator, fromPubKey, sigdata); @@ -226,7 +226,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutab assert(txin.prevout.n < txFrom.vout.size()); const CTxOut& txout = txFrom.vout[txin.prevout.n]; - return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType); } static vector CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, diff --git a/src/script/sign.h b/src/script/sign.h index 8496f8711545d..19cff6f290320 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -38,7 +38,7 @@ class TransactionSignatureCreator : public BaseSignatureCreator { const TransactionSignatureChecker checker; public: - TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, int nHashTypeIn=SIGHASH_ALL); + TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn); const BaseSignatureChecker& Checker() const { return checker; } bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; }; @@ -47,7 +47,7 @@ class MutableTransactionSignatureCreator : public TransactionSignatureCreator { CTransaction tx; public: - MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, int nHashTypeIn=SIGHASH_ALL) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, nHashTypeIn), tx(*txToIn) {} + MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn), tx(*txToIn) {} }; /** A signature creator that just produces 72-byte empty signatyres. */ @@ -70,7 +70,7 @@ struct SignatureData { bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); /** Produce a script signature for a transaction. */ -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType=SIGHASH_ALL); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL); /** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */ diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 8dedb23e43d21..0a66487803c52 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -48,6 +48,7 @@ BOOST_AUTO_TEST_CASE(multisig_verify) ScriptError err; CKey key[4]; + CAmount amount = 0; for (int i = 0; i < 4; i++) key[i].MakeNewKey(true); @@ -83,20 +84,20 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.assign(1,key[0]); keys.push_back(key[1]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err)); + BOOST_CHECK(VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); for (int i = 0; i < 4; i++) { keys.assign(1,key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 1: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); keys.assign(1,key[1]); keys.push_back(key[i]); s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0), &err), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[0], 0, amount), &err), strprintf("a&b 2: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } @@ -107,18 +108,18 @@ BOOST_AUTO_TEST_CASE(multisig_verify) s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) { - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err), strprintf("a|b: %d", i)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0), &err)); + BOOST_CHECK(!VerifyScript(s, a_or_b, NULL, flags, MutableTransactionSignatureChecker(&txTo[1], 0, amount), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_SIG_DER, ScriptErrorString(err)); @@ -130,12 +131,12 @@ BOOST_AUTO_TEST_CASE(multisig_verify) s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) { - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, NULL, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, NULL, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), &err), strprintf("escrow 1: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } else { - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, NULL, flags, MutableTransactionSignatureChecker(&txTo[2], 0), &err), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, NULL, flags, MutableTransactionSignatureChecker(&txTo[2], 0, amount), &err), strprintf("escrow 2: %d %d", i, j)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } } diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index b026ce5ba9c59..bb447c4f7cd47 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -46,7 +46,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri txTo.vin[0].scriptSig = scriptSig; txTo.vout[0].nValue = 1; - return VerifyScript(scriptSig, scriptPubKey, NULL, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0), &err); + return VerifyScript(scriptSig, scriptPubKey, NULL, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue), &err); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 92e5945ddf124..9edfde9fd03cc 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -94,14 +94,15 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, bo flags |= SCRIPT_VERIFY_WITNESS; } ScriptError err; - CMutableTransaction tx = BuildSpendingTransaction(scriptSig, BuildCreditingTransaction(scriptPubKey)); + CMutableTransaction txCredit = BuildCreditingTransaction(scriptPubKey); + CMutableTransaction tx = BuildSpendingTransaction(scriptSig, txCredit); CMutableTransaction tx2 = tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, NULL, flags, MutableTransactionSignatureChecker(&tx, 0), &err) == expect, message); + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, txCredit.vout[0].scriptPubKey, NULL, flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), &err) == expect, message); BOOST_CHECK_MESSAGE(expect == (err == SCRIPT_ERR_OK), std::string(ScriptErrorString(err)) + ": " + message); #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << tx2; - BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); #endif } @@ -762,18 +763,18 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), txFrom12); CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, NULL, flags, MutableTransactionSignatureChecker(&txTo12, 0, txFrom12.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); } @@ -795,54 +796,54 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EVAL_FALSE, ScriptErrorString(err)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0), &err)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, NULL, flags, MutableTransactionSignatureChecker(&txTo23, 0, txFrom23.vout[0].nValue), &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err)); } @@ -850,6 +851,7 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) { // Test the CombineSignatures function CBasicKeyStore keystore; + CAmount amount = 0; vector keys; vector pubkeys; for (int i = 0; i < 3; i++) @@ -867,19 +869,19 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) CScript& scriptSig = txTo.vin[0].scriptSig; SignatureData empty; - SignatureData combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), empty, empty); + SignatureData combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, empty); BOOST_CHECK(combined.scriptSig.empty()); // Single signature case: SignSignature(keystore, txFrom, txTo, 0); // changes scriptSig - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSig), empty); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty); BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), empty, SignatureData(scriptSig)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig)); BOOST_CHECK(combined.scriptSig == scriptSig); CScript scriptSigCopy = scriptSig; // Signing again will give a different, valid signature: SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSigCopy), SignatureData(scriptSig)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig)); BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig); // P2SH, single-signature case: @@ -887,28 +889,28 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) keystore.AddCScript(pkSingle); scriptPubKey = GetScriptForDestination(CScriptID(pkSingle)); SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSig), empty); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty); BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), empty, SignatureData(scriptSig)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig)); BOOST_CHECK(combined.scriptSig == scriptSig); scriptSigCopy = scriptSig; SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSigCopy), SignatureData(scriptSig)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig)); BOOST_CHECK(combined.scriptSig == scriptSigCopy || combined.scriptSig == scriptSig); // dummy scriptSigCopy with placeholder, should always choose non-placeholder: scriptSigCopy = CScript() << OP_0 << std::vector(pkSingle.begin(), pkSingle.end()); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSigCopy), SignatureData(scriptSig)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig)); BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSig), SignatureData(scriptSigCopy)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), SignatureData(scriptSigCopy)); BOOST_CHECK(combined.scriptSig == scriptSig); // Hardest case: Multisig 2-of-3 scriptPubKey = GetScriptForMultisig(2, pubkeys); keystore.AddCScript(scriptPubKey); SignSignature(keystore, txFrom, txTo, 0); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(scriptSig), empty); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(scriptSig), empty); BOOST_CHECK(combined.scriptSig == scriptSig); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), empty, SignatureData(scriptSig)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig)); BOOST_CHECK(combined.scriptSig == scriptSig); // A couple of partially-signed versions: @@ -937,21 +939,21 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) CScript complete13 = CScript() << OP_0 << sig1 << sig3; CScript complete23 = CScript() << OP_0 << sig2 << sig3; - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial1a), SignatureData(partial1b)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial1b)); BOOST_CHECK(combined.scriptSig == partial1a); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial1a), SignatureData(partial2a)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial2a)); BOOST_CHECK(combined.scriptSig == complete12); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial2a), SignatureData(partial1a)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial1a)); BOOST_CHECK(combined.scriptSig == complete12); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial1b), SignatureData(partial2b)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1b), SignatureData(partial2b)); BOOST_CHECK(combined.scriptSig == complete12); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial3b), SignatureData(partial1b)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial1b)); BOOST_CHECK(combined.scriptSig == complete13); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial2a), SignatureData(partial3a)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial3a)); BOOST_CHECK(combined.scriptSig == complete23); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial3b), SignatureData(partial2b)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial2b)); BOOST_CHECK(combined.scriptSig == complete23); - combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0), SignatureData(partial3b), SignatureData(partial3a)); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial3a)); BOOST_CHECK(combined.scriptSig == partial3c); } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 53a717b240d34..3acaffc8a8478 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -153,9 +153,10 @@ BOOST_AUTO_TEST_CASE(tx_valid) break; } + CAmount amount = 0; unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - NULL, verify_flags, TransactionSignatureChecker(&tx, i), &err), + NULL, verify_flags, TransactionSignatureChecker(&tx, i, amount), &err), strTest); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } @@ -228,8 +229,9 @@ BOOST_AUTO_TEST_CASE(tx_invalid) } unsigned int verify_flags = ParseScriptFlags(test[2].get_str()); + CAmount amount = 0; fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout], - NULL, verify_flags, TransactionSignatureChecker(&tx, i), &err); + NULL, verify_flags, TransactionSignatureChecker(&tx, i, amount), &err); } BOOST_CHECK_MESSAGE(!fValid, strTest); BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err)); @@ -368,7 +370,7 @@ void CheckWithFlag(const CTransaction& output, const CMutableTransaction& input, { ScriptError error; CTransaction inputi(input); - bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, inputi.wit.vtxinwit.size() > 0 ? &inputi.wit.vtxinwit[0].scriptWitness : NULL, flags, TransactionSignatureChecker(&inputi, 0), &error); + bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, inputi.wit.vtxinwit.size() > 0 ? &inputi.wit.vtxinwit[0].scriptWitness : NULL, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue), &error); assert(ret == success); } @@ -549,7 +551,7 @@ BOOST_AUTO_TEST_CASE(test_witness) CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false); CheckWithFlag(output2, input2, 0, false); BOOST_CHECK(output1 == output2); - UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); // P2SH 2-of-2 multisig @@ -560,7 +562,7 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output2, input2, 0, true); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false); BOOST_CHECK(output1 == output2); - UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); @@ -572,7 +574,7 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output2, input2, 0, true); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); BOOST_CHECK(output1 == output2); - UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); @@ -584,7 +586,7 @@ BOOST_AUTO_TEST_CASE(test_witness) CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true); CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false); BOOST_CHECK(output1 == output2); - UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); + UpdateTransaction(input1, 0, CombineSignatures(output1.vout[0].scriptPubKey, MutableTransactionSignatureChecker(&input1, 0, output1.vout[0].nValue), DataFromTransaction(input1, 0), DataFromTransaction(input2, 0))); CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true); CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7b9df1c85d365..225e4f0eba0c4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2165,7 +2165,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey; SignatureData sigdata; if (sign) { - signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, SIGHASH_ALL), scriptPubKey, sigdata); + signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata); UpdateTransaction(txNew, nIn, sigdata); } else signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata); From 8e01adb6bc4a4451dbfd8f9ccf5757d53600ad99 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 27 Dec 2015 19:49:08 +0100 Subject: [PATCH 18/32] Add signature version 1 with updated sighash Includes simplifications by Eric Lombrozo. --- src/script/interpreter.cpp | 84 ++++++++++++++++++++++++---- src/script/interpreter.h | 11 ++-- src/script/sign.cpp | 48 ++++++++-------- src/script/sign.h | 7 ++- src/test/multisig_tests.cpp | 2 +- src/test/script_tests.cpp | 18 +++--- src/test/sighash_tests.cpp | 4 +- src/test/transaction_tests.cpp | 2 +- src/test/txvalidationcache_tests.cpp | 2 +- 9 files changed, 120 insertions(+), 58 deletions(-) diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 36c4380c0a3b5..a85598f785560 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -229,7 +229,7 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) { return true; } -bool EvalScript(vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) +bool EvalScript(vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, int sigversion, ScriptError* serror) { static const CScriptNum bnZero(0); static const CScriptNum bnOne(1); @@ -832,13 +832,15 @@ bool EvalScript(vector >& stack, const CScript& script, un CScript scriptCode(pbegincodehash, pend); // Drop the signature, since there's no way for a signature to sign itself - scriptCode.FindAndDelete(CScript(vchSig)); + if (sigversion == 0) { + scriptCode.FindAndDelete(CScript(vchSig)); + } if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { //serror is set return false; } - bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode); + bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); popstack(stack); popstack(stack); @@ -888,7 +890,9 @@ bool EvalScript(vector >& stack, const CScript& script, un for (int k = 0; k < nSigsCount; k++) { valtype& vchSig = stacktop(-isig-k); - scriptCode.FindAndDelete(CScript(vchSig)); + if (sigversion == 0) { + scriptCode.FindAndDelete(CScript(vchSig)); + } } bool fSuccess = true; @@ -906,7 +910,7 @@ bool EvalScript(vector >& stack, const CScript& script, un } // Check signature - bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode); + bool fOk = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); if (fOk) { isig++; @@ -1069,8 +1073,64 @@ class CTransactionSignatureSerializer { } // anon namespace -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, int sigversion) { + if (sigversion == 1) { + uint256 hashPrevouts; + uint256 hashSequence; + uint256 hashOutputs; + + if (!(nHashType & SIGHASH_ANYONECANPAY)) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].prevout; + } + hashPrevouts = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + } + + if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vin.size(); n++) { + ss << txTo.vin[n].nSequence; + } + hashSequence = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + } + + if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { + CHashWriter ss(SER_GETHASH, 0); + for (unsigned int n = 0; n < txTo.vout.size(); n++) { + ss << txTo.vout[n]; + } + hashOutputs = ss.GetHash(); // TODO: cache this value for all signatures in a transaction + } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { + CHashWriter ss(SER_GETHASH, 0); + ss << txTo.vout[nIn]; + hashOutputs = ss.GetHash(); + } + + CHashWriter ss(SER_GETHASH, 0); + // Version + ss << txTo.nVersion; + // Input prevouts/nSequence (none/all, depending on flags) + ss << hashPrevouts; + ss << hashSequence; + // The input being signed (replacing the scriptSig with scriptCode + amount) + // The prevout may already be contained in hashPrevout, and the nSequence + // may already be contain in hashSequence. + ss << txTo.vin[nIn].prevout; + ss << static_cast(scriptCode); + ss << amount; + ss << txTo.vin[nIn].nSequence; + // Outputs (none/one/all, depending on flags) + ss << hashOutputs; + // Locktime + ss << txTo.nLockTime; + // Sighash type + ss << nHashType; + + return ss.GetHash(); + } + static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); if (nIn >= txTo.vin.size()) { // nIn out of range @@ -1099,7 +1159,7 @@ bool TransactionSignatureChecker::VerifySignature(const std::vector& vchSigIn, const vector& vchPubKey, const CScript& scriptCode) const +bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn, const vector& vchPubKey, const CScript& scriptCode, int sigversion) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) @@ -1112,7 +1172,7 @@ bool TransactionSignatureChecker::CheckSig(const vector& vchSigIn int nHashType = vchSig.back(); vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType); + uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); if (!VerifySignature(vchSig, pubkey, sighash)) return false; @@ -1197,7 +1257,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, return set_error(serror, SCRIPT_ERR_PUSH_SIZE); } - if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) { + if (!EvalScript(stack, scriptPubKey, flags, checker, 1, serror)) { return false; } @@ -1224,12 +1284,12 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C } vector > stack, stackCopy; - if (!EvalScript(stack, scriptSig, flags, checker, serror)) + if (!EvalScript(stack, scriptSig, flags, checker, 0, serror)) // serror is set return false; if (flags & SCRIPT_VERIFY_P2SH) stackCopy = stack; - if (!EvalScript(stack, scriptPubKey, flags, checker, serror)) + if (!EvalScript(stack, scriptPubKey, flags, checker, 0, serror)) // serror is set return false; if (stack.empty()) @@ -1275,7 +1335,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stack); - if (!EvalScript(stack, pubKey2, flags, checker, serror)) + if (!EvalScript(stack, pubKey2, flags, checker, 0, serror)) // serror is set return false; if (stack.empty()) diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e263c7f7cd834..ccf13c1450c70 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -93,12 +93,12 @@ enum bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); +uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, int sigversion); class BaseSignatureChecker { public: - virtual bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const + virtual bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, int sigversion) const { return false; } @@ -116,13 +116,14 @@ class TransactionSignatureChecker : public BaseSignatureChecker private: const CTransaction* txTo; unsigned int nIn; + const CAmount amount; protected: virtual bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : txTo(txToIn), nIn(nInIn) {} - bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const; + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn) {} + bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, int sigversion) const; bool CheckLockTime(const CScriptNum& nLockTime) const; }; @@ -135,7 +136,7 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : TransactionSignatureChecker(&txTo, nInIn, amount), txTo(*txToIn) {} }; -bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* error = NULL); +bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, int sigversion, ScriptError* error = NULL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL); #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 76bf9f27318e6..498c27dc3b4a2 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -18,31 +18,31 @@ using namespace std; typedef std::vector valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), checker(txTo, nIn, amount) {} +TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} -bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode) const +bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, int sigversion) const { CKey key; if (!keystore->GetKey(address, key)) return false; - uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType); + uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); if (!key.Sign(hash, vchSig)) return false; vchSig.push_back((unsigned char)nHashType); return true; } -static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret) +static bool Sign1(const CKeyID& address, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret, int sigversion) { vector vchSig; - if (!creator.CreateSig(vchSig, address, scriptCode)) + if (!creator.CreateSig(vchSig, address, scriptCode, sigversion)) return false; ret.push_back(vchSig); return true; } -static bool SignN(const vector& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret) +static bool SignN(const vector& multisigdata, const BaseSignatureCreator& creator, const CScript& scriptCode, std::vector& ret, int sigversion) { int nSigned = 0; int nRequired = multisigdata.front()[0]; @@ -50,7 +50,7 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato { const valtype& pubkey = multisigdata[i]; CKeyID keyID = CPubKey(pubkey).GetID(); - if (Sign1(keyID, creator, scriptCode, ret)) + if (Sign1(keyID, creator, scriptCode, ret, sigversion)) ++nSigned; } return nSigned==nRequired; @@ -63,7 +63,7 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato * Returns false if scriptPubKey could not be completely satisfied. */ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, - std::vector& ret, txnouttype& whichTypeRet) + std::vector& ret, txnouttype& whichTypeRet, int sigversion) { CScript scriptRet; uint160 h160; @@ -81,10 +81,10 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); - return Sign1(keyID, creator, scriptPubKey, ret); + return Sign1(keyID, creator, scriptPubKey, ret, sigversion); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, ret)) + if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) return false; else { @@ -102,7 +102,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP case TX_MULTISIG: ret.push_back(valtype()); // workaround CHECKMULTISIG bug - return (SignN(vSolutions, creator, scriptPubKey, ret)); + return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion)); case TX_WITNESS_V0_KEYHASH: ret.push_back(vSolutions[0]); @@ -142,7 +142,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu bool solved = true; std::vector result; txnouttype whichType; - solved = SignStep(creator, script, result, whichType); + solved = SignStep(creator, script, result, whichType, 0); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); @@ -153,7 +153,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu // the final scriptSig is the signatures from that // and then the serialized subscript: script = subscript = CScript(result[0].begin(), result[0].end()); - solved = solved && SignStep(creator, script, result, whichType) && whichType != TX_SCRIPTHASH; + solved = solved && SignStep(creator, script, result, whichType, 0) && whichType != TX_SCRIPTHASH; P2SH = true; } @@ -170,7 +170,7 @@ bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPu { CScript witnessscript(result[0].begin(), result[0].end()); txnouttype subType; - solved = solved && SignStep(creator, witnessscript, result, subType) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; + solved = solved && SignStep(creator, witnessscript, result, subType, 1) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH; result.push_back(std::vector(witnessscript.begin(), witnessscript.end())); sigdata.scriptWitness.stack = result; result.clear(); @@ -231,7 +231,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutab static vector CombineMultisig(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const vector& vSolutions, - const vector& sigs1, const vector& sigs2) + const vector& sigs1, const vector& sigs2, int sigversion) { // Combine all the signatures we've got: set allsigs; @@ -259,7 +259,7 @@ static vector CombineMultisig(const CScript& scriptPubKey, const BaseSi if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey - if (checker.CheckSig(sig, pubkey, scriptPubKey)) + if (checker.CheckSig(sig, pubkey, scriptPubKey, sigversion)) { sigs[pubkey] = sig; break; @@ -294,7 +294,7 @@ struct Stacks Stacks() {} explicit Stacks(const std::vector& scriptSigStack_) : script(scriptSigStack_), witness() {} explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) { - EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); + EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), 0); } SignatureData Output() const { @@ -308,7 +308,7 @@ struct Stacks static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignatureChecker& checker, const txnouttype txType, const vector& vSolutions, - Stacks sigs1, Stacks sigs2) + Stacks sigs1, Stacks sigs2, int sigversion) { switch (txType) { @@ -345,12 +345,12 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature Solver(pubKey2, txType2, vSolutions2); sigs1.script.pop_back(); sigs2.script.pop_back(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2); + Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, sigversion); result.script.push_back(spk); return result; } case TX_MULTISIG: - return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script)); + return Stacks(CombineMultisig(scriptPubKey, checker, vSolutions, sigs1.script, sigs2.script, sigversion)); case TX_WITNESS_V0_SCRIPTHASH: if (sigs1.witness.empty() || sigs1.witness.back().empty()) return sigs2; @@ -369,7 +369,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature sigs2.witness.pop_back(); sigs2.script = sigs2.witness; sigs2.witness.clear(); - Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2); + Stacks result = CombineSignatures(pubKey2, checker, txType2, vSolutions2, sigs1, sigs2, 1); result.witness = result.script; result.script.clear(); result.witness.push_back(valtype(pubKey2.begin(), pubKey2.end())); @@ -387,7 +387,7 @@ SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignature vector > vSolutions; Solver(scriptPubKey, txType, vSolutions); - return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2)).Output(); + return CombineSignatures(scriptPubKey, checker, txType, vSolutions, Stacks(scriptSig1), Stacks(scriptSig2), 0).Output(); } namespace { @@ -397,7 +397,7 @@ class DummySignatureChecker : public BaseSignatureChecker public: DummySignatureChecker() {} - bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode) const + bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, int sigversion) const { return true; } @@ -410,7 +410,7 @@ const BaseSignatureChecker& DummySignatureCreator::Checker() const return dummyChecker; } -bool DummySignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const +bool DummySignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, int sigversion) const { // Create a dummy signature that is a valid DER-encoding vchSig.assign(72, '\000'); diff --git a/src/script/sign.h b/src/script/sign.h index 19cff6f290320..04ea8b0150fc1 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -27,7 +27,7 @@ class BaseSignatureCreator { virtual const BaseSignatureChecker& Checker() const =0; /** Create a singular (non-script) signature. */ - virtual bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const =0; + virtual bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, int sigversion) const =0; }; /** A signature creator for transactions. */ @@ -35,12 +35,13 @@ class TransactionSignatureCreator : public BaseSignatureCreator { const CTransaction* txTo; unsigned int nIn; int nHashType; + CAmount amount; const TransactionSignatureChecker checker; public: TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn); const BaseSignatureChecker& Checker() const { return checker; } - bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, int sigversion) const; }; class MutableTransactionSignatureCreator : public TransactionSignatureCreator { @@ -55,7 +56,7 @@ class DummySignatureCreator : public BaseSignatureCreator { public: DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} const BaseSignatureChecker& Checker() const; - bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode) const; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, int sigversion) const; }; struct SignatureData { diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 0a66487803c52..9105a78f092c3 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -28,7 +28,7 @@ BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup) CScript sign_multisig(CScript scriptPubKey, vector keys, CTransaction transaction, int whichIn) { - uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL); + uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, 0); CScript result; result << OP_0; // CHECKMULTISIG bug workaround diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 9edfde9fd03cc..2415345b63bf1 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -241,7 +241,7 @@ class TestBuilder TestBuilder& PushSig(const CKey& key, int nHashType = SIGHASH_ALL, unsigned int lenR = 32, unsigned int lenS = 32) { - uint256 hash = SignatureHash(scriptPubKey, spendTx, 0, nHashType); + uint256 hash = SignatureHash(scriptPubKey, spendTx, 0, nHashType, 0, 0); std::vector vchSig, r, s; uint32_t iter = 0; do { @@ -697,21 +697,21 @@ BOOST_AUTO_TEST_CASE(script_PushData) ScriptError err; vector > directStack; - BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), &err)); + BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), 0, &err)); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); vector > pushdata1Stack; - BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), &err)); + BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), 0, &err)); BOOST_CHECK(pushdata1Stack == directStack); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); vector > pushdata2Stack; - BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), &err)); + BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), 0, &err)); BOOST_CHECK(pushdata2Stack == directStack); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); vector > pushdata4Stack; - BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), &err)); + BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), 0, &err)); BOOST_CHECK(pushdata4Stack == directStack); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } @@ -719,7 +719,7 @@ BOOST_AUTO_TEST_CASE(script_PushData) CScript sign_multisig(CScript scriptPubKey, std::vector keys, CTransaction transaction) { - uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL); + uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, 0); CScript result; // @@ -915,15 +915,15 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) // A couple of partially-signed versions: vector sig1; - uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL); + uint256 hash1 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_ALL, 0, 0); BOOST_CHECK(keys[0].Sign(hash1, sig1)); sig1.push_back(SIGHASH_ALL); vector sig2; - uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE); + uint256 hash2 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_NONE, 0, 0); BOOST_CHECK(keys[1].Sign(hash2, sig2)); sig2.push_back(SIGHASH_NONE); vector sig3; - uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE); + uint256 hash3 = SignatureHash(scriptPubKey, txTo, 0, SIGHASH_SINGLE, 0, 0); BOOST_CHECK(keys[2].Sign(hash3, sig3)); sig3.push_back(SIGHASH_SINGLE); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 04c6fa9625caa..0df5f3c88ba2c 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(sighash_test) uint256 sh, sho; sho = SignatureHashOld(scriptCode, txTo, nIn, nHashType); - sh = SignatureHash(scriptCode, txTo, nIn, nHashType); + sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, 0); #if defined(PRINT_SIGHASH_JSON) CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << txTo; @@ -210,7 +210,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) continue; } - sh = SignatureHash(scriptCode, tx, nIn, nHashType); + sh = SignatureHash(scriptCode, tx, nIn, nHashType, 0, 0); BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest); } } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 3acaffc8a8478..934fc2a722e51 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -392,7 +392,7 @@ static CScript PushAll(const vector& values) void ReplaceRedeemScript(CScript& script, const CScript& redeemScript) { vector stack; - EvalScript(stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); + EvalScript(stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), 0); assert(stack.size() > 0); stack.back() = std::vector(redeemScript.begin(), redeemScript.end()); script = PushAll(stack); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 66be9d3d5e3fb..968807bd8722f 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -48,7 +48,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) // Sign: std::vector vchSig; - uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL); + uint256 hash = SignatureHash(scriptPubKey, spends[i], 0, SIGHASH_ALL, 0, 0); BOOST_CHECK(coinbaseKey.Sign(hash, vchSig)); vchSig.push_back((unsigned char)SIGHASH_ALL); spends[i].vin[0].scriptSig << vchSig; From 38183e64fe2987a084475b67847fd373c75e5fd8 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 30 Dec 2015 01:13:08 +0100 Subject: [PATCH 19/32] Add witness address RPCs (using P2SH) Includes support for pushkeyhash wit v0 by Alex Morcos. --- src/rpcmisc.cpp | 33 +++++++++++++++++++ src/rpcserver.cpp | 2 ++ src/rpcserver.h | 2 ++ src/wallet/rpcwallet.cpp | 69 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 9871c3fcc9031..c38cbf6e44a4b 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -312,6 +312,39 @@ UniValue createmultisig(const UniValue& params, bool fHelp) return result; } +UniValue createwitnessaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + { + string msg = "createwitnessaddress \"script\"\n" + "\nCreates a witness address for a particular script.\n" + "It returns a json object with the address and witness script.\n" + + "\nArguments:\n" + "1. \"script\" (string, required) A hex encoded script\n" + + "\nResult:\n" + "{\n" + " \"address\":\"multisigaddress\", (string) The value of the new address (P2SH of witness script).\n" + " \"witnessScript\":\"script\" (string) The string value of the hex-encoded witness script.\n" + "}\n" + ; + throw runtime_error(msg); + } + + std::vector code = ParseHex(params[0].get_str()); + CScript script(code.begin(), code.end()); + CScript witscript = GetScriptForWitness(script); + CScriptID witscriptid(witscript); + CBitcoinAddress address(witscriptid); + + UniValue result(UniValue::VOBJ); + result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("witnessScript", HexStr(witscript.begin(), witscript.end()))); + + return result; +} + UniValue verifymessage(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 3) diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index b3abeec4a3ce8..e8410fe765941 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -315,6 +315,7 @@ static const CRPCCommand vRPCCommands[] = /* Utility functions */ { "util", "createmultisig", &createmultisig, true }, + { "util", "createwitnessaddress", &createwitnessaddress, true }, { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */ { "util", "verifymessage", &verifymessage, true }, { "util", "estimatefee", &estimatefee, true }, @@ -333,6 +334,7 @@ static const CRPCCommand vRPCCommands[] = #ifdef ENABLE_WALLET /* Wallet */ { "wallet", "addmultisigaddress", &addmultisigaddress, true }, + { "wallet", "addwitnessaddress", &addwitnessaddress, true }, { "wallet", "backupwallet", &backupwallet, true }, { "wallet", "dumpprivkey", &dumpprivkey, true }, { "wallet", "dumpwallet", &dumpwallet, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index babf7c8d2e1f2..a3779dd92ce16 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -213,7 +213,9 @@ extern UniValue movecmd(const UniValue& params, bool fHelp); extern UniValue sendfrom(const UniValue& params, bool fHelp); extern UniValue sendmany(const UniValue& params, bool fHelp); extern UniValue addmultisigaddress(const UniValue& params, bool fHelp); +extern UniValue addwitnessaddress(const UniValue& params, bool fHelp); extern UniValue createmultisig(const UniValue& params, bool fHelp); +extern UniValue createwitnessaddress(const UniValue& params, bool fHelp); extern UniValue listreceivedbyaddress(const UniValue& params, bool fHelp); extern UniValue listreceivedbyaccount(const UniValue& params, bool fHelp); extern UniValue listtransactions(const UniValue& params, bool fHelp); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index f7f1467631f98..5e5ee5d07851e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1100,6 +1100,75 @@ UniValue addmultisigaddress(const UniValue& params, bool fHelp) return CBitcoinAddress(innerID).ToString(); } +class Witnessifier : public boost::static_visitor +{ +public: + CScriptID result; + + bool operator()(const CNoDestination &dest) const { return false; } + + bool operator()(const CKeyID &keyID) { + CPubKey pubkey; + if (pwalletMain && pwalletMain->GetPubKey(keyID, pubkey)) { + CScript basescript; + basescript << ToByteVector(pubkey) << OP_CHECKSIG; + CScript witscript = GetScriptForWitness(basescript); + pwalletMain->AddCScript(witscript); + result = CScriptID(witscript); + return true; + } + return false; + } + + bool operator()(const CScriptID &scriptID) { + CScript subscript; + if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) { + int witnessversion; + std::vector witprog; + if (subscript.IsWitnessProgram(witnessversion, witprog)) { + result = scriptID; + return true; + } + CScript witscript = GetScriptForWitness(subscript); + pwalletMain->AddCScript(witscript); + result = CScriptID(witscript); + return true; + } + return false; + } +}; + +UniValue addwitnessaddress(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 1) + { + string msg = "addwitnessaddress \"address\"\n" + "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" + "It returns the witness script.\n" + + "\nArguments:\n" + "1. \"address\" (string, required) An address known to the wallet\n" + + "\nResult:\n" + "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n" + "}\n" + ; + throw runtime_error(msg); + } + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + + Witnessifier w; + CTxDestination dest = address.Get(); + bool ret = boost::apply_visitor(w, dest); + if (!ret) { + throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet"); + } + + return CBitcoinAddress(w.result).ToString(); +} struct tallyitem { From 0724fefea6dbe0e63596f94bb1e3a8eae6df92f1 Mon Sep 17 00:00:00 2001 From: Johnson Lau Date: Sat, 23 Jan 2016 01:46:11 +0800 Subject: [PATCH 20/32] Return witness data --- src/rpcblockchain.cpp | 5 +++++ src/rpcrawtransaction.cpp | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 7ce7de48b8eab..3e0d21b183432 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -96,6 +96,8 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx confirmations = chainActive.Height() - blockindex->nHeight + 1; result.push_back(Pair("confirmations", confirmations)); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); + result.push_back(Pair("wsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS))); + result.push_back(Pair("vsize", (int)::GetVirtualBlockSize(block))); result.push_back(Pair("height", blockindex->nHeight)); result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); @@ -325,6 +327,7 @@ UniValue getblockheader(const UniValue& params, bool fHelp) " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" " \"nextblockhash\" : \"hash\", (string) The hash of the next block\n" " \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n" + " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" "}\n" "\nResult (for verbose=false):\n" "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" @@ -373,6 +376,8 @@ UniValue getblock(const UniValue& params, bool fHelp) " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" " \"size\" : n, (numeric) The block size\n" + " \"wsize\" : n, (numeric) The block size with witness data\n" + " \"vsize\" : n, (numeric) The virtual transaction size (size + (wsize-size)/4)\n" " \"height\" : n, (numeric) The block height or index\n" " \"version\" : n, (numeric) The block version\n" " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index ffcfa2820681a..2d4790f70e59c 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -59,14 +59,35 @@ void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fInclud out.push_back(Pair("addresses", a)); } +string WitnessToStr(const CTxinWitness& witness) +{ + string str; + for (unsigned int j = 0; j < witness.scriptWitness.stack.size(); j++) { + if (j > 0) + str += " "; + std::vector item = witness.scriptWitness.stack[j]; + str += HexStr(item.begin(), item.end()); + } + return str; +} + void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { entry.push_back(Pair("txid", tx.GetHash().GetHex())); entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); + + if (!tx.wit.IsNull()) { + if (!tx.IsCoinBase()) + entry.push_back(Pair("wtxid", tx.GetWitnessHash().GetHex())); + entry.push_back(Pair("wsize", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS))); + entry.push_back(Pair("vsize", (int)::GetVirtualTransactionSize(tx))); + } + UniValue vin(UniValue::VARR); - BOOST_FOREACH(const CTxIn& txin, tx.vin) { + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const CTxIn& txin = tx.vin[i]; UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); @@ -78,6 +99,11 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); in.push_back(Pair("scriptSig", o)); } + if (!tx.wit.IsNull()) { + if (!tx.wit.vtxinwit[i].IsNull()) { + in.push_back(Pair("txinwitness", WitnessToStr(tx.wit.vtxinwit[i]))); + } + } in.push_back(Pair("sequence", (int64_t)txin.nSequence)); vin.push_back(in); } @@ -137,6 +163,9 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) " \"size\" : n, (numeric) The transaction size\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" + " \"wtxid\" : \"id\", (string) The witness id (segwit tx only)\n" + " \"wsize\" : n, (numeric) The transaction size with witness data (segwit tx only)\n" + " \"vsize\" : n, (numeric) The virtual transaction size (size + (wsize-size)/4)\n" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" @@ -146,6 +175,7 @@ UniValue getrawtransaction(const UniValue& params, bool fHelp) " \"hex\": \"hex\" (string) hex\n" " },\n" " \"sequence\": n (numeric) The script sequence number\n" + " \"txinwitness\": \"hex\" (string) witness data (if any)\n" " }\n" " ,...\n" " ],\n" @@ -434,6 +464,9 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) " \"size\" : n, (numeric) The transaction size\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" + " \"wtxid\" : \"id\", (string) The witness id (segwit tx only)\n" + " \"wsize\" : n, (numeric) The transaction size with witness data (segwit tx only)\n" + " \"vsize\" : n, (numeric) The virtual transaction size (size + (wsize-size)/4)\n" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" @@ -442,6 +475,7 @@ UniValue decoderawtransaction(const UniValue& params, bool fHelp) " \"asm\": \"asm\", (string) asm\n" " \"hex\": \"hex\" (string) hex\n" " },\n" + " \"txinwitness\": \"hex\" (string) txin witness data (if any)\n" " \"sequence\": n (numeric) The script sequence number\n" " }\n" " ,...\n" From dca8111a09351b55c50c86591ea251c676cbf1fa Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 3 Jan 2016 18:54:50 +0100 Subject: [PATCH 21/32] Implementing base+wit/4 block size limit rule Includes fixup by Suhas Daftuar. --- src/main.cpp | 18 ++++++++++++++++-- src/primitives/block.cpp | 10 ++++++++++ src/primitives/block.h | 3 +++ src/primitives/transaction.cpp | 5 +++++ src/primitives/transaction.h | 3 +++ src/test/mempool_tests.cpp | 4 ++-- src/test/policyestimator_tests.cpp | 2 +- src/txmempool.cpp | 2 +- src/wallet/wallet.cpp | 2 +- 9 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index e72ac2b7e3219..632f0590c449c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -755,7 +755,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty"); if (tx.vout.empty()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); - // Size limits + // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability) if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); @@ -2978,6 +2978,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo // All potential-corruption validation must be done before we do any // transaction validation, as otherwise we may mark the header as invalid // because we receive the wrong transactions for it. + // Note that witness malleability is checked in ContextualCheckBlock, so no + // checks that use witness data may be performed here. // Size limits if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) @@ -3183,10 +3185,22 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) { return state.DoS(100, error("%s : witness merkle commitment mismatch", __func__), REJECT_INVALID, "bad-witness-merkle-match", true); } + + // After the coinbase witness nonce and commitment are verified, + // we can check if the virtual size passes (before we've checked + // the coinbase witness, it would be possible for the virtual + // size to be too large by filling up the coinbase witness, + // which doesn't change the block hash, so we couldn't mark + // the block as permanently failed). + // Note: we aren't checking virtual size for blocks that aren't + // witness enabled, but that's okay because CheckBlock still + // checks the block size without any witnesses. + if (GetVirtualBlockSize(block) > MAX_BLOCK_SIZE) { + return state.DoS(100, error("ContextualCheckBlock(): witness size limits failed"), REJECT_INVALID, "bad-blk-wit-length"); + } return true; } } - // No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room from spam. for (size_t i = 0; i < block.vtx.size(); i++) { if (!block.vtx[i].wit.IsNull()) { return state.DoS(100, error("%s : unexpected witness data found", __func__), REJECT_INVALID, "unexpected-witness", true); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 59e949d71a329..ab1333351ce1f 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -31,3 +31,13 @@ std::string CBlock::ToString() const } return s.str(); } + +size_t GetVirtualBlockSize(const CBlock& block) +{ + // The intended approximate formula is: vsize = base_size + witness_size / 4. + // We can only serialize base or base+witness, so the formula + // becomes: vsize = base_size + (total_size - base_size) / 4 or + // vsize = (total_size + 3 * base_size) / 4, which we round up to + // vsize = (total_size + 3 * base_size + 3) / 4. + return (::GetSerializeSize(block, SER_NETWORK, 0) * 3 + ::GetSerializeSize(block, SER_NETWORK, SERIALIZE_TRANSACTION_WITNESS) + 3) / 4; +} diff --git a/src/primitives/block.h b/src/primitives/block.h index 353e47c6b7665..44ae695c5509d 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -155,4 +155,7 @@ struct CBlockLocator } }; +/** Compute the consensus-critical virtual block size. */ +size_t GetVirtualBlockSize(const CBlock& tx); + #endif // BITCOIN_PRIMITIVES_BLOCK_H diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 514e206c71761..01a344006ece3 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -148,3 +148,8 @@ std::string CTransaction::ToString() const str += " " + vout[i].ToString() + "\n"; return str; } + +size_t GetVirtualTransactionSize(const CTransaction& tx) +{ + return (::GetSerializeSize(tx, SER_NETWORK, 0) * 3 + ::GetSerializeSize(tx, SER_NETWORK, SERIALIZE_TRANSACTION_WITNESS) + 3) / 4; +} diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 327bdcd66876b..6e5ed8879dc7e 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -409,4 +409,7 @@ struct CMutableTransaction uint256 GetHash() const; }; +/** Compute the consensus-critical virtual transaction size. */ +size_t GetVirtualTransactionSize(const CTransaction& tx); + #endif // BITCOIN_PRIMITIVES_TRANSACTION_H diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 1347d23656462..4dae407793f9c 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -363,12 +363,12 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) BOOST_CHECK(pool.exists(tx2.GetHash())); BOOST_CHECK(pool.exists(tx3.GetHash())); - pool.TrimToSize(::GetSerializeSize(CTransaction(tx1), SER_NETWORK, PROTOCOL_VERSION)); // mempool is limited to tx1's size in memory usage, so nothing fits + pool.TrimToSize(GetVirtualTransactionSize(tx1)); // mempool is limited to tx1's size in memory usage, so nothing fits BOOST_CHECK(!pool.exists(tx1.GetHash())); BOOST_CHECK(!pool.exists(tx2.GetHash())); BOOST_CHECK(!pool.exists(tx3.GetHash())); - CFeeRate maxFeeRateRemoved(25000, ::GetSerializeSize(CTransaction(tx3), SER_NETWORK, PROTOCOL_VERSION) + ::GetSerializeSize(CTransaction(tx2), SER_NETWORK, PROTOCOL_VERSION)); + CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(tx3) + GetVirtualTransactionSize(tx2)); BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); CMutableTransaction tx4 = CMutableTransaction(); diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index 644c3da213a99..4e5e35b295cf0 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) tx.vin[0].scriptSig = garbage; tx.vout.resize(1); tx.vout[0].nValue=0LL; - CFeeRate baseRate(basefee, ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)); + CFeeRate baseRate(basefee, GetVirtualTransactionSize(tx)); // Create a fake block std::vector block; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f8e03c25334d3..792604ee46f13 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -27,7 +27,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee, hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps) { - nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + nTxSize = GetVirtualTransactionSize(tx); nModSize = tx.CalculateModifiedSize(nTxSize); nUsageSize = RecursiveDynamicUsage(tx); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 225e4f0eba0c4..8d21f6a5ed337 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2178,7 +2178,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt nIn++; } - unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); + unsigned int nBytes = GetVirtualTransactionSize(txNew); // Remove scriptSigs if we used dummy signatures for fee calculation if (!sign) { From 616e69b2093f4dbf6a215e2a86713b290c56c354 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Fri, 1 Jan 2016 23:18:34 -0600 Subject: [PATCH 22/32] Add command line options to loosen mempool acceptance rules --- src/main.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 632f0590c449c..8a6809bf3c14f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -837,7 +837,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C return state.DoS(0, false, REJECT_NONSTANDARD, reason); // Don't accept witness transactions before the final threshold passes - if (!tx.wit.IsNull() && !(chainActive.Tip()->nHeight + 1 >= Params().GetConsensus().SegWitHeight && IsSuperMajority(5, chainActive.Tip(), Params().GetConsensus().nMajorityRejectBlockOutdated, Params().GetConsensus()))) { + if (!GetBoolArg("-prematurewitness",false) && !tx.wit.IsNull() && !(chainActive.Tip()->nHeight + 1 >= Params().GetConsensus().SegWitHeight && IsSuperMajority(5, chainActive.Tip(), Params().GetConsensus().nMajorityRejectBlockOutdated, Params().GetConsensus()))) { return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet"); } @@ -1181,11 +1181,16 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C } } + unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; + if (!Params().RequireStandard()) { + scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags); + } + // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) { - if (CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) && - !CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS & ~SCRIPT_VERIFY_CLEANSTACK, true)) { + if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true)) { + if (CheckInputs(tx, state, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true) && + !CheckInputs(tx, state, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true)) { // Only the witness is wrong, so the transaction itself may be fine. state.SetCorruptionPossible(); } From 156c41a92ff38d5bce9d32220fdaaad2d5ddc6c1 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Fri, 1 Jan 2016 23:18:47 -0600 Subject: [PATCH 23/32] Add rpc test for segwit --- qa/rpc-tests/segwit.py | 269 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100755 qa/rpc-tests/segwit.py diff --git a/qa/rpc-tests/segwit.py b/qa/rpc-tests/segwit.py new file mode 100755 index 0000000000000..fda65c50b6573 --- /dev/null +++ b/qa/rpc-tests/segwit.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python2 +# Copyright (c) 2014 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test the SegWit changeover logic +# + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import os +import shutil +import hashlib +from binascii import hexlify + +NODE_0 = 0 +NODE_1 = 1 +NODE_2 = 2 +WIT_V0 = 0 +WIT_V1 = 1 + +def sha256(s): + return hashlib.new('sha256', s).digest() + +def ripemd160(s): + return hashlib.new('ripemd160', s).digest() + +def witness_script(version, pubkey): + if (version == 0): + pubkeyhash = hexlify(ripemd160(sha256(pubkey.decode("hex")))) + pkscript = "001976a914" + pubkeyhash + "88ac" + elif (version == 1): + witnessprogram = "21"+pubkey+"ac" + hashwitnessprogram = hexlify(sha256(witnessprogram.decode("hex"))) + pkscript = "5120" + hashwitnessprogram + else: + assert("Wrong version" == "0 or 1") + return pkscript + +def addlength(script): + scriptlen = format(len(script)/2, 'x') + assert(len(scriptlen) == 2) + return scriptlen + script + +def create_witnessprogram(version, node, utxo, pubkey, encode_p2sh, amount): + pkscript = witness_script(version, pubkey); + if (encode_p2sh): + p2sh_hash = hexlify(ripemd160(sha256(pkscript.decode("hex")))) + pkscript = "a914"+p2sh_hash+"87" + inputs = [] + outputs = {} + inputs.append({ "txid" : utxo["txid"], "vout" : utxo["vout"]} ) + DUMMY_P2SH = "2MySexEGVzZpRgNQ1JdjdP5bRETznm3roQ2" # P2SH of "OP_1 OP_DROP" + outputs[DUMMY_P2SH] = amount + tx_to_witness = node.createrawtransaction(inputs,outputs) + #replace dummy output with our own + tx_to_witness = tx_to_witness[0:110] + addlength(pkscript) + tx_to_witness[-8:] + return tx_to_witness + +def send_to_witness(version, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""): + tx_to_witness = create_witnessprogram(version, node, utxo, pubkey, encode_p2sh, amount) + if (sign): + signed = node.signrawtransaction(tx_to_witness) + return node.sendrawtransaction(signed["hex"]) + else: + if (insert_redeem_script): + tx_to_witness = tx_to_witness[0:82] + addlength(insert_redeem_script) + tx_to_witness[84:] + + return node.sendrawtransaction(tx_to_witness) + +def getutxo(txid): + utxo = {} + utxo["vout"] = 0 + utxo["txid"] = txid + return utxo + +class SegWitTest(BitcoinTestFramework): + + def setup_chain(self): + print("Initializing test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, 3) + + def setup_network(self): + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-logtimemicros", "-debug"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness"])) + self.nodes.append(start_node(2, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=5", "-promiscuousmempoolflags=517", "-prematurewitness"])) + connect_nodes(self.nodes[1], 0) + connect_nodes(self.nodes[2], 1) + connect_nodes(self.nodes[0], 2) + self.is_network_split = False + self.sync_all() + + def success_mine(self, node, txid, sign, redeem_script=""): + send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + block = node.generate(1) + assert_equal(len(node.getblock(block[0])["tx"]), 2) + sync_blocks(self.nodes) + + def skip_mine(self, node, txid, sign, redeem_script=""): + send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + block = node.generate(1) + assert_equal(len(node.getblock(block[0])["tx"]), 1) + sync_blocks(self.nodes) + + def fail_accept(self, node, txid, sign, redeem_script=""): + try: + send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + except JSONRPCException as exp: + assert(exp.error["code"] == -26) + else: + raise AssertionError("Tx should not have been accepted") + + def fail_mine(self, node, txid, sign, redeem_script=""): + send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + try: + node.generate(1) + except JSONRPCException as exp: + assert(exp.error["code"] == -1) + else: + raise AssertionError("Created valid block when TestBlockValidity should have failed") + sync_blocks(self.nodes) + + def run_test(self): + self.nodes[0].generate(160) #block 160 + + self.pubkey = [] + p2sh_ids = [] # p2sh_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE embedded in p2sh + wit_ids = [] # wit_ids[NODE][VER] is an array of txids that spend to a witness version VER pkscript to an address for NODE via bare witness + for i in xrange(3): + newaddress = self.nodes[i].getnewaddress() + self.nodes[i].addwitnessaddress(newaddress) + self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"]) + p2sh_ids.append([]) + wit_ids.append([]) + for v in xrange(2): + p2sh_ids[i].append([]) + wit_ids[i].append([]) + + for i in xrange(5): + for n in xrange(3): + for v in xrange(2): + wit_ids[n][v].append(send_to_witness(v, self.nodes[0], self.nodes[0].listunspent()[0], self.pubkey[n], False, Decimal("49.999"))) + p2sh_ids[n][v].append(send_to_witness(v, self.nodes[0], self.nodes[0].listunspent()[0], self.pubkey[n], True, Decimal("49.999"))) + + self.nodes[0].generate(1) #block 161 + sync_blocks(self.nodes) + + # Make sure all nodes recognize the transactions as theirs + assert_equal(self.nodes[0].getbalance(), 60*50 - 60*50 + 20*Decimal("49.999") + 50) + assert_equal(self.nodes[1].getbalance(), 20*Decimal("49.999")) + assert_equal(self.nodes[2].getbalance(), 20*Decimal("49.999")) + + self.nodes[0].generate(581) #block 742 + sync_blocks(self.nodes) + + print "Verify default node can't accept any witness format txs before fork" + # unsigned, no scriptsig + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], False) + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], False) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], False) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], False) + # unsigned with redeem script + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], False, addlength(witness_script(0, self.pubkey[0]))) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], False, addlength(witness_script(1, self.pubkey[0]))) + # signed + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) + + print "Verify witness txs are skipped for mining before the fork" + self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][0], True) #block 743 + self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][0], True) #block 744 + self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][0], True) #block 745 + self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][0], True) #block 746 + + # TODO: An old node would see these txs without witnesses and be able to mine them + + print "Verify unsigned bare witness txs in version 5 blocks are valid before the fork" + self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][1], False) #block 747 + self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][1], False) #block 748 + + print "Verify unsigned p2sh witness txs without a redeem script are invalid" + self.fail_accept(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][1], False) + self.fail_accept(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][1], False) + + print "Verify unsigned p2sh witness txs with a redeem script in version 5 blocks are valid before the fork" + self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][1], False, addlength(witness_script(0, self.pubkey[2]))) #block 749 + self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][1], False, addlength(witness_script(1, self.pubkey[2]))) #block 750 + + print "Verify previous witness txs skipped for mining can now be mined" + assert_equal(len(self.nodes[2].getrawmempool()), 4) + block = self.nodes[2].generate(1) #block 751 + sync_blocks(self.nodes) + assert_equal(len(self.nodes[2].getrawmempool()), 0) + assert_equal(len(self.nodes[2].getblock(block[0])["tx"]), 5) + + print "Verify witness txs without witness data in version 5 blocks are invalid after the fork" + self.fail_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][2], False) + self.fail_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][2], False) + self.fail_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][2], False, addlength(witness_script(0, self.pubkey[2]))) + self.fail_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][2], False, addlength(witness_script(1, self.pubkey[2]))) + + + print "Verify that a version 4 block can still mine those unsigned txs" + assert_equal(len(self.nodes[2].getrawmempool()), 4) + sync_mempools(self.nodes[1:3]) + block = self.nodes[1].generate(1) #block 752 + sync_blocks(self.nodes) + assert_equal(len(self.nodes[2].getrawmempool()), 0) + assert_equal(len(self.nodes[1].getblock(block[0])["tx"]), 5) + + print "Verify all types of witness txs can be submitted signed after the fork to node with -prematurewitness" + self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][3], True) #block 753 + self.success_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][3], True) #block 754 + self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V0][3], True) #block 755 + self.success_mine(self.nodes[2], p2sh_ids[NODE_2][WIT_V1][3], True) #block 756 + + print "Verify default node can't accept any witness format txs between enforce and reject points of fork" + # unsigned, no scriptsig + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], False) + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], False) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], False) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], False) + # unsigned with redeem script + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], False, addlength(witness_script(0, self.pubkey[0]))) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], False, addlength(witness_script(1, self.pubkey[0]))) + # signed + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) + self.fail_accept(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) + self.fail_accept(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) + + # TODO: verify witness txs are invalid if in a v4 block + print "Verify witness txs aren't mined in a v4 block" + self.skip_mine(self.nodes[1], wit_ids[NODE_1][WIT_V0][0], True) #block 757 + self.skip_mine(self.nodes[1], wit_ids[NODE_1][WIT_V1][0], True) #block 758 + self.skip_mine(self.nodes[1], p2sh_ids[NODE_1][WIT_V0][0], True) #block 759 + self.skip_mine(self.nodes[1], p2sh_ids[NODE_1][WIT_V1][0], True) #block 760 + + # Mine them from ver 5 node + sync_mempools(self.nodes[1:3]) + assert_equal(len(self.nodes[2].getrawmempool()), 4) + block = self.nodes[2].generate(1) #block 761 + sync_blocks(self.nodes) + assert_equal(len(self.nodes[2].getrawmempool()), 0) + assert_equal(len(self.nodes[2].getblock(block[0])["tx"]), 5) + + self.nodes[0].generate(195) #block 956 (5 of which are v4 blocks) + sync_blocks(self.nodes) + + print "Verify that version 4 blocks are invalid period after reject point" + try: + self.nodes[1].generate(1) + except JSONRPCException as exp: + assert(exp.error["code"] == -1) + else: + raise AssertionError("Created valid block when TestBlockValidity should have failed") + + print "Verify default node can now use witness txs" + self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V0][0], True) #block 957 + self.success_mine(self.nodes[0], wit_ids[NODE_0][WIT_V1][0], True) #block 958 + self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V0][0], True) #block 959 + self.success_mine(self.nodes[0], p2sh_ids[NODE_0][WIT_V1][0], True) #block 960 + +if __name__ == '__main__': + SegWitTest().main() From 0b08ed9c9d9cafde6e3e2e74210df2b3e627d066 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 6 Jan 2016 01:15:24 +0100 Subject: [PATCH 24/32] SigOp counting in witnesses Includes fixup by Thomas Kerin. --- src/main.cpp | 23 ++++++++++++++++++- src/script/interpreter.cpp | 47 ++++++++++++++++++++++++++++++++++++++ src/script/interpreter.h | 2 ++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 8a6809bf3c14f..83137c3d020c5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -741,6 +741,19 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in return nSigOps; } +unsigned int GetWitnessSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs, int flags) +{ + if (tx.IsCoinBase()) + return 0; + + unsigned int nSigOps = 0; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const CTxOut &prevout = inputs.GetOutputFor(tx.vin[i]); + nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, i < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[i].scriptWitness : NULL, flags); + } + return nSigOps; +} @@ -946,6 +959,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C unsigned int nSigOps = GetLegacySigOpCount(tx); nSigOps += GetP2SHSigOpCount(tx, view); + nSigOps += (GetWitnessSigOpCount(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS) + 3) / 4; + + if (nSigOps > MAX_STANDARD_TX_SIGOPS) + return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, + strprintf("%d > %d", nSigOps, MAX_STANDARD_TX_SIGOPS)); CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; @@ -2085,6 +2103,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CAmount nFees = 0; int nInputs = 0; unsigned int nSigOps = 0; + unsigned int nWitSigOps = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; vPos.reserve(block.vtx.size()); @@ -2105,13 +2124,15 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), REJECT_INVALID, "bad-txns-inputs-missingorspent"); + nWitSigOps += GetWitnessSigOpCount(tx, view, flags); + if (fStrictPayToScriptHash) { // Add in sigops done by pay-to-script-hash inputs; // this is to prevent a "rogue miner" from creating // an incredibly-expensive-to-validate block. nSigOps += GetP2SHSigOpCount(tx, view); - if (nSigOps > MAX_BLOCK_SIGOPS) + if (nSigOps + (nWitSigOps + 3) / 4 > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index a85598f785560..fa33928f8a479 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1387,3 +1387,50 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return set_success(serror); } + +size_t static WitnessSigOps(int witversion, const std::vector& witprogram, const CScriptWitness& witness, int flags) +{ + if (witversion == 0) { + if (witprogram.size() == 20) + return 1; + + if (witprogram.size() == 32 && witness.stack.size() > 0) { + CScript subscript(witness.stack.back().begin(), witness.stack.back().end()); + return subscript.GetSigOpCount(true); + } + } + + // Future flags may be implemented here. + return 0; +} + +size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags) +{ + static const CScriptWitness witnessEmpty; + + if ((flags & SCRIPT_VERIFY_WITNESS) == 0) { + return 0; + } + assert((flags & SCRIPT_VERIFY_P2SH) != 0); + + int witnessversion; + std::vector witnessprogram; + if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + } + + if (scriptPubKey.IsPayToScriptHash() && scriptSig.IsPushOnly()) { + CScript::const_iterator pc = scriptSig.begin(); + vector data; + while (pc < scriptSig.end()) { + opcodetype opcode; + scriptSig.GetOp(pc, opcode, data); + } + CScript subscript(data.begin(), data.end()); + if (subscript.IsWitnessProgram(witnessversion, witnessprogram)) { + return WitnessSigOps(witnessversion, witnessprogram, witness ? *witness : witnessEmpty, flags); + } + } + + return 0; +} diff --git a/src/script/interpreter.h b/src/script/interpreter.h index ccf13c1450c70..b617a5c0762a9 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -139,4 +139,6 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, int sigversion, ScriptError* error = NULL); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = NULL); +size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags); + #endif // BITCOIN_SCRIPT_INTERPRETER_H From 2161d22ddb95798aa0d12cfbfad76db0ad9f5f9a Mon Sep 17 00:00:00 2001 From: Thomas Kerin Date: Sun, 24 Jan 2016 16:29:39 +0000 Subject: [PATCH 25/32] bitcoinconsensus: add method that accepts amount, and return error when verify_script receives VERIFY_WITNESS flag script_tests: always test bitcoinconsensus_verify_script_with_amount if VERIFY_WITNESS isn't set Rename internal method + make it static trim bitcoinconsensus_ prefix Add SERIALIZE_TRANSACTION_WITNESS flag --- src/script/bitcoinconsensus.cpp | 34 ++++++++++++++++++++++++++------- src/script/bitcoinconsensus.h | 11 +++++++++-- src/test/script_tests.cpp | 7 ++++++- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 7c0cc29017981..b3bace56ccfb9 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -69,29 +69,49 @@ struct ECCryptoClosure ECCryptoClosure instance_of_eccryptoclosure; } -int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, uint64_t amount, +static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { try { - TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); + TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS, txTo, txToLen); CTransaction tx; stream >> tx; if (nIn >= tx.vin.size()) return set_error(err, bitcoinconsensus_ERR_TX_INDEX); - if (tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) != txToLen) + if (tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_WITNESS) != txToLen) return set_error(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH); - // Regardless of the verification result, the tx did not error. - set_error(err, bitcoinconsensus_ERR_OK); + // Regardless of the verification result, the tx did not error. + set_error(err, bitcoinconsensus_ERR_OK); - CAmount am(amount); - return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, am), NULL); + return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount), NULL); } catch (const std::exception&) { return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing } } +int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, uint64_t amount, + const unsigned char *txTo , unsigned int txToLen, + unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) +{ + CAmount am(amount); + return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err); +} + + +int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, + const unsigned char *txTo , unsigned int txToLen, + unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) +{ + if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) { + return set_error(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED); + } + + CAmount am(0); + return ::verify_script(scriptPubKey, scriptPubKeyLen, am, txTo, txToLen, nIn, flags, err); +} + unsigned int bitcoinconsensus_version() { // Just use the API version for now diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index 40b396a880bbd..f0ffd37a75db4 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -33,7 +33,7 @@ extern "C" { #endif -#define BITCOINCONSENSUS_API_VER 0 +#define BITCOINCONSENSUS_API_VER 1 typedef enum bitcoinconsensus_error_t { @@ -41,6 +41,7 @@ typedef enum bitcoinconsensus_error_t bitcoinconsensus_ERR_TX_INDEX, bitcoinconsensus_ERR_TX_SIZE_MISMATCH, bitcoinconsensus_ERR_TX_DESERIALIZE, + bitcoinconsensus_ERR_AMOUNT_REQUIRED, } bitcoinconsensus_error; /** Script verification flags */ @@ -50,13 +51,19 @@ enum bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 10), // enable WITNESS (BIP141) + }; /// Returns 1 if the input nIn of the serialized transaction pointed to by /// txTo correctly spends the scriptPubKey pointed to by scriptPubKey under /// the additional constraints specified by flags. /// If not NULL, err will contain an error/success code for the operation -EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, uint64_t amount, +EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, + const unsigned char *txTo , unsigned int txToLen, + unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); + +EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, uint64_t amount, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 2415345b63bf1..332dcb9df2d12 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -102,7 +102,12 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, int flags, bo #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << tx2; - BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); + if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) { + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); + } else { + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), 0, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); + } #endif } From b002a8c1f06c4424591de2f3491f50b75df91aa0 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Sun, 17 Jan 2016 21:41:42 -0500 Subject: [PATCH 26/32] Increase MAX_PROTOCOL_MESSAGE_LENGTH Witness blocks can be greater than 2MiB, but cannot be validly greater than 4MB. --- src/net.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net.h b/src/net.h index bda8d6ba149bc..ef2c3c3160ba9 100644 --- a/src/net.h +++ b/src/net.h @@ -43,8 +43,8 @@ static const int TIMEOUT_INTERVAL = 20 * 60; static const unsigned int MAX_INV_SZ = 50000; /** The maximum number of new addresses to accumulate before announcing. */ static const unsigned int MAX_ADDR_TO_SEND = 1000; -/** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */ -static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; +/** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */ +static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000; /** Maximum length of strSubVer in `version` message */ static const unsigned int MAX_SUBVERSION_LENGTH = 256; /** -listen default */ From 590d715524f2672bee268691eda0b614b2e9d726 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 1 Feb 2016 17:03:35 +0100 Subject: [PATCH 27/32] Temporary: still send/receive 'havewitness' to/from old nodes --- src/main.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 83137c3d020c5..6ab82f3468602 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4591,6 +4591,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->PushMessage(NetMsgType::SENDHEADERS); } + // Temporary hack to make old segnet nodes not fail (needed because we switched to a service bit instead) + if (pfrom->nVersion >= WITNESS_VERSION) { + pfrom->PushMessage("havewitness"); + } } @@ -4666,6 +4670,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, State(pfrom->GetId())->fPreferHeaders = true; } + // Temporary hack to make old segnet nodes not fail (needed because we switched to a service bit instead) + else if (strCommand == "havewitness") + { + LOCK(cs_main); + State(pfrom->GetId())->fHaveWitness = true; + } + else if (strCommand == NetMsgType::INV) { vector vInv; From 99c8d540373cfdf4c202a18db00cf5b1c84916e9 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 16 Mar 2016 17:51:03 +0100 Subject: [PATCH 28/32] [HACK] Very temporary fix for reindex failure --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6ab82f3468602..83b7bf7959d82 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3877,7 +3877,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB int nLoaded = 0; try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor - CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS); + CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE*4, 4*MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION | SERIALIZE_TRANSACTION_WITNESS); uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); @@ -3896,7 +3896,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB continue; // read size blkdat >> nSize; - if (nSize < 80 || nSize > MAX_BLOCK_SIZE) + if (nSize < 80 || nSize > 4*MAX_BLOCK_SIZE) continue; } catch (const std::exception&) { // no valid block header found; don't complain From cd943d63d59bd2c024d5e95b81cf97bb4f6b3c22 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Tue, 15 Mar 2016 19:02:06 +1000 Subject: [PATCH 29/32] Refactor Witness commitment checks Break out witness commit position search and witness commitment and nonce check into separate functions. --- src/main.cpp | 67 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 83b7bf7959d82..3f333ca0e8711 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3063,14 +3063,48 @@ bool IsWitnessEnabled(const CBlock& block, const CBlockIndex* pindexPrev, const } -void UpdateUncommitedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +bool CheckWitnessCommitAndNonce(const CBlock& block, int commitpos, CValidationState& state) +{ + bool malleated = false; + uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated); + // The malleation check is ignored; as the transaction tree itself + // already does not permit it, it is impossible to trigger in the + // witness tree. + + if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) { + return state.DoS(100, error("%s : invalid witness commitment size", __func__), REJECT_INVALID, "bad-witness-merkle-size", true); + } + + CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin()); + if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) { + return state.DoS(100, error("%s : witness merkle commitment mismatch", __func__), REJECT_INVALID, "bad-witness-merkle-match", true); + } + + return true; +} + +int FindWitnessCommitPos(const CBlock& block) { int commitpos = -1; for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { - if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) { + if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 + && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN + && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 + && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa + && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 + && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 + && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) + { commitpos = o; } } + return commitpos; +} + + +void UpdateUncommitedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) +{ + int commitpos = FindWitnessCommitPos(block); static const std::vector nonce(32, 0x00); if (commitpos != -1 && IsWitnessEnabled(block, pindexPrev, consensusParams) && block.vtx[0].wit.IsEmpty()) { block.vtx[0].wit.vtxinwit.resize(1); @@ -3082,12 +3116,7 @@ void UpdateUncommitedBlockStructures(CBlock& block, const CBlockIndex* pindexPre std::vector GenerateCoinbaseCommitment(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) { std::vector commitment; - int commitpos = -1; - for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { - if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) { - commitpos = o; - } - } + int commitpos = FindWitnessCommitPos(block); bool fHaveWitness = false; for (size_t t = 1; t < block.vtx.size(); t++) { if (!block.vtx[t].wit.IsNull()) { @@ -3192,26 +3221,12 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256(witness root, witness nonce). In case there are // multiple, the last one is used. if (IsWitnessEnabled(block, pindexPrev, consensusParams)) { - int commitpos = -1; - for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { - if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) { - commitpos = o; - } - } + int commitpos = FindWitnessCommitPos(block); if (commitpos != -1) { - bool malleated = false; - uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated); - // The malleation check is ignored; as the transaction tree itself - // already does not permit it, it is impossible to trigger in the - // witness tree. - if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) { - return state.DoS(100, error("%s : invalid witness commitment size", __func__), REJECT_INVALID, "bad-witness-merkle-size", true); - } - CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin()); - if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) { - return state.DoS(100, error("%s : witness merkle commitment mismatch", __func__), REJECT_INVALID, "bad-witness-merkle-match", true); + if (!CheckWitnessCommitAndNonce(block, commitpos, state)) { + return false; } - + // After the coinbase witness nonce and commitment are verified, // we can check if the virtual size passes (before we've checked // the coinbase witness, it would be possible for the virtual From ffe4700bdec617085db681c482d16dcbfa5a46f1 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Tue, 15 Mar 2016 22:45:36 +1000 Subject: [PATCH 30/32] Rename BlockWitnessMerkleRoot to BlockTxWitnessMerkleRoot --- src/consensus/merkle.cpp | 2 +- src/consensus/merkle.h | 2 +- src/main.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index b10f1bae51adf..f03456350bfa7 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -161,7 +161,7 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated) return ComputeMerkleRoot(leaves, mutated); } -uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated) +uint256 BlockTxWitnessMerkleRoot(const CBlock& block, bool* mutated) { std::vector leaves; leaves.resize(block.vtx.size()); diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h index 194aea9b75dc3..34dc34318d669 100644 --- a/src/consensus/merkle.h +++ b/src/consensus/merkle.h @@ -26,7 +26,7 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated = NULL); * Compute the Merkle root of the witness transactions in a block. * *mutated is set to true if a duplicated subtree was found. */ -uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated = NULL); +uint256 BlockTxWitnessMerkleRoot(const CBlock& block, bool* mutated = NULL); /* * Compute the Merkle branch for the tree of transactions in a block, for a diff --git a/src/main.cpp b/src/main.cpp index 3f333ca0e8711..0fdcb6bef3cc2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3066,7 +3066,7 @@ bool IsWitnessEnabled(const CBlock& block, const CBlockIndex* pindexPrev, const bool CheckWitnessCommitAndNonce(const CBlock& block, int commitpos, CValidationState& state) { bool malleated = false; - uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated); + uint256 hashWitness = BlockTxWitnessMerkleRoot(block, &malleated); // The malleation check is ignored; as the transaction tree itself // already does not permit it, it is impossible to trigger in the // witness tree. @@ -3127,7 +3127,7 @@ std::vector GenerateCoinbaseCommitment(CBlock& block, const CBloc std::vector ret(32, 0x00); if (fHaveWitness && IsWitnessEnabled(block, pindexPrev, consensusParams)) { if (commitpos == -1) { - uint256 witnessroot = BlockWitnessMerkleRoot(block, NULL); + uint256 witnessroot = BlockTxWitnessMerkleRoot(block, NULL); CHash256().Write(witnessroot.begin(), 32).Write(&ret[0], 32).Finalize(witnessroot.begin()); CTxOut out; out.nValue = 0; From 0a25cca0097934a00bfe6020d9c039ea57c20ee1 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Wed, 16 Mar 2016 00:05:42 +1000 Subject: [PATCH 31/32] Reconsider coinbase witness commitment Rather than having the coinbase witness commitment be the hash of the concatenation of the transaction witness merkle root and a single 32-byte nonce from the coinbase witness, treat it instead as the merkle root of a tree with the leaves being the transaction witness merkle root, and each of the items from the coinbase stack. For the case where the coinbase witness stack contains a single, 32-byte item this change has no effect, however it should generalise well to allow the coinbase witness stack to contain an arbitrary number of elements. (If an element on the coinbase stack is not already exactly 32 bytes, it is first hashed before being added as a leaf to the merkle tree) --- src/consensus/merkle.cpp | 19 +++++++++++++++++++ src/consensus/merkle.h | 6 ++++++ src/main.cpp | 11 +++++------ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index f03456350bfa7..d9706f6f4bfbf 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -172,6 +172,25 @@ uint256 BlockTxWitnessMerkleRoot(const CBlock& block, bool* mutated) return ComputeMerkleRoot(leaves, mutated); } +uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* pmutated) +{ + const CScriptWitness& cbsw = block.vtx[0].wit.vtxinwit[0].scriptWitness; + std::vector leaves; + leaves.resize(cbsw.stack.size() + 1); + leaves[0] = BlockTxWitnessMerkleRoot(block, pmutated); + for (size_t s = 0; s < cbsw.stack.size(); s++) { + if (cbsw.stack[s].size() == 32) { + // dumb idea, but should make it backwards compatible with segnet3 + leaves[s+1] = uint256(cbsw.stack[s]); + } else { + leaves[s+1] = Hash(cbsw.stack[s].begin(), cbsw.stack[s].end()); + } + } + if (pmutated && *pmutated) + pmutated = NULL; + return ComputeMerkleRoot(leaves, pmutated); +} + std::vector BlockMerkleBranch(const CBlock& block, uint32_t position) { std::vector leaves; diff --git a/src/consensus/merkle.h b/src/consensus/merkle.h index 34dc34318d669..dd0ee37280cfa 100644 --- a/src/consensus/merkle.h +++ b/src/consensus/merkle.h @@ -28,6 +28,12 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated = NULL); */ uint256 BlockTxWitnessMerkleRoot(const CBlock& block, bool* mutated = NULL); +/* + * Compute the Merkle root of the coinbase transaction's witness stack. + * *mutated is set to true if a duplicated subtree was found. + */ +uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated = NULL); + /* * Compute the Merkle branch for the tree of transactions in a block, for a * given position. diff --git a/src/main.cpp b/src/main.cpp index 0fdcb6bef3cc2..22c2a1fc4221c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3065,17 +3065,16 @@ bool IsWitnessEnabled(const CBlock& block, const CBlockIndex* pindexPrev, const bool CheckWitnessCommitAndNonce(const CBlock& block, int commitpos, CValidationState& state) { + if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) { + return state.DoS(100, error("%s : invalid witness commitment size", __func__), REJECT_INVALID, "bad-witness-merkle-size", true); + } + bool malleated = false; - uint256 hashWitness = BlockTxWitnessMerkleRoot(block, &malleated); + uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated); // The malleation check is ignored; as the transaction tree itself // already does not permit it, it is impossible to trigger in the // witness tree. - if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) { - return state.DoS(100, error("%s : invalid witness commitment size", __func__), REJECT_INVALID, "bad-witness-merkle-size", true); - } - - CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin()); if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) { return state.DoS(100, error("%s : witness merkle commitment mismatch", __func__), REJECT_INVALID, "bad-witness-merkle-match", true); } From 86c55b04b2b68d1d0dae78af5a1dd83aec10af62 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Wed, 16 Mar 2016 00:19:51 +1000 Subject: [PATCH 32/32] Remove restrictions on coinbase witness This relaxes the restriction on the coinbase witness that limited it to only having one entry that must be 32 bytes; instead there may be up to 255 items on the coinbase witness stack, and they may be any length. In order to have multiple entries on the coinbase witness stack, the number of entries must be specified in the coinbase commitment, by changing the witness commitment from a 36 byte push: OP_RETURN [ 0xaa 0x21 0xa9 0xed HASH ] to a 37 byte push: OP_RETURN [ 0xaa 0x21 0xa9 0xed HASH N ] where N is the number of entries on the coinbase witness stack (from 0 to 255). --- src/main.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 22c2a1fc4221c..514d6707e2caf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3063,9 +3063,17 @@ bool IsWitnessEnabled(const CBlock& block, const CBlockIndex* pindexPrev, const } -bool CheckWitnessCommitAndNonce(const CBlock& block, int commitpos, CValidationState& state) +bool CheckWitnessCommit(const CBlock& block, int commitpos, CValidationState& state) { - if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) { + size_t stack_size; + + if (block.vtx[0].vout[commitpos].scriptPubKey[1] == 0x24) { + stack_size = 1; + } else { + stack_size = block.vtx[0].vout[commitpos].scriptPubKey[38]; + } + + if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != stack_size) { return state.DoS(100, error("%s : invalid witness commitment size", __func__), REJECT_INVALID, "bad-witness-merkle-size", true); } @@ -3088,7 +3096,9 @@ int FindWitnessCommitPos(const CBlock& block) for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN - && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 + && (block.vtx[0].vout[o].scriptPubKey[1] == 0x24 + || (block.vtx[0].vout[o].scriptPubKey[1] == 0x25 + && block.vtx[0].vout[o].scriptPubKey.size() >= 39)) && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 @@ -3222,7 +3232,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn if (IsWitnessEnabled(block, pindexPrev, consensusParams)) { int commitpos = FindWitnessCommitPos(block); if (commitpos != -1) { - if (!CheckWitnessCommitAndNonce(block, commitpos, state)) { + if (!CheckWitnessCommit(block, commitpos, state)) { return false; }