Merge #18267: BIP-325: Signet [consensus]

8258c4c007 test: some sanity checks for consensus logic (Anthony Towns)
e47ad375bf test: basic signet tests (Karl-Johan Alm)
4c189abdc4 test: add small signet fuzzer (practicalswift)
ec9b25d046 test: signet network selection tests (Karl-Johan Alm)
3efe298dcc signet: hard-coded parameters for Signet Global Network VI (2020-09-07) (Karl-Johan Alm)
c7898bca4e qt: update QT to support signet network (Karl-Johan Alm)
a8de47a1c9 consensus: add signet validation (Karl-Johan Alm)
e8990f1214 add signet chain and accompanying parameters (Karl-Johan Alm)
404682b7cd add signet basic support (signet.cpp) (Karl-Johan Alm)
a2147d7dad validation: move GetWitnessCommitmentIndex to consensus/validation (Karl-Johan Alm)

Pull request description:

  This PR is a part of BIP-325 (https://github.com/bitcoin/bips/blob/master/bip-0325.mediawiki), and is a sub-PR of #16411.

  * Signet consensus (this)
  * Signet RPC tools (pending)
  * Signet utility scripts (contrib/signet) (pending)

ACKs for top commit:
  jonatack:
    re-ACK 8258c4c007 per `git diff dbeea65 8258c4c`, only change since last review is updated `-signet*` config option naming.
  fjahr:
    re-ACK 8258c4c
  laanwj:
    ACK 8258c4c007
  MarcoFalke:
    Approach ACK 8258c4c007 🌵

Tree-SHA512: 5d158add96755910837feafa8214e13695b769a6aec3a2da753cf672618bef377fac43b0f4b772a87b25dd9f0c1c9b29f2789785d7a7d47a155cdcf48f7c975d
This commit is contained in:
Wladimir J. van der Laan
2020-09-21 22:32:05 +02:00
21 changed files with 532 additions and 44 deletions

View File

@@ -202,6 +202,7 @@ BITCOIN_CORE_H = \
script/signingprovider.h \
script/standard.h \
shutdown.h \
signet.h \
streams.h \
support/allocators/secure.h \
support/allocators/zeroafterfree.h \
@@ -322,6 +323,7 @@ libbitcoin_server_a_SOURCES = \
rpc/server.cpp \
script/sigcache.cpp \
shutdown.cpp \
signet.cpp \
timedata.cpp \
torcontrol.cpp \
txdb.cpp \

View File

@@ -137,6 +137,7 @@ FUZZ_TARGETS = \
test/fuzz/secp256k1_ecdsa_signature_parse_der_lax \
test/fuzz/service_deserialize \
test/fuzz/signature_checker \
test/fuzz/signet \
test/fuzz/snapshotmetadata_deserialize \
test/fuzz/span \
test/fuzz/spanparsing \
@@ -1129,6 +1130,12 @@ test_fuzz_signature_checker_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_signature_checker_LDFLAGS = $(FUZZ_SUITE_LDFLAGS_COMMON)
test_fuzz_signature_checker_SOURCES = test/fuzz/signature_checker.cpp
test_fuzz_signet_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_signet_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_signet_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_signet_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_signet_SOURCES = test/fuzz/signet.cpp
test_fuzz_snapshotmetadata_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSNAPSHOTMETADATA_DESERIALIZE=1
test_fuzz_snapshotmetadata_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_snapshotmetadata_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)

View File

