diff --git a/README.md b/README.md index fbaf17ad..18712747 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Zclassic 1.0.8 +Zclassic 1.0.8-2 ============== What is Zclassic? diff --git a/configure.ac b/configure.ac index cb9db1fa..1f879470 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 1) define(_CLIENT_VERSION_MINOR, 0) define(_CLIENT_VERSION_REVISION, 8) -define(_CLIENT_VERSION_BUILD, 50) +define(_CLIENT_VERSION_BUILD, 51) define(_ZC_BUILD_VAL, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, m4_incr(_CLIENT_VERSION_BUILD), m4_eval(_CLIENT_VERSION_BUILD < 50), 1, m4_eval(_CLIENT_VERSION_BUILD - 24), m4_eval(_CLIENT_VERSION_BUILD == 50), 1, , m4_eval(_CLIENT_VERSION_BUILD - 50))) define(_CLIENT_VERSION_SUFFIX, m4_if(m4_eval(_CLIENT_VERSION_BUILD < 25), 1, _CLIENT_VERSION_REVISION-beta$1, m4_eval(_CLIENT_VERSION_BUILD < 50), 1, _CLIENT_VERSION_REVISION-rc$1, m4_eval(_CLIENT_VERSION_BUILD == 50), 1, _CLIENT_VERSION_REVISION, _CLIENT_VERSION_REVISION-$1))) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 90c21be4..816893d9 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,5 +1,5 @@ --- -name: "zcash-1.0.8" +name: "zcash-1.0.8-2" enable_cache: true distro: "debian" suites: diff --git a/doc/man/zcash-tx.1 b/doc/man/zcash-tx.1 index 4bd1b6b1..8e7b2c91 100644 --- a/doc/man/zcash-tx.1 +++ b/doc/man/zcash-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH ZCASH-TX "1" "March 2017" "zcash-tx v1.0.8" "User Commands" +.TH ZCASH-TX "1" "March 2017" "zcash-tx v1.0.8-2" "User Commands" .SH NAME -zcash-tx \- manual page for zcash-tx v1.0.8 +zcash-tx \- manual page for zcash-tx v1.0.8-2 .SH DESCRIPTION -Zcash zcash\-tx utility version v1.0.8 +Zcash zcash\-tx utility version v1.0.8-2 .SS "Usage:" .TP zcash\-tx [options] [commands] diff --git a/doc/man/zcashd.1 b/doc/man/zcashd.1 index e0851518..3c2013ba 100644 --- a/doc/man/zcashd.1 +++ b/doc/man/zcashd.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH ZCASHD "1" "March 2017" "zcashd v1.0.8" "User Commands" +.TH ZCASHD "1" "March 2017" "zcashd v1.0.8-2" "User Commands" .SH NAME -zcashd \- manual page for zcashd v1.0.8 +zcashd \- manual page for zcashd v1.0.8-2 .SH DESCRIPTION -Zcash Daemon version v1.0.8 +Zcash Daemon version v1.0.8-2 .PP In order to ensure you are adequately protecting your privacy when using Zclassic, please see . diff --git a/src/Makefile.am b/src/Makefile.am index 9963f285..297ddcb4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -176,6 +176,7 @@ BITCOIN_CORE_H = \ utilstrencodings.h \ utiltime.h \ validationinterface.h \ + versionbits.h \ version.h \ wallet/asyncrpcoperation_sendmany.h \ wallet/crypter.h \ @@ -232,6 +233,7 @@ libbitcoin_server_a_SOURCES = \ txdb.cpp \ txmempool.cpp \ validationinterface.cpp \ + versionbits.cpp \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) @@ -426,8 +428,8 @@ zcash_cli_LDADD = \ # # zcash-tx binary # -zcash_tx_SOURCES = bitcoin-tx.cpp -zcash_tx_CPPFLAGS = $(BITCOIN_INCLUDES) +zcash_tx_SOURCES = bitcoin-tx.cpp script/standard.cpp +zcash_tx_CPPFLAGS = $(BITCOIN_INCLUDES) -DBITCOIN_TX zcash_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) if TARGET_WINDOWS diff --git a/src/Makefile.gtest.include b/src/Makefile.gtest.include index e8cb48a8..d06d23da 100644 --- a/src/Makefile.gtest.include +++ b/src/Makefile.gtest.include @@ -5,6 +5,7 @@ noinst_PROGRAMS += zcash-gtest # test_checktransaction.cpp MUST be before # any test that calls SelectParams(). zcash_gtest_SOURCES = \ + script/standard.cpp \ gtest/main.cpp \ gtest/utils.cpp \ gtest/test_checktransaction.cpp \ @@ -40,7 +41,7 @@ zcash_gtest_SOURCES += \ wallet/gtest/test_wallet.cpp endif -zcash_gtest_CPPFLAGS = -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES) +zcash_gtest_CPPFLAGS = -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -DSTATIC $(BITCOIN_INCLUDES) -DBITCOIN_TX zcash_gtest_LDADD = -lgtest -lgmock $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 1fbf53e9..d146e261 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -35,15 +35,16 @@ JSON_TEST_FILES = \ test/data/g1_compressed.json \ test/data/g2_compressed.json -RAW_TEST_FILES = test/data/alertTests.raw +#RAW_TEST_FILES = test/data/alertTests.raw GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) BITCOIN_TESTS =\ + script/standard.cpp \ test/arith_uint256_tests.cpp \ test/bignum.h \ test/addrman_tests.cpp \ - test/alert_tests.cpp \ + #test/alert_tests.cpp \ test/allocator_tests.cpp \ test/base32_tests.cpp \ test/base58_tests.cpp \ @@ -99,7 +100,7 @@ BITCOIN_TESTS += \ endif test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) -test_test_bitcoin_CPPFLAGS = -fopenmp $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS) +test_test_bitcoin_CPPFLAGS = -fopenmp $(BITCOIN_INCLUDES) -I$(builddir)/test/ $(TESTDEFS) $(EVENT_CFLAGS) -DBITCOIN_TX test_test_bitcoin_LDADD = $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) if ENABLE_WALLET diff --git a/src/bloom.cpp b/src/bloom.cpp index de872065..b7a6a959 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -170,7 +170,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) txnouttype type; vector > vSolutions; if (Solver(txout.scriptPubKey, type, vSolutions) && - (type == TX_PUBKEY || type == TX_MULTISIG)) + (type == TX_PUBKEY || type == TX_PUBKEY_REPLAY || type == TX_MULTISIG || type == TX_MULTISIG_REPLAY)) insert(COutPoint(hash, i)); } break; diff --git a/src/chain.h b/src/chain.h index b7e8a917..66d95c3e 100644 --- a/src/chain.h +++ b/src/chain.h @@ -14,8 +14,6 @@ #include -#include - struct CDiskBlockPos { int nFile; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index e0e6221e..f9daf10d 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -50,6 +50,14 @@ public: consensus.nPowMaxAdjustUp = 16; // 16% adjustment up consensus.nPowTargetSpacing = 2.5 * 60; consensus.fPowAllowMinDifficultyBlocks = false; + consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016 + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + + // Deployment of OP_CHECKBLOCKATHEIGHT softfork + consensus.vDeployments[Consensus::DEPLOYMENT_CBAH].bit = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_CBAH].nStartTime = 1493596800; // May 1, 2017 + consensus.vDeployments[Consensus::DEPLOYMENT_CBAH].nTimeout = 1525132800; // May 1, 2018 + /** * The message start string should be awesome! ⓩ❤ */ @@ -126,11 +134,12 @@ public: checkpointData = (Checkpoints::CCheckpointData) { boost::assign::map_list_of ( 0, consensus.hashGenesisBlock) - ( 30000, uint256S("0x000000005c2ad200c3c7c8e627f67b306659efca1268c9bb014335fdadc0c392")), - 1482914970, // * UNIX timestamp of last checkpoint block - 82372, // * total number of transactions between genesis and last checkpoint + ( 30000, uint256S("0x000000005c2ad200c3c7c8e627f67b306659efca1268c9bb014335fdadc0c392")) + ( 96577, uint256S("0x0000000177751545bd1af3ccf276ec2920d258453ab01f3d2f8f7fcc5f3a37b8")), + 1493090861, // * UNIX timestamp of last checkpoint block + 241684, // * total number of transactions between genesis and last checkpoint // (the tx=... number in the SetBestChain debug.log lines) - 1581 // * estimated number of transactions per day after checkpoint + 1441 // * estimated number of transactions per day after checkpoint // total number of tx / (checkpoint block height / (24 * 24)) }; @@ -211,6 +220,14 @@ public: consensus.powLimit = uint256S("07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); consensus.fPowAllowMinDifficultyBlocks = true; + consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + + // Deployment of OP_CHECKBLOCKATHEIGHT softfork + consensus.vDeployments[Consensus::DEPLOYMENT_CBAH].bit = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_CBAH].nStartTime = 1493596800; // May 1, 2017 + consensus.vDeployments[Consensus::DEPLOYMENT_CBAH].nTimeout = 1525132800; // May 1, 2018 + pchMessageStart[0] = 0xfa; pchMessageStart[1] = 0x1a; pchMessageStart[2] = 0xf9; @@ -302,6 +319,13 @@ public: assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up + consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains + + // Deployment of OP_CHECKBLOCKATHEIGHT softfork + consensus.vDeployments[Consensus::DEPLOYMENT_CBAH].bit = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_CBAH].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_CBAH].nTimeout = 999999999999ULL; + pchMessageStart[0] = 0xaa; pchMessageStart[1] = 0xe8; pchMessageStart[2] = 0x3f; diff --git a/src/consensus/params.h b/src/consensus/params.h index 9987f2e7..dd56f572 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -7,8 +7,31 @@ #define BITCOIN_CONSENSUS_PARAMS_H #include "uint256.h" +#include +#include namespace Consensus { + +enum DeploymentPos +{ + //DEPLOYMENT_SEGWIT, // Deployment of BIP141, BIP143, and BIP147. + // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp + DEPLOYMENT_CBAH, + MAX_VERSION_BITS_DEPLOYMENTS +}; + +/** + * Struct for each individual consensus rule change using BIP9. + */ +struct BIP9Deployment { + /** Bit position to select the particular bit in nVersion. */ + int bit; + /** Start MedianTime for version bits miner confirmation. Can be a date in the past */ + int64_t nStartTime; + /** Timeout/expiry MedianTime for the deployment attempt. */ + int64_t nTimeout; +}; + /** * Parameters that influence chain consensus. */ @@ -40,6 +63,14 @@ struct Params { int nMajorityEnforceBlockUpgrade; int nMajorityRejectBlockOutdated; int nMajorityWindow; + /** + * Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period, + * (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments. + * Examples: 1916 for 95%, 1512 for testchains. + */ + uint32_t nRuleChangeActivationThreshold; + uint32_t nMinerConfirmationWindow; + BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS]; /** Proof of work parameters */ uint256 powLimit; bool fPowAllowMinDifficultyBlocks; diff --git a/src/init.cpp b/src/init.cpp index da6d4482..fe0835fd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -461,7 +461,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); strUsage += HelpMessageOpt("-blockprioritysize=", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); if (GetBoolArg("-help-debug", false)) - strUsage += HelpMessageOpt("-blockversion=", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION)); + strUsage += HelpMessageOpt("-blockversion=", "Override block version to test forking scenarios"); #ifdef ENABLE_MINING strUsage += HelpMessageGroup(_("Mining options:")); diff --git a/src/main.cpp b/src/main.cpp index 1a1743b7..7e8760fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,7 @@ #include "util.h" #include "utilmoneystr.h" #include "validationinterface.h" +#include "versionbits.h" #include "wallet/asyncrpcoperation_sendmany.h" #include @@ -2022,6 +2023,51 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const } } +// Protected by cs_main +VersionBitsCache versionbitscache; + +int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + int32_t nVersion = VERSIONBITS_TOP_BITS; + + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { + ThresholdState state = VersionBitsState(pindexPrev, params, (Consensus::DeploymentPos)i, versionbitscache); + if (state == THRESHOLD_LOCKED_IN || state == THRESHOLD_STARTED) { + nVersion |= VersionBitsMask(params, (Consensus::DeploymentPos)i); + } + } + + return nVersion; +} + +/** + * Threshold condition checker that triggers when unknown versionbits are seen on the network. + */ +class WarningBitsConditionChecker : public AbstractThresholdConditionChecker +{ +private: + int bit; + +public: + WarningBitsConditionChecker(int bitIn) : bit(bitIn) {} + + int64_t BeginTime(const Consensus::Params& params) const { return 0; } + int64_t EndTime(const Consensus::Params& params) const { return std::numeric_limits::max(); } + int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + { + return ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && + ((pindex->nVersion >> bit) & 1) != 0 && + ((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0; + } +}; + +// Protected by cs_main +static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; + static int64_t nTimeVerify = 0; static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; @@ -2090,6 +2136,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; } + // Start enforcing OP_CHECKBLOCKATHEIGHT rules using versionbits logic. + if (VersionBitsState(pindex->pprev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_CBAH, versionbitscache) == THRESHOLD_ACTIVE) { + flags |= SCRIPT_VERIFY_CHECKBLOCKATHEIGHT; + } + CBlockUndo blockundo; CCheckQueueControl control(fExpensiveChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); @@ -2378,24 +2429,42 @@ void static UpdateTip(CBlockIndex *pindexNew) { // Check the version of the last 100 blocks to see if we need to upgrade: static bool fWarned = false; - if (!IsInitialBlockDownload() && !fWarned) + if (!IsInitialBlockDownload()) { int nUpgraded = 0; const CBlockIndex* pindex = chainActive.Tip(); + for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) { + WarningBitsConditionChecker checker(bit); + ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]); + if (state == THRESHOLD_ACTIVE || state == THRESHOLD_LOCKED_IN) { + if (state == THRESHOLD_ACTIVE) { + strMiscWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit); + if (!fWarned) { + CAlert::Notify(strMiscWarning, true); + fWarned = true; + } + } else { + LogPrintf("%s: unknown new rules are about to activate (versionbit %i)\n", __func__, bit); + } + } + } for (int i = 0; i < 100 && pindex != NULL; i++) { - if (pindex->nVersion > CBlock::CURRENT_VERSION) + int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); + if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) ++nUpgraded; pindex = pindex->pprev; } if (nUpgraded > 0) - LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION); + LogPrintf("%s: %d of last 100 blocks have unexpected version\n", __func__, nUpgraded); if (nUpgraded > 100/2) { // strMiscWarning is read by GetWarnings(), called by the JSON-RPC code to warn the user: - strMiscWarning = _("Warning: This version is obsolete; upgrade required!"); - CAlert::Notify(strMiscWarning, true); - fWarned = true; + strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect"); + if (!fWarned) { + CAlert::Notify(strMiscWarning, true); + fWarned = true; + } } } } @@ -3707,6 +3776,10 @@ void UnloadBlockIndex() setDirtyFileInfo.clear(); mapNodeState.clear(); recentRejects.reset(NULL); + versionbitscache.Clear(); + for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) { + warningcache[b].clear(); + } BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { delete entry.second; @@ -4057,6 +4130,24 @@ void static CheckBlockIndex() assert(nNodes == forward.size()); } +ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + LOCK(cs_main); + return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache); +} + +BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + LOCK(cs_main); + return VersionBitsStatistics(chainActive.Tip(), params, pos); +} + +int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + LOCK(cs_main); + return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, versionbitscache); +} + ////////////////////////////////////////////////////////////////////////////// // // CAlert diff --git a/src/main.h b/src/main.h index 886542db..a95c0793 100644 --- a/src/main.h +++ b/src/main.h @@ -25,6 +25,7 @@ #include "tinyformat.h" #include "txmempool.h" #include "uint256.h" +#include "versionbits.h" #include #include @@ -251,6 +252,14 @@ void PruneAndFlush(); bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectAbsurdFee=false); +/** Get the BIP9 state for a given deployment at the current tip. */ +ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos); + +/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */ +int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos); + +/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */ +BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos); struct CNodeStateStats { int nMisbehavior; @@ -523,6 +532,13 @@ extern CBlockTreeDB *pblocktree; */ int GetSpendHeight(const CCoinsViewCache& inputs); +extern VersionBitsCache versionbitscache; + +/** +* Determine what nVersion a new block should use. +*/ +int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params); + namespace Consensus { bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams); } diff --git a/src/miner.cpp b/src/miner.cpp index a601585c..67023a62 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -149,6 +149,13 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) const int nHeight = pindexPrev->nHeight + 1; pblock->nTime = GetAdjustedTime(); const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); + + pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios + if (chainparams.MineBlocksOnDemand()) + pblock->nVersion = GetArg("-blockversion", pblock->nVersion); + CCoinsViewCache view(pcoinsTip); // Priority order to process transactions diff --git a/src/primitives/block.h b/src/primitives/block.h index 6b3f13a8..7bc035eb 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -22,7 +22,6 @@ class CBlockHeader public: // header static const size_t HEADER_SIZE=4+32+32+32+4+4+32; // excluding Equihash solution - static const int32_t CURRENT_VERSION=4; int32_t nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; @@ -54,7 +53,7 @@ public: void SetNull() { - nVersion = CBlockHeader::CURRENT_VERSION; + nVersion = 0; hashPrevBlock.SetNull(); hashMerkleRoot.SetNull(); hashReserved.SetNull(); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 939430be..3c51d8c0 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -634,6 +634,38 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* return rv; } +static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +{ + UniValue rv(UniValue::VOBJ); + const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id); + switch (thresholdState) { + case THRESHOLD_DEFINED: rv.push_back(Pair("status", "defined")); break; + case THRESHOLD_STARTED: rv.push_back(Pair("status", "started")); break; + case THRESHOLD_LOCKED_IN: rv.push_back(Pair("status", "locked_in")); break; + case THRESHOLD_ACTIVE: rv.push_back(Pair("status", "active")); break; + case THRESHOLD_FAILED: rv.push_back(Pair("status", "failed")); break; + } + if (THRESHOLD_STARTED == thresholdState) + { + rv.push_back(Pair("bit", consensusParams.vDeployments[id].bit)); + } + rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime)); + rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout)); + rv.push_back(Pair("since", VersionBitsTipStateSinceHeight(consensusParams, id))); + if (THRESHOLD_STARTED == thresholdState) + { + UniValue statsUV(UniValue::VOBJ); + BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id); + statsUV.push_back(Pair("period", statsStruct.period)); + statsUV.push_back(Pair("threshold", statsStruct.threshold)); + statsUV.push_back(Pair("elapsed", statsStruct.elapsed)); + statsUV.push_back(Pair("count", statsStruct.count)); + statsUV.push_back(Pair("possible", statsStruct.possible)); + rv.push_back(Pair("statistics", statsUV)); + } + return rv; +} + UniValue getblockchaininfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -662,8 +694,22 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) " },\n" " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" " }, ...\n" - " ]\n" + " \n" "}\n" + " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n" + " \"xxxx\" : { (string) name of the softfork\n" + " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"lockedin\", \"active\", \"failed\"\n" + " \"bit\": xx, (numeric) the bit, 0-28, in the block version field used to signal this soft fork\n" + " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" + " \"timeout\": xx (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" + " \"since\": xx, (numeric) height of the first block to which the status applies\n" + " \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n" + " \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n" + " \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n" + " \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n" + " \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n" + " \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n" + " }\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "") @@ -688,10 +734,14 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); UniValue softforks(UniValue::VARR); + UniValue bip9_softforks(UniValue::VOBJ); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); + bip9_softforks.push_back(Pair("cbah", BIP9SoftForkDesc(consensusParams, Consensus::DEPLOYMENT_CBAH))); + obj.push_back(Pair("softforks", softforks)); + obj.push_back(Pair("bip9_softforks", bip9_softforks)); if (fPruneMode) { diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 19a6a8f4..8597b405 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -14,6 +14,10 @@ #include "script/script.h" #include "uint256.h" +#include "main.h" +#include +#include + using namespace std; typedef vector valtype; @@ -188,6 +192,40 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) { return true; } +// Generic anti-replay protection using Script +bool CheckBlockIndex(int &txBlockIndex, int blockIndex) +{ + // checks for relative txBlockIndex + if (txBlockIndex < 0) + return false; + // checks for absolute blockIndex + else if (txBlockIndex >= 0) { + int blockDelta = txBlockIndex - blockIndex; + // blockDelta must be negative + if (blockDelta > 0) + return false; + // blockDelta must be greater than 100 but lesser than 262144 + if ((blockDelta > -101) || (blockDelta < -262144) && (blockDelta != 0)) + return false; + // check if txBlockIndex refers to an actual block + if (txBlockIndex > blockIndex) + return false; + return true; + } + + return false; +} + +// Generic anti-replay protection using Script +bool CheckBlockHash(uint256 &txBlockHash, uint256 &blockHash) +{ + // OP_CHECKBLOCKATHEIGHT matches active chain + if (txBlockHash == blockHash) + return true; + + return false; +} + bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) { // Empty signature. Not strictly DER encoded, but allowed to provide a // compact way to provide an invalid signature for use with CHECK(MULTI)SIG @@ -379,7 +417,104 @@ bool EvalScript(vector >& stack, const CScript& script, un break; } - case OP_NOP1: case OP_NOP3: case OP_NOP4: case OP_NOP5: +#ifndef BITCOIN_ZCASHCONSENSUS_H // zen-tx can't process OP_CHECKBLOCKATHEIGHT because it requires an active chain + case OP_CHECKBLOCKATHEIGHT: + { + // we need two objects on the stack + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + valtype vchBlockHash(stacktop(-2)); + valtype vchBlockIndex(stacktop(-1)); + + if ((vchBlockIndex.size() > sizeof(int)) || (vchBlockHash.size() > 32)) + return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT); + + // since we can't access chain data then we treat OP_CHECKBLOCKATHEIGHT as a NOP + popstack(stack); + popstack(stack); + stack.push_back(vchTrue); + } + break; +#else + // Generic anti-replay protection using Script + // https://github.com/luke-jr/bips/blob/bip-noreplay/bip-noreplay.mediawiki + // Author: Luke Dashjr + // Implemented by: @movrcx; See: https://github.com/zencashio/zen/issues/12 + case OP_CHECKBLOCKATHEIGHT: + { + if (!(flags & SCRIPT_VERIFY_CHECKBLOCKATHEIGHT)) { + // not enabled; treat as a NOP5 + if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { + return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS); + } + break; + } + + // we need two objects on the stack + if (stack.size() < 2) + return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); + + CBlockIndex *currentBlock = chainActive.Tip(); + valtype vchBlockHash(stacktop(-2)); + valtype vchBlockIndex(stacktop(-1)); + int txBlockIndex; + uint256 blockHash; + uint256 txBlockHash; + bool fSuccess = false; + + // check for overflow before casting + if ((vchBlockIndex.size() > sizeof(int)) || (vchBlockHash.size() > 32)) + return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT); + else + { + // get txBlockIndex from stack vch + txBlockIndex = *reinterpret_cast(&vchBlockIndex[0]); + + // get txBlockHash from stack vch and convert to hex string + std::string str; + boost::algorithm::hex(vchBlockHash.begin(), vchBlockHash.end(), back_inserter(str)); + boost::algorithm::to_lower(str); + txBlockHash.SetHex(str); + } + + if (currentBlock == NULL) + return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED); + else + { + // relative blockIndex lookups are not permitted + if (txBlockIndex < 0) + return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT); + else if (txBlockIndex >= 0) + blockHash.SetHex(chainActive[txBlockIndex]->GetBlockHash().GetHex()); + + // ensure we are not doing any null comparisons + if (blockHash.IsNull() || txBlockHash.IsNull()) + return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED); + + // check tx against blockchain + fSuccess = ((CheckBlockIndex(txBlockIndex, currentBlock->nHeight)) && (CheckBlockHash(txBlockHash, blockHash))); + } + + // pop OP_CHECKBLOCKATHEIGHT related vars off the stack + popstack(stack); + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); + + if (opcode == OP_CHECKBLOCKATHEIGHT) + { + if (fSuccess) + { + popstack(stack); + } + else + return set_error(serror, SCRIPT_ERR_CHECKBLOCKATHEIGHT); + } + } + break; +#endif + + case OP_NOP1: case OP_NOP3: case OP_NOP4: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: { if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) diff --git a/src/script/interpreter.h b/src/script/interpreter.h index b94916fa..cdfaa79c 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -85,6 +85,10 @@ enum // // See BIP65 for details. SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), + + // Verify OP_CHECKBLOCKATHEIGHT + SCRIPT_VERIFY_CHECKBLOCKATHEIGHT = (1U << 10), + }; uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); diff --git a/src/script/script.cpp b/src/script/script.cpp index fd339247..8109a442 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -138,13 +138,14 @@ const char* GetOpName(opcodetype opcode) case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; + case OP_CHECKBLOCKATHEIGHT : return "OP_CHECKBLOCKATHEIGHT"; // expanson case OP_NOP1 : return "OP_NOP1"; case OP_NOP2 : return "OP_NOP2"; case OP_NOP3 : return "OP_NOP3"; case OP_NOP4 : return "OP_NOP4"; - case OP_NOP5 : return "OP_NOP5"; + //case OP_NOP5 : return "OP_NOP5"; Generic anti-replay protection using Script case OP_NOP6 : return "OP_NOP6"; case OP_NOP7 : return "OP_NOP7"; case OP_NOP8 : return "OP_NOP8"; diff --git a/src/script/script.h b/src/script/script.h index e0e89185..33189c9e 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -162,6 +162,7 @@ enum opcodetype OP_NOP3 = 0xb2, OP_NOP4 = 0xb3, OP_NOP5 = 0xb4, + OP_CHECKBLOCKATHEIGHT = OP_NOP5, OP_NOP6 = 0xb5, OP_NOP7 = 0xb6, OP_NOP8 = 0xb7, diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index f1aa1fb4..f4fa04cf 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -67,6 +67,10 @@ const char* ScriptErrorString(const ScriptError serror) return "NOPx reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: return "Public key is neither compressed or uncompressed"; + case SCRIPT_ERR_CHECKBLOCKATHEIGHT: + return "Transaction failed to pass replay prevention checks"; + case SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED: + return "OP_CHECKBLOCKATHEIGHT cannot be verified with zen-tx"; 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 bb10b8a2..4965e92a 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -51,8 +51,12 @@ typedef enum ScriptError_t /* softfork safeness */ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, + SCRIPT_ERR_ERROR_COUNT, + + /* tx replay prevention */ + SCRIPT_ERR_CHECKBLOCKATHEIGHT, + SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED - SCRIPT_ERR_ERROR_COUNT } ScriptError; #define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 75e6c6d9..169e3635 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -82,9 +82,14 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP case TX_NONSTANDARD: case TX_NULL_DATA: return false; + case TX_NULL_DATA_REPLAY: + return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); return Sign1(keyID, creator, scriptPubKey, scriptSigRet); + case TX_PUBKEY_REPLAY: + keyID = CPubKey(vSolutions[0]).GetID(); + return Sign1(keyID, creator, scriptPubKey, scriptSigRet); case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) @@ -96,12 +101,26 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP scriptSigRet << ToByteVector(vch); } return true; + case TX_PUBKEYHASH_REPLAY: + keyID = CKeyID(uint160(vSolutions[0])); + if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) + return false; + else + { + CPubKey vch; + creator.KeyStore().GetPubKey(keyID, vch); + scriptSigRet << ToByteVector(vch); + } + return true; case TX_SCRIPTHASH: return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet); case TX_MULTISIG: scriptSigRet << OP_0; // workaround CHECKMULTISIG bug return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet)); + case TX_MULTISIG_REPLAY: + scriptSigRet << OP_0; // workaround CHECKMULTISIG bug + return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet)); } return false; } @@ -227,12 +246,23 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatur if (sigs1.size() >= sigs2.size()) return PushAll(sigs1); return PushAll(sigs2); + case TX_NULL_DATA_REPLAY: + // Don't know anything about this, assume bigger one is correct: + if (sigs1.size() >= sigs2.size()) + return PushAll(sigs1); + return PushAll(sigs2); case TX_PUBKEY: + case TX_PUBKEY_REPLAY: case TX_PUBKEYHASH: // Signatures are bigger than placeholders or empty scripts: if (sigs1.empty() || sigs1[0].empty()) return PushAll(sigs2); return PushAll(sigs1); + case TX_PUBKEYHASH_REPLAY: + // Signatures are bigger than placeholders or empty scripts: + if (sigs1.empty() || sigs1[0].empty()) + return PushAll(sigs2); + return PushAll(sigs1); case TX_SCRIPTHASH: if (sigs1.empty() || sigs1.back().empty()) return PushAll(sigs2); @@ -255,6 +285,8 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatur } case TX_MULTISIG: return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2); + case TX_MULTISIG_REPLAY: + return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2); } return CScript(); diff --git a/src/script/standard.cpp b/src/script/standard.cpp index ce50e3aa..f313f165 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -12,6 +12,9 @@ #include +#include "main.h" +#include "versionbits.h" + using namespace std; typedef vector valtype; @@ -26,10 +29,14 @@ const char* GetTxnOutputType(txnouttype t) { case TX_NONSTANDARD: return "nonstandard"; case TX_PUBKEY: return "pubkey"; + case TX_PUBKEY_REPLAY: return "pubkeyreplay"; case TX_PUBKEYHASH: return "pubkeyhash"; + case TX_PUBKEYHASH_REPLAY: return "pubkeyhashreplay"; case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; + case TX_MULTISIG_REPLAY: return "multisigreplay"; case TX_NULL_DATA: return "nulldata"; + case TX_NULL_DATA_REPLAY: return "nulldatareplay"; } return NULL; } @@ -45,17 +52,24 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector vSolutions; if (!Solver(scriptPubKey, typeRet, vSolutions)) return false; - if (typeRet == TX_NULL_DATA){ + if (typeRet == TX_NULL_DATA || typeRet == TX_NULL_DATA_REPLAY){ // This is data, not addresses return false; } - if (typeRet == TX_MULTISIG) + if (typeRet == TX_MULTISIG || typeRet == TX_MULTISIG_REPLAY) { nRequiredRet = vSolutions.front()[0]; for (unsigned int i = 1; i < vSolutions.size()-1; i++) @@ -284,6 +308,7 @@ public: return false; } +#ifdef BITCOIN_TX // zen-tx does not have access to chain state so no replay protection is possible bool operator()(const CKeyID &keyID) const { script->clear(); *script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; @@ -293,8 +318,38 @@ public: bool operator()(const CScriptID &scriptID) const { script->clear(); *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; + } +#else + bool operator()(const CKeyID &keyID) const { + script->clear(); + CBlockIndex *currentBlock = chainActive.Tip(); + if (THRESHOLD_ACTIVE == VersionBitsState(chainActive[(currentBlock->nHeight - 1) < 0 ? 0 : (currentBlock->nHeight - 1)], Params().GetConsensus(), Consensus::DEPLOYMENT_CBAH, versionbitscache)) { + CBlockIndex *currentBlock = chainActive.Tip(); + int blockIndex = currentBlock->nHeight - 300; + if (blockIndex < 0) + blockIndex = 0; + *script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG << ToByteVector(chainActive[blockIndex]->GetBlockHash()) << chainActive[blockIndex]->nHeight << OP_CHECKBLOCKATHEIGHT; + } + else + *script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; return true; } + + bool operator()(const CScriptID &scriptID) const { + script->clear(); + CBlockIndex *currentBlock = chainActive.Tip(); + if (THRESHOLD_ACTIVE == VersionBitsState(chainActive[(currentBlock->nHeight - 1) < 0 ? 0 : (currentBlock->nHeight - 1)], Params().GetConsensus(), Consensus::DEPLOYMENT_CBAH, versionbitscache)) { + int blockIndex = currentBlock->nHeight - 300; + if (blockIndex < 0) + blockIndex = 0; + *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL << ToByteVector(chainActive[blockIndex]->GetBlockHash()) << chainActive[blockIndex]->nHeight << OP_CHECKBLOCKATHEIGHT; + } + else + *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; + return true; + } +#endif + }; } diff --git a/src/script/standard.h b/src/script/standard.h index 6d72bad2..4460387b 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -52,7 +52,8 @@ 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_CHECKBLOCKATHEIGHT; /** 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; @@ -62,10 +63,14 @@ enum txnouttype TX_NONSTANDARD, // 'standard' transaction types: TX_PUBKEY, + TX_PUBKEY_REPLAY, TX_PUBKEYHASH, + TX_PUBKEYHASH_REPLAY, TX_SCRIPTHASH, TX_MULTISIG, + TX_MULTISIG_REPLAY, TX_NULL_DATA, + TX_NULL_DATA_REPLAY, }; class CNoDestination { diff --git a/src/test/data/script_invalid.json b/src/test/data/script_invalid.json index 7afa2abf..8228bec4 100644 --- a/src/test/data/script_invalid.json +++ b/src/test/data/script_invalid.json @@ -160,15 +160,14 @@ ["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC", "disabled"], ["2 1 RSHIFT", "1 EQUAL", "P2SH,STRICTENC", "disabled"], -["1","NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL", "P2SH,STRICTENC"], -["'NOP_1_to_10' NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL", "P2SH,STRICTENC"], +["1","NOP1 NOP2 NOP3 NOP4 NOP6 NOP7 NOP8 NOP9 NOP10 2 EQUAL", "P2SH,STRICTENC"], +["'NOP_1_to_10' NOP1 NOP2 NOP3 NOP4 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_11' EQUAL", "P2SH,STRICTENC"], ["Ensure 100% coverage of discouraged NOPS"], ["1", "NOP1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP2", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP3", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP4", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], -["1", "NOP5", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP6", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP7", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP8", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], @@ -545,6 +544,24 @@ "", "P2PKH, bad pubkey" ], +[ + "0x47 0x304402206e05a6fe23c59196ffe176c9ddc31e73a9885638f9d1328d47c0c703863b8876022076feb53811aa5b04e0e79f938eb19906cc5e67548bc555a8e8b8b0fc603d840c01 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51507", + "DUP HASH160 0x14 0x1018853670f9f3b0582c5b9ee8ce93764ac32b93 EQUALVERIFY CHECKSIG 0x20 0x0206260143838b5ff52dc2eb7b4b8099d4e4c99dc3ef19794289a2cd4c100700 0x01 0x00 0xb4", + "", + "P2PKH OP_CHECKBLOCKATHEIGHT, bad pubkey" +], +[ + "0x47 0x304402206e05a6fe23c59196ffe176c9ddc31e73a9885638f9d1328d47c0c703863b8876022076feb53811aa5b04e0e79f938eb19906cc5e67548bc555a8e8b8b0fc603d840c01 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508", + "DUP HASH160 0x14 0x1018853670f9f3b0582c5b9ee8ce93764ac32b93 EQUALVERIFY CHECKSIG 0x20 0x0206260143838b5ff52dc2eb7b4b8099d4e4c99dc3ef19794289a2cd4c100700 0x05 0xffffffffff 0xb4", + "", + "P2PKH OP_CHECKBLOCKATHEIGHT, bad blockindex" +], +[ + "0x47 0x304402206e05a6fe23c59196ffe176c9ddc31e73a9885638f9d1328d47c0c703863b8876022076feb53811aa5b04e0e79f938eb19906cc5e67548bc555a8e8b8b0fc603d840c01 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508", + "DUP HASH160 0x14 0x1018853670f9f3b0582c5b9ee8ce93764ac32b93 EQUALVERIFY CHECKSIG 0x21 0x0206260143838b5ff52dc2eb7b4b8099d4e4c99dc3ef19794289a2cd4c10070000 0x01 0x00 0xb4", + "", + "P2PKH OP_CHECKBLOCKATHEIGHT, bad blockhash" +], [ "0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790201", "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", diff --git a/src/test/data/script_valid.json b/src/test/data/script_valid.json index 4b10e3f1..17400c3b 100644 --- a/src/test/data/script_valid.json +++ b/src/test/data/script_valid.json @@ -232,8 +232,8 @@ ["'abcdefghijklmnopqrstuvwxyz'", "HASH256 0x4c 0x20 0xca139bc10c2f660da42666f72e89a225936fc60f193c161124a672050c434671 EQUAL", "P2SH,STRICTENC"], -["1","NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL", "P2SH,STRICTENC"], -["'NOP_1_to_10' NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' EQUAL", "P2SH,STRICTENC"], +["1","NOP1 NOP2 NOP3 NOP4 NOP6 NOP7 NOP8 NOP9 NOP10 1 EQUAL", "P2SH,STRICTENC"], +["'NOP_1_to_10' NOP1 NOP2 NOP3 NOP4 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' EQUAL", "P2SH,STRICTENC"], ["1", "NOP", "P2SH,STRICTENC,DISCOURAGE_UPGRADABLE_NOPS", "Discourage NOPx flag allows OP_NOP"], @@ -444,7 +444,6 @@ ["NOP", "NOP2 1", "P2SH,STRICTENC"], ["NOP", "NOP3 1", "P2SH,STRICTENC"], ["NOP", "NOP4 1", "P2SH,STRICTENC"], -["NOP", "NOP5 1", "P2SH,STRICTENC"], ["NOP", "NOP6 1", "P2SH,STRICTENC"], ["NOP", "NOP7 1", "P2SH,STRICTENC"], ["NOP", "NOP8 1", "P2SH,STRICTENC"], @@ -713,6 +712,12 @@ "", "P2PKH" ], +[ + "0x47 0x304402206e05a6fe23c59196ffe176c9ddc31e73a9885638f9d1328d47c0c703863b8876022076feb53811aa5b04e0e79f938eb19906cc5e67548bc555a8e8b8b0fc603d840c01 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508", + "DUP HASH160 0x14 0x1018853670f9f3b0582c5b9ee8ce93764ac32b93 EQUALVERIFY CHECKSIG 0x20 0x0206260143838b5ff52dc2eb7b4b8099d4e4c99dc3ef19794289a2cd4c100700 0x04 0x00000000 0xb4", + "", + "P2PKH OP_CHECKBLOCKATHEIGHT" +], [ "0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790281", "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", diff --git a/src/test/main_tests.cpp b/src/test/main_tests.cpp index ec02352e..012c2634 100644 --- a/src/test/main_tests.cpp +++ b/src/test/main_tests.cpp @@ -59,7 +59,7 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test) nSum += nSubsidy; BOOST_CHECK(MoneyRange(nSum)); } - BOOST_CHECK_EQUAL(nSum, 12500000000000ULL); + BOOST_CHECK_EQUAL(nSum, 1250000000ULL); // Remainder of first period for (int nHeight = consensusParams.nSubsidySlowStartInterval; nHeight < consensusParams.nSubsidyHalvingInterval + consensusParams.SubsidySlowStartShift(); nHeight ++) { CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 4a960df7..d70ae5f1 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -378,13 +378,40 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // subsidy changing int nHeight = chainActive.Height(); - chainActive.Tip()->nHeight = 209999; + // Create an actual 209999-long block chain (without valid blocks). + while (chainActive.Tip()->nHeight < 209999) { + CBlockIndex* prev = chainActive.Tip(); + CBlockIndex* next = new CBlockIndex(); + next->phashBlock = new uint256(GetRandHash()); + pcoinsTip->SetBestBlock(next->GetBlockHash()); + next->pprev = prev; + next->nHeight = prev->nHeight + 1; + next->BuildSkip(); + chainActive.SetTip(next); + } BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; - chainActive.Tip()->nHeight = 210000; + // Extend to a 210000-long block chain. + while (chainActive.Tip()->nHeight < 210000) { + CBlockIndex* prev = chainActive.Tip(); + CBlockIndex* next = new CBlockIndex(); + next->phashBlock = new uint256(GetRandHash()); + pcoinsTip->SetBestBlock(next->GetBlockHash()); + next->pprev = prev; + next->nHeight = prev->nHeight + 1; + next->BuildSkip(); + chainActive.SetTip(next); + } BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey)); delete pblocktemplate; - chainActive.Tip()->nHeight = nHeight; + // Delete the dummy blocks again. + while (chainActive.Tip()->nHeight > nHeight) { + CBlockIndex* del = chainActive.Tip(); + chainActive.SetTip(del->pprev); + pcoinsTip->SetBestBlock(del->pprev->GetBlockHash()); + delete del->phashBlock; + delete del; + } // non-final txs in mempool SetMockTime(chainActive.Tip()->GetMedianTimePast()+1); diff --git a/src/versionbits.cpp b/src/versionbits.cpp new file mode 100644 index 00000000..7902418e --- /dev/null +++ b/src/versionbits.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "versionbits.h" + +#include "consensus/params.h" + +const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { + { + /*.name =*/ "cbah", + /*.gbt_force =*/ true, + } +}; + +// return the numerical statistics of blocks signalling the specified BIP9 condition in this current period +BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const +{ + BIP9Stats stats; + + stats.period = Period(params); + stats.threshold = Threshold(params); + + if (pindex == NULL) + return stats; + + // Find beginning of period + const CBlockIndex* pindexEndOfPrevPeriod = pindex->GetAncestor(pindex->nHeight - ((pindex->nHeight + 1) % stats.period)); + stats.elapsed = pindex->nHeight - pindexEndOfPrevPeriod->nHeight; + + // Count from current block to beginning of period + int count = 0; + const CBlockIndex* currentIndex = pindex; + while (pindexEndOfPrevPeriod->nHeight != currentIndex->nHeight){ + if (Condition(currentIndex, params)) + count++; + currentIndex = currentIndex->pprev; + } + + stats.count = count; + stats.possible = (stats.period - stats.threshold ) >= (stats.elapsed - count); + + return stats; +} + +ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const +{ + int nPeriod = Period(params); + int nThreshold = Threshold(params); + int64_t nTimeStart = BeginTime(params); + int64_t nTimeTimeout = EndTime(params); + + // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. + if (pindexPrev != NULL) { + pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); + } + + // Walk backwards in steps of nPeriod to find a pindexPrev whose information is known + std::vector vToCompute; + while (cache.count(pindexPrev) == 0) { + if (pindexPrev == NULL) { + // The genesis block is by definition defined. + cache[pindexPrev] = THRESHOLD_DEFINED; + break; + } + if (pindexPrev->GetMedianTimePast() < nTimeStart) { + // Optimization: don't recompute down further, as we know every earlier block will be before the start time + cache[pindexPrev] = THRESHOLD_DEFINED; + break; + } + vToCompute.push_back(pindexPrev); + pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); + } + + // At this point, cache[pindexPrev] is known + assert(cache.count(pindexPrev)); + ThresholdState state = cache[pindexPrev]; + + // Now walk forward and compute the state of descendants of pindexPrev + while (!vToCompute.empty()) { + ThresholdState stateNext = state; + pindexPrev = vToCompute.back(); + vToCompute.pop_back(); + + switch (state) { + case THRESHOLD_DEFINED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + stateNext = THRESHOLD_FAILED; + } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { + stateNext = THRESHOLD_STARTED; + } + break; + } + case THRESHOLD_STARTED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + stateNext = THRESHOLD_FAILED; + break; + } + // We need to count + const CBlockIndex* pindexCount = pindexPrev; + int count = 0; + for (int i = 0; i < nPeriod; i++) { + if (Condition(pindexCount, params)) { + count++; + } + pindexCount = pindexCount->pprev; + } + if (count >= nThreshold) { + stateNext = THRESHOLD_LOCKED_IN; + } + break; + } + case THRESHOLD_LOCKED_IN: { + // Always progresses into ACTIVE. + stateNext = THRESHOLD_ACTIVE; + break; + } + case THRESHOLD_FAILED: + case THRESHOLD_ACTIVE: { + // Nothing happens, these are terminal states. + break; + } + } + cache[pindexPrev] = state = stateNext; + } + + return state; +} + +int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const +{ + const ThresholdState initialState = GetStateFor(pindexPrev, params, cache); + + // BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment." + if (initialState == THRESHOLD_DEFINED) { + return 0; + } + + const int nPeriod = Period(params); + + // A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. + // To ease understanding of the following height calculation, it helps to remember that + // right now pindexPrev points to the block prior to the block that we are computing for, thus: + // if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and + // if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period. + // The parent of the genesis block is represented by NULL. + pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); + + const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); + + while (previousPeriodParent != NULL && GetStateFor(previousPeriodParent, params, cache) == initialState) { + pindexPrev = previousPeriodParent; + previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); + } + + // Adjust the result because right now we point to the parent block. + return pindexPrev->nHeight + 1; +} + +namespace +{ +/** + * Class to implement versionbits logic. + */ +class VersionBitsConditionChecker : public AbstractThresholdConditionChecker { +private: + const Consensus::DeploymentPos id; + +protected: + int64_t BeginTime(const Consensus::Params& params) const { return params.vDeployments[id].nStartTime; } + int64_t EndTime(const Consensus::Params& params) const { return params.vDeployments[id].nTimeout; } + int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; } + int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; } + + bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const + { + return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0); + } + +public: + VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {} + uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; } +}; + +} + +ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) +{ + return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]); +} + +BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params); +} + + +int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) +{ + return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, cache.caches[pos]); +} + +uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos) +{ + return VersionBitsConditionChecker(pos).Mask(params); +} + +void VersionBitsCache::Clear() +{ + for (unsigned int d = 0; d < Consensus::MAX_VERSION_BITS_DEPLOYMENTS; d++) { + caches[d].clear(); + } +} diff --git a/src/versionbits.h b/src/versionbits.h new file mode 100644 index 00000000..f1d31ea0 --- /dev/null +++ b/src/versionbits.h @@ -0,0 +1,80 @@ +// Copyright (c) 2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_CONSENSUS_VERSIONBITS +#define BITCOIN_CONSENSUS_VERSIONBITS + +#include "chain.h" +#include + +/** What block version to use for new blocks (pre versionbits) */ +static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4; +/** What bits to set in version for versionbits blocks */ +static const int32_t VERSIONBITS_TOP_BITS = 0x20000000UL; +/** What bitmask determines whether versionbits is in use */ +static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL; +/** Total bits available for versionbits */ +static const int32_t VERSIONBITS_NUM_BITS = 29; + +enum ThresholdState { + THRESHOLD_DEFINED, + THRESHOLD_STARTED, + THRESHOLD_LOCKED_IN, + THRESHOLD_ACTIVE, + THRESHOLD_FAILED, +}; + +// A map that gives the state for blocks whose height is a multiple of Period(). +// The map is indexed by the block's parent, however, so all keys in the map +// will either be NULL or a block with (height + 1) % Period() == 0. +typedef std::map ThresholdConditionCache; + +struct BIP9DeploymentInfo { + /** Deployment name */ + const char *name; + /** Whether GBT clients can safely ignore this rule in simplified usage */ + bool gbt_force; +}; + +struct BIP9Stats { + int period; + int threshold; + int elapsed; + int count; + bool possible; +}; + +extern const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[]; + +/** + * Abstract class that implements BIP9-style threshold logic, and caches results. + */ +class AbstractThresholdConditionChecker { +protected: + virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0; + virtual int64_t BeginTime(const Consensus::Params& params) const =0; + virtual int64_t EndTime(const Consensus::Params& params) const =0; + virtual int Period(const Consensus::Params& params) const =0; + virtual int Threshold(const Consensus::Params& params) const =0; + +public: + BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const; + // Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent. + ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const; + int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const; +}; + +struct VersionBitsCache +{ + ThresholdConditionCache caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS]; + + void Clear(); +}; + +ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); +BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos); +int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); +uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos); + +#endif diff --git a/src/wallet/wallet_ismine.cpp b/src/wallet/wallet_ismine.cpp index 5482348e..a23f53c1 100644 --- a/src/wallet/wallet_ismine.cpp +++ b/src/wallet/wallet_ismine.cpp @@ -50,16 +50,29 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) case TX_NONSTANDARD: case TX_NULL_DATA: break; + case TX_NULL_DATA_REPLAY: + break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; + case TX_PUBKEY_REPLAY: + keyID = CPubKey(vSolutions[0]).GetID(); + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; + case TX_PUBKEYHASH_REPLAY: + keyID = CKeyID(uint160(vSolutions[0])); + if (keystore.HaveKey(keyID)) + return ISMINE_SPENDABLE; + break; + case TX_SCRIPTHASH: { CScriptID scriptID = CScriptID(uint160(vSolutions[0])); @@ -83,6 +96,19 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) return ISMINE_SPENDABLE; break; } + case TX_MULTISIG_REPLAY: + { + // Only consider transactions "mine" if we own ALL the + // keys involved. Multi-signature transactions that are + // partially owned (somebody else has a key that can spend + // them) enable spend-out-from-under-you attacks, especially + // in shared-wallet situations. + vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + if (HaveKeys(keys, keystore) == keys.size()) + return ISMINE_SPENDABLE; + break; + } + } if (keystore.HaveWatchOnly(scriptPubKey))