diff --git a/src/Makefile.am b/src/Makefile.am index b23bf062c5b..aa63b5f516c 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 77e50cf22cd..06dde87ddd3 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 ffd2076c9ad..d7f7888ef3e 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -7,6 +7,7 @@ #include #include +#include // for signet block challenge hash #include #include #include @@ -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 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(1,111); + base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,196); + base58Prefixes[SECRET_KEY] = std::vector(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 CreateChainParams(const std::string& chain) { - if (chain == CBaseChainParams::MAIN) + if (chain == CBaseChainParams::MAIN) { return std::unique_ptr(new CMainParams()); - else if (chain == CBaseChainParams::TESTNET) + } else if (chain == CBaseChainParams::TESTNET) { return std::unique_ptr(new CTestNetParams()); - else if (chain == CBaseChainParams::REGTEST) + } else if (chain == CBaseChainParams::SIGNET) { + return std::unique_ptr(new SigNetParams(gArgs)); + } else if (chain == CBaseChainParams::REGTEST) { return std::unique_ptr(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 1825ced6405..034e897ca6c 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=", "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 globalChainBaseParams; @@ -35,14 +39,16 @@ const CBaseChainParams& BaseParams() std::unique_ptr CreateBaseChainParams(const std::string& chain) { - if (chain == CBaseChainParams::MAIN) + if (chain == CBaseChainParams::MAIN) { return MakeUnique("", 8332); - else if (chain == CBaseChainParams::TESTNET) + } else if (chain == CBaseChainParams::TESTNET) { return MakeUnique("testnet3", 18332); - else if (chain == CBaseChainParams::REGTEST) + } else if (chain == CBaseChainParams::SIGNET) { + return MakeUnique("signet", 38332); + } else if (chain == CBaseChainParams::REGTEST) { return MakeUnique("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 1c52d0ea97b..9852446b3c3 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 61b1fbc2e5a..85ab3f61ef5 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 signet_challenge; }; } // namespace Consensus diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 2a93a090d6e..e007c481df4 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -12,6 +12,12 @@ #include #include +/** 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 9457ea37d6c..882d2c8f52d 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 3a251e05734..b1081f6aee8 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 00000000000..a29f89b58e7 --- /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 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include