@@ -7,6 +7,7 @@
#include <chainparamsseeds.h>
#include <consensus/merkle.h>
#include <hash.h> // for signet block challenge hash
#include <tinyformat.h>
#include <util/system.h>
#include <util/strencodings.h>
@@ -63,6 +64,8 @@ class CMainParams : public CChainParams {
public:
CMainParams() {
strNetworkID = CBaseChainParams::MAIN;
consensus.signet_blocks = false;
consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 210000;
consensus.BIP16Exception = uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22");
consensus.BIP34Height = 227931;
@@ -172,6 +175,8 @@ class CTestNetParams : public CChainParams {
public:
CTestNetParams() {
strNetworkID = CBaseChainParams::TESTNET;
consensus.signet_blocks = false;
consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 210000;
consensus.BIP16Exception = uint256S("0x00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105");
consensus.BIP34Height = 21111;
@@ -250,6 +255,95 @@ public:
}
};
/**
* Signet
*/
class SigNetParams : public CChainParams {
public:
explicit SigNetParams(const ArgsManager& args) {
std::vector<uint8_t> bin;
vSeeds.clear();
if (!args.IsArgSet("-signetchallenge")) {
LogPrintf("Using default signet network\n");
bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae");
vSeeds.emplace_back("178.128.221.177");
vSeeds.emplace_back("2a01:7c8:d005:390::5");
vSeeds.emplace_back("ntv3mtqw5wt63red.onion:38333");
} else {
const auto signet_challenge = args.GetArgs("-signetchallenge");
if (signet_challenge.size() != 1) {
throw std::runtime_error(strprintf("%s: -signetchallenge cannot be multiple values.", __func__));
}
bin = ParseHex(signet_challenge[0]);
LogPrintf("Signet with challenge %s\n", signet_challenge[0]);
}
if (args.IsArgSet("-signetseednode")) {
vSeeds = args.GetArgs("-signetseednode");
}
strNetworkID = CBaseChainParams::SIGNET;
consensus.signet_blocks = true;
consensus.signet_challenge.assign(bin.begin(), bin.end());
consensus.nSubsidyHalvingInterval = 210000;
consensus.BIP34Height = 1;
consensus.BIP65Height = 1;
consensus.BIP66Height = 1;
consensus.CSVHeight = 1;
consensus.SegwitHeight = 1;
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
consensus.nPowTargetSpacing = 10 * 60;
consensus.fPowAllowMinDifficultyBlocks = false;
consensus.fPowNoRetargeting = false;
consensus.nRuleChangeActivationThreshold = 1916;
consensus.nMinerConfirmationWindow = 2016;
consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000");
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
// message start is defined as the first 4 bytes of the sha256d of the block script
CHashWriter h(SER_DISK, 0);
h << consensus.signet_challenge;
uint256 hash = h.GetHash();
memcpy(pchMessageStart, hash.begin(), 4);
LogPrintf("Signet derived magic (message start): %s\n", HexStr({pchMessageStart, pchMessageStart + 4}));
nDefaultPort = 38333;
nPruneAfterHeight = 1000;
m_assumed_blockchain_size = 0;
m_assumed_chain_state_size = 0;
genesis = CreateGenesisBlock(1598918400, 52613770, 0x1e0377ae, 1, 50 * COIN);
consensus.hashGenesisBlock = genesis.GetHash();
assert(consensus.hashGenesisBlock == uint256S("0x00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6"));
assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
vFixedSeeds.clear();
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239);
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
bech32_hrp = "tb";
fDefaultConsistencyChecks = false;
fRequireStandard = true;
m_is_test_chain = true;
m_is_mockable_chain = false;
chainTxData = ChainTxData{
0,
0,
0
};
}
};
/**
* Regression test
*/
@@ -257,6 +351,8 @@ class CRegTestParams : public CChainParams {
public:
explicit CRegTestParams(const ArgsManager& args) {
strNetworkID = CBaseChainParams::REGTEST;
consensus.signet_blocks = false;
consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 150;
consensus.BIP16Exception = uint256();
consensus.BIP34Height = 500; // BIP34 activated on regtest (Used in functional tests)
@@ -391,12 +487,15 @@ const CChainParams &Params() {
std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain)
{
if (chain == CBaseChainParams::MAIN)
if (chain == CBaseChainParams::MAIN) {
return std::unique_ptr<CChainParams>(new CMainParams());
else if (chain == CBaseChainParams::TESTNET)
} else if (chain == CBaseChainParams::TESTNET) {
return std::unique_ptr<CChainParams>(new CTestNetParams());
else if (chain == CBaseChainParams::REGTEST)
} else if (chain == CBaseChainParams::SIGNET) {
return std::unique_ptr<CChainParams>(new SigNetParams(gArgs));
} else if (chain == CBaseChainParams::REGTEST) {
return std::unique_ptr<CChainParams>(new CRegTestParams(gArgs));
}
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}

View File

@@ -13,6 +13,7 @@
const std::string CBaseChainParams::MAIN = "main";
const std::string CBaseChainParams::TESTNET = "test";
const std::string CBaseChainParams::SIGNET = "signet";
const std::string CBaseChainParams::REGTEST = "regtest";
void SetupChainParamsBaseOptions(ArgsManager& argsman)
@@ -23,6 +24,9 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signet", "Use the signet chain. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
}
static std::unique_ptr<CBaseChainParams> globalChainBaseParams;
@@ -35,14 +39,16 @@ const CBaseChainParams& BaseParams()
std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain)
{
if (chain == CBaseChainParams::MAIN)
if (chain == CBaseChainParams::MAIN) {
return MakeUnique<CBaseChainParams>("", 8332);
else if (chain == CBaseChainParams::TESTNET)
} else if (chain == CBaseChainParams::TESTNET) {
return MakeUnique<CBaseChainParams>("testnet3", 18332);
else if (chain == CBaseChainParams::REGTEST)
} else if (chain == CBaseChainParams::SIGNET) {
return MakeUnique<CBaseChainParams>("signet", 38332);
} else if (chain == CBaseChainParams::REGTEST) {
return MakeUnique<CBaseChainParams>("regtest", 18443);
else
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
void SelectBaseParams(const std::string& chain)

View File

@@ -21,6 +21,7 @@ public:
/** Chain name strings */
static const std::string MAIN;
static const std::string TESTNET;
static const std::string SIGNET;
static const std::string REGTEST;
///@}

View File

@@ -80,6 +80,13 @@ struct Params {
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
uint256 nMinimumChainWork;
uint256 defaultAssumeValid;
/**
* If true, witness commitments contain a payload equal to a Bitcoin Script solution
* to the signet challenge. See BIP325.
*/
bool signet_blocks{false};
std::vector<uint8_t> signet_challenge;
};
} // namespace Consensus

View File

@@ -12,6 +12,12 @@
#include <primitives/transaction.h>
#include <primitives/block.h>
/** Index marker for when no witness commitment is present in a coinbase transaction. */
static constexpr int NO_WITNESS_COMMITMENT{-1};
/** Minimum size of a witness commitment structure. Defined in BIP 141. **/
static constexpr size_t MINIMUM_WITNESS_COMMITMENT{38};
/** A "reason" why a transaction was invalid, suitable for determining whether the
* provider of the transaction should be banned/ignored/disconnected/etc.
*/
@@ -151,4 +157,25 @@ static inline int64_t GetTransactionInputWeight(const CTxIn& txin)
return ::GetSerializeSize(txin, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, PROTOCOL_VERSION);
}
/** Compute at which vout of the block's coinbase transaction the witness commitment occurs, or -1 if not found */
inline int GetWitnessCommitmentIndex(const CBlock& block)
{
int commitpos = NO_WITNESS_COMMITMENT;
if (!block.vtx.empty()) {
for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
const CTxOut& vout = block.vtx[0]->vout[o];
if (vout.scriptPubKey.size() >= MINIMUM_WITNESS_COMMITMENT &&
vout.scriptPubKey[0] == OP_RETURN &&
vout.scriptPubKey[1] == 0x24 &&
vout.scriptPubKey[2] == 0xaa &&
vout.scriptPubKey[3] == 0x21 &&
vout.scriptPubKey[4] == 0xa9 &&
vout.scriptPubKey[5] == 0xed) {
commitpos = o;
}
}
}
return commitpos;
}
#endif // BITCOIN_CONSENSUS_VALIDATION_H

