Merge bitcoin/bitcoin#33446: rpc: fix getblock(header) returns target for tip

bf7996cbc3 rpc: fix getblock(header) returns target for tip (Sjors Provoost)
4c3c1f42cf test: add block 2016 to mock mainnet (Sjors Provoost)

Pull request description:

  A `target` field was added to the `getblock` and `getblockheader` RPC calls in #31583, but it mistakingly always used the tip value.

  This PR fixes it to return the target for the given block. Because regtest does not have difficulty adjustment, the mainnet test is expanded to cover the fix.

  A preliminary commit deals with mining block 2016 that's needed for the test. It also:
  - renames the `create_coinbase` `retarget_period` argument to `halving_period`. Before #31583 this was hardcoded for regtest where these values are the same.
  - drops unused `fees` argument from `mine` helper
  - expands the CPU miner instructions for generating the alternative mainnet chain

  Fixes #33440

ACKs for top commit:
  sipa:
    utACK bf7996cbc3
  luke-jr:
    crACK bf7996cbc3
  TheCharlatan:
    ACK bf7996cbc3
  ismaelsadeeq:
    Code review ACK bf7996cbc3

Tree-SHA512: 2a2e11efd91f4aaccf9d2ec4dff9fd82c366b8a7e797ce5981dca2e6f08028f69154f4e6a27aef20d78b0e6c3304416789267c2fad42d7aa5072f8537d0c8b0d
This commit is contained in:
merge-script 2025-09-24 10:08:10 -04:00
commit d41b503ae1
No known key found for this signature in database
GPG Key ID: 2EEB9F5CC09526C1
5 changed files with 43 additions and 14 deletions

View File

@ -166,7 +166,7 @@ UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex
result.pushKV("mediantime", blockindex.GetMedianTimePast());
result.pushKV("nonce", blockindex.nNonce);
result.pushKV("bits", strprintf("%08x", blockindex.nBits));
result.pushKV("target", GetTarget(tip, pow_limit).GetHex());
result.pushKV("target", GetTarget(blockindex, pow_limit).GetHex());
result.pushKV("difficulty", GetDifficulty(blockindex));
result.pushKV("chainwork", blockindex.nChainWork.GetHex());
result.pushKV("nTx", blockindex.nTx);

View File

@ -11,9 +11,10 @@ The alternate mainnet chain was generated as follows:
- restart node with a faketime 2 minutes later
```sh
for i in {1..2015}
for i in {1..2016}
do
faketime "`date -d @"$(( 1231006505 + $i * 120 ))" +'%Y-%m-%d %H:%M:%S'`" \
t=$(( 1231006505 + $i * 120 ))
faketime "`date -d @$t +'%Y-%m-%d %H:%M:%S'`" \
bitcoind -connect=0 -nocheckpoints -stopatheight=$i
done
```
@ -21,7 +22,9 @@ done
The CPU miner is kept running as follows:
```sh
./minerd --coinbase-addr 1NQpH6Nf8QtR2HphLRcvuVqfhXBXsiWn8r --no-stratum --algo sha256d --no-longpoll --scantime 3 --retry-pause 1
./minerd -u ... -p ... -o http://127.0.0.1:8332 --no-stratum \
--coinbase-addr 1NQpH6Nf8QtR2HphLRcvuVqfhXBXsiWn8r \
--algo sha256d --no-longpoll --scantime 3 --retry-pause 1
```
The payout address is derived from first BIP32 test vector master key:
@ -40,3 +43,8 @@ The timestamp was not kept constant because at difficulty 1 it's not sufficient
to only grind the nonce. Grinding the extra_nonce or version field instead
would have required additional (stratum) software. It would also make it more
complicated to reconstruct the blocks in this test.
The `getblocktemplate` RPC code needs to be patched to ignore not being connected
to any peers, and to ignore the IBD status check.
On macOS use `faketime "@$t"` instead.

