Merge pull request #78 from z-classic/tx-replay-protection

[Softfork] BIP Implementation for TX Replay Protection and BIP 9 implementation
This commit is contained in:
Joshua Yabut 2017-05-10 17:40:50 -04:00 committed by GitHub
commit df4de58ade
34 changed files with 881 additions and 52 deletions

View File

@ -1,4 +1,4 @@
Zclassic 1.0.8 Zclassic 1.0.8-2
============== ==============
What is Zclassic? What is Zclassic?

View File

@ -3,7 +3,7 @@ AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 1) define(_CLIENT_VERSION_MAJOR, 1)
define(_CLIENT_VERSION_MINOR, 0) define(_CLIENT_VERSION_MINOR, 0)
define(_CLIENT_VERSION_REVISION, 8) 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(_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_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) define(_CLIENT_VERSION_IS_RELEASE, true)

View File

@ -1,5 +1,5 @@
--- ---
name: "zcash-1.0.8" name: "zcash-1.0.8-2"
enable_cache: true enable_cache: true
distro: "debian" distro: "debian"
suites: suites:

View File

@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. .\" 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 .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 .SH DESCRIPTION
Zcash zcash\-tx utility version v1.0.8 Zcash zcash\-tx utility version v1.0.8-2
.SS "Usage:" .SS "Usage:"
.TP .TP
zcash\-tx [options] <hex\-tx> [commands] zcash\-tx [options] <hex\-tx> [commands]

View File

@ -1,9 +1,9 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. .\" 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 .SH NAME
zcashd \- manual page for zcashd v1.0.8 zcashd \- manual page for zcashd v1.0.8-2
.SH DESCRIPTION .SH DESCRIPTION
Zcash Daemon version v1.0.8 Zcash Daemon version v1.0.8-2
.PP .PP
In order to ensure you are adequately protecting your privacy when using Zclassic, In order to ensure you are adequately protecting your privacy when using Zclassic,
please see <https://z.cash/support/security/index.html>. please see <https://z.cash/support/security/index.html>.

View File

@ -176,6 +176,7 @@ BITCOIN_CORE_H = \
utilstrencodings.h \ utilstrencodings.h \
utiltime.h \ utiltime.h \
validationinterface.h \ validationinterface.h \
versionbits.h \
version.h \ version.h \
wallet/asyncrpcoperation_sendmany.h \ wallet/asyncrpcoperation_sendmany.h \
wallet/crypter.h \ wallet/crypter.h \
@ -232,6 +233,7 @@ libbitcoin_server_a_SOURCES = \
txdb.cpp \ txdb.cpp \
txmempool.cpp \ txmempool.cpp \
validationinterface.cpp \ validationinterface.cpp \
versionbits.cpp \
$(BITCOIN_CORE_H) \ $(BITCOIN_CORE_H) \
$(LIBZCASH_H) $(LIBZCASH_H)
@ -426,8 +428,8 @@ zcash_cli_LDADD = \
# #
# zcash-tx binary # # zcash-tx binary #
zcash_tx_SOURCES = bitcoin-tx.cpp zcash_tx_SOURCES = bitcoin-tx.cpp script/standard.cpp
zcash_tx_CPPFLAGS = $(BITCOIN_INCLUDES) zcash_tx_CPPFLAGS = $(BITCOIN_INCLUDES) -DBITCOIN_TX
zcash_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) zcash_tx_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
if TARGET_WINDOWS if TARGET_WINDOWS

View File

@ -5,6 +5,7 @@ noinst_PROGRAMS += zcash-gtest
# test_checktransaction.cpp MUST be before # test_checktransaction.cpp MUST be before
# any test that calls SelectParams(). # any test that calls SelectParams().
zcash_gtest_SOURCES = \ zcash_gtest_SOURCES = \
script/standard.cpp \
gtest/main.cpp \ gtest/main.cpp \
gtest/utils.cpp \ gtest/utils.cpp \
gtest/test_checktransaction.cpp \ gtest/test_checktransaction.cpp \
@ -40,7 +41,7 @@ zcash_gtest_SOURCES += \
wallet/gtest/test_wallet.cpp wallet/gtest/test_wallet.cpp
endif 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) \ 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) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)

View File