View File

@@ -46,6 +46,7 @@ static const int TOOLTIP_WRAP_THRESHOLD = 80;
#define QAPP_ORG_DOMAIN "bitcoin.org"
#define QAPP_APP_NAME_DEFAULT "Bitcoin-Qt"
#define QAPP_APP_NAME_TESTNET "Bitcoin-Qt-testnet"
#define QAPP_APP_NAME_SIGNET "Bitcoin-Qt-signet"
#define QAPP_APP_NAME_REGTEST "Bitcoin-Qt-regtest"
/* One gigabyte (GB) in bytes */

View File

@@ -19,7 +19,8 @@ static const struct {
} network_styles[] = {
{"main", QAPP_APP_NAME_DEFAULT, 0, 0},
{"test", QAPP_APP_NAME_TESTNET, 70, 30},
{"regtest", QAPP_APP_NAME_REGTEST, 160, 30}
{"signet", QAPP_APP_NAME_SIGNET, 35, 15},
{"regtest", QAPP_APP_NAME_REGTEST, 160, 30},
};
static const unsigned network_styles_count = sizeof(network_styles)/sizeof(*network_styles);

149
src/signet.cpp Normal file
View File

@@ -0,0 +1,149 @@
// Copyright (c) 2019-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.
#include <signet.h>
#include <array>
#include <cstdint>
#include <vector>
#include <consensus/merkle.h>
#include <consensus/params.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <hash.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <span.h>
#include <script/interpreter.h>
#include <script/standard.h>
#include <streams.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <uint256.h>
static constexpr uint8_t SIGNET_HEADER[4] = {0xec, 0xc7, 0xda, 0xa2};
static constexpr unsigned int BLOCK_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_NULLDUMMY;
static bool FetchAndClearCommitmentSection(const Span<const uint8_t> header, CScript& witness_commitment, std::vector<uint8_t>& result)
{
CScript replacement;
bool found_header = false;
result.clear();
opcodetype opcode;
CScript::const_iterator pc = witness_commitment.begin();
std::vector<uint8_t> pushdata;
while (witness_commitment.GetOp(pc, opcode, pushdata)) {
if (pushdata.size() > 0) {
if (!found_header && pushdata.size() > (size_t) header.size() && Span<const uint8_t>(pushdata.data(), header.size()) == header) {
// pushdata only counts if it has the header _and_ some data
result.insert(result.end(), pushdata.begin() + header.size(), pushdata.end());
pushdata.erase(pushdata.begin() + header.size(), pushdata.end());
found_header = true;
}
replacement << pushdata;
} else {
replacement << opcode;
}
}
if (found_header) witness_commitment = replacement;
return found_header;
}
static uint256 ComputeModifiedMerkleRoot(const CMutableTransaction& cb, const CBlock& block)
{
std::vector<uint256> leaves;
leaves.resize(block.vtx.size());
leaves[0] = cb.GetHash();
for (size_t s = 1; s < block.vtx.size(); ++s) {
leaves[s] = block.vtx[s]->GetHash();
}
return ComputeMerkleRoot(std::move(leaves));
}
SignetTxs SignetTxs::Create(const CBlock& block, const CScript& challenge)
{
CMutableTransaction tx_to_spend;
tx_to_spend.nVersion = 0;
tx_to_spend.nLockTime = 0;
tx_to_spend.vin.emplace_back(COutPoint(), CScript(OP_0), 0);
tx_to_spend.vout.emplace_back(0, challenge);
CMutableTransaction tx_spending;
tx_spending.nVersion = 0;
tx_spending.nLockTime = 0;
tx_spending.vin.emplace_back(COutPoint(), CScript(), 0);
tx_spending.vout.emplace_back(0, CScript(OP_RETURN));
// can't fill any other fields before extracting signet
// responses from block coinbase tx
// find and delete signet signature
if (block.vtx.empty()) return invalid(); // no coinbase tx in block; invalid
CMutableTransaction modified_cb(*block.vtx.at(0));
const int cidx = GetWitnessCommitmentIndex(block);
if (cidx == NO_WITNESS_COMMITMENT) {
return invalid(); // require a witness commitment
}
CScript& witness_commitment = modified_cb.vout.at(cidx).scriptPubKey;
std::vector<uint8_t> signet_solution;
if (!FetchAndClearCommitmentSection(SIGNET_HEADER, witness_commitment, signet_solution)) {
// no signet solution -- allow this to support OP_TRUE as trivial block challenge
} else {
try {
VectorReader v(SER_NETWORK, INIT_PROTO_VERSION, signet_solution, 0);
v >> tx_spending.vin[0].scriptSig;
v >> tx_spending.vin[0].scriptWitness.stack;
if (!v.empty()) return invalid(); // extraneous data encountered
} catch (const std::exception&) {
return invalid(); // parsing error
}
}
uint256 signet_merkle = ComputeModifiedMerkleRoot(modified_cb, block);
std::vector<uint8_t> block_data;
CVectorWriter writer(SER_NETWORK, INIT_PROTO_VERSION, block_data, 0);
writer << block.nVersion;
writer << block.hashPrevBlock;
writer << signet_merkle;
writer << block.nTime;
tx_to_spend.vin[0].scriptSig << block_data;
tx_spending.vin[0].prevout = COutPoint(tx_to_spend.GetHash(), 0);
return {tx_to_spend, tx_spending};
}
// Signet block solution checker
bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& consensusParams)
{
if (block.GetHash() == consensusParams.hashGenesisBlock) {
// genesis block solution is always valid
return true;
}
const CScript challenge(consensusParams.signet_challenge.begin(), consensusParams.signet_challenge.end());
const SignetTxs signet_txs(block, challenge);
if (!signet_txs.m_valid) {
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution parse failure)\n");
return false;
}
const CScript& scriptSig = signet_txs.m_to_sign.vin[0].scriptSig;
const CScriptWitness& witness = signet_txs.m_to_sign.vin[0].scriptWitness;
TransactionSignatureChecker sigcheck(&signet_txs.m_to_sign, /*nIn=*/ 0, /*amount=*/ signet_txs.m_to_spend.vout[0].nValue);
if (!VerifyScript(scriptSig, signet_txs.m_to_spend.vout[0].scriptPubKey, &witness, BLOCK_SCRIPT_VERIFY_FLAGS, sigcheck)) {
LogPrint(BCLog::VALIDATION, "CheckSignetBlockSolution: Errors in block (block solution invalid)\n");
return false;
}
return true;
}

