Merge branch 'master' into replay-protection

This commit is contained in:
jc23424 2018-02-23 08:21:01 -05:00 committed by GitHub
commit 94a1db4294
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1271 additions and 232 deletions

View File

@ -35,7 +35,16 @@ Install:
```
### Windows
Get dependencies:
There are two proven ways to build BTCP for Windows:
* On Linux using [Mingw-w64](https://mingw-w64.org/doku.php) cross compiler tool chain. Ubuntu 16.04 Xenial is proven to work and the instructions is for such release.
* On Windows 10 (64-bit version) using [Windows Subsystem for Linux (WSL)](https://msdn.microsoft.com/commandline/wsl/about) and Mingw-w64 cross compiler tool chain.
With Windows 10, Microsoft released a feature called WSL. It basically allows you to run a bash shell directly on Windows in an ubuntu environment. WSL can be installed with other Linux variants, but as mentioned before, the distro proven to work is Ubuntu.
Follow this [link](https://msdn.microsoft.com/en-us/commandline/wsl/install_guide) for installing WSL first
### Building for Windows 64-Bit
1. Get the usual dependencies:
```{r, engine='bash'}
sudo apt-get install \
build-essential pkg-config libc6-dev m4 g++-multilib \
@ -43,12 +52,45 @@ sudo apt-get install \
zlib1g-dev wget bsdmainutils automake mingw-w64
```
Install (Cross-Compiled, building on Windows is not supported yet):
2. Set the default ming32 gcc/g++ compiler option to posix, fix problem with packages in Xenial
```{r, engine='bash'}
# Build
sudo update-alternatives --config x86_64-w64-mingw32-gcc
sudo update-alternatives --config x86_64-w64-mingw32-g++
```
3. Install Rust
```{r, engine='bash'}
curl https://sh.rustup.rs -sSf | sh
source ~/.cargo/env
rustup install stable-x86_64-unknown-linux-gnu
rustup install stable-x86_64-pc-windows-gnu
rustup target add x86_64-pc-windows-gnu
vi ~/.cargo/config
```
and add:
```
[target.x86_64-pc-windows-gnu]
linker = "/usr/bin/x86_64-w64-mingw32-gcc"
```
Note that in WSL, the BTCPrivate source code must be somewhere in the default mount file system. i.e /usr/src/BTCPrivate, and not on /mnt/d/. What this means is that you cannot build directly on the windows system
4. Build for Windows
```{r, engine='bash'}
PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var
./btcputil/build-win.sh -j$(nproc)
```
The exe will save to `src` which you can then move to a windows machine
5. Installation
After building in WSL, you can make a copy of the compiled executables to a directory on your Windows file system. This is done the following way
```{r, engine='bash'}
make install DESTDIR=/mnt/c/btcp/BTCPrivate
```
This will install the executables to `c:\btcp\BTCPrivate
### Mac
Get dependencies:
@ -72,6 +114,10 @@ Install:
./src/btcpd
```
### Additional notes
If you plan to build for windows and linux at the same time, be sure to delete all the built files for whatever you build first. An easy way to do this is by taking the binaries out of the repo, delete all files except the .git folder and then do a git hard reset.
About
--------------

View File

@ -6,14 +6,21 @@ $(package)_download_file=$($(package)_git_commit).tar.gz
$(package)_sha256_hash=a5760a90d4a1045c8944204f29fa2a3cf2f800afee400f88bf89bbfe2cce1279
$(package)_git_commit=91348647a86201a9482ad4ad68398152dc3d635e
$(package)_dependencies=rust
ifeq ($(host_os),mingw32)
rust_build=cargo build --release --lib --target="x86_64-pc-windows-gnu"
rust_target=target/x86_64-pc-windows-gnu/release/rustzcash.lib $($(package)_staging_dir)$(host_prefix)/lib/librustzcash.a
else
rust_build=cargo build --release
rust_target=target/release/librustzcash.a $($(package)_staging_dir)$(host_prefix)/lib/
endif
define $(package)_build_cmds
cargo build --release
$(rust_build)
endef
define $(package)_stage_cmds
mkdir $($(package)_staging_dir)$(host_prefix)/lib/ && \
mkdir $($(package)_staging_dir)$(host_prefix)/include/ && \
cp target/release/librustzcash.a $($(package)_staging_dir)$(host_prefix)/lib/ && \
cp $(rust_target) && \
cp include/librustzcash.h $($(package)_staging_dir)$(host_prefix)/include/
endef
endef

View File

@ -5,7 +5,9 @@ $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.t
$(package)_sha256_hash_linux=48621912c242753ba37cad5145df375eeba41c81079df46f93ffb4896542e8fd
$(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz
$(package)_sha256_hash_darwin=2d08259ee038d3a2c77a93f1a31fc59e7a1d6d1bbfcba3dba3c8213b2e5d1926
$(package)_file_name_mingw32=rust-$($(package)_version)-x86_64-pc-windows-gnu.tar.gz
$(package)_sha256_hash_mingw32=523cd248363afdc4e2c0e1f219607897b6925294a33154d7e67224addfd15eb0
define $(package)_stage_cmds
./install.sh --destdir=$($(package)_staging_dir) --prefix=$(host_prefix)/native --disable-ldconfig
endef
endef

View File

@ -58,6 +58,7 @@ testScriptsExt=(
'smartfees.py'
'maxblocksinflight.py'
'invalidblockrequest.py'
'p2p-versionbits-warning.py'
# 'forknotify.py'
'p2p-acceptblock.py'
);

View File

@ -0,0 +1,160 @@
#!/usr/bin/env python2
# Copyright (c) 2016 The Bitcoin Core developers
# Distributed under the MIT/X11 software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
import time
from test_framework.blocktools import create_block, create_coinbase
'''
Test version bits' warning system.
Generate chains with block versions that appear to be signalling unknown
soft-forks, and test that warning alerts are generated.
'''
VB_PERIOD = 144 # versionbits period length for regtest
VB_THRESHOLD = 108 # versionbits activation threshold for regtest
VB_TOP_BITS = 0x20000000
VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment
# TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending
# p2p messages to a node, generating the messages in the main testing logic.
class TestNode(NodeConnCB):
def __init__(self):
NodeConnCB.__init__(self)
self.connection = None
self.ping_counter = 1
self.last_pong = msg_pong()
def add_connection(self, conn):
self.connection = conn
def on_inv(self, conn, message):
pass
# Wrapper for the NodeConn's send_message function
def send_message(self, message):
self.connection.send_message(message)
def on_pong(self, conn, message):
self.last_pong = message
# Sync up with the node after delivery of a block
def sync_with_ping(self, timeout=30):
self.connection.send_message(msg_ping(nonce=self.ping_counter))
received_pong = False
sleep_time = 0.05
while not received_pong and timeout > 0:
time.sleep(sleep_time)
timeout -= sleep_time
with mininode_lock:
if self.last_pong.nonce == self.ping_counter:
received_pong = True
self.ping_counter += 1
return received_pong
class VersionBitsWarningTest(BitcoinTestFramework):
def setup_chain(self):
initialize_chain_clean(self.options.tmpdir, 1)
def setup_network(self):
self.nodes = []
self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt")
# Open and close to create zero-length file
with open(self.alert_filename, 'w') as f:
pass
self.node_options = ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""]
self.nodes.append(start_node(0, self.options.tmpdir, self.node_options))
import re
self.vb_pattern = re.compile("^Warning.*versionbit")
# Send numblocks blocks via peer with nVersionToUse set.
def send_blocks_with_version(self, peer, numblocks, nVersionToUse):
tip = self.nodes[0].getbestblockhash()
height = self.nodes[0].getblockcount()
block_time = self.nodes[0].getblockheader(tip)["time"]+1
tip = int(tip, 16)
for i in xrange(numblocks):
block = create_block(tip, create_coinbase(height+1), block_time)
block.nVersion = nVersionToUse
block.solve()
peer.send_message(msg_block(block))
block_time += 1
height += 1
tip = block.sha256
peer.sync_with_ping()
def test_versionbits_in_alert_file(self):
with open(self.alert_filename, 'r') as f:
alert_text = f.read()
assert(self.vb_pattern.match(alert_text))
def run_test(self):
# Setup the p2p connection and start up the network thread.
test_node = TestNode()
connections = []
connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], test_node))
test_node.add_connection(connections[0])
NetworkThread().start() # Start up network handling in another thread
# Test logic begins here
test_node.wait_for_verack()
# 1. Have the node mine one period worth of blocks
self.nodes[0].generate(VB_PERIOD)
# 2. Now build one period of blocks on the tip, with < VB_THRESHOLD
# blocks signaling some unknown bit.
nVersion = VB_TOP_BITS | (1<<VB_UNKNOWN_BIT)
self.send_blocks_with_version(test_node, VB_THRESHOLD-1, nVersion)
# Fill rest of period with regular version blocks
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1)
# Check that we're not getting any versionbit-related errors in
# getinfo()
assert(not self.vb_pattern.match(self.nodes[0].getinfo()["errors"]))
# 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling
# some unknown bit
self.send_blocks_with_version(test_node, VB_THRESHOLD, nVersion)
self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD)
# Might not get a versionbits-related alert yet, as we should
# have gotten a different alert due to more than 51/100 blocks
# being of unexpected version.
# Check that getinfo() shows some kind of error.
assert(len(self.nodes[0].getinfo()["errors"]) != 0)
# Mine a period worth of expected blocks so the generic block-version warning
# is cleared, and restart the node. This should move the versionbit state
# to ACTIVE.
self.nodes[0].generate(VB_PERIOD)
stop_node(self.nodes[0], 0)
wait_bitcoinds()
# Empty out the alert file
with open(self.alert_filename, 'w') as f:
pass
self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])
# Connecting one block should be enough to generate an error.
self.nodes[0].generate(1)
assert(len(self.nodes[0].getinfo()["errors"]) != 0)
stop_node(self.nodes[0], 0)
wait_bitcoinds()
self.test_versionbits_in_alert_file()
# Test framework expects the node to still be running...
self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug", "-logtimemicros=1", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])
if __name__ == '__main__':
VersionBitsWarningTest().main()

View File

@ -184,6 +184,7 @@ BITCOIN_CORE_H = \
utiltime.h \
validationinterface.h \
version.h \
versionbits.h \
wallet/asyncrpcoperation_sendmany.h \
wallet/crypter.h \
wallet/db.h \
@ -240,6 +241,7 @@ libbitcoin_server_a_SOURCES = \
txdb.cpp \
txmempool.cpp \
validationinterface.cpp \
versionbits.cpp \
$(BITCOIN_CORE_H) \
$(LIBZCASH_H)

View File

@ -86,6 +86,7 @@ BITCOIN_TESTS =\
test/timedata_tests.cpp \
test/torcontrol_tests.cpp \
test/transaction_tests.cpp \
test/versionbits_tests.cpp \
test/uint256_tests.cpp \
test/univalue_tests.cpp \
test/util_tests.cpp \

View File

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

View File

@ -37,7 +37,7 @@ public:
CMainParams() {
strNetworkID = "main";
strCurrencyUnits = "BTCP";
consensus.fCoinbaseMustBeProtected = false;
consensus.fCoinbaseMustBeProtected = true;
consensus.nSubsidySlowStartInterval = 2;
consensus.nSubsidyHalvingInterval = 840000;
consensus.nMajorityEnforceBlockUpgrade = 750;
@ -50,6 +50,14 @@ public:
consensus.nPowMaxAdjustUp = 16; // 16% adjustment up
consensus.nPowTargetSpacing = 2.5 * 60;
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.nPowDifficultyBombHeight = 600000;
consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
pchMessageStart[0] = 0xa8;
pchMessageStart[1] = 0xea;
@ -188,8 +196,12 @@ public:
//// "t3fmYHU2DnVaQgPhDs6TMFVmyC3qbWEWgXN", /* main-index: 52*/
//// "t3T4WmAp6nrLkJ24iPpGeCe1fSWTPv47ASG", /* main-index: 53*/
//// "t3fP6GrDM4QVwdjFhmCxGNbe7jXXXSDQ5dv", /* main-index: 54*/
};
};
assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight());
//current ZCL height is 200K-300K, this value here is placeholder, it will have to be changed to correct fork block height
nForkStartHeight = 1000000;
nForkHeightRange = 65000;
}
};
static CMainParams mainParams;
@ -202,17 +214,28 @@ public:
CTestNetParams() {
strNetworkID = "test";
strCurrencyUnits = "BTCPT";
consensus.fCoinbaseMustBeProtected = false;
consensus.fCoinbaseMustBeProtected = true;
consensus.nMajorityEnforceBlockUpgrade = 51;
consensus.nMajorityRejectBlockOutdated = 75;
consensus.nMajorityWindow = 400;
consensus.powLimit = uint256S("07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow);
consensus.fPowAllowMinDifficultyBlocks = true;
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
consensus.nPowDifficultyBombHeight = 600000;
pchMessageStart[0] = 0xf6;
pchMessageStart[1] = 0x1b;
pchMessageStart[2] = 0xf6;
pchMessageStart[3] = 0xd6;
vAlertPubKey = ParseHex("048679fb891b15d0cada9692047fd0ae26ad8bfb83fabddbb50334ee5bc0683294deb410be20513c5af6e7b9cec717ade82b27080ee6ef9a245c36a795ab044bb3");
nDefaultPort = 17933;
nPruneAfterHeight = 1000;
@ -300,6 +323,15 @@ public:
assert(maxUint/UintToArith256(consensus.powLimit) >= consensus.nPowAveragingWindow);
consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down
consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL;
consensus.nPowDifficultyBombHeight = 600000;
pchMessageStart[0] = 0xaa;
pchMessageStart[1] = 0xe8;
pchMessageStart[2] = 0x3f;

View File

@ -81,6 +81,9 @@ public:
std::string GetFoundersRewardAddressAtIndex(int i) const;
/** Enforce coinbase consensus rule in regtest mode */
void SetRegTestCoinbaseMustBeProtected() { consensus.fCoinbaseMustBeProtected = true; }
uint64_t ForkStartHeight() const { return nForkStartHeight; };
uint64_t ForkHeightRange() const { return nForkHeightRange; };
protected:
CChainParams() {}
@ -106,6 +109,9 @@ protected:
bool fTestnetToBeDeprecatedFieldRPC = false;
Checkpoints::CCheckpointData checkpointData;
std::vector<std::string> vFoundersRewardAddress;
uint64_t nForkStartHeight;
uint64_t nForkHeightRange;
};
/**

View File

@ -13,7 +13,7 @@ static const int32_t MIN_TX_VERSION = 1;
/** The maximum allowed size for a serialized block, in bytes (network rule) */
static const unsigned int MAX_BLOCK_SIZE = 2000000;
/** The maximum allowed number of signature check operations in a block (network rule) */
static const unsigned int MAX_BLOCK_SIGOPS = 20000;
static const unsigned int MAX_BLOCK_SIGOPS = 50000;
/** The maximum size of a transaction (network rule) */
static const unsigned int MAX_TX_SIZE = 100000;
/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */

View File

@ -9,6 +9,25 @@
#include "uint256.h"
namespace Consensus {
enum DeploymentPos
{
DEPLOYMENT_TESTDUMMY,
MAX_VERSION_BITS_DEPLOYMENTS
};
/**
* Struct for each individual consensus rule change using BIP9.
*/
struct BIP9Deployment {
/** Bit position to select the particular bit in nVersion. */
int bit;
/** Start MedianTime for version bits miner confirmation. Can be a date in the past */
int64_t nStartTime;
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout;
};
/**
* Parameters that influence chain consensus.
*/
@ -40,6 +59,16 @@ struct Params {
int nMajorityEnforceBlockUpgrade;
int nMajorityRejectBlockOutdated;
int nMajorityWindow;
/**
* Minimum blocks including miner confirmation of the total of 2016 blocks in a retargetting period,
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
* Examples: 1916 for 95%, 1512 for testchains.
*/
uint32_t nRuleChangeActivationThreshold;
uint32_t nMinerConfirmationWindow;
BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS];
/** Proof of work parameters */
uint256 powLimit;
bool fPowAllowMinDifficultyBlocks;
@ -47,9 +76,10 @@ struct Params {
int64_t nPowMaxAdjustDown;
int64_t nPowMaxAdjustUp;
int64_t nPowTargetSpacing;
int64_t AveragingWindowTimespan() const { return nPowAveragingWindow * nPowTargetSpacing; }
int64_t MinActualTimespan() const { return (AveragingWindowTimespan() * (100 - nPowMaxAdjustUp )) / 100; }
int64_t MaxActualTimespan() const { return (AveragingWindowTimespan() * (100 + nPowMaxAdjustDown)) / 100; }
int64_t AveragingWindowTimespan(bool isFork = false) const { return nPowAveragingWindow * nPowTargetSpacing / (isFork ? 20 : 1); }
int64_t MinActualTimespan(bool isFork = false) const { return (AveragingWindowTimespan(isFork) * (100 - nPowMaxAdjustUp )) / 100; }
int64_t MaxActualTimespan(bool isFork = false) const { return (AveragingWindowTimespan(isFork) * (100 + nPowMaxAdjustDown)) / 100; }
};
} // namespace Consensus

View File

@ -490,7 +490,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("-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))
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 FORK_CB_INPUT
strUsage += HelpMessageGroup(_("Fork :"));
@ -898,8 +898,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#ifdef FORK_CB_INPUT
forkUtxoPath = GetArg("-utxo-path", "");
forkStartHeight = GetArg("-fork-startheight", FORK_BLOCK_HEIGHT_START);
forkHeightRange = GetArg("-fork-heightrange", FORK_BLOCK_HEIGHT_RANGE);
forkStartHeight = GetArg("-fork-startheight", chainparams.ForkStartHeight());
forkHeightRange = GetArg("-fork-heightrange", chainparams.ForkHeightRange());
forkCBPerBlock = GetArg("-fork-cbperblock", FORK_COINBASE_PER_BLOCK);
#endif

View File

@ -27,6 +27,7 @@
#include "util.h"
#include "utilmoneystr.h"
#include "validationinterface.h"
#include "versionbits.h"
#include "wallet/asyncrpcoperation_sendmany.h"
#include <sstream>
@ -76,9 +77,11 @@ bool fAlerts = DEFAULT_ALERTS;
#include <boost/range/combine.hpp>
std::string forkUtxoPath;
int64_t forkStartHeight = FORK_BLOCK_HEIGHT_START;
int64_t forkHeightRange = FORK_BLOCK_HEIGHT_RANGE;
int64_t forkCBPerBlock = FORK_COINBASE_PER_BLOCK;
int64_t forkStartHeight;
int64_t forkHeightRange;
int64_t forkCBPerBlock;
uint256 forkExtraHashSentinel = uint256S("f0f0f0f0fafafafaffffffffffffffffffffffffffffffffafafafaf0f0f0f0f");
uint256 hashPid = GetRandHash();
std::string GetUTXOFileName(int nHeight)
{
@ -1413,19 +1416,11 @@ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos
return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString());
}
#ifdef FORK_CB_INPUT
if (!isForkBlock(nHeight)) { //when block is in fork region - don't check Solution and PoW
#endif
// Check the header
if (!(CheckEquihashSolution(&block, Params()) &&
CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())))
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
#ifdef FORK_CB_INPUT
}
#endif
return true;
}
@ -1712,6 +1707,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
// Ensure that coinbases cannot be spent to transparent outputs
// Disabled on regtest
if (fCoinbaseEnforcedProtectionEnabled &&
!isForkBlock(coins->nHeight) &&
consensusParams.fCoinbaseMustBeProtected &&
!tx.vout.empty()) {
return state.Invalid(
@ -2102,6 +2098,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 nTimeConnect = 0;
static int64_t nTimeIndex = 0;
@ -2465,24 +2506,43 @@ void static UpdateTip(CBlockIndex *pindexNew) {
// Check the version of the last 100 blocks to see if we need to upgrade:
static bool fWarned = false;
if (!IsInitialBlockDownload() && !fWarned)
if (!IsInitialBlockDownload())
{
int nUpgraded = 0;
const CBlockIndex* pindex = chainActive.Tip();
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
WarningBitsConditionChecker checker(bit);
ThresholdState state = checker.GetStateFor(pindex, chainParams.GetConsensus(), warningcache[bit]);
if (state == THRESHOLD_ACTIVE || state == THRESHOLD_LOCKED_IN) {
if (state == THRESHOLD_ACTIVE) {
strMiscWarning = strprintf(_("Warning: unknown new rules activated (versionbit %i)"), bit);
if (!fWarned) {
CAlert::Notify(strMiscWarning, true);
fWarned = true;
}
} else {
LogPrintf("%s: unknown new rules are about to activate (versionbit %i)\n", __func__, bit);
}
}
}
for (int i = 0; i < 100 && pindex != NULL; i++)
{
if (pindex->nVersion > CBlock::CURRENT_VERSION)
int32_t nExpectedVersion = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus());
if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0)
++nUpgraded;
pindex = pindex->pprev;
}
if (nUpgraded > 0)
LogPrintf("%s: %d of last 100 blocks above version %d\n", __func__, nUpgraded, (int)CBlock::CURRENT_VERSION);
LogPrintf("%s: %d of last 100 blocks have unexpected version\n", __func__, nUpgraded);
if (nUpgraded > 100/2)
{
// strMiscWarning is read by GetWarnings(), called by the JSON-RPC code to warn the user:
strMiscWarning = _("Warning: This version is obsolete; upgrade required!");
CAlert::Notify(strMiscWarning, true);
fWarned = true;
strMiscWarning = _("Warning: Unknown block versions being mined! It's possible unknown rules are in effect");
if (!fWarned) {
CAlert::Notify(strMiscWarning, true);
fWarned = true;
}
}
}
}
@ -2595,6 +2655,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
mempool.check(pcoinsTip);
// Update chainActive & related variables.
UpdateTip(pindexNew);
LogPrintf("NEWTIP %s %d %d %s %s\n",
hashPid.ToString(), pindexNew->nHeight, GetTime(),
pblock->vtx[0].vin[0].scriptSig.ToString(), pblock->GetHash().ToString());
// Tell wallet about transactions that went from mempool
// to conflicted:
BOOST_FOREACH(const CTransaction &tx, txConflicted) {
@ -3054,10 +3118,6 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f
return state.DoS(100, error("CheckBlockHeader(): block version too low"),
REJECT_INVALID, "version-too-low");
#ifdef FORK_CB_INPUT
if (!isTipInForkRange()) { //when in FORK mode (tip is in forking region) - don't check Solution and PoW
#endif
// Check Equihash solution is valid
if (fCheckPOW && !CheckEquihashSolution(&block, Params()))
return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),
@ -3068,10 +3128,6 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f
return state.DoS(50, error("CheckBlockHeader(): proof of work failed"),
REJECT_INVALID, "high-hash");
#ifdef FORK_CB_INPUT
}
#endif
// Check timestamp
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"),
@ -3121,20 +3177,22 @@ bool CheckBlock(const CBlock& block, CValidationState& state,
return state.DoS(100, error("CheckBlock(): first tx is not coinbase"),
REJECT_INVALID, "bad-cb-missing");
#ifdef FORK_CB_INPUT
if (isTipInForkRange()) { //when in FORK mode (tip is in forking region) blocks might have up to fork pre-defined value coinbases
//fork blocks might have up to fork pre-defined value coinbases and nothing else
if (looksLikeForkBlockHeader(block)) {
if (block.vtx.size() > forkCBPerBlock)
return state.DoS(100, error("CheckBlock(): it is forking block but there are more than %d coinbase txns", forkCBPerBlock),
REJECT_INVALID, "bad-cb-multiple");
return state.DoS(100, error("CheckBlock(): fork block: too many txns %d > %d coinbase txns", block.vtx.size(), forkCBPerBlock),
REJECT_INVALID, "bad-fork-too-many-tx");
for (unsigned int i = 1; i < block.vtx.size(); i++)
if (!block.vtx[i].IsCoinBase())
return state.DoS(100, error("CheckBlock(): fork block: non-coinbase found"),
REJECT_INVALID, "bad-fork-non-cb");
} else {
#endif
for (unsigned int i = 1; i < block.vtx.size(); i++)
if (block.vtx[i].IsCoinBase())
return state.DoS(100, error("CheckBlock(): more than one coinbase"),
REJECT_INVALID, "bad-cb-multiple");
#ifdef FORK_CB_INPUT
}
#endif
// Check transactions
BOOST_FOREACH(const CTransaction& tx, block.vtx)
@ -3165,16 +3223,21 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta
int nHeight = pindexPrev->nHeight+1;
// because we bypass checks using the indicia in the header
// we reject any blocks that look like fork blocks but really
// are non-fork blocks
if(looksLikeForkBlockHeader(block) && !isForkBlock(nHeight))
return state.DoS(100, error("%s: non-fork block looks like fork block", __func__),
REJECT_INVALID, "bad-fork-hashreserved");
if(!looksLikeForkBlockHeader(block) && isForkBlock(nHeight))
return state.DoS(100, error("%s: fork block does not look like fork block", __func__),
REJECT_INVALID, "bad-fork-hashreserved");
// Check proof of work
#ifdef FORK_CB_INPUT
if (!isForkBlock(nHeight)) { //If current block is FORK, don't check work required
#endif
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams))
return state.DoS(100, error("%s: incorrect proof of work", __func__),
REJECT_INVALID, "bad-diffbits");
#ifdef FORK_CB_INPUT
}
#endif
// Check timestamp against prev
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
@ -3329,7 +3392,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
if (!if_utxo.is_open()) {
LogPrintf("AcceptBlock(): FORK Block - Cannot open UTXO file - %s\n", utxo_file_path);
} else {
LogPrintf("AcceptBlock(): FORK Block - Validating block - %u with UTXO file - %s\n", nHeight, utxo_file_path);
LogPrintf("AcceptBlock(): FORK Block - Validating block - %u / %s with UTXO file - %s\n",
nHeight, block.GetHash().ToString(), utxo_file_path);
vector<pair<uint64_t, CScript> > txFromFile;
txFromFile.reserve(forkCBPerBlock);
@ -3689,11 +3753,7 @@ bool static LoadBlockIndexDB()
{
const CChainParams& chainparams = Params();
#ifdef FORK_CB_INPUT
if (!pblocktree->LoadBlockIndexGuts(forkStartHeight, forkStartHeight+forkHeightRange))
#else
if (!pblocktree->LoadBlockIndexGuts())
#endif
return false;
boost::this_thread::interruption_point();
@ -3931,6 +3991,10 @@ void UnloadBlockIndex()
setDirtyFileInfo.clear();
mapNodeState.clear();
recentRejects.reset(NULL);
versionbitscache.Clear();
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
warningcache[b].clear();
}
BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) {
delete entry.second;
@ -5746,7 +5810,11 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
return strprintf("CBlockFileInfo(blocks=%u, size=%u, heights=%u...%u, time=%s...%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst), DateTimeStrFormat("%Y-%m-%d", nTimeLast));
}
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
LOCK(cs_main);
return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache);
}
class CMainCleanup
{

View File

@ -25,6 +25,7 @@
#include "tinyformat.h"
#include "txmempool.h"
#include "uint256.h"
#include "versionbits.h"
#include <algorithm>
#include <exception>
@ -257,6 +258,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
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);
struct CNodeStateStats {
int nMisbehavior;
int nSyncHeight;
@ -538,19 +542,25 @@ extern CBlockTreeDB *pblocktree;
*/
int GetSpendHeight(const CCoinsViewCache& inputs);
extern VersionBitsCache versionbitscache;
/**
* Determine what nVersion a new block should use.
*/
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params);
namespace Consensus {
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams);
}
#ifdef FORK_CB_INPUT
#define FORK_BLOCK_HEIGHT_START 1000000 //current ZCL height is 200K-300K, this value here is placeholder, it will have to be changed to correct fork block height
#define FORK_BLOCK_HEIGHT_RANGE 65000
#define FORK_COINBASE_PER_BLOCK 10000
extern std::string forkUtxoPath;
extern int64_t forkStartHeight;
extern int64_t forkHeightRange;
extern int64_t forkCBPerBlock;
extern uint256 forkExtraHashSentinel;
std::string GetUTXOFileName(int nHeight);
@ -587,14 +597,12 @@ inline bool isForkBlock(int nHeight)
{
return (nHeight > forkStartHeight && nHeight <= forkStartHeight + forkHeightRange);
}
inline bool isTipInForkRange()
inline bool looksLikeForkBlockHeader(const CBlockHeader& header)
{
return chainActive.Tip()? isForkBlock(chainActive.Tip()->nHeight): false;
}
inline bool isNextTipInForkRange()
{
return chainActive.Tip()? isForkBlock(chainActive.Tip()->nHeight + 1): false;
return header.hashReserved == forkExtraHashSentinel;
}
inline uint64_t bytes2uint64(char *array)
{
uint64_t x =
@ -615,4 +623,6 @@ inline bool isForkEnabled(int nHeight)
return nHeight > forkStartHeight;
}
extern uint256 hashPid;
#endif // BITCOIN_MAIN_H

View File

@ -114,72 +114,120 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams,
#ifdef FORK_CB_INPUT
CBlockTemplate* CreateNewForkBlock(bool& bFileNotFound)
{
CBlockIndex* pindexPrev = chainActive.Tip();
const int nHeight = pindexPrev->nHeight + 1;
/*
Because CreateNewForkBlock does file io when
reading utxos, we grab the main lock to peek
at the tip, release it to read the file and
fill in the template and then reacquire it
at the end to check if the active tip changed
while we were doing the above
*/
CBlockTemplate * ret;
int tipHeight;
int snappedHeight;
const CChainParams& chainparams = Params();
{
LOCK(cs_main);
tipHeight = chainActive.Tip()->nHeight;
}
do
{
snappedHeight = tipHeight;
ret = CreateNewForkBlock(bFileNotFound, snappedHeight + 1);
{
LOCK(cs_main);
CBlockIndex* pindexPrev = chainActive.Tip();
CBlock *pblock = &ret->block; // pointer for convenience
tipHeight = pindexPrev->nHeight;
if(tipHeight != snappedHeight)
{
LogPrintf("WARN: tip changed from %u to %u while generating block template\n",
snappedHeight, tipHeight);
continue;
}
// if we are good then fill in the final details
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, Params().GetConsensus());
CValidationState state;
if (!TestBlockValidity(state, *pblock, pindexPrev, false, false))
throw std::runtime_error("CreateNewForkBlock(): TestBlockValidity failed");
}
} while(snappedHeight != tipHeight);
return ret;
}
CBlockTemplate* CreateNewForkBlock(bool& bFileNotFound, const int nHeight)
{
const CChainParams& chainparams = Params();
const int nForkHeight = nHeight - forkStartHeight;
string utxo_file_path = GetUTXOFileName(nHeight);
std::ifstream if_utxo(utxo_file_path, std::ios::binary | std::ios::in);
if (!if_utxo.is_open()) {
bFileNotFound = true;
LogPrintf("CreateNewForkBlock(): Cannot open UTXO file - %s\n", utxo_file_path);
LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: Cannot open UTXO file - %s\n",
nHeight, nForkHeight, forkHeightRange, utxo_file_path);
return NULL;
}
const CChainParams& chainparams = Params();
// Create new block
std::unique_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
if(!pblocktemplate.get())
return NULL;
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (Params().MineBlocksOnDemand())
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
// Largest block you're willing to create:
unsigned int nBlockMaxSize = (unsigned int)(MAX_BLOCK_SIZE-1000);
// Minimum block size you want to create; block will be filled with free transactions
// until there are no more or the block reaches this size:
unsigned int nBlockMinSize = DEFAULT_BLOCK_MIN_SIZE;
uint64_t nBlockTotalAmount = 0;
uint64_t nBlockSize = 1000;
uint64_t nBlockTx = 0;
uint64_t nBlockSigOps = 100;
while (if_utxo && nBlockTx < forkCBPerBlock) {
char term = 0;
char coin[8] = {};
if (!if_utxo.read(coin, 8)) {
LogPrintf("CreateNewForkBlock(): UTXO file corrupted? - No more data (Amount)\n");
// the last file may be shorter than forkCBPerBlock
if(!if_utxo.eof() || nForkHeight != forkHeightRange)
LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: UTXO file corrupted? - No more data (Amount)\n",
nHeight, nForkHeight, forkHeightRange);
break;
}
char pubkeysize[8] = {};
if (!if_utxo.read(pubkeysize, 8)) {
LogPrintf("CreateNewForkBlock(): UTXO file corrupted? - Not more data (PubKeyScript size)\n");
LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: UTXO file corrupted? - Not more data (PubKeyScript size)\n",
nHeight, nForkHeight, forkHeightRange);
break;
}
int pbsize = bytes2uint64(pubkeysize);
//LogPrintf("CreateNewForkBlock():PubKeyScript size = %d\n", pbsize);
if (pbsize == 0) {
LogPrintf("CreateNewForkBlock(): Warning! UTXO file corrupted? PubKeyScript size = 0\n");
LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: UTXO file corrupted? - PubKeyScript size = 0\n",
nHeight, nForkHeight, forkHeightRange);
//but proceed
}
std::unique_ptr<char[]> pubKeyScript(new char[pbsize]);
if (!if_utxo.read(&pubKeyScript[0], pbsize)) {
LogPrintf("CreateNewForkBlock(): UTXO file corrupted? Not more data (PubKeyScript)\n");
LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: UTXO file corrupted? Not more data (PubKeyScript)\n",
nHeight, nForkHeight, forkHeightRange);
break;
}
uint64_t amount = bytes2uint64(coin);
//LogPrintf("CreateNewForkBlock(): txn %u has ammount %u\n", nBlockTx, amount);
// Add coinbase tx's
CMutableTransaction txNew;
@ -190,34 +238,45 @@ CBlockTemplate* CreateNewForkBlock(bool& bFileNotFound)
unsigned char* pks = (unsigned char*)pubKeyScript.get();
txNew.vout[0].scriptPubKey = CScript(pks, pks+pbsize);
//LogPrintf("CreateNewForkBlock(): ScriptPubKey: %s\n", txNew.vout[0].scriptPubKey.ToString());
txNew.vout[0].nValue = amount;
txNew.vin[0].scriptSig = CScript() << nHeight+nBlockTx << OP_0;
if(nBlockTx == 0)
txNew.vin[0].scriptSig = CScript() << nHeight << CScriptNum(nBlockTx) << ToByteVector(hashPid) << OP_0;
else
txNew.vin[0].scriptSig = CScript() << nHeight << CScriptNum(nBlockTx) << OP_0;
unsigned int nTxSize = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBlockSize + nTxSize >= nBlockMaxSize)
break;//We cannot skip transaction here as in regular case - or we will loose that skipped transaction
pblock->vtx.push_back(txNew);
pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOps.push_back(-1); // updated at end
nBlockSize += nTxSize;
++nBlockTx;
if (!if_utxo.read(&term, 1)) {
LogPrintf("CreateNewForkBlock(): No more data (record separator)\n");
{
LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: %u: block would exceed max size\n",
nHeight, nForkHeight, forkHeightRange, nBlockTx);
break;
}
if (term != '\n') {
//This maybe not an error, but warning none the less
LogPrintf("CreateNewForkBlock(): Warning! No record separator ('0xA') was found\n");
if_utxo.seekg(-1, ios_base::cur); //move one char back - if it is not a separator, maybe there is not separators at all
// Legacy limits on sigOps:
unsigned int nTxSigOps = GetLegacySigOpCount(txNew);
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
{
LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: %u: block would exceed max sigops\n",
nHeight, nForkHeight, forkHeightRange, nBlockTx);
break;
}
pblock->vtx.push_back(txNew);
pblocktemplate->vTxFees.push_back(0);
pblocktemplate->vTxSigOps.push_back(nTxSigOps);
nBlockSize += nTxSize;
nBlockSigOps += nTxSigOps;
nBlockTotalAmount += amount;
++nBlockTx;
if (!if_utxo.read(&term, 1) || term != '\n') {
LogPrintf("ERROR: CreateNewForkBlock(): [%u, %u of %u]: invalid record separator\n",
nHeight, nForkHeight, forkHeightRange);
break;
}
}
LogPrintf("CreateNewForkBlock(): %u tnxs and total size %u\n", nBlockTx, nBlockSize);
LogPrintf("CreateNewForkBlock(): [%u, %u of %u]: txns=%u size=%u amount=%u sigops=%u\n",
nHeight, nForkHeight, forkHeightRange, nBlockTx, nBlockSize, nBlockTotalAmount, nBlockSigOps);
// Randomise nonce
arith_uint256 nonce = UintToArith256(GetRandHash());
@ -227,22 +286,8 @@ CBlockTemplate* CreateNewForkBlock(bool& bFileNotFound)
pblock->nNonce = ArithToUint256(nonce);
// Fill in header
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
pblock->hashReserved = uint256();
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
pblock->nBits = 0x207fffff; // Difficulty = 1
//0x207fffff -> 0x 7fffff00 00000000 00000000 00000000 00000000 00000000 00000000 00000000
//alternativly
//0x1d00ffff -> 0x 00000000 ffff0000 00000000 00000000 00000000 00000000 00000000 00000000
// or
//0x1f07ffff -> 0x 007fffff 00000000 00000000 00000000 00000000 00000000 00000000 00000000
pblock->hashReserved = forkExtraHashSentinel;
pblock->nSolution.clear();
pblocktemplate->vTxSigOps[0] = 0; //NO SigOps
CValidationState state;
if (!TestBlockValidity(state, *pblock, pindexPrev, false, false))
throw std::runtime_error("CreateNewForkBlock(): TestBlockValidity failed");
return pblocktemplate.release();
}
@ -257,11 +302,6 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
return NULL;
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (Params().MineBlocksOnDemand())
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
// Add dummy coinbase tx as first transaction
pblock->vtx.push_back(CTransaction());
pblocktemplate->vTxFees.push_back(-1); // updated at end
@ -291,6 +331,12 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
const int nHeight = pindexPrev->nHeight + 1;
pblock->nTime = GetAdjustedTime();
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand())
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
CCoinsViewCache view(pcoinsTip);
// Priority order to process transactions
@ -593,13 +639,10 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese
static bool ProcessBlockFound(CBlock* pblock)
#endif // ENABLE_WALLET
{
#ifdef FORK_CB_INPUT
if (!isNextTipInForkRange()) {
#endif
LogPrintf("%s\n", pblock->ToString());
#ifdef FORK_CB_INPUT
if (!looksLikeForkBlockHeader(*pblock)) {
LogPrintf("%s\n", pblock->ToString());
}
#endif
LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue));
// Found a solution
@ -642,7 +685,7 @@ void static BitcoinMiner(CWallet *pwallet)
void static BitcoinMiner()
#endif
{
LogPrintf("BTCPrivate Miner started\n");
LogPrintf("BTCPrivate Miner started %s\n");
SetThreadPriority(THREAD_PRIORITY_LOWEST);
RenameThread("btcp-miner");
const CChainParams& chainparams = Params();
@ -672,10 +715,7 @@ void static BitcoinMiner()
);
miningTimer.start();
#ifdef FORK_CB_INPUT
bool bForkModeStarted = false;
#endif
try {
while (true) {
if (chainparams.MiningRequiresPeers()) {
@ -704,8 +744,19 @@ void static BitcoinMiner()
//
unique_ptr<CBlockTemplate> pblocktemplate;
#ifdef FORK_CB_INPUT
bool isNextBlockFork = isForkBlock(pindexPrev->nHeight+1);
bool mineOnlyFork = GetBoolArg("-forkmineonly", false);
if(mineOnlyFork && !isNextBlockFork) {
miningTimer.stop();
do {
MilliSleep(100);
pindexPrev = chainActive.Tip();
isNextBlockFork = isForkBlock(pindexPrev->nHeight + 1);
} while(!isNextBlockFork);
miningTimer.start();
}
if (isNextBlockFork) {
if (!bForkModeStarted) {
LogPrintf("BTCPrivate Miner: switching into fork mode\n");
@ -717,28 +768,26 @@ void static BitcoinMiner()
if (!pblocktemplate.get()) {
if (bFileNotFound) {
MilliSleep(1000);
continue;
continue;
} else {
LogPrintf("Error in BTCPrivate Miner: Cannot create Fork Block\n");
return;
}
}
pblock = &pblocktemplate->block;
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
n = 48; k = 5;
LogPrintf("Running BTCPrivate Miner with %u forking transactions in block (%u bytes) and N = %d, K = %d\n", pblock->vtx.size(),
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION),
n, k);
LogPrintf("Running BTCPrivate Miner with %u forking transactions in block (%u bytes) and N = %d, K = %d\n",
pblock->vtx.size(),
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION),
n, k);
} else {
//if not in forking mode and/or provided file is read to the end - exit
if (bForkModeStarted) {
LogPrintf("BTCPrivate Miner: Fork is done - switching back to regular miner\n");
n = chainparams.EquihashN();
k = chainparams.EquihashK();
bForkModeStarted = false;
}
#endif
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
#ifdef ENABLE_WALLET
@ -759,13 +808,12 @@ void static BitcoinMiner()
pblock = &pblocktemplate->block;
LogPrintf("Running BTCPrivate Miner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
#ifdef FORK_CB_INPUT
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
} //else
#endif
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
//
// Search
@ -798,38 +846,31 @@ void static BitcoinMiner()
solver, pblock->nNonce.ToString());
std::function<bool(std::vector<unsigned char>)> validBlock =
[&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams
[&pblock, &hashTarget, &m_cs, &cancelSolver, &chainparams
#ifdef ENABLE_WALLET
, &pwallet, &reservekey
, &pwallet, &reservekey
#endif
#ifdef FORK_CB_INPUT
, &isNextBlockFork
#endif
] (std::vector<unsigned char> soln) {
, &isNextBlockFork] (std::vector<unsigned char> soln) {
// Write the solution to the hash and compute the result.
LogPrint("pow", "- Checking solution against target\n");
pblock->nSolution = soln;
solutionTargetChecks.increment();
#ifdef FORK_CB_INPUT
if (!isNextBlockFork) {
#endif
if (UintToArith256(pblock->GetHash()) > hashTarget) {
return false;
}
#ifdef FORK_CB_INPUT
}
#endif
// Found a solution
SetThreadPriority(THREAD_PRIORITY_NORMAL);
LogPrintf("BTCPrivate Miner:\n");
LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", pblock->GetHash().GetHex(), hashTarget.GetHex());
if (ProcessBlockFound(pblock
#ifdef ENABLE_WALLET
if (ProcessBlockFound(pblock, *pwallet, reservekey)) {
#else
if (ProcessBlockFound(pblock)) {
, *pwallet, reservekey
#endif
)) {
// Ignore chain updates caused by us
std::lock_guard<std::mutex> lock{m_cs};
cancelSolver = false;
@ -837,7 +878,7 @@ void static BitcoinMiner()
SetThreadPriority(THREAD_PRIORITY_LOWEST);
// In regression test mode, stop mining after a block is found.
if (chainparams.MineBlocksOnDemand()) {
if (chainparams.MineBlocksOnDemand() && !isNextBlockFork) {
// Increment here because throwing skips the call below
ehSolverRuns.increment();
throw boost::thread_interrupted();
@ -845,6 +886,7 @@ void static BitcoinMiner()
return true;
};
std::function<bool(EhSolverCancelCheck)> cancelled = [&m_cs, &cancelSolver](EhSolverCancelCheck pos) {
std::lock_guard<std::mutex> lock{m_cs};
return cancelSolver;
@ -901,20 +943,13 @@ void static BitcoinMiner()
// Check for stop or if block needs to be rebuilt
boost::this_thread::interruption_point();
#ifdef FORK_CB_INPUT
if (!isNextBlockFork) {
#endif
// Regtest mode doesn't require peers
if (vNodes.empty() && chainparams.MiningRequiresPeers())
break;
if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
break;
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
#ifdef FORK_CB_INPUT
}
#endif
// Regtest mode doesn't require peers
if (vNodes.empty() && chainparams.MiningRequiresPeers())
break;
if ((UintToArith256(pblock->nNonce) & 0xffff) == 0xffff)
break;
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
if (pindexPrev != chainActive.Tip())
break;
@ -922,17 +957,11 @@ void static BitcoinMiner()
pblock->nNonce = ArithToUint256(UintToArith256(pblock->nNonce) + 1);
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
#ifdef FORK_CB_INPUT
if (!isNextBlockFork) {
#endif
if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks)
{
// Changing pblock->nTime can change work required on testnet:
hashTarget.SetCompact(pblock->nBits);
}
#ifdef FORK_CB_INPUT
if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks)
{
// Changing pblock->nTime can change work required on testnet:
hashTarget.SetCompact(pblock->nBits);
}
#endif
}
}
}

View File

@ -50,7 +50,8 @@ void GenerateBitcoins(bool fGenerate, int nThreads);
void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
#ifdef FORK_CB_INPUT
CBlockTemplate* CreateNewForkBlock();
CBlockTemplate* CreateNewForkBlock(bool&);
CBlockTemplate* CreateNewForkBlock(bool&,const int);
#endif
#endif // BITCOIN_MINER_H

View File

@ -9,6 +9,7 @@
#include "chain.h"
#include "chainparams.h"
#include "crypto/equihash.h"
#include "main.h"
#include "primitives/block.h"
#include "streams.h"
#include "uint256.h"
@ -23,11 +24,20 @@
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params)
{
unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact();
unsigned int nProofOfWorkBomb = UintToArith256(uint256S("000000000000000000000000000000000000000000000000000000000000ffff")).GetCompact();
// Genesis block
if (pindexLast == NULL)
return nProofOfWorkLimit;
// right post fork
else if(!isForkBlock(pindexLast->nHeight + 1) && isForkBlock(pindexLast->nHeight + 1 - params.nPowAveragingWindow))
return nProofOfWorkLimit;
// difficulty bomb
else if(pindexLast->nHeight > params.nPowDifficultyBombHeight)
return nProofOfWorkBomb;
// Find the first block in the averaging interval
const CBlockIndex* pindexFirst = pindexLast;
arith_uint256 bnTot {0};
@ -44,29 +54,30 @@ unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHead
arith_uint256 bnAvg {bnTot / params.nPowAveragingWindow};
return CalculateNextWorkRequired(bnAvg, pindexLast->GetMedianTimePast(), pindexFirst->GetMedianTimePast(), params);
bool isFork = isForkBlock(pindexLast->nHeight + 1);
return CalculateNextWorkRequired(bnAvg, pindexLast->GetMedianTimePast(), pindexFirst->GetMedianTimePast(), params, isFork);
}
unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
int64_t nLastBlockTime, int64_t nFirstBlockTime,
const Consensus::Params& params)
const Consensus::Params& params, bool isFork)
{
// Limit adjustment step
// Use medians to prevent time-warp attacks
int64_t nActualTimespan = nLastBlockTime - nFirstBlockTime;
LogPrint("pow", " nActualTimespan = %d before dampening\n", nActualTimespan);
nActualTimespan = params.AveragingWindowTimespan() + (nActualTimespan - params.AveragingWindowTimespan())/4;
nActualTimespan = params.AveragingWindowTimespan(isFork) + (nActualTimespan - params.AveragingWindowTimespan(isFork))/4;
LogPrint("pow", " nActualTimespan = %d before bounds\n", nActualTimespan);
if (nActualTimespan < params.MinActualTimespan())
nActualTimespan = params.MinActualTimespan();
if (nActualTimespan > params.MaxActualTimespan())
nActualTimespan = params.MaxActualTimespan();
if (nActualTimespan < params.MinActualTimespan(isFork))
nActualTimespan = params.MinActualTimespan(isFork);
if (nActualTimespan > params.MaxActualTimespan(isFork))
nActualTimespan = params.MaxActualTimespan(isFork);
// Retarget
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
arith_uint256 bnNew {bnAvg};
bnNew /= params.AveragingWindowTimespan();
bnNew /= params.AveragingWindowTimespan(isFork);
bnNew *= nActualTimespan;
if (bnNew > bnPowLimit)

View File

@ -19,7 +19,7 @@ class arith_uint256;
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params&);
unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg,
int64_t nLastBlockTime, int64_t nFirstBlockTime,
const Consensus::Params&);
const Consensus::Params&, bool isFork = false);
/** Check whether the Equihash solution in a block header is valid */
bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams&);

View File

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

View File

@ -634,6 +634,20 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex*
return rv;
}
static UniValue BIP9SoftForkDesc(const std::string& name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
{
UniValue rv(UniValue::VOBJ);
rv.push_back(Pair("id", name));
switch (VersionBitsTipState(consensusParams, id)) {
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;
}
return rv;
}
UniValue getblockchaininfo(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 0)
@ -662,6 +676,12 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
" },\n"
" \"reject\": { ... } (object) progress toward rejecting pre-softfork blocks (same fields as \"enforce\")\n"
" }, ...\n"
" ],\n"
" \"bip9_softforks\": [ (array) status of BIP9 softforks in progress\n"
" {\n"
" \"id\": \"xxxx\", (string) name of the softfork\n"
" \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"lockedin\", \"active\", \"failed\"\n"
" }\n"
" ]\n"
"}\n"
"\nExamples:\n"
@ -688,10 +708,12 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp)
const Consensus::Params& consensusParams = Params().GetConsensus();
CBlockIndex* tip = chainActive.Tip();
UniValue softforks(UniValue::VARR);
UniValue bip9_softforks(UniValue::VARR);
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams));
obj.push_back(Pair("softforks", softforks));
obj.push_back(Pair("bip9_softforks", bip9_softforks));
if (fPruneMode)
{

View File

@ -6,6 +6,7 @@
#include "amount.h"
#include "chainparams.h"
#include "consensus/consensus.h"
#include "consensus/params.h"
#include "consensus/validation.h"
#include "core_io.h"
#ifdef ENABLE_MINING
@ -425,6 +426,15 @@ static UniValue BIP22ValidationResult(const CValidationState& state)
return "valid?";
}
std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
std::string s = vbinfo.name;
if (!vbinfo.gbt_force) {
s.insert(s.begin(), '!');
}
return s;
}
UniValue getblocktemplate(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() > 1)
@ -432,7 +442,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
"getblocktemplate ( \"jsonrequestobject\" )\n"
"\nIf the request parameters include a 'mode' key, that is used to explicitly select between the default 'template' request or a 'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
"For full specification, see BIPs 22 and 9:\n"
" https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
" https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki#getblocktemplate_changes\n"
"\nArguments:\n"
"1. \"jsonrequestobject\" (string, optional) A json object in the following spec\n"
@ -448,6 +460,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
"\nResult:\n"
"{\n"
" \"version\" : n, (numeric) The block version\n"
" \"rules\" : [ \"rulename\", ... ], (array of strings) specific block rules that are to be enforced\n"
" \"vbavailable\" : { (json object) set of pending, supported versionbit (BIP 9) softfork deployments\n"
" \"rulename\" : bitnumber (numeric) identifies the bit number as indicating acceptance and readiness for the named softfork rule\n"
" ,...\n"
" },\n"
" \"vbrequired\" : n, (numeric) bit mask of versionbits the server requires set in submissions\n"
" \"previousblockhash\" : \"xxxx\", (string) The hash of current highest block\n"
" \"transactions\" : [ (array) contents of non-coinbase transactions that should be included in the next block\n"
" {\n"
@ -502,8 +520,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
std::string strMode = "template";
UniValue lpval = NullUniValue;
// TODO: Re-enable coinbasevalue once a specification has been written
bool coinbasetxn = true;
std::set<std::string> setClientRules;
if (params.size() > 0)
{
const UniValue& oparam = params[0].get_obj();
@ -547,6 +567,14 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
TestBlockValidity(state, block, pindexPrev, false, true);
return BIP22ValidationResult(state);
}
const UniValue& aClientRules = find_value(oparam, "rules");
if (aClientRules.isArray()) {
for (unsigned int i = 0; i < aClientRules.size(); ++i) {
const UniValue& v = aClientRules[i];
setClientRules.insert(v.get_str());
}
}
}
if (strMode != "template")
@ -640,9 +668,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
pindexPrev = pindexPrevNew;
}
CBlock* pblock = &pblocktemplate->block; // pointer for convenience
const Consensus::Params& consensusParams = Params().GetConsensus();
// Update nTime
UpdateTime(pblock, Params().GetConsensus(), pindexPrev);
UpdateTime(pblock, consensusParams, pindexPrev);
pblock->nNonce = uint256();
UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal");
@ -704,7 +733,53 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
UniValue result(UniValue::VOBJ);
result.push_back(Pair("capabilities", aCaps));
UniValue aRules(UniValue::VARR);
UniValue vbavailable(UniValue::VOBJ);
for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) {
Consensus::DeploymentPos pos = Consensus::DeploymentPos(i);
ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache);
switch (state) {
case THRESHOLD_DEFINED:
case THRESHOLD_FAILED:
// Not exposed to GBT at all
break;
case THRESHOLD_LOCKED_IN:
// Ensure bit is set in block version
pblock->nVersion |= VersionBitsMask(consensusParams, pos);
// FALL THROUGH to get vbavailable set...
case THRESHOLD_STARTED:
{
const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
vbavailable.push_back(Pair(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit));
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
if (!vbinfo.gbt_force) {
// If the client doesn't support this, don't indicate it in the [default] version
pblock->nVersion &= ~VersionBitsMask(consensusParams, pos);
}
}
break;
}
case THRESHOLD_ACTIVE:
{
// Add to rules only
const struct BIP9DeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos];
aRules.push_back(gbt_vb_name(pos));
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
// Not supported by the client; make sure it's safe to proceed
if (!vbinfo.gbt_force) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Support for '%s' rule requires explicit client support", vbinfo.name));
}
}
break;
}
}
}
result.push_back(Pair("version", pblock->nVersion));
result.push_back(Pair("rules", aRules));
result.push_back(Pair("vbavailable", vbavailable));
result.push_back(Pair("vbrequired", int(0)));
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
result.push_back(Pair("transactions", transactions));
if (coinbasetxn) {

View File

@ -378,13 +378,40 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// subsidy changing
int nHeight = chainActive.Height();
chainActive.Tip()->nHeight = 209999;
// Create an actual 209999-long block chain (without valid blocks).
while (chainActive.Tip()->nHeight < 209999) {
CBlockIndex* prev = chainActive.Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(GetRandHash());
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
chainActive.SetTip(next);
}
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
delete pblocktemplate;
chainActive.Tip()->nHeight = 210000;
// Extend to a 210000-long block chain.
while (chainActive.Tip()->nHeight < 210000) {
CBlockIndex* prev = chainActive.Tip();
CBlockIndex* next = new CBlockIndex();
next->phashBlock = new uint256(GetRandHash());
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
chainActive.SetTip(next);
}
BOOST_CHECK(pblocktemplate = CreateNewBlock(scriptPubKey));
delete pblocktemplate;
chainActive.Tip()->nHeight = nHeight;
// Delete the dummy blocks again.
while (chainActive.Tip()->nHeight > nHeight) {
CBlockIndex* del = chainActive.Tip();
chainActive.SetTip(del->pprev);
pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
// non-final txs in mempool
SetMockTime(chainActive.Tip()->GetMedianTimePast()+1);

View File

@ -0,0 +1,316 @@
// Copyright (c) 2014-2015 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 "chain.h"
#include "random.h"
#include "versionbits.h"
#include "test/test_bitcoin.h"
#include "chainparams.h"
#include "main.h"
#include "consensus/params.h"
#include <boost/test/unit_test.hpp>
/* Define a virtual block time, one block per 10 minutes after Nov 14 2014, 0:55:36am */
int32_t TestTime(int nHeight) { return 1415926536 + 600 * nHeight; }
static const Consensus::Params paramsDummy = Consensus::Params();
class TestConditionChecker : public AbstractThresholdConditionChecker
{
private:
mutable ThresholdConditionCache cache;
public:
int64_t BeginTime(const Consensus::Params& params) const { return TestTime(10000); }
int64_t EndTime(const Consensus::Params& params) const { return TestTime(20000); }
int Period(const Consensus::Params& params) const { return 1000; }
int Threshold(const Consensus::Params& params) const { return 900; }
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const { return (pindex->nVersion & 0x100); }
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, paramsDummy, cache); }
};
#define CHECKERS 6
class VersionBitsTester
{
// A fake blockchain
std::vector<CBlockIndex*> vpblock;
// 6 independent checkers for the same bit.
// The first one performs all checks, the second only 50%, the third only 25%, etc...
// This is to test whether lack of cached information leads to the same results.
TestConditionChecker checker[CHECKERS];
// Test counter (to identify failures)
int num;
public:
VersionBitsTester() : num(0) {}
VersionBitsTester& Reset() {
for (unsigned int i = 0; i < vpblock.size(); i++) {
delete vpblock[i];
}
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i] = TestConditionChecker();
}
vpblock.clear();
return *this;
}
~VersionBitsTester() {
Reset();
}
VersionBitsTester& Mine(unsigned int height, int32_t nTime, int32_t nVersion) {
while (vpblock.size() < height) {
CBlockIndex* pindex = new CBlockIndex();
pindex->nHeight = vpblock.size();
pindex->pprev = vpblock.size() > 0 ? vpblock.back() : NULL;
pindex->nTime = nTime;
pindex->nVersion = nVersion;
pindex->BuildSkip();
vpblock.push_back(pindex);
}
return *this;
}
VersionBitsTester& TestDefined() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_DEFINED, strprintf("Test %i for DEFINED", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestStarted() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_STARTED, strprintf("Test %i for STARTED", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestLockedIn() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_LOCKED_IN, strprintf("Test %i for LOCKED_IN", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestActive() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_ACTIVE, strprintf("Test %i for ACTIVE", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestFailed() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_FAILED, strprintf("Test %i for FAILED", num));
}
}
num++;
return *this;
}
CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : NULL; }
};
BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(versionbits_test)
{
for (int i = 0; i < 64; i++) {
// DEFINED -> FAILED
VersionBitsTester().TestDefined()
.Mine(1, TestTime(1), 0x100).TestDefined()
.Mine(11, TestTime(11), 0x100).TestDefined()
.Mine(989, TestTime(989), 0x100).TestDefined()
.Mine(999, TestTime(20000), 0x100).TestDefined()
.Mine(1000, TestTime(20000), 0x100).TestFailed()
.Mine(1999, TestTime(30001), 0x100).TestFailed()
.Mine(2000, TestTime(30002), 0x100).TestFailed()
.Mine(2001, TestTime(30003), 0x100).TestFailed()
.Mine(2999, TestTime(30004), 0x100).TestFailed()
.Mine(3000, TestTime(30005), 0x100).TestFailed()
// DEFINED -> STARTED -> FAILED
.Reset().TestDefined()
.Mine(1, TestTime(1), 0).TestDefined()
.Mine(1000, TestTime(10000) - 1, 0x100).TestDefined() // One second more and it would be defined
.Mine(2000, TestTime(10000), 0x100).TestStarted() // So that's what happens the next period
.Mine(2051, TestTime(10010), 0).TestStarted() // 51 old blocks
.Mine(2950, TestTime(10020), 0x100).TestStarted() // 899 new blocks
.Mine(3000, TestTime(20000), 0).TestFailed() // 50 old blocks (so 899 out of the past 1000)
.Mine(4000, TestTime(20010), 0x100).TestFailed()
// DEFINED -> STARTED -> FAILED while threshold reached
.Reset().TestDefined()
.Mine(1, TestTime(1), 0).TestDefined()
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined
.Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period
.Mine(2999, TestTime(30000), 0x100).TestStarted() // 999 new blocks
.Mine(3000, TestTime(30000), 0x100).TestFailed() // 1 new block (so 1000 out of the past 1000 are new)
.Mine(3999, TestTime(30001), 0).TestFailed()
.Mine(4000, TestTime(30002), 0).TestFailed()
.Mine(14333, TestTime(30003), 0).TestFailed()
.Mine(24000, TestTime(40000), 0).TestFailed()
// DEFINED -> STARTED -> LOCKEDIN at the last minute -> ACTIVE
.Reset().TestDefined()
.Mine(1, TestTime(1), 0).TestDefined()
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined() // One second more and it would be defined
.Mine(2000, TestTime(10000), 0x101).TestStarted() // So that's what happens the next period
.Mine(2050, TestTime(10010), 0x200).TestStarted() // 50 old blocks
.Mine(2950, TestTime(10020), 0x100).TestStarted() // 900 new blocks
.Mine(2999, TestTime(19999), 0x200).TestStarted() // 49 old blocks
.Mine(3000, TestTime(29999), 0x200).TestLockedIn() // 1 old block (so 900 out of the past 1000)
.Mine(3999, TestTime(30001), 0).TestLockedIn()
.Mine(4000, TestTime(30002), 0).TestActive()
.Mine(14333, TestTime(30003), 0).TestActive()
.Mine(24000, TestTime(40000), 0).TestActive();
}
// Sanity checks of version bit deployments
const Consensus::Params &mainnetParams = Params(CBaseChainParams::MAIN).GetConsensus();
for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
uint32_t bitmask = VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)i);
// Make sure that no deployment tries to set an invalid bit.
BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask);
// Verify that the deployment windows of different deployment using the
// same bit are disjoint.
// This test may need modification at such time as a new deployment
// is proposed that reuses the bit of an activated soft fork, before the
// end time of that soft fork. (Alternatively, the end time of that
// activated soft fork could be later changed to be earlier to avoid
// overlap.)
for (int j=i+1; j<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; j++) {
if (VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)j) == bitmask) {
BOOST_CHECK(mainnetParams.vDeployments[j].nStartTime > mainnetParams.vDeployments[i].nTimeout ||
mainnetParams.vDeployments[i].nStartTime > mainnetParams.vDeployments[j].nTimeout);
}
}
}
}
BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
{
// Check that ComputeBlockVersion will set the appropriate bit correctly
// on mainnet.
const Consensus::Params &mainnetParams = Params(CBaseChainParams::MAIN).GetConsensus();
// Use the TESTDUMMY deployment for testing purposes.
int64_t bit = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit;
int64_t nStartTime = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime;
int64_t nTimeout = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout;
assert(nStartTime < nTimeout);
// In the first chain, test that the bit is set by CBV until it has failed.
// In the second chain, test the bit is set by CBV while STARTED and
// LOCKED-IN, and then no longer set while ACTIVE.
VersionBitsTester firstChain, secondChain;
// Start generating blocks before nStartTime
int64_t nTime = nStartTime - 1;
// Before MedianTimePast of the chain has crossed nStartTime, the bit
// should not be set.
CBlockIndex *lastBlock = NULL;
lastBlock = firstChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// Mine 2011 more blocks at the old time, and check that CBV isn't setting the bit yet.
for (int i=1; i<2012; i++) {
lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
// to be 4, and the bit we're testing happens to be bit 28.
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
}
// Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
// CBV should still not yet set the bit.
nTime = nStartTime;
for (int i=2012; i<=2016; i++) {
lastBlock = firstChain.Mine(2016+i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
}
// Advance to the next period and transition to STARTED,
lastBlock = firstChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// so ComputeBlockVersion should now set the bit,
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// and should also be using the VERSIONBITS_TOP_BITS.
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
// Check that ComputeBlockVersion will set the bit until nTimeout
nTime += 600;
int blocksToMine = 4032; // test blocks for up to 2 time periods
int nHeight = 6048;
// These blocks are all before nTimeout is reached.
while (nTime < nTimeout && blocksToMine > 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
blocksToMine--;
nTime += 600;
nHeight += 1;
};
nTime = nTimeout;
// FAILED is only triggered at the end of a period, so CBV should be setting
// the bit until the period transition.
for (int i=0; i<2015; i++) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
nHeight += 1;
}
// The next block should trigger no longer setting the bit.
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// On a new chain:
// verify that the bit will be set after lock-in, and then stop being set
// after activation.
nTime = nStartTime;
// Mine one period worth of blocks, and check that the bit will be on for the
// next period.
lastBlock = secondChain.Mine(2016, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Mine another period worth of blocks, signaling the new bit.
lastBlock = secondChain.Mine(4032, nStartTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
// After one period of setting the bit on each block, it should have locked in.
// We keep setting the bit for one more period though, until activation.
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
// Now check that we keep mining the block until the end of this period, and
// then stop at the beginning of the next period.
lastBlock = secondChain.Mine(6047, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
lastBlock = secondChain.Mine(6048, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// Finally, verify that after a soft fork has activated, CBV no longer uses
// VERSIONBITS_LAST_OLD_BLOCK_VERSION.
//BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -272,11 +272,7 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
return true;
}
#ifdef FORK_CB_INPUT
bool CBlockTreeDB::LoadBlockIndexGuts(int64_t forkStart, int64_t forkStop)
#else
bool CBlockTreeDB::LoadBlockIndexGuts()
#endif
{
boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator());
@ -308,6 +304,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
pindexNew->hashAnchor = diskindex.hashAnchor;
pindexNew->nVersion = diskindex.nVersion;
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
pindexNew->hashReserved = diskindex.hashReserved;
pindexNew->nTime = diskindex.nTime;
pindexNew->nBits = diskindex.nBits;
pindexNew->nNonce = diskindex.nNonce;
@ -315,14 +312,8 @@ bool CBlockTreeDB::LoadBlockIndexGuts()
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
#ifdef FORK_CB_INPUT
if (pindexNew->nHeight < forkStart || pindexNew->nHeight >= forkStop) {
#endif
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus()))
return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString());
#ifdef FORK_CB_INPUT
}
#endif
pcursor->Next();
} else {

View File

@ -68,12 +68,7 @@ public:
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
#ifdef FORK_CB_INPUT
bool LoadBlockIndexGuts(int64_t forkStart, int64_t forkStop);
#else
bool LoadBlockIndexGuts();
#endif
};
#endif // BITCOIN_TXDB_H

141
src/versionbits.cpp Normal file
View File

@ -0,0 +1,141 @@
// 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 =*/ "testdummy",
}
};
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;
}
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]);
}
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();
}
}

68
src/versionbits.h Normal file
View File

@ -0,0 +1,68 @@
// 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;
};
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:
// Note that the function below takes 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;
};
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);
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
#endif