diff options
author | Wladimir J. van der Laan <laanwj@protonmail.com> | 2020-09-21 22:32:05 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@protonmail.com> | 2020-09-21 22:33:00 +0200 |
commit | 8c5f68118cd03ff0b9babee351fee83117fb7afa (patch) | |
tree | e38b0f179c5440fcb835b6408d05690949d57172 | |
parent | c0c409dcd330e8b837291b1d331894aeb0015ead (diff) | |
parent | 8258c4c0076bb5f27efdc117a04b27fcd6dd00b2 (diff) |
Merge #18267: BIP-325: Signet [consensus]
8258c4c0076bb5f27efdc117a04b27fcd6dd00b2 test: some sanity checks for consensus logic (Anthony Towns)
e47ad375bf17557f805bd206e789b8db78c6338a test: basic signet tests (Karl-Johan Alm)
4c189abdc452f08dfa758564b5381bc78c42d481 test: add small signet fuzzer (practicalswift)
ec9b25d046793be50da1c11ba61d1b4b13b295b0 test: signet network selection tests (Karl-Johan Alm)
3efe298dccb248f25d6b01ab6a80b1cd6c9e1a1e signet: hard-coded parameters for Signet Global Network VI (2020-09-07) (Karl-Johan Alm)
c7898bca4e1ccbc6edafd3b72eaf80df38e3af32 qt: update QT to support signet network (Karl-Johan Alm)
a8de47a1c9033fac3355590f1fe2158a95011bb3 consensus: add signet validation (Karl-Johan Alm)
e8990f121405af8cd539b904ef082439261e6c93 add signet chain and accompanying parameters (Karl-Johan Alm)
404682b7cdb54494e7c98f0ba0cac8b51f379750 add signet basic support (signet.cpp) (Karl-Johan Alm)
a2147d7dadec1febcd9c2b8ebbbf78dce6d0556b 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 8258c4c0076bb5f27efdc117a04b27fcd6dd00b per `git diff dbeea65 8258c4c`, only change since last review is updated `-signet*` config option naming.
fjahr:
re-ACK 8258c4c
laanwj:
ACK 8258c4c0076bb5f27efdc117a04b27fcd6dd00b2
MarcoFalke:
Approach ACK 8258c4c007 🌵
Tree-SHA512: 5d158add96755910837feafa8214e13695b769a6aec3a2da753cf672618bef377fac43b0f4b772a87b25dd9f0c1c9b29f2789785d7a7d47a155cdcf48f7c975d
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/Makefile.test.include | 7 | ||||
-rw-r--r-- | src/chainparams.cpp | 105 | ||||
-rw-r--r-- | src/chainparamsbase.cpp | 16 | ||||
-rw-r--r-- | src/chainparamsbase.h | 1 | ||||
-rw-r--r-- | src/consensus/params.h | 7 | ||||
-rw-r--r-- | src/consensus/validation.h | 27 | ||||
-rw-r--r-- | src/qt/guiconstants.h | 1 | ||||
-rw-r--r-- | src/qt/networkstyle.cpp | 3 | ||||
-rw-r--r-- | src/signet.cpp | 149 | ||||
-rw-r--r-- | src/signet.h | 42 | ||||
-rw-r--r-- | src/test/fuzz/signet.cpp | 34 | ||||
-rw-r--r-- | src/test/key_io_tests.cpp | 2 | ||||
-rw-r--r-- | src/test/pow_tests.cpp | 47 | ||||
-rw-r--r-- | src/test/util_tests.cpp | 8 | ||||
-rw-r--r-- | src/util/system.cpp | 10 | ||||
-rw-r--r-- | src/validation.cpp | 37 | ||||
-rw-r--r-- | src/validation.h | 5 | ||||
-rwxr-xr-x | test/functional/feature_signet.py | 71 | ||||
-rwxr-xr-x | test/functional/test_framework/p2p.py | 1 | ||||
-rwxr-xr-x | test/functional/test_runner.py | 1 |
21 files changed, 532 insertions, 44 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index b23bf062c5..aa63b5f516 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 77e50cf22c..06dde87ddd 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -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) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index ffd2076c9a..d7f7888ef3 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -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; @@ -251,12 +256,103 @@ 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 */ 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)); } diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 1825ced640..034e897ca6 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -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) diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 1c52d0ea97..9852446b3c 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -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; ///@} diff --git a/src/consensus/params.h b/src/consensus/params.h index 61b1fbc2e5..85ab3f61ef 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -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 diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 2a93a090d6..e007c481df 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -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 diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 9457ea37d6..882d2c8f52 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -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 */ diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index 3a251e0573..b1081f6aee 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -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); diff --git a/src/signet.cpp b/src/signet.cpp new file mode 100644 index 0000000000..a29f89b58e --- /dev/null +++ b/src/signet.cpp @@ -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; +} diff --git a/src/signet.h b/src/signet.h new file mode 100644 index 0000000000..5694716fb6 --- /dev/null +++ b/src/signet.h @@ -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 diff --git a/src/test/fuzz/signet.cpp b/src/test/fuzz/signet.cpp new file mode 100644 index 0000000000..4736ae27f5 --- /dev/null +++ b/src/test/fuzz/signet.cpp @@ -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)); + } +} diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp index d465ee6759..611e9f2623 100644 --- a/src/test/key_io_tests.cpp +++ b/src/test/key_io_tests.cpp @@ -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); diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index 0f9872f434..ca49b89ad8 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -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() diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index bf7c6c3e3e..241c56934e 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -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) diff --git a/src/util/system.cpp b/src/util/system.cpp index 999937d906..41715aac1a 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -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); } diff --git a/src/validation.cpp b/src/validation.cpp index 0823c6b124..a96913e3a0 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -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 diff --git a/src/validation.h b/src/validation.h index 0bc80e1cee..0da62093a3 100644 --- a/src/validation.h +++ b/src/validation.h @@ -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); diff --git a/test/functional/feature_signet.py b/test/functional/feature_signet.py new file mode 100755 index 0000000000..f85431148d --- /dev/null +++ b/test/functional/feature_signet.py @@ -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() diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index 963a507f53..5f9b316b18 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -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 } diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index a3e160f12e..6c3d50df93 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -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', |