@ -35,15 +35,16 @@ JSON_TEST_FILES = \
test/data/g1_compressed.json \ test/data/g1_compressed.json \
test/data/g2_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) GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
BITCOIN_TESTS =\ BITCOIN_TESTS =\
script/standard.cpp \
test/arith_uint256_tests.cpp \ test/arith_uint256_tests.cpp \
test/bignum.h \ test/bignum.h \
test/addrman_tests.cpp \ test/addrman_tests.cpp \
test/alert_tests.cpp \ #test/alert_tests.cpp \
test/allocator_tests.cpp \ test/allocator_tests.cpp \
test/base32_tests.cpp \ test/base32_tests.cpp \
test/base58_tests.cpp \ test/base58_tests.cpp \
@ -99,7 +100,7 @@ BITCOIN_TESTS += \
endif endif
test_test_bitcoin_SOURCES = $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) 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) \ 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) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
if ENABLE_WALLET if ENABLE_WALLET

View File

@ -170,7 +170,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
txnouttype type; txnouttype type;
vector<vector<unsigned char> > vSolutions; vector<vector<unsigned char> > vSolutions;
if (Solver(txout.scriptPubKey, type, 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)); insert(COutPoint(hash, i));
} }
break; break;

View File

@ -14,8 +14,6 @@
#include <vector> #include <vector>
#include <boost/foreach.hpp>
struct CDiskBlockPos struct CDiskBlockPos
{ {
int nFile; int nFile;

View File

@ -50,6 +50,14 @@ public:
consensus.nPowMaxAdjustUp = 16; // 16% adjustment up consensus.nPowMaxAdjustUp = 16; // 16% adjustment up
consensus.nPowTargetSpacing = 2.5 * 60; consensus.nPowTargetSpacing = 2.5 * 60;
consensus.fPowAllowMinDifficultyBlocks = false; 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! * The message start string should be awesome!
*/ */
@ -126,11 +134,12 @@ public:
checkpointData = (Checkpoints::CCheckpointData) { checkpointData = (Checkpoints::CCheckpointData) {
boost::assign::map_list_of boost::assign::map_list_of
( 0, consensus.hashGenesisBlock) ( 0, consensus.hashGenesisBlock)
( 30000, uint256S("0x000000005c2ad200c3c7c8e627f67b306659efca1268c9bb014335fdadc0c392")), ( 30000, uint256S("0x000000005c2ad200c3c7c8e627f67b306659efca1268c9bb014335fdadc0c392"))
1482914970, // * UNIX timestamp of last checkpoint block ( 96577, uint256S("0x0000000177751545bd1af3ccf276ec2920d258453ab01f3d2f8f7fcc5f3a37b8")),
82372, // * total number of transactions between genesis and last checkpoint 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) // (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)) // total number of tx / (checkpoint block height / (24 * 24))
}; };
@ -211,6 +220,14 @@ public:
consensus.powLimit = uint256S("07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.powLimit = uint256S("07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow);
consensus.fPowAllowMinDifficultyBlocks = true; 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[0] = 0xfa;
pchMessageStart[1] = 0x1a; pchMessageStart[1] = 0x1a;
pchMessageStart[2] = 0xf9; pchMessageStart[2] = 0xf9;
@ -302,6 +319,13 @@ public:
assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow); assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow);
consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down
consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up 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[0] = 0xaa;
pchMessageStart[1] = 0xe8; pchMessageStart[1] = 0xe8;
pchMessageStart[2] = 0x3f; pchMessageStart[2] = 0x3f;

View File

@ -7,8 +7,31 @@
#define BITCOIN_CONSENSUS_PARAMS_H #define BITCOIN_CONSENSUS_PARAMS_H
#include "uint256.h" #include "uint256.h"
#include <map>
#include <string>
namespace Consensus { 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. * Parameters that influence chain consensus.
*/ */
@ -40,6 +63,14 @@ struct Params {
int nMajorityEnforceBlockUpgrade; int nMajorityEnforceBlockUpgrade;
int nMajorityRejectBlockOutdated; int nMajorityRejectBlockOutdated;
int nMajorityWindow; 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 */ /** Proof of work parameters */
uint256 powLimit; uint256 powLimit;
bool fPowAllowMinDifficultyBlocks; bool fPowAllowMinDifficultyBlocks;

View File

@ -461,7 +461,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE));
strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); strUsage += HelpMessageOpt("-blockprioritysize=<n>", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE));
if (GetBoolArg("-help-debug", false)) if (GetBoolArg("-help-debug", false))
strUsage += HelpMessageOpt("-blockversion=<n>", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION)); strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios");
#ifdef ENABLE_MINING #ifdef ENABLE_MINING
strUsage += HelpMessageGroup(_("Mining options:")); strUsage += HelpMessageGroup(_("Mining options:"));

