Skip to content

Commit a282b98

Browse files
Add support for Testnet4 (BIP94)
1 parent 91234ac commit a282b98

10 files changed

Lines changed: 219 additions & 14 deletions

File tree

include/bitcoin/system/chain/chain_state.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ class BC_API chain_state
185185
uint32_t minimum_block_version() const NOEXCEPT;
186186
uint32_t work_required() const NOEXCEPT;
187187
uint32_t timestamp() const NOEXCEPT;
188+
uint32_t previous_block_timestamp() const NOEXCEPT;
188189
uint32_t median_time_past() const NOEXCEPT;
189190
uint32_t flags() const NOEXCEPT;
190191
size_t height() const NOEXCEPT;
@@ -239,6 +240,7 @@ class BC_API chain_state
239240
static uint32_t work_required_retarget(const data& values,
240241
const forks& forks, uint32_t proof_of_work_limit,
241242
uint32_t minimum_timespan, uint32_t maximum_timespan,
243+
size_t retargeting_interval,
242244
uint32_t retargeting_interval_seconds) NOEXCEPT;
243245
static uint32_t retarget_timespan(const data& values,
244246
uint32_t minimum_timespan, uint32_t maximum_timespan) NOEXCEPT;

include/bitcoin/system/chain/enums/flags.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,10 @@ enum flags : uint32_t
137137
/// Reduces threshold segregated witness signaling (soft fork, feature).
138138
bip91_rule = bit_right<uint32_t>(27),
139139

140-
/// Agregates
140+
/// Testnet4 only
141+
bip94_rule = bit_right<uint32_t>(28),
142+
143+
/// Aggregates
141144
/// -----------------------------------------------------------------------
142145

143146
/// Rules that use bip34-based activation.

include/bitcoin/system/chain/enums/selection.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum class selection
3131
none,
3232
mainnet,
3333
testnet,
34+
testnet4,
3435
regtest
3536
};
3637

include/bitcoin/system/chain/header.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ class BC_API header
116116
/// Checkpoints and previous_block_hash are chain validation (not here).
117117

118118
code check(uint32_t timestamp_limit_seconds, uint32_t proof_of_work_limit,
119-
bool scrypt=false) const NOEXCEPT;
119+
size_t height, uint32_t current_timestamp,
120+
uint32_t previous_block_timestamp, uint32_t retarget_interval,
121+
bool bip94, bool scrypt=false) const NOEXCEPT;
120122
code accept(const context& ctx) const NOEXCEPT;
121123

122124
protected:
@@ -135,6 +137,9 @@ class BC_API header
135137
bool is_invalid_proof_of_work(uint32_t proof_of_work_limit,
136138
bool scrypt=false) const NOEXCEPT;
137139
bool is_futuristic_timestamp(uint32_t timestamp_limit_seconds) const NOEXCEPT;
140+
bool is_timestamp_early(size_t height, uint32_t timestamp,
141+
uint32_t previous_block_timestamp,
142+
uint32_t retarget_interval) const NOEXCEPT;
138143

139144
/// Accept (relative to chain_state).
140145
/// -----------------------------------------------------------------------

include/bitcoin/system/error/block_error_t.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ enum block_error_t : uint8_t
4242
insufficient_block_version,
4343
anachronistic_timestamp,
4444
incorrect_proof_of_work,
45+
early_timewarp_attack,
4546

4647
// confirm header
4748
orphan_block,

include/bitcoin/system/forks.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ typedef struct
3434
/// github.com/bitcoin/bitcoin/pull/8391
3535
bool bip90;
3636

37+
// https://github.com/bitcoin/bitcoin/pull/29775
38+
bool bip94;
39+
3740
/// github.com/bitcoin/bitcoin/commit/a206b0ea12eb4606b93323268fc81a4f1f952531
3841
/// github.com/bitcoin/bitcoin/pull/6931
3942
/// github.com/bitcoin/bitcoin/pull/12204

src/chain/chain_state.cpp

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ chain_state::activations chain_state::activation(const data& values,
151151
result.flags |= flags::bip90_rule;
152152
}
153153

154+
// bip94 is activated based on configuration alone (hard fork).
155+
if (forks.bip94)
156+
{
157+
result.flags |= flags::bip94_rule;
158+
}
159+
154160
// bip16 was activated by manual inspection of signal history (soft fork).
155161
if (forks.bip16 &&
156162
(values.timestamp.self >= settings.bip16_activation_time))
@@ -368,6 +374,7 @@ uint32_t chain_state::work_required(const data& values, const forks& forks,
368374
settings.proof_of_work_limit,
369375
settings.minimum_timespan(),
370376
settings.maximum_timespan(),
377+
settings.retargeting_interval(),
371378
settings.retargeting_interval_seconds);
372379

373380
// Testnet retargets easy on inter-interval.
@@ -406,14 +413,42 @@ constexpr bool patch_timewarp(const forks& forks, const uint256_t& limit,
406413
floored_log2(target) >= floored_log2(limit);
407414
}
408415

416+
// A retarget height, or a block that does not have proof_of_work_limit bits.
417+
constexpr bool is_retarget_or_non_limit(size_t height, uint32_t bits,
418+
size_t retargeting_interval, uint32_t proof_of_work_limit) NOEXCEPT
419+
{
420+
// Zero is a retarget height, termination required before height underflow.
421+
// This is guaranteed, just a comment here because it may not be obvious.
422+
return bits != proof_of_work_limit ||
423+
is_retarget_height(height, retargeting_interval);
424+
}
425+
409426
uint32_t chain_state::work_required_retarget(const data& values,
410427
const forks& forks, uint32_t proof_of_work_limit,
411428
uint32_t minimum_timespan, uint32_t maximum_timespan,
429+
size_t retargeting_interval,
412430
uint32_t retargeting_interval_seconds) NOEXCEPT
413431
{
414432
static const auto limit = compact::expand(proof_of_work_limit);
415433
auto target = compact::expand(bits_high(values));
416434

435+
if (forks.bip94)
436+
{
437+
auto height = values.height;
438+
439+
// Reverse iterate the ordered-by-height list of header bits.
440+
const auto& bits = values.bits.ordered;
441+
for (auto bit: std::views::reverse(bits))
442+
{
443+
if (is_retarget_or_non_limit(--height, bit, retargeting_interval,
444+
proof_of_work_limit))
445+
{
446+
target = compact::expand(bit);
447+
break;
448+
}
449+
}
450+
}
451+
417452
// Conditionally implement retarget overflow patch (e.g. Litecoin).
418453
const auto timewarp = to_int(patch_timewarp(forks, limit, target));
419454

@@ -427,16 +462,6 @@ uint32_t chain_state::work_required_retarget(const data& values,
427462
return target > limit ? proof_of_work_limit : compact::compress(target);
428463
}
429464

430-
// A retarget height, or a block that does not have proof_of_work_limit bits.
431-
constexpr bool is_retarget_or_non_limit(size_t height, uint32_t bits,
432-
size_t retargeting_interval, uint32_t proof_of_work_limit) NOEXCEPT
433-
{
434-
// Zero is a retarget height, termination required before height underflow.
435-
// This is guaranteed, just a comment here because it may not be obvious.
436-
return bits != proof_of_work_limit ||
437-
is_retarget_height(height, retargeting_interval);
438-
}
439-
440465
uint32_t chain_state::easy_work_required(const data& values,
441466
size_t retargeting_interval, uint32_t proof_of_work_limit,
442467
uint32_t block_spacing_seconds) NOEXCEPT
@@ -620,7 +645,7 @@ chain_state::data chain_state::to_pool(const chain_state& top,
620645

621646
// If this overflows height is zero and result is handled as invalid.
622647
const auto height = add1(data.height);
623-
648+
624649
// Enqueue previous block values to collections.
625650
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
626651
data.bits.ordered.push_back(data.bits.self);
@@ -824,6 +849,11 @@ uint32_t chain_state::timestamp() const NOEXCEPT
824849
return data_.timestamp.self;
825850
}
826851

852+
uint32_t chain_state::previous_block_timestamp() const NOEXCEPT
853+
{
854+
return timestamp_high(data_);
855+
}
856+
827857
uint32_t chain_state::median_time_past() const NOEXCEPT
828858
{
829859
return median_time_past_;

src/chain/header.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,14 +355,32 @@ bool header::is_futuristic_timestamp(
355355
return time > future;
356356
}
357357

358+
// BIP94 specific (the block's timestamp is too early on difficulty
359+
// adjustment block).
360+
bool header::is_timestamp_early(size_t height, uint32_t timestamp,
361+
uint32_t previous_block_timestamp,
362+
uint32_t retarget_interval) const NOEXCEPT
363+
{
364+
static const auto max_timewarp = 600;
365+
366+
return (height % retarget_interval == 0) &&
367+
(timestamp < previous_block_timestamp - max_timewarp);
368+
}
369+
370+
358371
// Validation.
359372
// ----------------------------------------------------------------------------
360373

361374
code header::check(uint32_t timestamp_limit_seconds,
362-
uint32_t proof_of_work_limit, bool scrypt) const NOEXCEPT
375+
uint32_t proof_of_work_limit, size_t height,
376+
uint32_t current_timestamp, uint32_t previous_block_timestamp,
377+
uint32_t retarget_interval, bool bip94, bool scrypt) const NOEXCEPT
363378
{
364379
if (is_invalid_proof_of_work(proof_of_work_limit, scrypt))
365380
return error::invalid_proof_of_work;
381+
if (bip94 && is_timestamp_early(height, current_timestamp,
382+
previous_block_timestamp, retarget_interval))
383+
return error::early_timewarp_attack;
366384
if (is_futuristic_timestamp(timestamp_limit_seconds))
367385
return error::futuristic_timestamp;
368386

src/settings.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ settings::settings() NOEXCEPT
5555
forks.bip66 = true;
5656
forks.bip68 = true;
5757
forks.bip90 = true;
58+
forks.bip94 = false;
5859
forks.bip112 = true;
5960
forks.bip113 = true;
6061
forks.bip141 = true;
@@ -129,6 +130,7 @@ settings::settings(chain::selection context) NOEXCEPT
129130
forks.bip66 = true;
130131
forks.bip68 = true;
131132
forks.bip90 = true;
133+
forks.bip94 = false;
132134
forks.bip112 = true;
133135
forks.bip113 = true;
134136
forks.bip141 = true;
@@ -230,6 +232,7 @@ settings::settings(chain::selection context) NOEXCEPT
230232
forks.bip66 = true;
231233
forks.bip68 = true;
232234
forks.bip90 = true;
235+
forks.bip94 = false;
233236
forks.bip112 = true;
234237
forks.bip113 = true;
235238
forks.bip141 = true;
@@ -267,6 +270,96 @@ settings::settings(chain::selection context) NOEXCEPT
267270
break;
268271
}
269272

273+
case chain::selection::testnet4:
274+
{
275+
genesis_block = chain::block
276+
{
277+
data_chunk
278+
{
279+
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
281+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
282+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
283+
0x00, 0x00, 0x00, 0x00, 0x4e, 0x7b, 0x2b, 0x91,
284+
0x28, 0xfe, 0x02, 0x91, 0xdb, 0x06, 0x93, 0xaf,
285+
0x2a, 0xe4, 0x18, 0xb7, 0x67, 0xe6, 0x57, 0xcd,
286+
0x40, 0x7e, 0x80, 0xcb, 0x14, 0x34, 0x22, 0x1e,
287+
0xae, 0xa7, 0xa0, 0x7a, 0x04, 0x6f, 0x35, 0x66,
288+
0xff, 0xff, 0x00, 0x1d, 0xbb, 0x0c, 0x78, 0x17,
289+
0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
290+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
291+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
292+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
294+
0xff, 0xff, 0x55, 0x04, 0xff, 0xff, 0x00, 0x1d,
295+
0x01, 0x04, 0x4c, 0x4c, 0x30, 0x33, 0x2f, 0x4d,
296+
0x61, 0x79, 0x2f, 0x32, 0x30, 0x32, 0x34, 0x20,
297+
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
298+
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
299+
0x30, 0x30, 0x30, 0x30, 0x31, 0x65, 0x62, 0x64,
300+
0x35, 0x38, 0x63, 0x32, 0x34, 0x34, 0x39, 0x37,
301+
0x30, 0x62, 0x33, 0x61, 0x61, 0x39, 0x64, 0x37,
302+
0x38, 0x33, 0x62, 0x62, 0x30, 0x30, 0x31, 0x30,
303+
0x31, 0x31, 0x66, 0x62, 0x65, 0x38, 0x65, 0x61,
304+
0x38, 0x65, 0x39, 0x38, 0x65, 0x30, 0x30, 0x65,
305+
0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0xf2, 0x05,
306+
0x2a, 0x01, 0x00, 0x00, 0x00, 0x23, 0x21, 0x00,
307+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
308+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
309+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
310+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
311+
0xac, 0x00, 0x00, 0x00, 0x00
312+
}, false
313+
};
314+
315+
forks.bip16 = true;
316+
forks.bip30 = true;
317+
forks.bip30_deactivate = true;
318+
forks.bip30_reactivate = true;
319+
forks.bip34 = true;
320+
forks.bip42 = true;
321+
forks.bip65 = true;
322+
forks.bip66 = true;
323+
forks.bip68 = true;
324+
forks.bip90 = true;
325+
forks.bip94 = true;
326+
forks.bip112 = true;
327+
forks.bip113 = true;
328+
forks.bip141 = true;
329+
forks.bip143 = true;
330+
forks.bip147 = true;
331+
forks.bip341 = true;
332+
forks.bip342 = true;
333+
forks.retarget = true; // !regtest
334+
forks.difficult = false; // !testnet
335+
forks.time_warp_patch = false; // litecoin
336+
forks.retarget_overflow_patch = false; // litecoin
337+
forks.scrypt_proof_of_work = false; // litecoin
338+
339+
bip16_activation_time = 1329264000;
340+
bip34_activation_threshold = 51;
341+
bip34_enforcement_threshold = 75;
342+
bip34_activation_sample = 100;
343+
bip90_bip34_height = 1;
344+
bip90_bip65_height = 1;
345+
bip90_bip66_height = 1;
346+
347+
// Approximation, see satoshi PR#12204.
348+
bip30_reactivate_height = 486'000'000_size;
349+
350+
bip30_deactivate_checkpoint = { "0000000012982b6d5f621229286b880e909984df669c2afabb102ce311b13f28", 1 };
351+
bip9_bit0_active_checkpoint = { "0000000012982b6d5f621229286b880e909984df669c2afabb102ce311b13f28", 1 };
352+
bip9_bit1_active_checkpoint = { "0000000012982b6d5f621229286b880e909984df669c2afabb102ce311b13f28", 1 };
353+
bip9_bit2_active_checkpoint = { "0000000012982b6d5f621229286b880e909984df669c2afabb102ce311b13f28", 1};
354+
checkpoints =
355+
{
356+
{ "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043", 0 }
357+
};
358+
milestone = { "0000000002368b1e4ee27e2e85676ae6f9f9e69579b29093e9a82c170bf7cf8a", 123613 };
359+
minimum_work = base16_hash("000000000000000000000000000000000000000000000b6a51f415a67c0da307");
360+
break;
361+
}
362+
270363
case chain::selection::regtest:
271364
{
272365
proof_of_work_limit = 0x207fffff;

test/settings.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,55 @@ BOOST_AUTO_TEST_CASE(settings__construct__testnet_context__expected)
154154
BOOST_REQUIRE_EQUAL(configuration.milestone, milestone);
155155
}
156156

157+
BOOST_AUTO_TEST_CASE(settings__construct__testnet4_context__expected)
158+
{
159+
const chain::checkpoint::list checkpoints
160+
{
161+
{ "00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043", 0 }
162+
};
163+
164+
settings configuration(chain::selection::testnet4);
165+
BOOST_REQUIRE_EQUAL(configuration.block_spacing_seconds, 600u);
166+
BOOST_REQUIRE_EQUAL(configuration.timestamp_limit_seconds, 7200u);
167+
BOOST_REQUIRE_EQUAL(configuration.retargeting_interval_seconds, 1209600u);
168+
BOOST_REQUIRE_EQUAL(configuration.proof_of_work_limit, 486604799u);
169+
BOOST_REQUIRE_EQUAL(configuration.minimum_timespan(), 302400u);
170+
BOOST_REQUIRE_EQUAL(configuration.maximum_timespan(), 4838400u);
171+
BOOST_REQUIRE_EQUAL(configuration.retargeting_interval(), 2016u);
172+
const chain::block genesis_block = configuration.genesis_block;
173+
BOOST_REQUIRE_EQUAL(encode_base16(genesis_block.to_data(false)), "0100000000000000000000000000000000000000000000000000000000000000000000004e7b2b9128fe0291db0693af2ae418b767e657cd407e80cb1434221eaea7a07a046f3566ffff001dbb0c78170101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5504ffff001d01044c4c30332f4d61792f323032342030303030303030303030303030303030303030303165626435386332343439373062336161396437383362623030313031316662653865613865393865303065ffffffff0100f2052a010000002321000000000000000000000000000000000000000000000000000000000000000000ac00000000");
174+
BOOST_REQUIRE_EQUAL(configuration.first_version, 1u);
175+
BOOST_REQUIRE_EQUAL(configuration.bip34_version, 2u);
176+
BOOST_REQUIRE_EQUAL(configuration.bip66_version, 3u);
177+
BOOST_REQUIRE_EQUAL(configuration.bip65_version, 4u);
178+
BOOST_REQUIRE_EQUAL(configuration.bip9_version_bit0, bit_right<uint32_t>(0));
179+
BOOST_REQUIRE_EQUAL(configuration.bip9_version_bit1, bit_right<uint32_t>(1));
180+
BOOST_REQUIRE_EQUAL(configuration.bip9_version_bit2, bit_right<uint32_t>(2));
181+
BOOST_REQUIRE_EQUAL(configuration.bip9_version_base, 0x20000000u);
182+
BOOST_REQUIRE_EQUAL(configuration.bip16_activation_time, 1329264000u);
183+
BOOST_REQUIRE_EQUAL(configuration.bip34_activation_threshold, 51u);
184+
BOOST_REQUIRE_EQUAL(configuration.bip34_enforcement_threshold, 75u);
185+
BOOST_REQUIRE_EQUAL(configuration.bip34_activation_sample, 100u);
186+
BOOST_REQUIRE_EQUAL(configuration.bip90_bip34_height, 1u);
187+
BOOST_REQUIRE_EQUAL(configuration.bip90_bip65_height, 1u);
188+
BOOST_REQUIRE_EQUAL(configuration.bip90_bip66_height, 1u);
189+
const chain::checkpoint bit0_active("0000000012982b6d5f621229286b880e909984df669c2afabb102ce311b13f28", 1u);
190+
BOOST_REQUIRE_EQUAL(configuration.bip9_bit0_active_checkpoint, bit0_active);
191+
const chain::checkpoint bit1_active("0000000012982b6d5f621229286b880e909984df669c2afabb102ce311b13f28", 1u);
192+
BOOST_REQUIRE_EQUAL(configuration.bip9_bit1_active_checkpoint, bit1_active);
193+
////const chain::checkpoint bit2_active("0000000000000000000687bca986194dc2c1f949318629b44bb54ec0a94d8244", 709632);
194+
////BOOST_REQUIRE_EQUAL(configuration.bip9_bit2_active_checkpoint, bit2_active);
195+
BOOST_REQUIRE_EQUAL(configuration.initial_subsidy_bitcoin, 50u);
196+
BOOST_REQUIRE_EQUAL(configuration.subsidy_interval_blocks, 210000u);
197+
BOOST_REQUIRE_EQUAL(configuration.bitcoin_to_satoshi(1), 100000000u);
198+
BOOST_REQUIRE_EQUAL(configuration.max_money(), 2099999997690000u);
199+
BOOST_REQUIRE_EQUAL(configuration.initial_subsidy(), 5'000'000'000_u64);
200+
BOOST_REQUIRE_EQUAL(configuration.checkpoints, checkpoints);
201+
BOOST_REQUIRE_EQUAL(configuration.minimum_work, to_uintx(base16_hash("000000000000000000000000000000000000000000000b6a51f415a67c0da307")));
202+
const chain::checkpoint milestone("0000000002368b1e4ee27e2e85676ae6f9f9e69579b29093e9a82c170bf7cf8a", 123613u);
203+
BOOST_REQUIRE_EQUAL(configuration.milestone, milestone);
204+
}
205+
157206
BOOST_AUTO_TEST_CASE(settings__construct__regtest_context__expected)
158207
{
159208
settings configuration(chain::selection::regtest);

0 commit comments

Comments
 (0)