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:
commit
df4de58ade
|
@ -1,4 +1,4 @@
|
||||||
Zclassic 1.0.8
|
Zclassic 1.0.8-2
|
||||||
==============
|
==============
|
||||||
|
|
||||||
What is Zclassic?
|
What is Zclassic?
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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>.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
|
|
||||||
struct CDiskBlockPos
|
struct CDiskBlockPos
|
||||||
{
|
{
|
||||||
int nFile;
|
int nFile;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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:"));
|
||||||
|
|
103
src/main.cpp
103
src/main.cpp
|
@ -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
|
||||||
|
|
16
src/main.h
16
src/main.h
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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))
|
||||||
|
|
Loading…
Reference in New Issue