42
src/signet.h Normal file
View File

@@ -0,0 +1,42 @@
// Copyright (c) 2019-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.
#ifndef BITCOIN_SIGNET_H
#define BITCOIN_SIGNET_H
#include <consensus/params.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
/**
* Extract signature and check whether a block has a valid solution
*/
bool CheckSignetBlockSolution(const CBlock& block, const Consensus::Params& consensusParams);
/**
* Generate the signet tx corresponding to the given block
*
* The signet tx commits to everything in the block except:
* 1. It hashes a modified merkle root with the signet signature removed.
* 2. It skips the nonce.
*/
class SignetTxs {
private:
struct invalid {};
SignetTxs(invalid i) : m_to_spend(), m_to_sign(), m_valid(false) { }
template<class T1, class T2>
SignetTxs(const T1& to_spend, const T2& to_sign) : m_to_spend{to_spend}, m_to_sign{to_sign}, m_valid(true) { }
static SignetTxs Create(const CBlock& block, const CScript& challenge);
public:
SignetTxs(const CBlock& block, const CScript& challenge) : SignetTxs(Create(block, challenge)) { }
const CTransaction m_to_spend;
const CTransaction m_to_sign;
const bool m_valid;
};
#endif // BITCOIN_SIGNET_H

34
src/test/fuzz/signet.cpp Normal file
View File

