Skip to content

Commit

Permalink
Fix disk block index for non-zero nNonce values.
Browse files Browse the repository at this point in the history
After the post-ICO fork, nNonce values in blocks are allowed to be
non-zero.  This is fine from a consensus point of view (and was tested),
but it caused corruption to the on-disk block index.

The reason for this is that the nNonce value was not included in the
C(Disk)BlockIndex instances, and was assumed to be zero when computing
the block hash from a CDiskBlockIndex that is being loaded.  Thus blocks
would be inserted into mapBlockIndex at a wrong hash, so that the pprev
pointer of the next block (referring to the correct block index) was
pointing to an empty struct instead.

This fix adds back nNonce to C(Disk)BlockIndex, so that all works fine.
Users who synced beyond the first affected block (529761 on mainnet).

See also #82.
  • Loading branch information
domob1812 committed Jan 23, 2019
1 parent a06b666 commit f064cfd
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 2 deletions.
6 changes: 5 additions & 1 deletion src/chain.h
Expand Up @@ -225,6 +225,7 @@ class CBlockIndex
* actual nBits, so that the total work of a chain can be computed from it.
*/
uint32_t nBits;
uint32_t nNonce;

/** Mining algorithm used (necessary to evaluate chain work). */
PowAlgo algo;
Expand Down Expand Up @@ -255,6 +256,7 @@ class CBlockIndex
hashMerkleRoot = uint256();
nTime = 0;
nBits = 0;
nNonce = 0;

algo = PowAlgo::INVALID;
}
Expand All @@ -272,6 +274,7 @@ class CBlockIndex
hashMerkleRoot = block.hashMerkleRoot;
nTime = block.nTime;
nBits = block.pow.getBits();
nNonce = block.nNonce;

algo = block.pow.getCoreAlgo();
}
Expand Down Expand Up @@ -426,6 +429,7 @@ class CDiskBlockIndex : public CBlockIndex
READWRITE(hashMerkleRoot);
READWRITE(nTime);
READWRITE(nBits);
READWRITE(nNonce);

READWRITE(algo);
}
Expand All @@ -438,7 +442,7 @@ class CDiskBlockIndex : public CBlockIndex
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
block.nBits = 0;
block.nNonce = 0;
block.nNonce = nNonce;
return block.GetHash();
}

Expand Down
4 changes: 4 additions & 0 deletions src/rpc/blockchain.cpp
Expand Up @@ -161,6 +161,7 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
result.pushKV("merkleroot", blockindex->hashMerkleRoot.GetHex());
result.pushKV("time", (int64_t)blockindex->nTime);
result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
result.pushKV("nonce", (uint64_t)blockindex->nNonce);
result.pushKV("chainwork", blockindex->nChainWork.GetHex());
result.pushKV("nTx", (uint64_t)blockindex->nTx);

Expand Down Expand Up @@ -200,6 +201,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
result.pushKV("tx", txs);
result.pushKV("time", block.GetBlockTime());
result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
result.pushKV("nonce", (uint64_t)block.nNonce);
result.pushKV("chainwork", blockindex->nChainWork.GetHex());
result.pushKV("nTx", (uint64_t)blockindex->nTx);

Expand Down Expand Up @@ -801,6 +803,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
" \"merkleroot\" : \"xxxx\", (string) The merkle root\n"
" \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"nonce\" : n, (numeric) The nonce\n"
" \"chainwork\" : \"0000...1f3\" (string) Expected number of hashes required to produce the current chain (in hex)\n"
" \"nTx\" : n, (numeric) The number of transactions in the block.\n"
" \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n"
Expand Down Expand Up @@ -893,6 +896,7 @@ static UniValue getblock(const JSONRPCRequest& request)
" ],\n"
" \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n"
" \"nonce\" : n, (numeric) The nonce\n"
" \"chainwork\" : \"xxxx\", (string) Expected number of hashes required to produce the chain up to this block (in hex)\n"
" \"nTx\" : n, (numeric) The number of transactions in the block.\n"
" \"powdata\" : {...}, (json object) The block's attached PoW data\n"
Expand Down
1 change: 1 addition & 0 deletions src/txdb.cpp
Expand Up @@ -468,6 +468,7 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
pindexNew->nTime = diskindex.nTime;
pindexNew->nBits = diskindex.nBits;
pindexNew->nNonce = diskindex.nNonce;
pindexNew->algo = diskindex.algo;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
Expand Down
14 changes: 13 additions & 1 deletion test/functional/xaya_postico_fork.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# Copyright (c) 2018 The Xaya developers
# Copyright (c) 2018-2019 The Xaya developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Tests the POST_ICO hard fork in Xaya."""
Expand Down Expand Up @@ -31,13 +31,25 @@ def run_test (self):
self.node.generate (FORK_HEIGHT - 1 - height)
assert_equal (FORK_HEIGHT - 1, self.node.getblockcount ())
assert_equal (self.tryNonzeroNonceBlock (), None)
blkHash = self.node.getbestblockhash ()
assert_equal (FORK_HEIGHT, self.node.getblockcount ())
assert_equal (42, self.node.getblock (blkHash)['nonce'])
assert_equal (42, self.node.getblockheader (blkHash)['nonce'])

# The fork also changes the block reward as well as the block intervals
# (depending on the algorithm), but that is nothing that can be easily
# tested in regtest mode. Thus we rely on unit tests and testnet for
# verifying those changes.

# Test that we can restart the client just fine. There was a bug
# with non-zero nNonce values in the on-disk block index that caused
# a crash here: https://github.com/xaya/xaya/issues/82
self.restart_node (0)
assert_equal (blkHash, self.nodes[0].getbestblockhash ())
assert_equal (FORK_HEIGHT, self.nodes[0].getblockcount ())
assert_equal (42, self.node.getblock (blkHash)['nonce'])
assert_equal (42, self.node.getblockheader (blkHash)['nonce'])

def tryNonzeroNonceBlock (self):
"""
Creates and mines a block with nonzero nonce and tries to submit it
Expand Down

0 comments on commit f064cfd

Please sign in to comment.