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/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() diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 2c502ead315f8..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,17 +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; - 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, amount, 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); - } - if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i))) + BOOST_FOREACH(const CTransaction& txv, txVariants) + 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, amount))) fComplete = false; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 9cf99492c912e..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; @@ -209,6 +211,59 @@ 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.SegWitHeight = 0; + 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 = std::vector(pnSeed6_seg, pnSeed6_seg + ARRAYLEN(pnSeed6_seg)); + + fMiningRequiresPeers = true; + fDefaultConsistencyChecks = false; + fRequireStandard = false; + fMineBlocksOnDemand = false; + fTestnetToBeDeprecatedFieldRPC = true; + + // checkpointData is empty + } +}; +static CSegNetParams segNetParams; + /** * Regression test */ @@ -223,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; @@ -279,6 +335,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; } 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}, diff --git a/src/consensus/merkle.cpp b/src/consensus/merkle.cpp index 9a8afa8a33f3b..d9706f6f4bfbf 100644 --- a/src/consensus/merkle.cpp +++ b/src/consensus/merkle.cpp @@ -161,6 +161,36 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated) return ComputeMerkleRoot(leaves, mutated); } +uint256 BlockTxWitnessMerkleRoot(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); +} + +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 6ef59745ac653..dd0ee37280cfa 100644 --- a/src/consensus/merkle.h +++ b/src/consensus/merkle.h @@ -22,6 +22,18 @@ uint256 ComputeMerkleRootFromBranch(const uint256& leaf, const std::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 5b27698d8b96a..514d6707e2caf 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; } }; @@ -738,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; +} @@ -752,7 +768,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"); @@ -833,6 +849,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C if (fRequireStandard && !IsStandardTx(tx, reason)) return state.DoS(0, false, REJECT_NONSTANDARD, reason); + // Don't accept witness transactions before the final threshold passes + 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"); + } + // 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. @@ -938,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; @@ -1173,10 +1199,21 @@ 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, 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(); + } return false; + } // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause @@ -1247,7 +1284,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 +1343,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 +1366,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()); @@ -1566,7 +1603,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, amount, cacheStore), &error)) { return false; } return true; @@ -1703,7 +1741,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 +1767,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()) @@ -2049,6 +2087,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); @@ -2059,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()); @@ -2079,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"); } @@ -2107,7 +2154,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); @@ -2957,6 +3004,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) @@ -3008,6 +3057,106 @@ 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)); +} + + +bool CheckWitnessCommit(const CBlock& block, int commitpos, CValidationState& state) +{ + 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); + } + + 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 (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[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 + && 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); + 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 = FindWitnessCommitPos(block); + 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 = BlockTxWitnessMerkleRoot(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(); @@ -3036,6 +3185,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; } @@ -3066,6 +3221,42 @@ 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 = FindWitnessCommitPos(block); + if (commitpos != -1) { + if (!CheckWitnessCommit(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 + // 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; + } + } + 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; } @@ -3159,7 +3350,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; @@ -3424,7 +3615,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(); @@ -3679,7 +3870,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 +3901,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*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(); @@ -3729,7 +3920,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 @@ -4069,6 +4260,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 +4279,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 +4304,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 +4346,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 +4382,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 +4409,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; } } @@ -4236,6 +4428,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(); @@ -4323,6 +4520,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())); @@ -4411,6 +4614,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // nodes) 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"); + } } @@ -4486,6 +4694,12 @@ 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) { @@ -4509,7 +4723,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); @@ -4517,6 +4731,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)) { @@ -4531,7 +4749,11 @@ 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; + } 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). @@ -4689,7 +4911,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); @@ -4777,8 +4999,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 @@ -4807,8 +5031,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); } @@ -4875,7 +5102,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); } @@ -4897,7 +5125,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); @@ -4918,7 +5146,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 +5771,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; @@ -5619,10 +5847,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(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) { diff --git a/src/main.h b/src/main.h index 9fd97d2126823..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); @@ -415,6 +417,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/net.cpp b/src/net.cpp index 2f60cfa1ca11b..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] = {}; @@ -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..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 */ @@ -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/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/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/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 0e93399c08e47..44ae695c5509d 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; @@ -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. @@ -157,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 aea96d8a124ce..01a344006ece3 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,7 +142,14 @@ 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; } + +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 8bd6d00e2eba2..6e5ed8879dc7e 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. */ @@ -181,7 +309,6 @@ class CTransaction private: /** Memory only. */ const uint256 hash; - void UpdateHash() const; public: static const int32_t CURRENT_VERSION=1; @@ -194,6 +321,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 +336,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 +350,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 @@ -252,6 +380,8 @@ class CTransaction } std::string ToString() const; + + void UpdateHash() const; }; /** A mutable version of CTransaction. */ @@ -260,6 +390,7 @@ struct CMutableTransaction int32_t nVersion; std::vector vin; std::vector vout; + CTxWitness wit; uint32_t nLockTime; CMutableTransaction(); @@ -269,11 +400,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 @@ -282,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/protocol.cpp b/src/protocol.cpp index c1c7c0b96bf7c..267450f752384 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -37,14 +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 -}; - /** All known message types. Keep this in the same order as the list of * messages above and in protocol.h. */ @@ -70,7 +62,7 @@ const static std::string allNetMessageTypes[] = { NetMsgType::FILTERADD, NetMsgType::FILTERCLEAR, NetMsgType::REJECT, - NetMsgType::SENDHEADERS + NetMsgType::SENDHEADERS, }; const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); @@ -159,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; } @@ -180,14 +168,20 @@ 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 { - 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 c8b8d20eaddd5..05f939f17fd60 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -238,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 @@ -281,6 +284,22 @@ class CAddress : public CService unsigned int nTime; }; +/** getdata message types */ +const uint32_t MSG_WITNESS_FLAG = 1 << 30; +const uint32_t MSG_TYPE_MASK = 0xffffffff >> 2; +enum GetDataMsg +{ + 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, + 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,12 +329,4 @@ class CInv uint256 hash; }; -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. - MSG_FILTERED_BLOCK, -}; - #endif // BITCOIN_PROTOCOL_H 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); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index f5b16bc7c49b1..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" @@ -419,7 +424,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/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/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/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index bd51aa0ab018d..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" @@ -559,7 +593,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" @@ -605,7 +640,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 { @@ -697,7 +732,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,18 +790,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; - 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, amount, 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, amount), sigdata, DataFromTransaction(txv, i)); } + + UpdateTransaction(mergedTx, i, sigdata); + 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, amount), &serror)) { TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); } } 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/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 47ad1d08073b9..b3bace56ccfb9 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -69,28 +69,49 @@ struct ECCryptoClosure ECCryptoClosure instance_of_eccryptoclosure; } -int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, +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); - 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, 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 5b8c33c6bf42e..f0ffd37a75db4 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) @@ -31,7 +33,7 @@ extern "C" { #endif -#define BITCOINCONSENSUS_API_VER 0 +#define BITCOINCONSENSUS_API_VER 1 typedef enum bitcoinconsensus_error_t { @@ -39,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 */ @@ -48,6 +51,8 @@ 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 @@ -55,6 +60,10 @@ enum /// 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, + 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/script/interpreter.cpp b/src/script/interpreter.cpp index 265131ae0d581..fa33928f8a479 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; @@ -1156,9 +1216,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); + } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) + if (!EvalScript(stack, scriptPubKey, flags, checker, 1, 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, 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()) { @@ -1166,12 +1284,12 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne } 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()) @@ -1179,6 +1297,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()) { @@ -1198,26 +1335,102 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigne 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()) 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); } + +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 7b34547ffbd7d..b617a5c0762a9 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -74,23 +74,31 @@ 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); -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; } @@ -108,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) : 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; }; @@ -124,10 +133,12 @@ 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); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, 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); + +size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags); #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/script.cpp b/src/script/script.cpp index 9f2809e593756..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()) @@ -231,3 +249,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..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; @@ -619,6 +620,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/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/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 2f4111f786420..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, 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& 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, CScript& scriptSigRet) +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; - 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 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, scriptSigRet)) + if (Sign1(keyID, creator, scriptCode, ret, sigversion)) ++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, int sigversion) { - 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, sigversion); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) + if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) 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, sigversion)); + + case TX_WITNESS_V0_KEYHASH: + ret.push_back(vSolutions[0]); + return true; + + case TX_WITNESS_V0_SCRIPTHASH: + CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin()); + if (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, 0); + 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, 0) && 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, 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(); } + if (P2SH) { + result.push_back(std::vector(subscript.begin(), subscript.end())); + } + sigdata.scriptSig = PushAll(result); + // Test solution - return VerifyScript(scriptSig, fromPubKey, 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) +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) { assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; CTransaction txToConst(txTo); - TransactionSignatureCreator creator(&keystore, &txToConst, nIn, nHashType); + TransactionSignatureCreator creator(&keystore, &txToConst, nIn, amount, 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) @@ -144,20 +226,12 @@ 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); -} - -static CScript PushAll(const vector& values) -{ - CScript result; - BOOST_FOREACH(const valtype& v, values) - result << v; - return result; + return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, txout.nValue, nHashType); } -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) + const vector& sigs1, const vector& sigs2, int sigversion) { // Combine all the signatures we've got: set allsigs; @@ -185,7 +259,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const BaseSignatureC 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; @@ -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(), 0); + } + + 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, int sigversion) { 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, sigversion); + 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, sigversion)); + 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, 1); + 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), 0).Output(); } namespace { @@ -284,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; } @@ -297,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 47a9cde7f4304..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,20 @@ 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, 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; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, int sigversion) const; +}; + +class MutableTransactionSignatureCreator : public TransactionSignatureCreator { + CTransaction tx; + +public: + 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. */ @@ -48,20 +56,29 @@ 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 { + 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, 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. */ -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/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/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/multisig_tests.cpp b/src/test/multisig_tests.cpp index b65c299adcbaa..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 @@ -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, 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, 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, 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, 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, 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, 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, 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, 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/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/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 28b85e8d290a7..bb447c4f7cd47 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" @@ -45,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, 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 10175ebe8e580..332dcb9df2d12 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -89,15 +89,25 @@ 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 txCredit = BuildCreditingTransaction(scriptPubKey); + CMutableTransaction tx = BuildSpendingTransaction(scriptSig, txCredit); CMutableTransaction tx2 = tx; - BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, 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); + 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 } @@ -236,7 +246,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 { @@ -692,21 +702,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)); } @@ -714,7 +724,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; // @@ -758,18 +768,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, txFrom12.vout[0].nValue), &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, 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, 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, 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)); } @@ -791,54 +801,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, 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, 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, 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, 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, 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, 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, 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, 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, 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)); } @@ -846,6 +856,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++) @@ -862,62 +873,62 @@ 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, amount), 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, amount), SignatureData(scriptSig), empty); + BOOST_CHECK(combined.scriptSig == 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, txTo, 0, scriptSigCopy, scriptSig); - BOOST_CHECK(combined == scriptSigCopy || combined == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), 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, amount), SignatureData(scriptSig), empty); + BOOST_CHECK(combined.scriptSig == 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, txTo, 0, scriptSigCopy, scriptSig); - BOOST_CHECK(combined == scriptSigCopy || combined == 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 << 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, amount), SignatureData(scriptSigCopy), SignatureData(scriptSig)); + BOOST_CHECK(combined.scriptSig == scriptSig); + 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, 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, amount), SignatureData(scriptSig), empty); + BOOST_CHECK(combined.scriptSig == scriptSig); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), empty, SignatureData(scriptSig)); + BOOST_CHECK(combined.scriptSig == scriptSig); // 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); @@ -933,22 +944,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, amount), SignatureData(partial1a), SignatureData(partial1b)); + BOOST_CHECK(combined.scriptSig == partial1a); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1a), SignatureData(partial2a)); + BOOST_CHECK(combined.scriptSig == complete12); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial1a)); + BOOST_CHECK(combined.scriptSig == complete12); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial1b), SignatureData(partial2b)); + BOOST_CHECK(combined.scriptSig == complete12); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial1b)); + BOOST_CHECK(combined.scriptSig == complete13); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial2a), SignatureData(partial3a)); + BOOST_CHECK(combined.scriptSig == complete23); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial2b)); + BOOST_CHECK(combined.scriptSig == complete23); + combined = CombineSignatures(scriptPubKey, MutableTransactionSignatureChecker(&txTo, 0, amount), SignatureData(partial3b), SignatureData(partial3a)); + BOOST_CHECK(combined.scriptSig == partial3c); } BOOST_AUTO_TEST_CASE(script_standard_push) @@ -958,7 +969,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 +978,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/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/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; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index c27f194b551bb..934fc2a722e51 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); @@ -148,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], - verify_flags, TransactionSignatureChecker(&tx, i), &err), + NULL, verify_flags, TransactionSignatureChecker(&tx, i, amount), &err), strTest); BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err)); } @@ -223,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], - 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)); @@ -312,6 +319,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, output.vout[0].nValue), &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(), 0); + 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, output1.vout[0].nValue), 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, 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); + + // 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, 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); + + // 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, 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); +} + BOOST_AUTO_TEST_CASE(test_IsStandard) { LOCK(cs_main); 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; 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 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/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 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/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 { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index be70240a6cbae..8d21f6a5ed337 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, coin.first->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata); + UpdateTransaction(txNew, nIn, sigdata); + } else + signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata); if (!signSuccess) { @@ -2177,12 +2178,13 @@ 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) { 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; } 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) { }