@@ -0,0 +1,34 @@
// Copyright (c) 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.
#include <chainparams.h>
#include <consensus/validation.h>
#include <primitives/block.h>
#include <signet.h>
#include <streams.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/util.h>
#include <cstdint>
#include <optional>
#include <vector>
void initialize()
{
InitializeFuzzingContext(CBaseChainParams::SIGNET);
}
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
const std::optional<CBlock> block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
if (!block) {
return;
}
(void)CheckSignetBlockSolution(*block, Params().GetConsensus());
if (GetWitnessCommitmentIndex(*block) != NO_WITNESS_COMMITMENT) {
(void)SignetTxs(*block, ConsumeScript(fuzzed_data_provider));
}
}

View File

@@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(key_io_invalid)
std::string exp_base58string = test[0].get_str();
// must be invalid as public and as private key
for (const auto& chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) {
for (const auto& chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET, CBaseChainParams::REGTEST }) {
SelectParams(chain);
destination = DecodeDestination(exp_base58string);
BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest);

View File

@@ -135,4 +135,51 @@ BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
}
}
void sanity_check_chainparams(std::string chainName)
{
const auto chainParams = CreateChainParams(chainName);
const auto consensus = chainParams->GetConsensus();
// hash genesis is correct
BOOST_CHECK_EQUAL(consensus.hashGenesisBlock, chainParams->GenesisBlock().GetHash());
// target timespan is an even multiple of spacing
BOOST_CHECK_EQUAL(consensus.nPowTargetTimespan % consensus.nPowTargetSpacing, 0);
// genesis nBits is positive, doesn't overflow and is lower than powLimit
arith_uint256 pow_compact;
bool neg, over;
pow_compact.SetCompact(chainParams->GenesisBlock().nBits, &neg, &over);
BOOST_CHECK(!neg && pow_compact != 0);
BOOST_CHECK(!over);
BOOST_CHECK(UintToArith256(consensus.powLimit) >= pow_compact);
// check max target * 4*nPowTargetTimespan doesn't overflow -- see pow.cpp:CalculateNextWorkRequired()
if (!consensus.fPowNoRetargeting) {
arith_uint256 targ_max("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
targ_max /= consensus.nPowTargetTimespan*4;
BOOST_CHECK(UintToArith256(consensus.powLimit) < targ_max);
}
}
BOOST_AUTO_TEST_CASE(ChainParams_MAIN_sanity)
{
sanity_check_chainparams(CBaseChainParams::MAIN);
}
BOOST_AUTO_TEST_CASE(ChainParams_REGTEST_sanity)
{
sanity_check_chainparams(CBaseChainParams::REGTEST);
}
BOOST_AUTO_TEST_CASE(ChainParams_TESTNET_sanity)
{
sanity_check_chainparams(CBaseChainParams::TESTNET);
}
BOOST_AUTO_TEST_CASE(ChainParams_SIGNET_sanity)
{
sanity_check_chainparams(CBaseChainParams::SIGNET);
}
BOOST_AUTO_TEST_SUITE_END()

View File

@@ -848,8 +848,8 @@ struct ArgsMergeTestingSetup : public BasicTestingSetup {
ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] {
for (bool soft_set : {false, true}) {
for (bool force_set : {false, true}) {
for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET}) {
for (const std::string& section : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET}) {
for (const std::string& network : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::SIGNET}) {
for (bool net_specific : {false, true}) {
fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific);
}
@@ -1003,7 +1003,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup)
// Results file is formatted like:
//
// <input> || <IsArgSet/IsArgNegated/GetArg output> | <GetArgs output> | <GetUnsuitable output>
BOOST_CHECK_EQUAL(out_sha_hex, "8fd4877bb8bf337badca950ede6c917441901962f160e52514e06a60dea46cde");
BOOST_CHECK_EQUAL(out_sha_hex, "d1e436c1cd510d0ec44d5205d4b4e3bee6387d316e0075c58206cb16603f3d82");
}
// Similar test as above, but for ArgsManager::GetChainName function.
@@ -1106,7 +1106,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup)
// Results file is formatted like:
//
// <input> || <output>
BOOST_CHECK_EQUAL(out_sha_hex, "f0b3a3c29869edc765d579c928f7f1690a71fbb673b49ccf39cbc4de18156a0d");
BOOST_CHECK_EQUAL(out_sha_hex, "f263493e300023b6509963887444c41386f44b63bc30047eb8402e8c1144854c");
}
BOOST_AUTO_TEST_CASE(util_ReadWriteSettings)

View File

@@ -263,6 +263,7 @@ const std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
// Section names to be recognized in the config file.
static const std::set<std::string> available_sections{
CBaseChainParams::REGTEST,
CBaseChainParams::SIGNET,
CBaseChainParams::TESTNET,
CBaseChainParams::MAIN
};
@@ -916,16 +917,21 @@ std::string ArgsManager::GetChainName() const
};
const bool fRegTest = get_net("-regtest");
const bool fSigNet = get_net("-signet");
const bool fTestNet = get_net("-testnet");
const bool is_chain_arg_set = IsArgSet("-chain");
if ((int)is_chain_arg_set + (int)fRegTest + (int)fTestNet > 1) {
throw std::runtime_error("Invalid combination of -regtest, -testnet and -chain. Can use at most one.");
if ((int)is_chain_arg_set + (int)fRegTest + (int)fSigNet + (int)fTestNet > 1) {
throw std::runtime_error("Invalid combination of -regtest, -signet, -testnet and -chain. Can use at most one.");
}
if (fRegTest)
return CBaseChainParams::REGTEST;
if (fSigNet) {
return CBaseChainParams::SIGNET;
}
if (fTestNet)
return CBaseChainParams::TESTNET;
return GetArg("-chain", CBaseChainParams::MAIN);
}

View File

@@ -33,6 +33,7 @@
#include <script/script.h>
#include <script/sigcache.h>
#include <shutdown.h>
#include <signet.h>
#include <timedata.h>
#include <tinyformat.h>
#include <txdb.h>
@@ -1169,6 +1170,11 @@ bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::P
if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams))
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
// Signet only: check block solution
if (consensusParams.signet_blocks && !CheckSignetBlockSolution(block, consensusParams)) {
return error("ReadBlockFromDisk: Errors in block solution at %s", pos.ToString());
}
return true;
}
@@ -3346,6 +3352,11 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW))
return false;
// Signet only: check block solution
if (consensusParams.signet_blocks && fCheckPOW && !CheckSignetBlockSolution(block, consensusParams)) {
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-signet-blksig", "signet block signature validation failure");
}
// Check the merkle root.
if (fCheckMerkleRoot) {
bool mutated;
@@ -3409,31 +3420,11 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
return (height >= params.SegwitHeight);
}
int GetWitnessCommitmentIndex(const CBlock& block)
{
int commitpos = -1;
if (!block.vtx.empty()) {
for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
const CTxOut& vout = block.vtx[0]->vout[o];
if (vout.scriptPubKey.size() >= MINIMUM_WITNESS_COMMITMENT &&
vout.scriptPubKey[0] == OP_RETURN &&
vout.scriptPubKey[1] == 0x24 &&
vout.scriptPubKey[2] == 0xaa &&
vout.scriptPubKey[3] == 0x21 &&
vout.scriptPubKey[4] == 0xa9 &&
vout.scriptPubKey[5] == 0xed) {
commitpos = o;
}
}
}
return commitpos;
}
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams)
{
int commitpos = GetWitnessCommitmentIndex(block);
static const std::vector<unsigned char> nonce(32, 0x00);
if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) {
if (commitpos != NO_WITNESS_COMMITMENT && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) {
CMutableTransaction tx(*block.vtx[0]);
tx.vin[0].scriptWitness.stack.resize(1);
tx.vin[0].scriptWitness.stack[0] = nonce;
@@ -3447,7 +3438,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
int commitpos = GetWitnessCommitmentIndex(block);
std::vector<unsigned char> ret(32, 0x00);
if (consensusParams.SegwitHeight != std::numeric_limits<int>::max()) {
if (commitpos == -1) {
if (commitpos == NO_WITNESS_COMMITMENT) {
uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr);
CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot);
CTxOut out;
@@ -3585,7 +3576,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
bool fHaveWitness = false;
if (nHeight >= consensusParams.SegwitHeight) {
int commitpos = GetWitnessCommitmentIndex(block);
if (commitpos != -1) {
if (commitpos != NO_WITNESS_COMMITMENT) {
bool malleated = false;
uint256 hashWitness = BlockWitnessMerkleRoot(block, &malleated);
// The malleation check is ignored; as the transaction tree itself

View File

@@ -93,8 +93,6 @@ static const unsigned int DEFAULT_CHECKLEVEL = 3;
// one 128MB block file + added 15% undo data = 147MB greater for a total of 545MB
// Setting the target to >= 550 MiB will make it likely we can respect the target.
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024;
/** Minimum size of a witness commitment structure. Defined in BIP 141. **/
static constexpr size_t MINIMUM_WITNESS_COMMITMENT{38};
struct BlockHasher
{
@@ -306,9 +304,6 @@ bool TestBlockValidity(BlockValidationState& state, const CChainParams& chainpar
* Note that transaction witness validation rules are always enforced when P2SH is enforced. */
bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params);
/** Compute at which vout of the block's coinbase transaction the witness commitment occurs, or -1 if not found */
int GetWitnessCommitmentIndex(const CBlock& block);
/** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams);

View File

@@ -0,0 +1,71 @@
#!/usr/bin/env python3
# Copyright (c) 2019-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 basic signet functionality"""
from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal
signet_blocks = [
'00000020f61eee3b63a380a477a063af32b2bbc97c9ff9f01f2c4225e973988108000000f575c83235984e7dc4afc1f30944c170462e84437ab6f2d52e16878a79e4678bd1914d5fae77031eccf4070001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025151feffffff0200f2052a010000001600149243f727dd5343293eb83174324019ec16c2630f0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402205e423a8754336ca99dbe16509b877ef1bf98d008836c725005b3c787c41ebe46022047246e4467ad7cc7f1ad98662afcaf14c115e0095a227c7b05c5182591c23e7e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
'00000020533b53ded9bff4adc94101d32400a144c54edc5ed492a3b26c63b2d686000000b38fef50592017cfafbcab88eb3d9cf50b2c801711cad8299495d26df5e54812e7914d5fae77031ecfdd0b0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025251feffffff0200f2052a01000000160014fd09839740f0e0b4fc6d5e2527e4022aa9b89dfa0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022031d64a1692cdad1fc0ced69838169fe19ae01be524d831b95fcf5ea4e6541c3c02204f9dea0801df8b4d0cd0857c62ab35c6c25cc47c930630dc7fe723531daa3e9b01000120000000000000000000000000000000000000000000000000000000000000000000000000',
'000000202960f3752f0bfa8858a3e333294aedc7808025e868c9dc03e71d88bb320000007765fcd3d5b4966beb338bba2675dc2cf2ad28d4ad1d83bdb6f286e7e27ac1f807924d5fae77031e81d60b0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025351feffffff0200f2052a010000001600141e5fb426042692ae0e87c070e78c39307a5661c20000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402205de93694763a42954865bcf1540cb82958bc62d0ec4eee02070fb7937cd037f4022067f333753bce47b10bc25eb6e1f311482e994c862a7e0b2d41ab1c8679fd1b1101000120000000000000000000000000000000000000000000000000000000000000000000000000',
'00000020b06443a13ae1d3d50faef5ecad38c6818194dc46abca3e972e2aacdae800000069a5829097e80fee00ac49a56ea9f82d741a6af84d32b3bc455cf31871e2a8ac27924d5fae77031e9c91050001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025451feffffff0200f2052a0100000016001430db2f8225dcf7751361ab38735de08190318cb70000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402200936f5f9872f6df5dd242026ad52241a68423f7f682e79169a8d85a374eab9b802202cd2979c48b321b3453e65e8f92460db3fca93cbea8539b450c959f4fbe630c601000120000000000000000000000000000000000000000000000000000000000000000000000000',
'000000207ed403758a4f228a1939418a155e2ebd4ae6b26e5ffd0ae433123f7694010000542e80b609c5bc58af5bdf492e26d4f60cd43a3966c2e063c50444c29b3757a636924d5fae77031ee8601d0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025551feffffff0200f2052a01000000160014edc207e014df34fa3885dff97d1129d356e1186a0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022021a3656609f85a66a2c5672ed9322c2158d57251040d2716ed202a1fe14f0c12022057d68bc6611f7a9424a7e00bbf3e27e6ae6b096f60bac624a094bc97a59aa1ff01000120000000000000000000000000000000000000000000000000000000000000000000000000',
'000000205bea0a88d1422c3df08d766ad72df95084d0700e6f873b75dd4e986c7703000002b57516d33ed60c2bdd9f93d6d5614083324c837e68e5ba6e04287a7285633585924d5fae77031ed171960001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025651feffffff0200f2052a010000001600143ae612599cf96f2442ce572633e0251116eaa52f0000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022059a7c54de76bfdbb1dd44c78ea2dbd2bb4e97f4abad38965f41e76433e56423c022054bf17f04fe17415c0141f60eebd2b839200f574d8ad8d55a0917b92b0eb913401000120000000000000000000000000000000000000000000000000000000000000000000000000',
'00000020daf3b60d374b19476461f97540498dcfa2eb7016238ec6b1d022f82fb60100007a7ae65b53cb988c2ec92d2384996713821d5645ffe61c9acea60da75cd5edfa1a944d5fae77031e9dbb050001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025751feffffff0200f2052a01000000160014ef2dceae02e35f8137de76768ae3345d99ca68860000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402202b3f946d6447f9bf17d00f3696cede7ee70b785495e5498274ee682a493befd5022045fc0bcf9332243168b5d35507175f9f374a8eba2336873885d12aada67ea5f601000120000000000000000000000000000000000000000000000000000000000000000000000000',
'00000020457cc5f3c2e1a5655bc20e20e48d33e1b7ea68786c614032b5c518f0b6000000541f36942d82c6e7248275ff15c8933487fbe1819c67a9ecc0f4b70bb7e6cf672a944d5fae77031e8f39860001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025851feffffff0200f2052a0100000016001472a27906947c06d034b38ba2fa13c6391a4832790000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402202d62805ce60cbd60591f97f949b5ea5bd7e2307bcde343e6ea8394da92758e72022053a25370b0aa20da100189b7899a8f8675a0fdc60e38ece6b8a4f98edd94569e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
'00000020a2eb61eb4f3831baa3a3363e1b42db4462663f756f07423e81ed30322102000077224de7dea0f8d0ec22b1d2e2e255f0a987b96fe7200e1a2e6373f48a2f5b7894954d5fae77031e36867e0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025951feffffff0200f2052a01000000160014aa0ad9f26801258382e0734dceec03a4a75f60240000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa2490047304402206fa0d59990eed369bd7375767c9a6c9369fae209152b8674e520da270605528c0220749eed3b12dbe3f583f505d21803e4aef59c8e24c5831951eafa4f15a8f92c4e01000120000000000000000000000000000000000000000000000000000000000000000000000000',
'00000020a868e8514be5e46dabd6a122132f423f36a43b716a40c394e2a8d063e1010000f4c6c717e99d800c699c25a2006a75a0c5c09f432a936f385e6fce139cdbd1a5e9964d5fae77031e7d026e0001010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025a51feffffff0200f2052a01000000160014aaa671c82b138e3b8f510cd801e5f2bd0aa305940000000000000000776a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf94c4fecc7daa24900473044022042309f4c3c7a1a2ac8c24f890f962df1c0086cec10be0868087cfc427520cb2702201dafee8911c269b7e786e242045bb57cef3f5b0f177010c6159abae42f646cc501000120000000000000000000000000000000000000000000000000000000000000000000000000',
]
class SignetBasicTest(BitcoinTestFramework):
def set_test_params(self):
self.chain = "signet"
self.num_nodes = 6
self.setup_clean_chain = True
shared_args1 = ["-signetchallenge=51"] # OP_TRUE
shared_args2 = [] # default challenge
# we use the exact same challenge except we do it as a 2-of-2, which means it should fail
shared_args3 = ["-signetchallenge=522103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"]
self.extra_args = [
shared_args1, shared_args1,
shared_args2, shared_args2,
shared_args3, shared_args3,
]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
self.log.info("basic tests using OP_TRUE challenge")
self.log.info('getmininginfo')
mining_info = self.nodes[0].getmininginfo()
assert_equal(mining_info['blocks'], 0)
assert_equal(mining_info['chain'], 'signet')
assert 'currentblocktx' not in mining_info
assert 'currentblockweight' not in mining_info
assert_equal(mining_info['networkhashps'], Decimal('0'))
assert_equal(mining_info['pooledtx'], 0)
self.nodes[0].generatetoaddress(1, self.nodes[0].getnewaddress())
self.log.info("pregenerated signet blocks check")
height = 0
for block in signet_blocks:
assert_equal(self.nodes[2].submitblock(block), None)
height = height + 1
assert_equal(self.nodes[2].getblockcount(), height)
self.log.info("pregenerated signet blocks check (incompatible solution)")
assert_equal(self.nodes[4].submitblock(signet_blocks[0]), 'bad-signet-blksig')
if __name__ == '__main__':
SignetBasicTest().main()

View File

@@ -109,6 +109,7 @@ MAGIC_BYTES = {
"mainnet": b"\xf9\xbe\xb4\xd9", # mainnet
"testnet3": b"\x0b\x11\x09\x07", # testnet3
"regtest": b"\xfa\xbf\xb5\xda", # regtest
"signet": b"\x0a\x03\xcf\x40", # signet
}

View File

@@ -208,6 +208,7 @@ BASE_SCRIPTS = [
'rpc_bind.py --ipv6',
'rpc_bind.py --nonloopback',
'mining_basic.py',
'feature_signet.py',
'wallet_bumpfee.py',
'wallet_implicitsegwit.py',
'rpc_named_arguments.py',