View File

@ -26,6 +26,7 @@
#include "util.h" #include "util.h"
#include "utilmoneystr.h" #include "utilmoneystr.h"
#include "validationinterface.h" #include "validationinterface.h"
#include "versionbits.h"
#include "wallet/asyncrpcoperation_sendmany.h" #include "wallet/asyncrpcoperation_sendmany.h"
#include <sstream> #include <sstream>
@ -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<int64_t>::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 nTimeVerify = 0;
static int64_t nTimeConnect = 0; static int64_t nTimeConnect = 0;
static int64_t nTimeIndex = 0; static int64_t nTimeIndex = 0;
@ -2090,6 +2136,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; 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; CBlockUndo blockundo;
CCheckQueueControl<CScriptCheck> control(fExpensiveChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); CCheckQueueControl<CScriptCheck> 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: // Check the version of the last 100 blocks to see if we need to upgrade:
static bool fWarned = false; static bool fWarned = false;
if (!IsInitialBlockDownload() && !fWarned) if (!IsInitialBlockDownload())
{ {
int nUpgraded = 0; int nUpgraded = 0;
const CBlockIndex* pindex = chainActive.Tip(); 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++) 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; ++nUpgraded;
pindex = pindex->pprev; pindex = pindex->pprev;
} }
if (nUpgraded > 0) 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) if (nUpgraded > 100/2)
{ {
// strMiscWarning is read by GetWarnings(), called by the JSON-RPC code to warn the user: // strMiscWarning is read by GetWarnings(), called by the JSON-RPC code to warn the user:
strMiscWarning = _("Warning: This version is obsolete; upgrade required!"); strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
CAlert::Notify(strMiscWarning, true); if (!fWarned) {
fWarned = true; CAlert::Notify(strMiscWarning, true);
fWarned = true;
}
} }
} }
} }
@ -3707,6 +3776,10 @@ void UnloadBlockIndex()
setDirtyFileInfo.clear(); setDirtyFileInfo.clear();
mapNodeState.clear(); mapNodeState.clear();
recentRejects.reset(NULL); recentRejects.reset(NULL);
versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear();
}
BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) {
delete entry.second; delete entry.second;
@ -4057,6 +4130,24 @@ void static CheckBlockIndex()
assert(nNodes == forward.size()); 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 // CAlert

View File

@ -25,6 +25,7 @@
#include "tinyformat.h" #include "tinyformat.h"
#include "txmempool.h" #include "txmempool.h"
#include "uint256.h" #include "uint256.h"
#include "versionbits.h"
#include <algorithm> #include <algorithm>
#include <exception> #include <exception>
@ -251,6 +252,14 @@ void PruneAndFlush();
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectAbsurdFee=false); 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 { struct CNodeStateStats {
int nMisbehavior; int nMisbehavior;
@ -523,6 +532,13 @@ extern CBlockTreeDB *pblocktree;
*/ */
int GetSpendHeight(const CCoinsViewCache& inputs); 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 { namespace Consensus {
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams); bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams);
} }

View File

@ -149,6 +149,13 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
const int nHeight = pindexPrev->nHeight + 1; const int nHeight = pindexPrev->nHeight + 1;
pblock->nTime = GetAdjustedTime(); pblock->nTime = GetAdjustedTime();
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); 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); CCoinsViewCache view(pcoinsTip);
// Priority order to process transactions // Priority order to process transactions

View File