View File

@ -2014,7 +2014,8 @@
1231247971,
1231248071,
1231248198,
1231248322
1231248322,
1231248621
],
"nonces": [
2345621585,
@ -4031,6 +4032,7 @@
3658502865,
2519048297,
1915965760,
1183846025
1183846025,
2713372123
]
}

View File

@ -53,15 +53,15 @@ class MiningMainnetTest(BitcoinTestFramework):
help='Block data file (default: %(default)s)',
)
def mine(self, height, prev_hash, blocks, node, fees=0):
def mine(self, height, prev_hash, blocks, node):
self.log.debug(f"height={height}")
block = CBlock()
block.nVersion = 0x20000000
block.hashPrevBlock = int(prev_hash, 16)
block.nTime = blocks['timestamps'][height - 1]
block.nBits = DIFF_1_N_BITS
block.nBits = DIFF_1_N_BITS if height < 2016 else DIFF_4_N_BITS
block.nNonce = blocks['nonces'][height - 1]
block.vtx = [create_coinbase(height=height, script_pubkey=bytes.fromhex(COINBASE_SCRIPT_PUBKEY), retarget_period=2016)]
block.vtx = [create_coinbase(height=height, script_pubkey=bytes.fromhex(COINBASE_SCRIPT_PUBKEY), halving_period=210000)]
# The alternate mainnet chain was mined with non-timelocked coinbase txs.
block.vtx[0].nLockTime = 0
block.vtx[0].vin[0].nSequence = SEQUENCE_FINAL
@ -82,10 +82,13 @@ class MiningMainnetTest(BitcoinTestFramework):
self.log.info("Load alternative mainnet blocks")
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.options.datafile)
prev_hash = node.getbestblockhash()
blocks = None
with open(path, encoding='utf-8') as f:
blocks = json.load(f)
n_blocks = len(blocks['timestamps'])
assert_equal(n_blocks, 2015)
assert_equal(n_blocks, 2016)
# Mine up to the last block of the first retarget period
for i in range(2015):
prev_hash = self.mine(i + 1, prev_hash, blocks, node)
@ -102,5 +105,21 @@ class MiningMainnetTest(BitcoinTestFramework):
assert_equal(mining_info['next']['bits'], nbits_str(DIFF_4_N_BITS))
assert_equal(mining_info['next']['target'], target_str(DIFF_4_TARGET))
# Mine first block of the second retarget period
height = 2016
prev_hash = self.mine(height, prev_hash, blocks, node)
assert_equal(node.getblockcount(), height)
mining_info = node.getmininginfo()
assert_equal(mining_info['difficulty'], 4)
self.log.info("getblock RPC should show historical target")
block_info = node.getblock(node.getblockhash(1))
assert_equal(block_info['difficulty'], 1)
assert_equal(block_info['bits'], nbits_str(DIFF_1_N_BITS))
assert_equal(block_info['target'], target_str(DIFF_1_TARGET))
if __name__ == '__main__':
MiningMainnetTest(__file__).main()

View File

@ -144,7 +144,7 @@ def script_BIP34_coinbase_height(height):
return CScript([CScriptNum(height)])
def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_script=None, fees=0, nValue=50, retarget_period=REGTEST_RETARGET_PERIOD):
def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_script=None, fees=0, nValue=50, halving_period=REGTEST_RETARGET_PERIOD):
"""Create a coinbase transaction.
If pubkey is passed in, the coinbase output will be a P2PK output;
@ -158,7 +158,7 @@ def create_coinbase(height, pubkey=None, *, script_pubkey=None, extra_output_scr
coinbaseoutput = CTxOut()
coinbaseoutput.nValue = nValue * COIN
if nValue == 50:
halvings = int(height / retarget_period)
halvings = int(height / halving_period)
coinbaseoutput.nValue >>= halvings
coinbaseoutput.nValue += fees
if pubkey is not None: