Merge ElementsProject/elements#1044: Define Taproot activation parameters for Liquid

3cb9612faa test: add test for Taproot activation (Andrew Poelstra)
5291c0d9d9 chainparams: add undocumented regtest/testnet only -con_taproot_signal_start option (Andrew Poelstra)
cc6b933478 add missing taproot activation params for Liquid v1 (Andrew Poelstra)
bec6bcf31b versionbits: allow specific deployments to override the signalling/threshold values (Andrew Poelstra)

Pull request description:

  Sets Taproot to start signalling around noon (California time) on Nov 1, 2021, assuming 95% of blocks are produced between now and then.

  Will activate after one week of 100% signalling. If we can pull this off on the first or second try, we will beat Bitcoin which currently looks like it will activate on Nov 16.

  **Edit:** actually, even without Speedy Trial, there is one full period (week) where Taproot will be "locked in" but not "active". So it will activate on Nov 15 at the earliest.

ACKs for top commit:
  achow101:
    ACK 3cb9612faa

Tree-SHA512: c3a80d39ba86a0d762a3057cb9c45e379c70c2daee8ff2e54978a4c32118c935c8909d872ac9f873e283441c52c1974c06268f10dc7bf9e9e000c21063ac84d7
This commit is contained in:
Andrew Poelstra 2021-09-18 03:22:04 +00:00
commit f7f9555792
No known key found for this signature in database
GPG key ID: C588D63CE41B97C1
6 changed files with 156 additions and 3 deletions

View file

@ -478,8 +478,10 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_DYNA_FED].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_DYNA_FED].nTimeout = 1230767999; // December 31, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = gArgs.GetArg("-con_taproot_signal_start", Consensus::BIP9Deployment::ALWAYS_ACTIVE);
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nPeriod = 128; // test ability to change from default
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nThreshold = 128;
consensus.nMinimumChainWork = uint256{};
consensus.defaultAssumeValid = uint256{};
@ -978,6 +980,12 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1554500; // November 1, 2021
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nPeriod = 10080; // one week...
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nThreshold = 10080; // ...of 100% signalling
// Activated from block 1,000,000.
consensus.vDeployments[Consensus::DEPLOYMENT_DYNA_FED].bit = 25;
// Allow blocksigners to delay activation.
@ -1245,6 +1253,10 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_DYNA_FED].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
}
if (args.IsArgSet("-con_taproot_signal_start")) {
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = gArgs.GetArg("-con_taproot_signal_start", 0);
}
// END ELEMENTS fields
}

View file

@ -60,6 +60,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-con_dyna_deploy_signal", "Whether to signal for the Dynamic Federations deployment (default: false).", ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
argsman.AddArg("-dynamic_epoch_length", "Per-chain parameter that sets how many blocks dynamic federation voting and enforcement are in effect for.", ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
argsman.AddArg("-total_valid_epochs", "Per-chain parameter that sets how long a particular fedpegscript is in effect for.", ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
argsman.AddArg("-con_taproot_signal_start", "Whether, and at what blockheight, to start signalling for Taproot activation (default: false) (regtest, Liquid testnet, or custom only).", ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
// END ELEMENTS
//
}

View file

@ -7,6 +7,7 @@
#define BITCOIN_CONSENSUS_PARAMS_H
#include <asset.h>
#include <optional.h>
#include <uint256.h>
#include <limits>
@ -37,6 +38,11 @@ struct BIP9Deployment {
// ELEMENTS: Interpreted as block height!
int64_t nTimeout;
// ELEMENTS: allow overriding the signalling period length rather than using `nMinerConfirmationWindow`
Optional<uint32_t> nPeriod{nullopt};
// ELEMENTS: allow overriding the activation threshold rather than using `nRuleChangeActivationThreshold`
Optional<uint32_t> nThreshold{nullopt};
/** Constant for nTimeout very far in the future. */
static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();

View file

@ -180,8 +180,20 @@ private:
protected:
int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; }
int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; }
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
int Period(const Consensus::Params& params) const override {
if (params.vDeployments[id].nPeriod) {
return *params.vDeployments[id].nPeriod;
} else {
return params.nMinerConfirmationWindow;
}
}
int Threshold(const Consensus::Params& params) const override {
if (params.vDeployments[id].nThreshold) {
return *params.vDeployments[id].nThreshold;
} else {
return params.nRuleChangeActivationThreshold;
}
}
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
{

View file

@ -0,0 +1,121 @@
#!/usr/bin/env python3
# Copyright (c) 2015-2020 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test Taproot soft fork activation for Elements
Unlike Bitcoin, this (a) does not use Speedy Trial, and (b) does use
a pair of new versionbits features which allows Taproot to have its
own signalling period length (128 blocks on regtest) and activation
threshold (100%).
The primary purpose of this test is to confirm that this configuration
works; the actual activation (e.g. how are Taproot-enabled transactions
treated) is covered by other tests and by manual testing.
There is an undocumented option `con_taproot_signal_start` which sets
the block at which signalling starts; otherwise it is set to "always
on" which means that signalling will not occur.
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
class TaprootActivationTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def test_activation(self, rpc, activation_height):
self.log.info("Testing activation at height %d" % activation_height)
activation_height = 128 * ((activation_height + 127) // 128)
assert_equal(rpc.getblockcount(), 0)
blocks = rpc.generatetoaddress(activation_height - 2, rpc.getnewaddress())
assert_equal(rpc.getblockcount(), activation_height - 2)
for n, block in enumerate(blocks):
decode = rpc.getblockheader(block)
if n < 143:
assert_equal (decode["versionHex"], "20000000")
elif n < 431:
# TESTDUMMY deployment: 144 blocks active, 144 blocks locked in
assert_equal (decode["versionHex"], "30000000")
else:
assert_equal (decode["versionHex"], "20000000")
assert_equal(rpc.getblockchaininfo()["softforks"]["taproot"]["bip9"]["status"], "defined")
# The 1023rd block does not signal, but changes the signalling state
# to "started" from "defined"
blocks = rpc.generatetoaddress(1, rpc.getnewaddress())
assert_equal(rpc.getblockchaininfo()["softforks"]["taproot"]["bip9"]["status"], "started")
assert_equal(rpc.getblockheader(blocks[0])["versionHex"], "20000000")
blocks = rpc.generatetoaddress(127, rpc.getnewaddress())
for n, block in enumerate(blocks):
decode = rpc.getblockheader(block)
assert_equal (decode["versionHex"], "20000004")
assert_equal(rpc.getblockchaininfo()["softforks"]["taproot"]["bip9"]["status"], "started")
# Fail to signal on the 128th block. Since the threshold for Taproot is
# 100% this will prevent activation. Note that our period is 128, not
# 144 (the default), as we have overridden the period for Taproot. On
# the main Liquid chain it is overridden to be one week of signalling.
block = rpc.getnewblockhex()
block = block[:1] + "0" + block[2:] # turn off Taproot signal
rpc.submitblock(block)
assert_equal(rpc.getblockchaininfo()["softforks"]["taproot"]["bip9"]["status"], "started")
# Run through another 128 blocks, without failing to signal
blocks = rpc.generatetoaddress(127, rpc.getnewaddress())
for n, block in enumerate(blocks):
decode = rpc.getblockheader(block)
assert_equal (decode["versionHex"], "20000004")
assert_equal(rpc.getblockchaininfo()["softforks"]["taproot"]["bip9"]["status"], "started")
# The 128th block then switches from "started" to "locked_in"
blocks = rpc.generatetoaddress(1, rpc.getnewaddress())
assert_equal(rpc.getblockchaininfo()["softforks"]["taproot"]["bip9"]["status"], "locked_in")
assert_equal(rpc.getblockheader(blocks[0])["versionHex"], "20000004")
# Run through another 128 blocks, which will go from "locked in" to "active" regardless of signalling
blocks = rpc.generatetoaddress(127, rpc.getnewaddress())
for n, block in enumerate(blocks):
decode = rpc.getblockheader(block)
assert_equal (decode["versionHex"], "20000004")
assert_equal(rpc.getblockchaininfo()["softforks"]["taproot"]["bip9"]["status"], "locked_in")
block = rpc.getnewblockhex()
block = block[:1] + "0" + block[2:] # turn off Taproot signal
rpc.submitblock(block)
assert_equal(rpc.getblockchaininfo()["softforks"]["taproot"]["bip9"]["status"], "active")
# After the state is "active", signallng stops by default.
blocks = rpc.generatetoaddress(1, self.nodes[0].getnewaddress())
assert_equal(rpc.getblockchaininfo()["softforks"]["taproot"]["bip9"]["status"], "active")
assert_equal(rpc.getblockheader(blocks[0])["versionHex"], "20000000")
def run_test(self):
# Test that regtest nodes without -con_taproot_signal_start never signal
self.log.info("Testing node not configured to activate taproot")
blocks = self.nodes[0].generatetoaddress(2500, self.nodes[0].getnewaddress())
assert_equal(self.nodes[0].getblockcount(), 2500)
for n, block in enumerate(blocks):
decode = self.nodes[0].getblockheader(block)
if n < 143:
assert_equal (decode["versionHex"], "20000000")
elif n < 431:
# TESTDUMMY deployment: 144 blocks active, 144 blocks locked in
assert_equal (decode["versionHex"], "30000000")
else:
assert_equal (decode["versionHex"], "20000000")
# Test activation starting from height 1000
self.restart_node(0, ["-con_taproot_signal_start=500"])
self.nodes[0].invalidateblock(self.nodes[0].getblockhash(1))
self.test_activation(self.nodes[0], 500)
if __name__ == '__main__':
TaprootActivationTest().main()

View file

@ -216,6 +216,7 @@ BASE_SCRIPTS = [
'mining_prioritisetransaction.py',
'p2p_invalid_locator.py',
'p2p_invalid_block.py',
'feature_elements_taproot_activation.py',
# ELEMENTS: needs to be fixed
#'p2p_invalid_messages.py',
'p2p_invalid_tx.py',