@ -22,7 +22,6 @@ class CBlockHeader
public: public:
// header // header
static const size_t HEADER_SIZE=4+32+32+32+4+4+32; // excluding Equihash solution 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; int32_t nVersion;
uint256 hashPrevBlock; uint256 hashPrevBlock;
uint256 hashMerkleRoot; uint256 hashMerkleRoot;
@ -54,7 +53,7 @@ public:
void SetNull() void SetNull()
{ {
nVersion = CBlockHeader::CURRENT_VERSION; nVersion = 0;
hashPrevBlock.SetNull(); hashPrevBlock.SetNull();
hashMerkleRoot.SetNull(); hashMerkleRoot.SetNull();
hashReserved.SetNull(); hashReserved.SetNull();

View File

@ -634,6 +634,38 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex*
return rv; 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) UniValue getblockchaininfo(const UniValue& params, bool fHelp)
{ {
if (fHelp || params.size() != 0) if (fHelp || params.size() != 0)
@ -662,8 +694,22 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
" },\n" " },\n"
" \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n" " \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n"
" }, ...\n" " }, ...\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" "\nExamples:\n"
+ HelpExampleCli("getblockchaininfo", "") + HelpExampleCli("getblockchaininfo", "")
+ HelpExampleRpc("getblockchaininfo", "") + HelpExampleRpc("getblockchaininfo", "")
@ -688,10 +734,14 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
const Consensus::Params& consensusParams = Params().GetConsensus(); const Consensus::Params& consensusParams = Params().GetConsensus();
CBlockIndex* tip = chainActive.Tip(); CBlockIndex* tip = chainActive.Tip();
UniValue softforks(UniValue::VARR); UniValue softforks(UniValue::VARR);
UniValue bip9_softforks(UniValue::VOBJ);
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip65", 4, 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("softforks", softforks));
obj.push_back(Pair("bip9_softforks", bip9_softforks));
if (fPruneMode) if (fPruneMode)
{ {

View File

@ -14,6 +14,10 @@
#include "script/script.h" #include "script/script.h"
#include "uint256.h" #include "uint256.h"
#include "main.h"
#include <boost/algorithm/hex.hpp>
#include <boost/algorithm/string.hpp>
using namespace std; using namespace std;
typedef vector<unsigned char> valtype; typedef vector<unsigned char> valtype;
@ -188,6 +192,40 @@ bool static IsDefinedHashtypeSignature(const valtype &vchSig) {
return true; 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) { bool static CheckSignatureEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) {
// Empty signature. Not strictly DER encoded, but allowed to provide a // Empty signature. Not strictly DER encoded, but allowed to provide a
// compact way to provide an invalid signature for use with CHECK(MULTI)SIG // compact way to provide an invalid signature for use with CHECK(MULTI)SIG
@ -379,7 +417,104 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un
break; 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 <luke+bip@dashjr.org>
// 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<const uint16_t*>(&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: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10:
{ {
if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS) if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)

View File

@ -85,6 +85,10 @@ enum
// //
// See BIP65 for details. // See BIP65 for details.
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), 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); uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);

View File

@ -138,13 +138,14 @@ const char* GetOpName(opcodetype opcode)
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY";
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG";
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY";
case OP_CHECKBLOCKATHEIGHT : return "OP_CHECKBLOCKATHEIGHT";
// expanson // expanson
case OP_NOP1 : return "OP_NOP1"; case OP_NOP1 : return "OP_NOP1";
case OP_NOP2 : return "OP_NOP2"; case OP_NOP2 : return "OP_NOP2";
case OP_NOP3 : return "OP_NOP3"; case OP_NOP3 : return "OP_NOP3";
case OP_NOP4 : return "OP_NOP4"; 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_NOP6 : return "OP_NOP6";
case OP_NOP7 : return "OP_NOP7"; case OP_NOP7 : return "OP_NOP7";
case OP_NOP8 : return "OP_NOP8"; case OP_NOP8 : return "OP_NOP8";

View File

@ -162,6 +162,7 @@ enum opcodetype
OP_NOP3 = 0xb2, OP_NOP3 = 0xb2,
OP_NOP4 = 0xb3, OP_NOP4 = 0xb3,
OP_NOP5 = 0xb4, OP_NOP5 = 0xb4,
OP_CHECKBLOCKATHEIGHT = OP_NOP5,
OP_NOP6 = 0xb5, OP_NOP6 = 0xb5,
OP_NOP7 = 0xb6, OP_NOP7 = 0xb6,
OP_NOP8 = 0xb7, OP_NOP8 = 0xb7,

View File

@ -67,6 +67,10 @@ const char* ScriptErrorString(const ScriptError serror)
return "NOPx reserved for soft-fork upgrades"; return "NOPx reserved for soft-fork upgrades";
case SCRIPT_ERR_PUBKEYTYPE: case SCRIPT_ERR_PUBKEYTYPE:
return "Public key is neither compressed or uncompressed"; 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_UNKNOWN_ERROR:
case SCRIPT_ERR_ERROR_COUNT: case SCRIPT_ERR_ERROR_COUNT:
default: break; default: break;

View File

@ -51,8 +51,12 @@ typedef enum ScriptError_t
/* softfork safeness */ /* softfork safeness */
SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS,
SCRIPT_ERR_ERROR_COUNT,
/* tx replay prevention */
SCRIPT_ERR_CHECKBLOCKATHEIGHT,
SCRIPT_ERR_CHECKBLOCKATHEIGHT_UNVERIFIED
SCRIPT_ERR_ERROR_COUNT
} ScriptError; } ScriptError;
#define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT #define SCRIPT_ERR_LAST SCRIPT_ERR_ERROR_COUNT

View File

@ -82,9 +82,14 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA: case TX_NULL_DATA:
return false; return false;
case TX_NULL_DATA_REPLAY:
return false;
case TX_PUBKEY: case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID(); keyID = CPubKey(vSolutions[0]).GetID();
return Sign1(keyID, creator, scriptPubKey, scriptSigRet); return Sign1(keyID, creator, scriptPubKey, scriptSigRet);
case TX_PUBKEY_REPLAY:
keyID = CPubKey(vSolutions[0]).GetID();
return Sign1(keyID, creator, scriptPubKey, scriptSigRet);
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0])); keyID = CKeyID(uint160(vSolutions[0]));
if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet)) if (!Sign1(keyID, creator, scriptPubKey, scriptSigRet))
@ -96,12 +101,26 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP
scriptSigRet << ToByteVector(vch); scriptSigRet << ToByteVector(vch);
} }
return true; 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: case TX_SCRIPTHASH:
return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet); return creator.KeyStore().GetCScript(uint160(vSolutions[0]), scriptSigRet);
case TX_MULTISIG: case TX_MULTISIG:
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet)); return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet));
case TX_MULTISIG_REPLAY:
scriptSigRet << OP_0; // workaround CHECKMULTISIG bug
return (SignN(vSolutions, creator, scriptPubKey, scriptSigRet));
} }
return false; return false;
} }
@ -227,12 +246,23 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatur
if (sigs1.size() >= sigs2.size()) if (sigs1.size() >= sigs2.size())
return PushAll(sigs1); return PushAll(sigs1);
return PushAll(sigs2); 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:
case TX_PUBKEY_REPLAY:
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
// Signatures are bigger than placeholders or empty scripts: // Signatures are bigger than placeholders or empty scripts:
if (sigs1.empty() || sigs1[0].empty()) if (sigs1.empty() || sigs1[0].empty())
return PushAll(sigs2); return PushAll(sigs2);
return PushAll(sigs1); 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: case TX_SCRIPTHASH:
if (sigs1.empty() || sigs1.back().empty()) if (sigs1.empty() || sigs1.back().empty())
return PushAll(sigs2); return PushAll(sigs2);
@ -255,6 +285,8 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const BaseSignatur
} }
case TX_MULTISIG: case TX_MULTISIG:
return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2); return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2);
case TX_MULTISIG_REPLAY:
return CombineMultisig(scriptPubKey, checker, vSolutions, sigs1, sigs2);
} }
return CScript(); return CScript();

View File

@ -12,6 +12,9 @@
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include "main.h"
#include "versionbits.h"
using namespace std; using namespace std;
typedef vector<unsigned char> valtype; typedef vector<unsigned char> valtype;
@ -26,10 +29,14 @@ const char* GetTxnOutputType(txnouttype t)
{ {
case TX_NONSTANDARD: return "nonstandard"; case TX_NONSTANDARD: return "nonstandard";
case TX_PUBKEY: return "pubkey"; case TX_PUBKEY: return "pubkey";
case TX_PUBKEY_REPLAY: return "pubkeyreplay";
case TX_PUBKEYHASH: return "pubkeyhash"; case TX_PUBKEYHASH: return "pubkeyhash";
case TX_PUBKEYHASH_REPLAY: return "pubkeyhashreplay";
case TX_SCRIPTHASH: return "scripthash"; case TX_SCRIPTHASH: return "scripthash";
case TX_MULTISIG: return "multisig"; case TX_MULTISIG: return "multisig";
case TX_MULTISIG_REPLAY: return "multisigreplay";
case TX_NULL_DATA: return "nulldata"; case TX_NULL_DATA: return "nulldata";
case TX_NULL_DATA_REPLAY: return "nulldatareplay";
} }
return NULL; return NULL;
} }
@ -45,17 +52,24 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
{ {
// Standard tx, sender provides pubkey, receiver adds signature // Standard tx, sender provides pubkey, receiver adds signature
mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); mTemplates.insert(make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
mTemplates.insert(make_pair(TX_PUBKEY_REPLAY, CScript() << OP_PUBKEY << OP_CHECKSIG << OP_SMALLDATA << OP_SMALLDATA << OP_CHECKBLOCKATHEIGHT));
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
mTemplates.insert(make_pair(TX_PUBKEYHASH_REPLAY, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG << OP_SMALLDATA << OP_SMALLDATA << OP_CHECKBLOCKATHEIGHT));
// Sender provides N pubkeys, receivers provides M signatures // Sender provides N pubkeys, receivers provides M signatures
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
mTemplates.insert(make_pair(TX_MULTISIG_REPLAY, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG << OP_SMALLDATA << OP_SMALLDATA << OP_CHECKBLOCKATHEIGHT));
// Empty, provably prunable, data-carrying output // Empty, provably prunable, data-carrying output
if (GetBoolArg("-datacarrier", true)) if (GetBoolArg("-datacarrier", true))
{
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA));
mTemplates.insert(make_pair(TX_NULL_DATA_REPLAY, CScript() << OP_RETURN << OP_SMALLDATA << OP_SMALLDATA << OP_SMALLDATA << OP_CHECKBLOCKATHEIGHT));
}
mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN)); mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN));
} }
// Shortcut for pay-to-script-hash, which are more constrained than the other types: // Shortcut for pay-to-script-hash, which are more constrained than the other types:
@ -87,7 +101,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
{ {
// Found a match // Found a match
typeRet = tplate.first; typeRet = tplate.first;
if (typeRet == TX_MULTISIG) if (typeRet == TX_MULTISIG || typeRet == TX_MULTISIG_REPLAY)
{ {
// Additional checks for TX_MULTISIG: // Additional checks for TX_MULTISIG:
unsigned char m = vSolutionsRet.front()[0]; unsigned char m = vSolutionsRet.front()[0];
@ -166,14 +180,24 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA: case TX_NULL_DATA:
return -1; return -1;
case TX_NULL_DATA_REPLAY:
return -1;
case TX_PUBKEY: case TX_PUBKEY:
return 1; return 1;
case TX_PUBKEY_REPLAY:
return 1;
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
return 2; return 2;
case TX_PUBKEYHASH_REPLAY:
return 2;
case TX_MULTISIG: case TX_MULTISIG:
if (vSolutions.size() < 1 || vSolutions[0].size() < 1) if (vSolutions.size() < 1 || vSolutions[0].size() < 1)
return -1; return -1;
return vSolutions[0][0] + 1; return vSolutions[0][0] + 1;
case TX_MULTISIG_REPLAY:
if (vSolutions.size() < 1 || vSolutions[0].size() < 1)
return -1;
return vSolutions[0][0] + 1;
case TX_SCRIPTHASH: case TX_SCRIPTHASH:
return 1; // doesn't include args needed by the script return 1; // doesn't include args needed by the script
} }
@ -186,7 +210,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
if (!Solver(scriptPubKey, whichType, vSolutions)) if (!Solver(scriptPubKey, whichType, vSolutions))
return false; return false;
if (whichType == TX_MULTISIG) if (whichType == TX_MULTISIG || whichType == TX_MULTISIG_REPLAY)
{ {
unsigned char m = vSolutions.front()[0]; unsigned char m = vSolutions.front()[0];
unsigned char n = vSolutions.back()[0]; unsigned char n = vSolutions.back()[0];
@ -207,7 +231,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
if (!Solver(scriptPubKey, whichType, vSolutions)) if (!Solver(scriptPubKey, whichType, vSolutions))
return false; return false;
if (whichType == TX_PUBKEY) if (whichType == TX_PUBKEY || whichType == TX_PUBKEY_REPLAY)
{ {
CPubKey pubKey(vSolutions[0]); CPubKey pubKey(vSolutions[0]);
if (!pubKey.IsValid()) if (!pubKey.IsValid())
@ -216,7 +240,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = pubKey.GetID(); addressRet = pubKey.GetID();
return true; return true;
} }
else if (whichType == TX_PUBKEYHASH) else if (whichType == TX_PUBKEYHASH || whichType == TX_PUBKEYHASH_REPLAY)
{ {
addressRet = CKeyID(uint160(vSolutions[0])); addressRet = CKeyID(uint160(vSolutions[0]));
return true; return true;
@ -237,12 +261,12 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto
vector<valtype> vSolutions; vector<valtype> vSolutions;
if (!Solver(scriptPubKey, typeRet, vSolutions)) if (!Solver(scriptPubKey, typeRet, vSolutions))
return false; return false;
if (typeRet == TX_NULL_DATA){ if (typeRet == TX_NULL_DATA || typeRet == TX_NULL_DATA_REPLAY){
// This is data, not addresses // This is data, not addresses
return false; return false;
} }
if (typeRet == TX_MULTISIG) if (typeRet == TX_MULTISIG || typeRet == TX_MULTISIG_REPLAY)
{ {
nRequiredRet = vSolutions.front()[0]; nRequiredRet = vSolutions.front()[0];
for (unsigned int i = 1; i < vSolutions.size()-1; i++) for (unsigned int i = 1; i < vSolutions.size()-1; i++)
@ -284,6 +308,7 @@ public:
return false; 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 { bool operator()(const CKeyID &keyID) const {
script->clear(); script->clear();
*script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG; *script << OP_DUP << OP_HASH160 << ToByteVector(keyID) << OP_EQUALVERIFY << OP_CHECKSIG;
@ -293,8 +318,38 @@ public:
bool operator()(const CScriptID &scriptID) const { bool operator()(const CScriptID &scriptID) const {
script->clear(); script->clear();
*script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; *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; 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
}; };
} }

View File

@ -52,7 +52,8 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS |
SCRIPT_VERIFY_CLEANSTACK | SCRIPT_VERIFY_CLEANSTACK |
SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY | SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
SCRIPT_VERIFY_LOW_S; SCRIPT_VERIFY_LOW_S |
SCRIPT_VERIFY_CHECKBLOCKATHEIGHT;
/** For convenience, standard but not mandatory verify flags. */ /** 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; 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, TX_NONSTANDARD,
// 'standard' transaction types: // 'standard' transaction types:
TX_PUBKEY, TX_PUBKEY,
TX_PUBKEY_REPLAY,
TX_PUBKEYHASH, TX_PUBKEYHASH,
TX_PUBKEYHASH_REPLAY,
TX_SCRIPTHASH, TX_SCRIPTHASH,
TX_MULTISIG, TX_MULTISIG,
TX_MULTISIG_REPLAY,
TX_NULL_DATA, TX_NULL_DATA,
TX_NULL_DATA_REPLAY,
}; };
class CNoDestination { class CNoDestination {

View File

@ -160,15 +160,14 @@
["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC", "disabled"], ["2 2 LSHIFT", "8 EQUAL", "P2SH,STRICTENC", "disabled"],
["2 1 RSHIFT", "1 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"], ["1","NOP1 NOP2 NOP3 NOP4 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"], ["'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"], ["Ensure 100% coverage of discouraged NOPS"],
["1", "NOP1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP1", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP2", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP2", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP3", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP3", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP4", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP4", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP5", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP6", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP6", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP7", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP7", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
["1", "NOP8", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"], ["1", "NOP8", "P2SH,DISCOURAGE_UPGRADABLE_NOPS"],
@ -545,6 +544,24 @@
"", "",
"P2PKH, bad pubkey" "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", "0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790201",
"0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG",

View File

@ -232,8 +232,8 @@
["'abcdefghijklmnopqrstuvwxyz'", "HASH256 0x4c 0x20 0xca139bc10c2f660da42666f72e89a225936fc60f193c161124a672050c434671 EQUAL", "P2SH,STRICTENC"], ["'abcdefghijklmnopqrstuvwxyz'", "HASH256 0x4c 0x20 0xca139bc10c2f660da42666f72e89a225936fc60f193c161124a672050c434671 EQUAL", "P2SH,STRICTENC"],
["1","NOP1 NOP2 NOP3 NOP4 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10 1 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 NOP5 NOP6 NOP7 NOP8 NOP9 NOP10","'NOP_1_to_10' 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"], ["1", "NOP", "P2SH,STRICTENC,DISCOURAGE_UPGRADABLE_NOPS", "Discourage NOPx flag allows OP_NOP"],
@ -444,7 +444,6 @@
["NOP", "NOP2 1", "P2SH,STRICTENC"], ["NOP", "NOP2 1", "P2SH,STRICTENC"],
["NOP", "NOP3 1", "P2SH,STRICTENC"], ["NOP", "NOP3 1", "P2SH,STRICTENC"],
["NOP", "NOP4 1", "P2SH,STRICTENC"], ["NOP", "NOP4 1", "P2SH,STRICTENC"],
["NOP", "NOP5 1", "P2SH,STRICTENC"],
["NOP", "NOP6 1", "P2SH,STRICTENC"], ["NOP", "NOP6 1", "P2SH,STRICTENC"],
["NOP", "NOP7 1", "P2SH,STRICTENC"], ["NOP", "NOP7 1", "P2SH,STRICTENC"],
["NOP", "NOP8 1", "P2SH,STRICTENC"], ["NOP", "NOP8 1", "P2SH,STRICTENC"],
@ -713,6 +712,12 @@
"", "",
"P2PKH" "P2PKH"
], ],
[
"0x47 0x304402206e05a6fe23c59196ffe176c9ddc31e73a9885638f9d1328d47c0c703863b8876022076feb53811aa5b04e0e79f938eb19906cc5e67548bc555a8e8b8b0fc603d840c01 0x21 0x038282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f51508",
"DUP HASH160 0x14 0x1018853670f9f3b0582c5b9ee8ce93764ac32b93 EQUALVERIFY CHECKSIG 0x20 0x0206260143838b5ff52dc2eb7b4b8099d4e4c99dc3ef19794289a2cd4c100700 0x04 0x00000000 0xb4",
"",
"P2PKH OP_CHECKBLOCKATHEIGHT"
],
[ [
"0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790281", "0x47 0x304402204710a85181663b32d25c70ec2bbd14adff5ddfff6cb50d09e155ef5f541fc86c0220056b0cc949be9386ecc5f6c2ac0493269031dbb185781db90171b54ac127790281",
"0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG", "0x41 0x048282263212c609d9ea2a6e3e172de238d8c39cabd5ac1ca10646e23fd5f5150811f8a8098557dfe45e8256e830b60ace62d613ac2f7b17bed31b6eaff6e26caf CHECKSIG",

View File

@ -59,7 +59,7 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test)
nSum += nSubsidy; nSum += nSubsidy;
BOOST_CHECK(MoneyRange(nSum)); BOOST_CHECK(MoneyRange(nSum));
} }
BOOST_CHECK_EQUAL(nSum, 12500000000000ULL); BOOST_CHECK_EQUAL(nSum, 1250000000ULL);
// Remainder of first period // Remainder of first period
for (int nHeight = consensusParams.nSubsidySlowStartInterval; nHeight < consensusParams.nSubsidyHalvingInterval + consensusParams.SubsidySlowStartShift(); nHeight ++) { for (int nHeight = consensusParams.nSubsidySlowStartInterval; nHeight < consensusParams.nSubsidyHalvingInterval + consensusParams.SubsidySlowStartShift(); nHeight ++) {
CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams); CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams);

View File

@ -378,13 +378,40 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// subsidy changing // subsidy changing
int nHeight = chainActive.Height(); 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)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
delete pblocktemplate; 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)); BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
delete pblocktemplate; 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 // non-final txs in mempool
SetMockTime(chainActive.Tip()->GetMedianTimePast()+1); SetMockTime(chainActive.Tip()->GetMedianTimePast()+1);

213
src/versionbits.cpp Normal file
View File

@ -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<const CBlockIndex*> 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();
}
}

80
src/versionbits.h Normal file
View File

@ -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 <map>
/** 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<const CBlockIndex*, ThresholdState> 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

View File

@ -50,16 +50,29 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA: case TX_NULL_DATA:
break; break;
case TX_NULL_DATA_REPLAY:
break;
case TX_PUBKEY: case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID(); keyID = CPubKey(vSolutions[0]).GetID();
if (keystore.HaveKey(keyID)) if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE; return ISMINE_SPENDABLE;
break; break;
case TX_PUBKEY_REPLAY:
keyID = CPubKey(vSolutions[0]).GetID();
if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0])); keyID = CKeyID(uint160(vSolutions[0]));
if (keystore.HaveKey(keyID)) if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE; return ISMINE_SPENDABLE;
break; break;
case TX_PUBKEYHASH_REPLAY:
keyID = CKeyID(uint160(vSolutions[0]));
if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
case TX_SCRIPTHASH: case TX_SCRIPTHASH:
{ {
CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
@ -83,6 +96,19 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
return ISMINE_SPENDABLE; return ISMINE_SPENDABLE;
break; 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<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
if (HaveKeys(keys, keystore) == keys.size())
return ISMINE_SPENDABLE;
break;
}
} }
if (keystore.HaveWatchOnly(scriptPubKey)) if (keystore.HaveWatchOnly(scriptPubKey))