diff --git a/contrib/devtools/gen-bitcoin-conf.sh b/contrib/devtools/gen-bitcoin-conf.sh index 2ebbd42022300..6923e823b12c2 100755 --- a/contrib/devtools/gen-bitcoin-conf.sh +++ b/contrib/devtools/gen-bitcoin-conf.sh @@ -80,4 +80,7 @@ cat >> "${EXAMPLE_CONF_FILE}" << 'EOF' # Options for regtest [regtest] + +# Options for blsctregtest +[blsctregtest] EOF diff --git a/src/Makefile.am b/src/Makefile.am index bbb978f74fbc7..db99ee47c2fe5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -203,6 +203,9 @@ BITCOIN_CORE_H = \ blsct/wallet/hdchain.h \ blsct/wallet/keyman.h \ blsct/wallet/keyring.h \ + blsct/wallet/txfactory.h \ + blsct/wallet/txfactory_global.h \ + blsct/wallet/verification.h \ dandelion.h \ chain.h \ chainparams.h \ @@ -482,6 +485,7 @@ libbitcoin_node_a_SOURCES = \ blsct/set_mem_proof/set_mem_proof.cpp \ blsct/set_mem_proof/set_mem_proof_setup.cpp \ blsct/set_mem_proof/set_mem_proof_prover.cpp \ + blsct/wallet/verification.cpp \ blsct/signature.cpp \ chain.cpp \ chainparams.cpp \ @@ -590,12 +594,46 @@ endif libbitcoin_wallet_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS) $(BDB_CPPFLAGS) $(SQLITE_CFLAGS) libbitcoin_wallet_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_wallet_a_SOURCES = \ + blsct/arith/mcl/mcl_g1point.cpp \ + blsct/arith/mcl/mcl_scalar.cpp \ + blsct/arith/elements.cpp \ + blsct/building_block/generator_deriver.cpp \ + blsct/building_block/g_h_gi_hi_zero_verifier.cpp \ + blsct/building_block/imp_inner_prod_arg.cpp \ + blsct/building_block/lazy_point.cpp \ + blsct/building_block/lazy_points.cpp \ + blsct/building_block/weighted_inner_prod_arg.cpp \ + blsct/common.cpp \ + blsct/double_public_key.cpp \ blsct/eip_2333/bls12_381_keygen.cpp \ blsct/private_key.cpp \ blsct/public_key.cpp \ + blsct/public_keys.cpp \ + blsct/range_proof/bulletproofs/amount_recovery_request.cpp \ + blsct/range_proof/bulletproofs/amount_recovery_result.cpp \ + blsct/range_proof/bulletproofs/range_proof_logic.cpp \ + blsct/range_proof/bulletproofs/range_proof.cpp \ + blsct/range_proof/bulletproofs/range_proof_with_transcript.cpp \ + blsct/range_proof/bulletproofs_plus/amount_recovery_request.cpp \ + blsct/range_proof/bulletproofs_plus/amount_recovery_result.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof_logic.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof_with_transcript.cpp \ + blsct/range_proof/bulletproofs_plus/util.cpp \ + blsct/range_proof/common.cpp \ + blsct/range_proof/generators.cpp \ + blsct/range_proof/msg_amt_cipher.cpp \ + blsct/range_proof/proof_base.cpp \ + blsct/set_mem_proof/set_mem_proof.cpp \ + blsct/set_mem_proof/set_mem_proof_setup.cpp \ + blsct/set_mem_proof/set_mem_proof_prover.cpp \ + blsct/signature.cpp \ blsct/wallet/address.cpp \ blsct/wallet/keyman.cpp \ blsct/wallet/keyring.cpp \ + blsct/wallet/txfactory.cpp \ + blsct/wallet/txfactory_global.cpp \ + blsct/wallet/verification.cpp \ wallet/coincontrol.cpp \ wallet/context.cpp \ wallet/crypter.cpp \ @@ -770,6 +808,7 @@ libbitcoin_consensus_a_SOURCES = \ tinyformat.h \ uint256.cpp \ uint256.h \ + util/moneystr.cpp \ util/strencodings.cpp \ util/strencodings.h \ version.h @@ -781,10 +820,45 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ base58.cpp \ bech32.cpp \ + blsct/arith/mcl/mcl_g1point.cpp \ + blsct/arith/mcl/mcl_scalar.cpp \ bech32_mod.cpp \ blsct/arith/elements.cpp \ + blsct/building_block/generator_deriver.cpp \ + blsct/building_block/g_h_gi_hi_zero_verifier.cpp \ + blsct/building_block/imp_inner_prod_arg.cpp \ + blsct/building_block/lazy_point.cpp \ + blsct/building_block/lazy_points.cpp \ + blsct/building_block/weighted_inner_prod_arg.cpp \ + blsct/common.cpp \ blsct/double_public_key.cpp \ + blsct/private_key.cpp \ blsct/public_key.cpp \ + blsct/public_keys.cpp \ + blsct/range_proof/bulletproofs/range_proof.cpp \ + blsct/range_proof/bulletproofs/range_proof_logic.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof.cpp \ + blsct/range_proof/bulletproofs/amount_recovery_request.cpp \ + blsct/range_proof/bulletproofs/amount_recovery_result.cpp \ + blsct/range_proof/bulletproofs/range_proof_logic.cpp \ + blsct/range_proof/bulletproofs/range_proof.cpp \ + blsct/range_proof/bulletproofs/range_proof_with_transcript.cpp \ + blsct/range_proof/bulletproofs_plus/amount_recovery_request.cpp \ + blsct/range_proof/bulletproofs_plus/amount_recovery_result.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof_logic.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof_with_transcript.cpp \ + blsct/range_proof/bulletproofs_plus/util.cpp \ + blsct/range_proof/common.cpp \ + blsct/range_proof/generators.cpp \ + blsct/range_proof/msg_amt_cipher.cpp \ + blsct/range_proof/proof_base.cpp \ + blsct/set_mem_proof/set_mem_proof.cpp \ + blsct/set_mem_proof/set_mem_proof_setup.cpp \ + blsct/set_mem_proof/set_mem_proof_prover.cpp \ + blsct/signature.cpp \ + blsct/wallet/address.cpp \ + blsct/wallet/txfactory_global.cpp \ chainparams.cpp \ coins.cpp \ common/args.cpp \ @@ -1054,15 +1128,41 @@ libbitcoinkernel_la_SOURCES = \ blsct/arith/mcl/mcl_g1point.cpp \ blsct/arith/mcl/mcl_scalar.cpp \ blsct/arith/elements.cpp \ + blsct/building_block/generator_deriver.cpp \ + blsct/building_block/g_h_gi_hi_zero_verifier.cpp \ + blsct/building_block/imp_inner_prod_arg.cpp \ + blsct/building_block/lazy_point.cpp \ + blsct/building_block/lazy_points.cpp \ + blsct/building_block/weighted_inner_prod_arg.cpp \ blsct/common.cpp \ blsct/double_public_key.cpp \ blsct/private_key.cpp \ blsct/public_key.cpp \ blsct/public_keys.cpp \ - blsct/range_proof/common.cpp \ blsct/range_proof/bulletproofs/range_proof.cpp \ + blsct/range_proof/bulletproofs/range_proof_logic.cpp \ blsct/range_proof/bulletproofs_plus/range_proof.cpp \ + blsct/range_proof/bulletproofs/amount_recovery_request.cpp \ + blsct/range_proof/bulletproofs/amount_recovery_result.cpp \ + blsct/range_proof/bulletproofs/range_proof_logic.cpp \ + blsct/range_proof/bulletproofs/range_proof.cpp \ + blsct/range_proof/bulletproofs/range_proof_with_transcript.cpp \ + blsct/range_proof/bulletproofs_plus/amount_recovery_request.cpp \ + blsct/range_proof/bulletproofs_plus/amount_recovery_result.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof_logic.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof.cpp \ + blsct/range_proof/bulletproofs_plus/range_proof_with_transcript.cpp \ + blsct/range_proof/bulletproofs_plus/util.cpp \ + blsct/range_proof/common.cpp \ + blsct/range_proof/generators.cpp \ + blsct/range_proof/msg_amt_cipher.cpp \ + blsct/range_proof/proof_base.cpp \ + blsct/set_mem_proof/set_mem_proof.cpp \ + blsct/set_mem_proof/set_mem_proof_setup.cpp \ + blsct/set_mem_proof/set_mem_proof_prover.cpp \ blsct/signature.cpp \ + blsct/wallet/txfactory_global.cpp \ + blsct/wallet/verification.cpp \ chain.cpp \ chainparamsbase.cpp \ chainparams.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 69acb20bfc7da..46d151020dd7d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -196,6 +196,12 @@ BITCOIN_TESTS =\ if ENABLE_WALLET BITCOIN_TESTS += \ + test/blsct/wallet/address_tests.cpp \ + test/blsct/wallet/chain_tests.cpp \ + test/blsct/wallet/keyman_tests.cpp \ + test/blsct/wallet/txfactory_global_tests.cpp \ + test/blsct/wallet/txfactory_tests.cpp \ + test/blsct/wallet/validation_tests.cpp \ wallet/test/feebumper_tests.cpp \ wallet/test/psbt_wallet_tests.cpp \ wallet/test/spend_tests.cpp \ @@ -368,7 +374,6 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/transaction.cpp \ test/fuzz/tx_in.cpp \ test/fuzz/tx_out.cpp \ - test/fuzz/tx_pool.cpp \ test/fuzz/txorphan.cpp \ test/fuzz/txrequest.cpp \ test/fuzz/utxo_snapshot.cpp \ diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include index b33cd8c1a6859..d4a9f620759bb 100644 --- a/src/Makefile.test_util.include +++ b/src/Makefile.test_util.include @@ -52,6 +52,7 @@ libtest_util_a_SOURCES = \ blsct/public_key.cpp \ blsct/public_keys.cpp \ blsct/signature.h \ + blsct/wallet/txfactory_global.cpp \ test/util/blockfilter.cpp \ test/util/coins.cpp \ test/util/json.cpp \ diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 48dc11b95a564..ddb1701814929 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -78,6 +78,7 @@ static void SetupCliArgs(ArgsManager& argsman) const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET); const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET); const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST); + const auto blsctRegtestBaseParams = CreateBaseChainParams(ChainType::BLSCTREGTEST); argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-conf=", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -99,7 +100,7 @@ static void SetupCliArgs(ArgsManager& argsman) argsman.AddArg("-rpcconnect=", strprintf("Send commands to node running on (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpccookiefile=", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcpassword=", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-rpcport=", strprintf("Connect to JSON-RPC on (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); + argsman.AddArg("-rpcport=", strprintf("Connect to JSON-RPC on (default: %u, testnet: %u, signet: %u, regtest: %u, blsctregtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort(), blsctRegtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcuser=", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcwaittimeout=", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS); @@ -434,6 +435,8 @@ class NetinfoRequestHandler : public BaseRequestHandler return " signet"; case ChainType::REGTEST: return " regtest"; + case ChainType::BLSCTREGTEST: + return " blsctregtest"; case ChainType::MAIN: return ""; } diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index 9f1be5a48a338..16daaf30c4096 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -72,7 +72,7 @@ static std::optional WalletAppInit(ArgsManager& args, int argc, char* argv[ strUsage += "\n" "bitcoin-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n" "By default navcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n" - "To change the target wallet, use the -datadir, -wallet and -regtest/-signet/-testnet arguments.\n\n" + "To change the target wallet, use the -datadir, -wallet and -regtest/-blsctregtest/-signet/-testnet arguments.\n\n" "Usage:\n" " navcoin-wallet [options] \n"; strUsage += "\n" + args.GetHelpMessage(); diff --git a/src/blsct/building_block/imp_inner_prod_arg.cpp b/src/blsct/building_block/imp_inner_prod_arg.cpp index 6a238f7755377..f916361c3a4be 100644 --- a/src/blsct/building_block/imp_inner_prod_arg.cpp +++ b/src/blsct/building_block/imp_inner_prod_arg.cpp @@ -142,30 +142,28 @@ void ImpInnerProdArg::LoopWithYPows( template std::optional> ImpInnerProdArg::GenAllRoundXs( - const size_t& num_rounds, const Elements& Ls, const Elements& Rs, - CHashWriter& fiat_shamir -) { + CHashWriter& fiat_shamir) +{ using Scalar = typename T::Scalar; using Scalars = Elements; Scalars xs; - for (size_t i = 0; i < num_rounds; ++i) { + + for (size_t i = 0; i < Ls.Size(); ++i) { fiat_shamir << Ls[i]; fiat_shamir << Rs[i]; GEN_FIAT_SHAMIR_VAR(x, fiat_shamir, retry); xs.Add(x); } + return xs; retry: return std::nullopt; } -template -std::optional> ImpInnerProdArg::GenAllRoundXs( - const size_t& num_rounds, +template std::optional> ImpInnerProdArg::GenAllRoundXs( const Elements& Ls, const Elements& Rs, - CHashWriter& fiat_shamir -); + CHashWriter& fiat_shamir); diff --git a/src/blsct/building_block/imp_inner_prod_arg.h b/src/blsct/building_block/imp_inner_prod_arg.h index 2fd3ac086dc9e..e39135a3ffd93 100644 --- a/src/blsct/building_block/imp_inner_prod_arg.h +++ b/src/blsct/building_block/imp_inner_prod_arg.h @@ -57,7 +57,6 @@ struct ImpInnerProdArg { // inner product argument from a given hasher template static std::optional> GenAllRoundXs( - const size_t& num_rounds, const Elements& Ls, const Elements& Rs, CHashWriter& fiat_shamir diff --git a/src/blsct/common.h b/src/blsct/common.h index c9f9b663177d4..b4951ec334ef3 100644 --- a/src/blsct/common.h +++ b/src/blsct/common.h @@ -17,8 +17,13 @@ class Common { public: inline static const std::vector BLSCTBALANCE = { - 'B', 'L', 'S', 'C', 'T', 'B', 'A', 'L', 'A', 'N', 'C', 'E' - }; + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + 'B', 'L', 'S', 'C', 'T', 'B', 'A', 'L', 'A', 'N', 'C', 'E', + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '0', '0', '0', '0', '0', '0', '0', '0'}; static std::vector CDataStreamToVector(const CDataStream& st); diff --git a/src/blsct/double_public_key.cpp b/src/blsct/double_public_key.cpp index 4bf3840d978e6..02ae940b1b432 100644 --- a/src/blsct/double_public_key.cpp +++ b/src/blsct/double_public_key.cpp @@ -27,7 +27,7 @@ DoublePublicKey::DoublePublicKey(const std::vector& keys) CKeyID DoublePublicKey::GetID() const { - return CKeyID(Hash160(GetVch())); + return sk.GetID(); } bool DoublePublicKey::GetViewKey(PublicKey& ret) const diff --git a/src/blsct/private_key.cpp b/src/blsct/private_key.cpp index fb91657b1fbef..106f1aa6a30b1 100644 --- a/src/blsct/private_key.cpp +++ b/src/blsct/private_key.cpp @@ -69,6 +69,11 @@ Signature PrivateKey::SignBalance() const return CoreSign(Common::BLSCTBALANCE); } +Signature PrivateKey::Sign(const uint256& msg) const +{ + return Sign(Message(msg.begin(), msg.end())); +} + Signature PrivateKey::Sign(const Message& msg) const { auto pk = GetPublicKey(); @@ -77,8 +82,9 @@ Signature PrivateKey::Sign(const Message& msg) const return sig; } -bool PrivateKey::VerifyPubKey(const PublicKey& pk) const { +bool PrivateKey::VerifyPubKey(const PublicKey& pk) const +{ return GetPublicKey() == pk; } -} // namespace blsct +} // namespace blsct diff --git a/src/blsct/private_key.h b/src/blsct/private_key.h index 6b3590f17c52b..c61acb81b5aac 100644 --- a/src/blsct/private_key.h +++ b/src/blsct/private_key.h @@ -54,6 +54,7 @@ class PrivateKey Signature SignBalance() const; // Message augmentation scheme + Signature Sign(const uint256& msg) const; Signature Sign(const Message& msg) const; // Core operations diff --git a/src/blsct/public_keys.cpp b/src/blsct/public_keys.cpp index 2c5534dc7add2..2b3595b93515a 100644 --- a/src/blsct/public_keys.cpp +++ b/src/blsct/public_keys.cpp @@ -69,7 +69,7 @@ bool PublicKeys::CoreAggregateVerify(const std::vector& msgs return res == 1; } -bool PublicKeys::VerifyBatch(const std::vector& msgs, const Signature& sig) const +bool PublicKeys::VerifyBatch(const std::vector& msgs, const Signature& sig, const bool& fVerifyTx) const { if (m_pks.size() != msgs.size() || m_pks.size() == 0) { throw std::runtime_error(std::string(__func__) + strprintf( @@ -78,7 +78,11 @@ bool PublicKeys::VerifyBatch(const std::vector& msgs, const std::vector> aug_msgs; auto msg = msgs.begin(); for (auto pk = m_pks.begin(), end = m_pks.end(); pk != end; ++pk, ++msg) { - aug_msgs.push_back(pk->AugmentMessage(*msg)); + if (*msg == blsct::Common::BLSCTBALANCE && fVerifyTx) { + aug_msgs.push_back(*msg); + } else { + aug_msgs.push_back(pk->AugmentMessage(*msg)); + } } return CoreAggregateVerify(aug_msgs, sig); } diff --git a/src/blsct/public_keys.h b/src/blsct/public_keys.h index 81ec6901138ff..d23736fedab0e 100644 --- a/src/blsct/public_keys.h +++ b/src/blsct/public_keys.h @@ -23,7 +23,7 @@ class PublicKeys bool VerifyBalanceBatch(const Signature& sig) const; // Message augmentation scheme - bool VerifyBatch(const std::vector& msgs, const Signature& sig) const; + bool VerifyBatch(const std::vector& msgs, const Signature& sig, const bool& fVerifyTx = false) const; private: // Core operations diff --git a/src/blsct/range_proof/bulletproofs/range_proof_logic.cpp b/src/blsct/range_proof/bulletproofs/range_proof_logic.cpp index c77c729e33fa6..d672e76db697f 100644 --- a/src/blsct/range_proof/bulletproofs/range_proof_logic.cpp +++ b/src/blsct/range_proof/bulletproofs/range_proof_logic.cpp @@ -228,6 +228,8 @@ bool RangeProofLogic::VerifyProofs( using Scalars = Elements; for (const RangeProofWithTranscript& p : proof_transcripts) { + if (p.proof.Ls.Size() != p.proof.Rs.Size()) return false; + const range_proof::Generators gens = m_common.Gf().GetInstance(p.proof.token_id); G_H_Gi_Hi_ZeroVerifier verifier(max_mn); @@ -370,7 +372,8 @@ AmountRecoveryResult RangeProofLogic::RecoverAmounts( // will contain result of successful requests only std::vector> xs; - for (const AmountRecoveryRequest& req : reqs) { + for (size_t i = 0; i < reqs.size(); ++i) { + auto req = reqs[i]; const range_proof::Generators gens = m_common.Gf().GetInstance(req.token_id); Point G = gens.G; Point H = gens.H; @@ -378,7 +381,7 @@ AmountRecoveryResult RangeProofLogic::RecoverAmounts( // failure if sizes of Ls and Rs differ or Vs is empty auto Ls_Rs_valid = req.Ls.Size() > 0 && req.Ls.Size() == req.Rs.Size(); if (req.Vs.Size() == 0 || !Ls_Rs_valid) { - return AmountRecoveryResult::failure(); + continue; } // recovery can only be done when the number of value commitment is 1 if (req.Vs.Size() != 1) { @@ -412,25 +415,23 @@ AmountRecoveryResult RangeProofLogic::RecoverAmounts( m_common.Uint64Max(), H, G, - req.Vs[0] - ); + req.Vs[0]); if (maybe_msg_amt == std::nullopt) { continue; } auto msg_amt = maybe_msg_amt.value(); auto x = range_proof::RecoveredData( - req.id, + i, msg_amt.amount, req.nonce.GetHashWithSalt(100), // gamma for vs[0] - msg_amt.msg - ); + msg_amt.msg); + xs.push_back(x); } return { true, - xs - }; + xs}; } template AmountRecoveryResult RangeProofLogic::RecoverAmounts( const std::vector>& diff --git a/src/blsct/range_proof/bulletproofs/range_proof_with_transcript.cpp b/src/blsct/range_proof/bulletproofs/range_proof_with_transcript.cpp index 8bd08f17f32e0..e94b71a0e801f 100644 --- a/src/blsct/range_proof/bulletproofs/range_proof_with_transcript.cpp +++ b/src/blsct/range_proof/bulletproofs/range_proof_with_transcript.cpp @@ -43,11 +43,7 @@ RangeProofWithTranscript RangeProofWithTranscript::Build(const RangeProof< GEN_FIAT_SHAMIR_VAR(c_factor, fiat_shamir, retry); - auto num_rounds = range_proof::Common::GetNumRoundsExclLast( - proof.Vs.Size() - ); auto maybe_xs = ImpInnerProdArg::GenAllRoundXs( - num_rounds, proof.Ls, proof.Rs, fiat_shamir diff --git a/src/blsct/range_proof/bulletproofs_plus/range_proof_logic.cpp b/src/blsct/range_proof/bulletproofs_plus/range_proof_logic.cpp index a52c6c766056a..2743c6d8e093d 100644 --- a/src/blsct/range_proof/bulletproofs_plus/range_proof_logic.cpp +++ b/src/blsct/range_proof/bulletproofs_plus/range_proof_logic.cpp @@ -386,6 +386,8 @@ bool RangeProofLogic::VerifyProofs( using Scalars = Elements; for (const RangeProofWithTranscript& pt : proof_transcripts) { + if (pt.proof.Ls.Size() != pt.proof.Rs.Size()) return false; + range_proof::Generators gens = m_common.Gf().GetInstance(pt.proof.token_id); auto gs = gens.GetGiSubset(pt.mn); diff --git a/src/blsct/range_proof/recovered_data.h b/src/blsct/range_proof/recovered_data.h index 79dfffc916594..ef34237c542c7 100644 --- a/src/blsct/range_proof/recovered_data.h +++ b/src/blsct/range_proof/recovered_data.h @@ -6,6 +6,7 @@ #define NAVCOIN_BLSCT_ARITH_RANGE_PROOF_RECOVERED_DATA_H #include +#include #include #include @@ -24,10 +25,17 @@ struct RecoveredData const std::string& message ): id{id}, amount{amount}, gamma{gamma}, message{message} {} + RecoveredData() {} + size_t id; CAmount amount; Scalar gamma; std::string message; + + SERIALIZE_METHODS(RecoveredData, obj) + { + READWRITE(obj.amount, obj.gamma, obj.message); + } }; } // namespace range_proof diff --git a/src/blsct/set_mem_proof/set_mem_proof_prover.cpp b/src/blsct/set_mem_proof/set_mem_proof_prover.cpp index 64b00ebb08c8a..f70033467a893 100644 --- a/src/blsct/set_mem_proof/set_mem_proof_prover.cpp +++ b/src/blsct/set_mem_proof/set_mem_proof_prover.cpp @@ -258,6 +258,8 @@ bool SetMemProofProver::Verify( ) { using LazyPoint = LazyPoint; + if (proof.Ls.Size() != proof.Rs.Size()) return false; + size_t n = blsct::Common::GetFirstPowerOf2GreaterOrEqTo(Ys_src.Size()); if (n > setup.N) { throw std::runtime_error(std::string(__func__) + ": # of commitments exceeds the setup maximum"); @@ -320,7 +322,7 @@ bool SetMemProofProver::Verify( Scalars xs; { - auto maybe_xs = ImpInnerProdArg::GenAllRoundXs(num_rounds, proof.Ls, proof.Rs, fiat_shamir); + auto maybe_xs = ImpInnerProdArg::GenAllRoundXs(proof.Ls, proof.Rs, fiat_shamir); if (!maybe_xs.has_value()) goto retry; xs = maybe_xs.value(); } diff --git a/src/blsct/wallet/address.cpp b/src/blsct/wallet/address.cpp index 7da62ab5e4188..c52d807587f8a 100644 --- a/src/blsct/wallet/address.cpp +++ b/src/blsct/wallet/address.cpp @@ -5,6 +5,14 @@ #include namespace blsct { +SubAddress::SubAddress(const std::string& sAddress) +{ + auto dest = DecodeDestination(sAddress); + if (std::holds_alternative(dest)) { + pk = std::get(dest); + } +} + SubAddress::SubAddress(const PrivateKey& viewKey, const PublicKey& spendKey, const SubAddressIdentifier& subAddressId) { if (!viewKey.IsValid() || !spendKey.IsValid()) { @@ -47,4 +55,14 @@ bool SubAddress::IsValid() const { return pk.IsValid(); } + +bool SubAddress::operator==(const SubAddress& rhs) const +{ + return pk == rhs.pk; +} + +bool SubAddress::operator<(const SubAddress& rhs) const +{ + return pk == rhs.pk ? pk < rhs.pk : pk < rhs.pk; +}; } // namespace blsct diff --git a/src/blsct/wallet/address.h b/src/blsct/wallet/address.h index 1a0d0c72a13b3..b037e8ae64c3c 100644 --- a/src/blsct/wallet/address.h +++ b/src/blsct/wallet/address.h @@ -30,7 +30,7 @@ class SubAddressPool }; struct SubAddressIdentifier { - uint64_t account; + int64_t account; uint64_t address; }; @@ -40,14 +40,22 @@ class SubAddress DoublePublicKey pk; public: + SubAddress(){}; + SubAddress(const std::string& sAddress); SubAddress(const PrivateKey& viewKey, const PublicKey& spendKey, const SubAddressIdentifier& subAddressId); SubAddress(const DoublePublicKey& pk) : pk(pk){}; + SubAddress(const CTxDestination& dest) : pk(std::get(dest)){}; bool IsValid() const; std::string GetString() const; CTxDestination GetDestination() const; DoublePublicKey GetKeys() const { return pk; }; + + SERIALIZE_METHODS(SubAddress, obj) { READWRITE(obj.pk); } + + bool operator==(const SubAddress& rhs) const; + bool operator<(const SubAddress& rhs) const; }; } // namespace blsct diff --git a/src/blsct/wallet/hdchain.h b/src/blsct/wallet/hdchain.h index 7135a1d10ed08..c10f87aa713e2 100644 --- a/src/blsct/wallet/hdchain.h +++ b/src/blsct/wallet/hdchain.h @@ -16,8 +16,9 @@ class HDChain CKeyID seed_id; //!< seed hash160 CKeyID spend_id; //!< spend hash160 CKeyID view_id; //!< view hash160 + CKeyID blinding_id; //!< blinding hash160 CKeyID token_id; //!< token hash160 - std::map nSubAddressCounter; + std::map nSubAddressCounter; static const int VERSION_HD_BASE = 1; static const int CURRENT_VERSION = VERSION_HD_BASE; @@ -36,13 +37,14 @@ class HDChain seed_id.SetNull(); spend_id.SetNull(); view_id.SetNull(); + blinding_id.SetNull(); token_id.SetNull(); nSubAddressCounter.clear(); } bool operator==(const HDChain& chain) const { - return seed_id == chain.seed_id && spend_id == chain.spend_id && view_id == chain.view_id && token_id == chain.token_id && nSubAddressCounter == chain.nSubAddressCounter; + return seed_id == chain.seed_id && spend_id == chain.spend_id && view_id == chain.view_id && token_id == chain.token_id && blinding_id == chain.blinding_id && nSubAddressCounter == chain.nSubAddressCounter; } }; } // namespace blsct diff --git a/src/blsct/wallet/keyman.cpp b/src/blsct/wallet/keyman.cpp index 9ad9d5098b663..b8443a3961df3 100644 --- a/src/blsct/wallet/keyman.cpp +++ b/src/blsct/wallet/keyman.cpp @@ -198,7 +198,7 @@ void KeyMan::SetHDSeed(const PrivateKey& key) auto scalarMasterKey = key.GetScalar(); auto childKey = BLS12_381_KeyGen::derive_child_SK(scalarMasterKey, 130); auto transactionKey = BLS12_381_KeyGen::derive_child_SK(childKey, 0); - // auto blindingKey = BLS12_381_KeyGen::derive_child_SK(childKey, 1); + auto blindingKey = PrivateKey(BLS12_381_KeyGen::derive_child_SK(childKey, 1)); auto tokenKey = PrivateKey(BLS12_381_KeyGen::derive_child_SK(childKey, 2)); auto viewKey = PrivateKey(BLS12_381_KeyGen::derive_child_SK(transactionKey, 0)); auto spendKey = PrivateKey(BLS12_381_KeyGen::derive_child_SK(transactionKey, 1)); @@ -208,11 +208,13 @@ void KeyMan::SetHDSeed(const PrivateKey& key) newHdChain.spend_id = spendKey.GetPublicKey().GetID(); newHdChain.view_id = viewKey.GetPublicKey().GetID(); newHdChain.token_id = tokenKey.GetPublicKey().GetID(); + newHdChain.blinding_id = blindingKey.GetPublicKey().GetID(); int64_t nCreationTime = GetTime(); wallet::CKeyMetadata spendMetadata(nCreationTime); wallet::CKeyMetadata viewMetadata(nCreationTime); + wallet::CKeyMetadata blindingMetadata(nCreationTime); wallet::CKeyMetadata tokenMetadata(nCreationTime); spendMetadata.hdKeypath = "spend"; @@ -223,6 +225,10 @@ void KeyMan::SetHDSeed(const PrivateKey& key) viewMetadata.has_key_origin = false; viewMetadata.hd_seed_id = newHdChain.view_id; + blindingMetadata.hdKeypath = "blinding"; + blindingMetadata.has_key_origin = false; + blindingMetadata.hd_seed_id = newHdChain.blinding_id; + tokenMetadata.hdKeypath = "token"; tokenMetadata.has_key_origin = false; tokenMetadata.hd_seed_id = newHdChain.token_id; @@ -230,6 +236,7 @@ void KeyMan::SetHDSeed(const PrivateKey& key) // mem store the metadata mapKeyMetadata[newHdChain.spend_id] = spendMetadata; mapKeyMetadata[newHdChain.view_id] = viewMetadata; + mapKeyMetadata[newHdChain.blinding_id] = blindingMetadata; mapKeyMetadata[newHdChain.token_id] = tokenMetadata; // write the keys to the database @@ -248,6 +255,9 @@ void KeyMan::SetHDSeed(const PrivateKey& key) if (!AddKeyPubKey(tokenKey, tokenKey.GetPublicKey())) throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); + if (!AddKeyPubKey(blindingKey, blindingKey.GetPublicKey())) + throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); + AddHDChain(newHdChain); NotifyCanGetAddressesChanged(); wallet::WalletBatch batch(m_storage.GetDatabase()); @@ -260,7 +270,7 @@ bool KeyMan::SetupGeneration(bool force) } SetHDSeed(GenerateNewSeed()); - if (!NewSubAddressPool()) { + if (!NewSubAddressPool() || !NewSubAddressPool(-1)) { return false; } return true; @@ -341,7 +351,7 @@ void KeyMan::UpdateTimeFirstKey(int64_t nCreateTime) } } -SubAddress KeyMan::GetSubAddress(const SubAddressIdentifier& id) +SubAddress KeyMan::GetSubAddress(const SubAddressIdentifier& id) const { return SubAddress(viewKey, spendPublicKey, id); }; @@ -414,21 +424,127 @@ bool KeyMan::Encrypt(const wallet::CKeyingMaterial& master_key, wallet::WalletBa return true; } -bool KeyMan::IsMine(const blsct::PublicKey& ephemeralKey, const blsct::PublicKey& spendingKey, const uint16_t& viewTag) +CTxDestination KeyMan::GetDestination(const CTxOut& txout) const +{ + auto hashId = GetHashId(txout); + blsct::SubAddress subAdd; + CTxDestination ret; + if (!GetSubAddress(hashId, subAdd)) { + ret = CNoDestination(); + } else { + ret = CTxDestination(subAdd.GetKeys()); + } + return ret; +} + +CKeyID KeyMan::GetHashId(const blsct::PublicKey& blindingKey, const blsct::PublicKey& spendingKey) const +{ + if (!fViewKeyDefined || !viewKey.IsValid()) + throw std::runtime_error(strprintf("%s: the wallet has no view key available", __func__)); + + auto t = blindingKey.GetG1Point() * viewKey.GetScalar(); + auto dh = MclG1Point::GetBasePoint() * t.GetHashWithSalt(0).Negate(); + auto D_prime = spendingKey.GetG1Point() + dh; + + return PublicKey(D_prime).GetID(); +}; + +blsct::PrivateKey KeyMan::GetMasterSeedKey() const +{ + if (!IsHDEnabled()) + throw std::runtime_error(strprintf("%s: the wallet has no HD enabled")); + + auto seedId = m_hd_chain.seed_id; + + PrivateKey ret; + + if (!GetKey(seedId, ret)) + throw std::runtime_error(strprintf("%s: could not access the master seed key", __func__)); + + return ret; +} + +blsct::PrivateKey KeyMan::GetSpendingKey() const +{ + if (!fSpendKeyDefined) + throw std::runtime_error(strprintf("%s: the wallet has no spend key available")); + + auto spendingKeyId = m_hd_chain.spend_id; + + PrivateKey ret; + + if (!GetKey(spendingKeyId, ret)) + throw std::runtime_error(strprintf("%s: could not access the spend key", __func__)); + + return ret; +} + +blsct::PrivateKey KeyMan::GetSpendingKeyForOutput(const CTxOut& out) const +{ + auto hashId = GetHashId(out); + + return GetSpendingKeyForOutput(out, hashId); +} + +blsct::PrivateKey KeyMan::GetSpendingKeyForOutput(const CTxOut& out, const CKeyID& hashId) const +{ + SubAddressIdentifier id; + + if (!GetSubAddressId(hashId, id)) + throw std::runtime_error(strprintf("%s: could not read subaddress id", __func__)); + + return GetSpendingKeyForOutput(out, id); +} + +blsct::PrivateKey KeyMan::GetSpendingKeyForOutput(const CTxOut& out, const SubAddressIdentifier& id) const +{ + if (!fViewKeyDefined || !viewKey.IsValid()) + throw std::runtime_error(strprintf("%s: the wallet has no view key available", __func__)); + + auto sk = GetSpendingKey(); + + CHashWriter string(SER_GETHASH, 0); + + string << std::vector(subAddressHeader.begin(), subAddressHeader.end()); + string << viewKey; + string << id.account; + string << id.address; + + MclG1Point t = out.blsctData.blindingKey * viewKey.GetScalar(); + MclScalar ret = t.GetHashWithSalt(0) + sk.GetScalar() + MclScalar(string.GetHash()); + + return ret; +} + +bulletproofs::AmountRecoveryResult KeyMan::RecoverOutputs(const std::vector& outs) +{ + if (!fViewKeyDefined || !viewKey.IsValid()) + return bulletproofs::AmountRecoveryResult::failure(); + + bulletproofs::RangeProofLogic rp; + std::vector> reqs; + reqs.reserve(outs.size()); + + for (size_t i = 0; i < outs.size(); i++) { + CTxOut out = outs[i]; + auto nonce = out.blsctData.blindingKey * viewKey.GetScalar(); + reqs.push_back(bulletproofs::AmountRecoveryRequest::of({out.blsctData.rangeProof}, nonce)); + } + + return rp.RecoverAmounts(reqs); +} + +bool KeyMan::IsMine(const blsct::PublicKey& blindingKey, const blsct::PublicKey& spendingKey, const uint16_t& viewTag) { if (!fViewKeyDefined || !viewKey.IsValid()) return false; CHashWriter hash(SER_GETHASH, PROTOCOL_VERSION); - hash << (ephemeralKey.GetG1Point() * viewKey.GetScalar()); + hash << (blindingKey.GetG1Point() * viewKey.GetScalar()); - if (viewTag != (hash.GetHash().GetUint64(0) & 0xFF)) - return false; + if (viewTag != (hash.GetHash().GetUint64(0) & 0xFFFF)) return false; - auto t = ephemeralKey.GetG1Point() * viewKey.GetScalar(); - auto dh = MclG1Point::GetBasePoint() * t.GetHashWithSalt(0).Invert(); - auto D_prime = spendingKey.GetG1Point() + dh; - auto hashId = PublicKey(D_prime).GetID(); + auto hashId = GetHashId(blindingKey, spendingKey); { LOCK(cs_KeyStore); @@ -458,7 +574,46 @@ bool KeyMan::HaveSubAddress(const CKeyID& hashId) const return mapSubAddresses.count(hashId) > 0; } -SubAddress KeyMan::GenerateNewSubAddress(const uint64_t& account, SubAddressIdentifier& id) +bool KeyMan::GetSubAddress(const CKeyID& hashId, SubAddress& address) const +{ + LOCK(cs_KeyStore); + if (!HaveSubAddress(hashId)) return false; + address = GetSubAddress(mapSubAddresses.at(hashId)); + return true; +} + +bool KeyMan::GetSubAddressId(const CKeyID& hashId, SubAddressIdentifier& id) const +{ + LOCK(cs_KeyStore); + if (!HaveSubAddress(hashId)) return false; + id = mapSubAddresses.at(hashId); + return true; +} + +void KeyMan::LoadSubAddressStr(const SubAddress& subAddress, const CKeyID& hashId) +{ + LOCK(cs_KeyStore); + mapSubAddressesStr[subAddress] = hashId; +} + +bool KeyMan::AddSubAddressStr(const SubAddress& subAddress, const CKeyID& hashId) +{ + LOCK(cs_KeyStore); + wallet::WalletBatch batch(m_storage.GetDatabase()); + AssertLockHeld(cs_KeyStore); + + mapSubAddressesStr[subAddress] = hashId; + + return batch.WriteSubAddressStr(subAddress, hashId); +} + +bool KeyMan::HaveSubAddressStr(const SubAddress& subAddress) const +{ + LOCK(cs_KeyStore); + return mapSubAddressesStr.count(subAddress) > 0; +} + +SubAddress KeyMan::GenerateNewSubAddress(const int64_t& account, SubAddressIdentifier& id) { if (m_hd_chain.nSubAddressCounter.count(account) == 0) m_hd_chain.nSubAddressCounter.insert(std::make_pair(account, 0)); @@ -487,12 +642,15 @@ SubAddress KeyMan::GenerateNewSubAddress(const uint64_t& account, SubAddressIden if (!AddSubAddress(subAddress.GetKeys().GetID(), id)) throw std::runtime_error(std::string(__func__) + ": AddSubAddress failed"); + if (!AddSubAddressStr(subAddress, subAddress.GetKeys().GetID())) + throw std::runtime_error(std::string(__func__) + ": AddSubAddressStr failed"); + return subAddress; } // BLSCT Sub Address Key Pool -bool KeyMan::NewSubAddressPool(const uint64_t& account) +bool KeyMan::NewSubAddressPool(const int64_t& account) { LOCK(cs_KeyStore); wallet::WalletBatch batch(m_storage.GetDatabase()); @@ -531,7 +689,7 @@ bool KeyMan::TopUp(const unsigned int& size) return true; } -bool KeyMan::TopUpAccount(const uint64_t& account, const unsigned int& size) +bool KeyMan::TopUpAccount(const int64_t& account, const unsigned int& size) { LOCK(cs_KeyStore); @@ -561,7 +719,7 @@ bool KeyMan::TopUpAccount(const uint64_t& account, const unsigned int& size) return true; } -void KeyMan::ReserveSubAddressFromPool(const uint64_t& account, int64_t& nIndex, SubAddressPool& keypool) +void KeyMan::ReserveSubAddressFromPool(const int64_t& account, int64_t& nIndex, SubAddressPool& keypool) { nIndex = -1; keypool.hashId = CKeyID(); @@ -622,7 +780,7 @@ void KeyMan::ReturnSubAddress(const SubAddressIdentifier& id) WalletLogPrintf("KeyMan::ReturnSubAddress(): return %d/%d\n", id.account / id.address); } -bool KeyMan::GetSubAddressFromPool(const uint64_t& account, CKeyID& result, SubAddressIdentifier& id) +bool KeyMan::GetSubAddressFromPool(const int64_t& account, CKeyID& result, SubAddressIdentifier& id) { LOCK(cs_KeyStore); @@ -643,13 +801,13 @@ bool KeyMan::GetSubAddressFromPool(const uint64_t& account, CKeyID& result, SubA return true; } -int KeyMan::GetSubAddressPoolSize(const uint64_t& account) const +int KeyMan::GetSubAddressPoolSize(const int64_t& account) const { LOCK(cs_KeyStore); return setSubAddressPool.count(account) > 0 ? setSubAddressPool.at(account).size() : 0; } -int64_t KeyMan::GetOldestSubAddressPoolTime(const uint64_t& account) +int64_t KeyMan::GetOldestSubAddressPoolTime(const int64_t& account) { LOCK(cs_KeyStore); @@ -669,7 +827,7 @@ int64_t KeyMan::GetOldestSubAddressPoolTime(const uint64_t& account) return keypool.nTime; } -util::Result KeyMan::GetNewDestination(const uint64_t& account) +util::Result KeyMan::GetNewDestination(const int64_t& account) { // Fill-up keypool if needed TopUp(); @@ -684,4 +842,16 @@ util::Result KeyMan::GetNewDestination(const uint64_t& account) } return CTxDestination(GetSubAddress(id).GetKeys()); } + +bool KeyMan::OutputIsChange(const CTxOut& out) const +{ + auto id = GetHashId(out); + blsct::SubAddressIdentifier subAddId; + + if (GetSubAddressId(id, subAddId)) { + return subAddId.account == -1; + } + + return false; +} } // namespace blsct diff --git a/src/blsct/wallet/keyman.h b/src/blsct/wallet/keyman.h index 9fcc029af7fd7..88e613cdfae23 100644 --- a/src/blsct/wallet/keyman.h +++ b/src/blsct/wallet/keyman.h @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -47,10 +50,12 @@ class KeyMan : public Manager, public KeyRing using CryptedKeyMap = std::map>>; using SubAddressMap = std::map; - using SubAddressPoolMapSet = std::map>; + using SubAddressStrMap = std::map; + using SubAddressPoolMapSet = std::map>; CryptedKeyMap mapCryptedKeys GUARDED_BY(cs_KeyStore); SubAddressMap mapSubAddresses GUARDED_BY(cs_KeyStore); + SubAddressStrMap mapSubAddressesStr GUARDED_BY(cs_KeyStore); SubAddressPoolMapSet setSubAddressPool GUARDED_BY(cs_KeyStore); SubAddressPoolMapSet setSubAddressReservePool GUARDED_BY(cs_KeyStore); @@ -103,9 +108,9 @@ class KeyMan : public Manager, public KeyRing bool Encrypt(const wallet::CKeyingMaterial& master_key, wallet::WalletBatch* batch); bool CheckDecryptionKey(const wallet::CKeyingMaterial& master_key, bool accept_no_keys); - SubAddress GenerateNewSubAddress(const uint64_t& account, SubAddressIdentifier& id); - SubAddress GetSubAddress(const SubAddressIdentifier& id = {0, 0}); - util::Result GetNewDestination(const uint64_t& account = 0); + SubAddress GenerateNewSubAddress(const int64_t& account, SubAddressIdentifier& id); + SubAddress GetSubAddress(const SubAddressIdentifier& id = {0, 0}) const; + util::Result GetNewDestination(const int64_t& account = 0); /* Set the HD chain model (chain child index counters) and writes it to the database */ void AddHDChain(const blsct::HDChain& chain); @@ -121,25 +126,42 @@ class KeyMan : public Manager, public KeyRing bool DeleteKeys(); /** Detect ownership of outputs **/ - bool IsMine(const CTxOut& txout) { return IsMine(txout.blsctData.ephemeralKey, txout.blsctData.spendingKey, txout.blsctData.viewTag); }; - bool IsMine(const blsct::PublicKey& ephemeralKey, const blsct::PublicKey& spendingKey, const uint16_t& viewTag); + bool IsMine(const CTxOut& txout) { return IsMine(txout.blsctData.blindingKey, txout.blsctData.spendingKey, txout.blsctData.viewTag); }; + bool IsMine(const blsct::PublicKey& blindingKey, const blsct::PublicKey& spendingKey, const uint16_t& viewTag); + CKeyID GetHashId(const CTxOut& txout) const { return GetHashId(txout.blsctData.blindingKey, txout.blsctData.spendingKey); } + CKeyID GetHashId(const blsct::PublicKey& blindingKey, const blsct::PublicKey& spendingKey) const; + CTxDestination GetDestination(const CTxOut& txout) const; + blsct::PrivateKey GetMasterSeedKey() const; + blsct::PrivateKey GetSpendingKey() const; + blsct::PrivateKey GetSpendingKeyForOutput(const CTxOut& out) const; + blsct::PrivateKey GetSpendingKeyForOutput(const CTxOut& out, const CKeyID& id) const; + blsct::PrivateKey GetSpendingKeyForOutput(const CTxOut& out, const SubAddressIdentifier& id) const; + bulletproofs::AmountRecoveryResult RecoverOutputs(const std::vector& outs); /** SubAddress keypool */ void LoadSubAddress(const CKeyID& hashId, const SubAddressIdentifier& index); bool AddSubAddress(const CKeyID& hashId, const SubAddressIdentifier& index); bool HaveSubAddress(const CKeyID& hashId) const EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - bool NewSubAddressPool(const uint64_t& account = 0); + bool GetSubAddress(const CKeyID& hashId, SubAddress& address) const; + bool GetSubAddressId(const CKeyID& hashId, SubAddressIdentifier& subAddId) const; + void LoadSubAddressStr(const SubAddress& subAddress, const CKeyID& hashId); + bool AddSubAddressStr(const SubAddress& subAddress, const CKeyID& hashId); + bool HaveSubAddressStr(const SubAddress& subAddress) const; + bool NewSubAddressPool(const int64_t& account = 0); bool TopUp(const unsigned int& size = 0); - bool TopUpAccount(const uint64_t& account, const unsigned int& size = 0); - void ReserveSubAddressFromPool(const uint64_t& account, int64_t& nIndex, SubAddressPool& keypool); + bool TopUpAccount(const int64_t& account, const unsigned int& size = 0); + void ReserveSubAddressFromPool(const int64_t& account, int64_t& nIndex, SubAddressPool& keypool); void KeepSubAddress(const SubAddressIdentifier& id); void ReturnSubAddress(const SubAddressIdentifier& id); - bool GetSubAddressFromPool(const uint64_t& account, CKeyID& result, SubAddressIdentifier& id); - int64_t GetOldestSubAddressPoolTime(const uint64_t& account); - int GetSubAddressPoolSize(const uint64_t& account) const; + bool GetSubAddressFromPool(const int64_t& account, CKeyID& result, SubAddressIdentifier& id); + int64_t GetOldestSubAddressPoolTime(const int64_t& account); + int GetSubAddressPoolSize(const int64_t& account) const; + + bool OutputIsChange(const CTxOut& out) const; /** Keypool has new keys */ - boost::signals2::signal NotifyCanGetAddressesChanged; + boost::signals2::signal + NotifyCanGetAddressesChanged; // Map from Key ID to key metadata. std::map mapKeyMetadata GUARDED_BY(cs_KeyStore); diff --git a/src/blsct/wallet/txfactory.cpp b/src/blsct/wallet/txfactory.cpp new file mode 100644 index 0000000000000..2d9cb7baa3611 --- /dev/null +++ b/src/blsct/wallet/txfactory.cpp @@ -0,0 +1,159 @@ +// Copyright (c) 2023 The Navcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +using T = Mcl; +using Point = T::Point; +using Points = Elements; +using Scalar = T::Scalar; +using Scalars = Elements; + +namespace blsct { + +void TxFactory::AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& tokenId) +{ + UnsignedOutput out; + out = CreateOutput(destination.GetKeys(), nAmount, sMemo, tokenId); + + if (nAmounts.count(tokenId) <= 0) + nAmounts[tokenId] = {0, 0}; + + nAmounts[tokenId].nFromOutputs += nAmount; + + if (vOutputs.count(tokenId) <= 0) + vOutputs[tokenId] = std::vector(); + + vOutputs[tokenId].push_back(out); +} + +bool TxFactory::AddInput(const CCoinsViewCache& cache, const COutPoint& outpoint, const bool& rbf) +{ + Coin coin; + + if (!cache.GetCoin(outpoint, coin)) + return false; + + auto recoveredInfo = km->RecoverOutputs(std::vector{coin.out}); + + if (!recoveredInfo.is_completed) + return false; + + if (vInputs.count(coin.out.tokenId) <= 0) + vInputs[coin.out.tokenId] = std::vector(); + + vInputs[coin.out.tokenId].push_back({CTxIn(outpoint, CScript(), rbf ? MAX_BIP125_RBF_SEQUENCE : CTxIn::SEQUENCE_FINAL), recoveredInfo.amounts[0].amount, recoveredInfo.amounts[0].gamma, km->GetSpendingKeyForOutput(coin.out)}); + + if (nAmounts.count(coin.out.tokenId) <= 0) + nAmounts[coin.out.tokenId] = {0, 0}; + + nAmounts[coin.out.tokenId].nFromInputs += recoveredInfo.amounts[0].amount; + + return true; +} +bool TxFactory::AddInput(wallet::CWallet* wallet, const COutPoint& outpoint, const bool& rbf) +{ + AssertLockHeld(wallet->cs_wallet); + + auto tx = wallet->GetWalletTx(outpoint.hash); + + if (tx == nullptr) + return false; + + auto out = tx->tx->vout[outpoint.n]; + + if (vInputs.count(out.tokenId) <= 0) + vInputs[out.tokenId] = std::vector(); + + auto recoveredInfo = tx->GetBLSCTRecoveryData(outpoint.n); + + vInputs[out.tokenId].push_back({CTxIn(outpoint, CScript(), rbf ? MAX_BIP125_RBF_SEQUENCE : CTxIn::SEQUENCE_FINAL), recoveredInfo.amount, recoveredInfo.gamma, km->GetSpendingKeyForOutput(out)}); + + if (nAmounts.count(out.tokenId) <= 0) + nAmounts[out.tokenId] = {0, 0}; + + nAmounts[out.tokenId].nFromInputs += recoveredInfo.amount; + + return true; +} + +std::optional TxFactory::BuildTx() +{ + CAmount nFee = BLSCT_DEFAULT_FEE * (vInputs.size() + vOutputs.size() + 1); + + while (true) { + CMutableTransaction tx; + tx.nVersion |= CTransaction::BLSCT_MARKER; + + Scalar gammaAcc; + std::map mapChange; + std::vector txSigs; + + for (auto& amounts : nAmounts) { + if (amounts.second.nFromInputs < amounts.second.nFromOutputs) + return std::nullopt; + mapChange[amounts.first] = amounts.second.nFromInputs - amounts.second.nFromOutputs - nFee; + } + + for (auto& in_ : vInputs) { + for (auto& in : in_.second) { + tx.vin.push_back(in.in); + gammaAcc = gammaAcc + in.gamma; + txSigs.push_back(in.sk.Sign(in.in.GetHash())); + } + } + + for (auto& out_ : vOutputs) { + for (auto& out : out_.second) { + tx.vout.push_back(out.out); + gammaAcc = gammaAcc - out.gamma; + txSigs.push_back(PrivateKey(out.blindingKey).Sign(out.out.GetHash())); + } + } + + for (auto& change : mapChange) { + auto changeOutput = CreateOutput(std::get(km->GetNewDestination(-1).value()), change.second, "Change", change.first); + tx.vout.push_back(changeOutput.out); + gammaAcc = gammaAcc - changeOutput.gamma; + txSigs.push_back(PrivateKey(changeOutput.blindingKey).Sign(changeOutput.out.GetHash())); + } + + if (nFee == (long long)(BLSCT_DEFAULT_FEE * (tx.vin.size() + tx.vout.size()))) { + CTxOut fee_out{nFee, CScript(OP_RETURN)}; + tx.vout.push_back(fee_out); + txSigs.push_back(PrivateKey(gammaAcc).SignBalance()); + tx.txSig = Signature::Aggregate(txSigs); + return tx; + } + + nFee = BLSCT_DEFAULT_FEE * (tx.vin.size() + tx.vout.size()); + } + + return std::nullopt; +} + +std::optional TxFactory::CreateTransaction(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& tokenId) +{ + LOCK(wallet->cs_wallet); + + wallet::CoinFilterParams coins_params; + coins_params.min_amount = 0; + coins_params.only_blsct = true; + coins_params.token_id = tokenId; + + auto tx = blsct::TxFactory(blsct_km); + + for (const wallet::COutput& output : AvailableCoins(*wallet, nullptr, std::nullopt, coins_params).All()) { + tx.AddInput(wallet, COutPoint(output.outpoint.hash, output.outpoint.n)); + + if (tx.nAmounts[tokenId].nFromInputs > nAmount + (long long)(BLSCT_DEFAULT_FEE * (tx.vInputs.size() + 3))) break; + } + + tx.AddOutput(destination, nAmount, sMemo, tokenId); + + return tx.BuildTx(); +} + +} // namespace blsct \ No newline at end of file diff --git a/src/blsct/wallet/txfactory.h b/src/blsct/wallet/txfactory.h new file mode 100644 index 0000000000000..c6b77a012b230 --- /dev/null +++ b/src/blsct/wallet/txfactory.h @@ -0,0 +1,37 @@ +// Copyright (c) 2023 The Navcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef TXFACTORY_H +#define TXFACTORY_H + +#include +#include +#include +#include +#include +#include +#include + +namespace blsct { +class TxFactory +{ +private: + KeyMan* km; + CMutableTransaction tx; + std::map> vOutputs; + std::map> vInputs; + std::map nAmounts; + +public: + TxFactory(KeyMan* km) : km(km){}; + + void AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& tokenId = TokenId()); + bool AddInput(wallet::CWallet* wallet, const COutPoint& outpoint, const bool& rbf = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); + bool AddInput(const CCoinsViewCache& cache, const COutPoint& outpoint, const bool& rbf = false); + std::optional BuildTx(); + static std::optional CreateTransaction(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& tokenId = TokenId()); +}; +} // namespace blsct + +#endif // TXFACTORY_H \ No newline at end of file diff --git a/src/blsct/wallet/txfactory_global.cpp b/src/blsct/wallet/txfactory_global.cpp new file mode 100644 index 0000000000000..2ea1e0d0b34d6 --- /dev/null +++ b/src/blsct/wallet/txfactory_global.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2023 The Navcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +using T = Mcl; +using Point = T::Point; +using Points = Elements; +using Scalar = T::Scalar; +using Scalars = Elements; + +namespace blsct { +void UnsignedOutput::GenerateKeys(Scalar blindingKey, DoublePublicKey destKeys) +{ + out.blsctData.ephemeralKey = PrivateKey(blindingKey).GetPoint(); + + Point vk, sk; + + if (!destKeys.GetViewKey(vk)) { + throw std::runtime_error(strprintf("%s: could not get view key from destination address\n", __func__)); + } + + if (!destKeys.GetSpendKey(sk)) { + throw std::runtime_error(strprintf("%s: could not get spend key from destination address\n", __func__)); + } + + out.blsctData.blindingKey = sk * blindingKey; + + auto rV = vk * blindingKey; + + out.blsctData.spendingKey = sk + (PrivateKey(Scalar(rV.GetHashWithSalt(0))).GetPoint()); +} + +Signature UnsignedOutput::GetSignature() const +{ + std::vector txSigs; + + txSigs.push_back(blsct::PrivateKey(blindingKey).Sign(out.GetHash())); + txSigs.push_back(blsct::PrivateKey(gamma.Negate()).SignBalance()); + + return Signature::Aggregate(txSigs); +} + +UnsignedOutput CreateOutput(const blsct::DoublePublicKey& destKeys, const CAmount& nAmount, std::string sMemo, const TokenId& tokenId, const Scalar& blindingKey, const std::vector& outputNonce) +{ + auto ret = UnsignedOutput(); + + ret.out.nValue = 0; + ret.out.tokenId = tokenId; + ret.out.scriptPubKey = CScript(OP_TRUE); + + if (outputNonce.size() > 0) + ret.out.scriptPubKey << outputNonce; + + Scalars vs; + vs.Add(nAmount); + + ret.blindingKey = blindingKey.IsZero() ? MclScalar::Rand() : blindingKey; + + Points nonces; + Point vk; + + if (!destKeys.GetViewKey(vk)) { + throw std::runtime_error(strprintf("%s: could not get view key from destination address\n", __func__)); + } + + auto nonce = vk * blindingKey; + nonces.Add(nonce); + + ret.value = nAmount; + ret.gamma = nonce.GetHashWithSalt(100); + + std::vector memo{sMemo.begin(), sMemo.end()}; + + bulletproofs::RangeProofLogic rp; + auto p = rp.Prove(vs, nonce, memo, tokenId); + + ret.out.blsctData.rangeProof = p; + + CHashWriter hash(SER_GETHASH, PROTOCOL_VERSION); + hash << nonce; + + ret.GenerateKeys(blindingKey, destKeys); + ret.out.blsctData.viewTag = (hash.GetHash().GetUint64(0) & 0xFFFF); + + return ret; +} + +CTransactionRef AggregateTransactions(const std::vector& txs) +{ + auto ret = CMutableTransaction(); + std::vector vSigs; + CAmount nFee = 0; + + for (auto& tx : txs) { + vSigs.push_back(tx->txSig); + for (auto& in : tx->vin) { + ret.vin.push_back(in); + } + for (auto& out : tx->vout) { + if (out.scriptPubKey.IsFee()) { + nFee += out.nValue; + continue; + } + ret.vout.push_back(out); + } + } + + ret.vout.push_back(CTxOut{nFee, CScript{OP_RETURN}}); + + ret.txSig = blsct::Signature::Aggregate(vSigs); + ret.nVersion = CTransaction::BLSCT_MARKER; + + return MakeTransactionRef(ret); +} +} // namespace blsct \ No newline at end of file diff --git a/src/blsct/wallet/txfactory_global.h b/src/blsct/wallet/txfactory_global.h new file mode 100644 index 0000000000000..51c7989150bef --- /dev/null +++ b/src/blsct/wallet/txfactory_global.h @@ -0,0 +1,66 @@ +// Copyright (c) 2023 The Navcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef TXFACTORY_GLOBAL_H +#define TXFACTORY_GLOBAL_H + +#include +#include +#include + +using T = Mcl; +using Point = T::Point; +using Points = Elements; +using Scalar = T::Scalar; +using Scalars = Elements; + +#define BLSCT_DEFAULT_FEE 200000 + +namespace blsct { +struct UnsignedOutput { + CTxOut out; + Scalar blindingKey; + Scalar value; + Scalar gamma; + + void GenerateKeys(Scalar blindingKey, DoublePublicKey destKeys); + + Signature GetSignature() const; + + template + void Serialize(Stream& s) const + { + ::Serialize(s, out); + ::Serialize(s, blindingKey); + ::Serialize(s, value); + ::Serialize(s, gamma); + } + + template + void Unserialize(Stream& s) + { + ::Unserialize(s, out); + ::Unserialize(s, blindingKey); + ::Unserialize(s, value); + ::Unserialize(s, gamma); + } +}; + +struct UnsignedInput { + CTxIn in; + Scalar value; + Scalar gamma; + PrivateKey sk; +}; + +struct Amounts { + CAmount nFromInputs; + CAmount nFromOutputs; +}; + +CTransactionRef AggregateTransactions(const std::vector& txs); +UnsignedOutput CreateOutput(const blsct::DoublePublicKey& destination, const CAmount& nAmount, std::string sMemo, const TokenId& tokenId = TokenId(), const Scalar& blindingKey = Scalar::Rand(), const std::vector& outputNonce = std::vector()); +} // namespace blsct + +#endif // TXFACTORY_GLOBAL_H \ No newline at end of file diff --git a/src/blsct/wallet/verification.cpp b/src/blsct/wallet/verification.cpp new file mode 100644 index 0000000000000..67c973673f5e7 --- /dev/null +++ b/src/blsct/wallet/verification.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2023 The Navcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +namespace blsct { +bool VerifyTx(const CTransaction& tx, const CCoinsViewCache& view, const CAmount& blockReward) +{ + if (!view.HaveInputs(tx)) { + return false; + } + + range_proof::GeneratorsFactory gf; + bulletproofs::RangeProofLogic rp; + std::vector> vProofs; + std::vector vMessages; + std::vector vPubKeys; + MclG1Point balanceKey; + + if (blockReward > 0) { + range_proof::Generators gen = gf.GetInstance(TokenId()); + balanceKey = (gen.G * MclScalar(blockReward)); + } + + if (!tx.IsCoinBase()) { + for (auto& in : tx.vin) { + Coin coin; + + if (!view.GetCoin(in.prevout, coin)) { + return false; + } + + vPubKeys.push_back(coin.out.blsctData.spendingKey); + auto in_hash = in.GetHash(); + vMessages.push_back(Message(in_hash.begin(), in_hash.end())); + balanceKey = balanceKey + coin.out.blsctData.rangeProof.Vs[0]; + } + } + + CAmount nFee = 0; + + for (auto& out : tx.vout) { + if (out.IsBLSCT()) { + vPubKeys.push_back(out.blsctData.ephemeralKey); + auto out_hash = out.GetHash(); + vMessages.push_back(Message(out_hash.begin(), out_hash.end())); + vProofs.push_back(out.blsctData.rangeProof); + balanceKey = balanceKey - out.blsctData.rangeProof.Vs[0]; + } else { + if (!out.scriptPubKey.IsUnspendable() && out.nValue > 0) { + return false; + } + if (nFee > 0 || !MoneyRange(out.nValue)) { + return false; + } + if (out.nValue == 0) continue; + nFee = out.nValue; + range_proof::Generators gen = gf.GetInstance(out.tokenId); + balanceKey = balanceKey - (gen.G * MclScalar(out.nValue)); + } + } + + vMessages.push_back(blsct::Common::BLSCTBALANCE); + vPubKeys.push_back(balanceKey); + + return PublicKeys{vPubKeys}.VerifyBatch(vMessages, tx.txSig, true) && + rp.Verify(vProofs); +} +} // namespace blsct \ No newline at end of file diff --git a/src/blsct/wallet/verification.h b/src/blsct/wallet/verification.h new file mode 100644 index 0000000000000..149759025ffbc --- /dev/null +++ b/src/blsct/wallet/verification.h @@ -0,0 +1,17 @@ +// Copyright (c) 2023 The Navcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BLSCT_VERIFICATION_H +#define BLSCT_VERIFICATION_H + +#include +#include +#include +#include +#include + +namespace blsct { +bool VerifyTx(const CTransaction& tx, const CCoinsViewCache& view, const CAmount& blockReward = 0); +} +#endif // BLSCT_VERIFICATION_H \ No newline at end of file diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6f4453d1fe2d7..fd0f990996d89 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -78,7 +78,67 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti vbparams.min_activation_height = 0; } bool found = false; - for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { + for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { + if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) { + options.version_bits_parameters[Consensus::DeploymentPos(j)] = vbparams; + found = true; + LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d\n", vDeploymentParams[0], vbparams.start_time, vbparams.timeout, vbparams.min_activation_height); + break; + } + } + if (!found) { + throw std::runtime_error(strprintf("Invalid deployment (%s)", vDeploymentParams[0])); + } + } +} + +void ReadBLSCTRegTestArgs(const ArgsManager& args, CChainParams::BLSCTRegTestOptions& options) +{ + if (auto value = args.GetBoolArg("-fastprune")) options.fastprune = *value; + + for (const std::string& arg : args.GetArgs("-testactivationheight")) { + const auto found{arg.find('@')}; + if (found == std::string::npos) { + throw std::runtime_error(strprintf("Invalid format (%s) for -testactivationheight=name@height.", arg)); + } + + const auto value{arg.substr(found + 1)}; + int32_t height; + if (!ParseInt32(value, &height) || height < 0 || height >= std::numeric_limits::max()) { + throw std::runtime_error(strprintf("Invalid height value (%s) for -testactivationheight=name@height.", arg)); + } + + const auto deployment_name{arg.substr(0, found)}; + if (const auto buried_deployment = GetBuriedDeployment(deployment_name)) { + options.activation_heights[*buried_deployment] = height; + } else { + throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg)); + } + } + + if (!args.IsArgSet("-vbparams")) return; + + for (const std::string& strDeployment : args.GetArgs("-vbparams")) { + std::vector vDeploymentParams = SplitString(strDeployment, ':'); + if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) { + throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]"); + } + CChainParams::VersionBitsParameters vbparams{}; + if (!ParseInt64(vDeploymentParams[1], &vbparams.start_time)) { + throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1])); + } + if (!ParseInt64(vDeploymentParams[2], &vbparams.timeout)) { + throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2])); + } + if (vDeploymentParams.size() >= 4) { + if (!ParseInt32(vDeploymentParams[3], &vbparams.min_activation_height)) { + throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3])); + } + } else { + vbparams.min_activation_height = 0; + } + bool found = false; + for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) { options.version_bits_parameters[Consensus::DeploymentPos(j)] = vbparams; found = true; @@ -116,6 +176,11 @@ std::unique_ptr CreateChainParams(const ArgsManager& args, c ReadRegTestArgs(args, opts); return CChainParams::RegTest(opts); } + case ChainType::BLSCTREGTEST: { + auto opts = CChainParams::BLSCTRegTestOptions{}; + ReadBLSCTRegTestArgs(args, opts); + return CChainParams::BLSCTRegTest(opts); + } } assert(false); } diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index b69224a1273a0..a52c292a98a48 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -13,12 +13,15 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) { - argsman.AddArg("-chain=", "Use the chain (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-chain=", "Use the chain (default: main). Allowed values: main, test, signet, regtest, blsctregtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " "This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-blsctregtest", "Enter blsct regression test mode, which uses a special chain in which blocks can be solved instantly and BLSCT is activated. " + "This is intended for regression testing tools and app development. Equivalent to -chain=blsctregtest.", + ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (*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[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (*regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. 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_ANY | ArgsManager::DISALLOW_NEGATION, 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_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS); @@ -47,6 +50,8 @@ std::unique_ptr CreateBaseChainParams(const ChainType chain) return std::make_unique("signet", 48487, 38334); case ChainType::REGTEST: return std::make_unique("regtest", 48486, 18445); + case ChainType::BLSCTREGTEST: + return std::make_unique("blsctregtest", 48485, 18444); } assert(false); } diff --git a/src/coins.cpp b/src/coins.cpp index 8badd3f4908dc..48826772c7107 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -316,7 +316,7 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const { if (!tx.IsCoinBase()) { for (unsigned int i = 0; i < tx.vin.size(); i++) { - if (!HaveCoin(tx.vin[i].prevout)) { + if (!tx.vin[i].prevout.IsNull() && !HaveCoin(tx.vin[i].prevout)) { return false; } } diff --git a/src/coins.h b/src/coins.h index 65af8863dab70..2505ccd7d42c3 100644 --- a/src/coins.h +++ b/src/coins.h @@ -76,6 +76,9 @@ class Coin nHeight = code >> 1; fCoinBase = code & 1; ::Unserialize(s, Using(out)); + if (IsSpent()) { + throw std::ios_base::failure("Coin unserialization error, coin is spent."); + } } /** Either this coin never existed (see e.g. coinEmpty in coins.cpp), or it diff --git a/src/common/args.cpp b/src/common/args.cpp index 62dca8fc10772..b3cdc5dec12e2 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -158,6 +158,7 @@ std::list ArgsManager::GetUnrecognizedSections() const // Section names to be recognized in the config file. static const std::set available_sections{ ChainTypeToString(ChainType::REGTEST), + ChainTypeToString(ChainType::BLSCTREGTEST), ChainTypeToString(ChainType::SIGNET), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::MAIN), @@ -744,19 +745,21 @@ std::variant ArgsManager::GetChainArg() const return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str()); }; + const bool fBLSCTRegTest = get_net("-blsctregtest"); const bool fRegTest = get_net("-regtest"); const bool fSigNet = get_net("-signet"); const bool fTestNet = get_net("-testnet"); const auto chain_arg = GetArg("-chain"); - if ((int)chain_arg.has_value() + (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 ((int)chain_arg.has_value() + (int)fRegTest + (int)fBLSCTRegTest + (int)fSigNet + (int)fTestNet > 1) { + throw std::runtime_error("Invalid combination of -blsctregtest, -regtest, -signet, -testnet and -chain. Can use at most one."); } if (chain_arg) { if (auto parsed = ChainTypeFromString(*chain_arg)) return *parsed; // Not a known string, so return original string return *chain_arg; } + if (fBLSCTRegTest) return ChainType::BLSCTREGTEST; if (fRegTest) return ChainType::REGTEST; if (fSigNet) return ChainType::SIGNET; if (fTestNet) return ChainType::TESTNET; diff --git a/src/compressor.h b/src/compressor.h index 0968454679b56..e9a9002eefb32 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -95,6 +95,7 @@ struct ScriptCompression } }; + struct AmountCompression { template void Ser(Stream& s, I val) @@ -109,10 +110,11 @@ struct AmountCompression } }; -/** wrapper for CTxOut that provides a more compact serialization */ -struct TxOutCompression -{ - FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using(obj.nValue), Using(obj.scriptPubKey)); } +/** wrapper for CTxOut that provides a more compact serialization + * TODO: Compress BLSCT fields + */ +struct TxOutCompression { + FORMATTER_METHODS(CTxOut, obj) { READWRITE(obj); } }; #endif // BITCOIN_COMPRESSOR_H diff --git a/src/consensus/params.h b/src/consensus/params.h index 25f53eb620773..aaa4af6e07426 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_CONSENSUS_PARAMS_H #define BITCOIN_CONSENSUS_PARAMS_H +#include #include #include @@ -74,6 +75,7 @@ struct BIP9Deployment { struct Params { uint256 hashGenesisBlock; int nSubsidyHalvingInterval; + CAmount nBLSCTBlockReward; /** * Hashes of blocks that * - are known to be consensus valid, and @@ -97,6 +99,8 @@ struct Params { /** Don't warn about unknown BIP 9 activations below this height. * This prevents us from warning about the CSV and segwit activations. */ int MinBIP9WarningHeight; + /** Whether BLSCT is activated */ + bool fBLSCT; /** * Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period, * (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments. diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp index f9496559093da..f5a7bc94cb254 100644 --- a/src/consensus/tx_check.cpp +++ b/src/consensus/tx_check.cpp @@ -8,10 +8,10 @@ #include #include -bool CheckTransaction(const CTransaction& tx, TxValidationState& state) +bool CheckTransaction(const CTransaction& tx, TxValidationState& state, const bool& fBLSCT) { // Basic checks that don't depend on any context - if (tx.vin.empty()) + if (tx.vin.empty() && !tx.IsBLSCT()) return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vin-empty"); if (tx.vout.empty()) return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-empty"); diff --git a/src/consensus/tx_check.h b/src/consensus/tx_check.h index 21f842408aef3..4e38a0ff54e1a 100644 --- a/src/consensus/tx_check.h +++ b/src/consensus/tx_check.h @@ -15,6 +15,6 @@ class CTransaction; class TxValidationState; -bool CheckTransaction(const CTransaction& tx, TxValidationState& state); +bool CheckTransaction(const CTransaction& tx, TxValidationState& state, const bool& fBLSCT = false); #endif // BITCOIN_CONSENSUS_TX_CHECK_H diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 154146f08d308..07ada34127351 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -175,14 +175,14 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, CAmount nValueIn = 0; for (unsigned int i = 0; i < tx.vin.size(); ++i) { - const COutPoint &prevout = tx.vin[i].prevout; + const COutPoint& prevout = tx.vin[i].prevout; const Coin& coin = inputs.AccessCoin(prevout); assert(!coin.IsSpent()); // If prev is coinbase, check that it's matured if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) { return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase", - strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); + strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); } // Check for negative or overflow input values @@ -192,18 +192,26 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, } } - const CAmount value_out = tx.GetValueOut(); - if (nValueIn < value_out) { - return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout", - strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out))); - } + if (!tx.IsBLSCT()) { + const CAmount value_out = tx.GetValueOut(); + if (nValueIn < value_out) { + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout", + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out))); + } - // Tally transaction fees - const CAmount txfee_aux = nValueIn - value_out; - if (!MoneyRange(txfee_aux)) { - return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange"); + // Tally transaction fees + const CAmount txfee_aux = nValueIn - value_out; + if (!MoneyRange(txfee_aux)) { + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange"); + } + + txfee = txfee_aux; + } else { + for (auto& out : tx.vout) { + if (out.scriptPubKey.IsFee()) + txfee = out.nValue; + } } - txfee = txfee_aux; return true; } diff --git a/src/init.cpp b/src/init.cpp index 4ef1e1551c8cb..4c8482e1271dd 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -414,10 +414,12 @@ void SetupServerArgs(ArgsManager& argsman) const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET); const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET); const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST); + const auto blsctRegtestBaseParams = CreateBaseChainParams(ChainType::BLSCTREGTEST); const auto defaultChainParams = CreateChainParams(argsman, ChainType::MAIN); const auto testnetChainParams = CreateChainParams(argsman, ChainType::TESTNET); const auto signetChainParams = CreateChainParams(argsman, ChainType::SIGNET); const auto regtestChainParams = CreateChainParams(argsman, ChainType::REGTEST); + const auto blsctRegtestChainParams = CreateChainParams(argsman, ChainType::BLSCTREGTEST); // Hidden Options std::vector hidden_args = { @@ -471,7 +473,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-addnode=", strprintf("Add a node to connect to and attempt to keep the connection open (see the addnode RPC help for more info). This option can be specified multiple times to add multiple nodes; connections are limited to %u at a time and are counted separately from the -maxconnections limit.", MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-asmap=", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-bantime=", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-bind=[:][=onion]", strprintf("Bind to given address and always listen on it (default: 0.0.0.0). Use [host]:port notation for IPv6. Append =onion to tag any incoming connections to that address and port as incoming Tor connections (default: 127.0.0.1:%u=onion, testnet: 127.0.0.1:%u=onion, signet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)", defaultBaseParams->OnionServiceTargetPort(), testnetBaseParams->OnionServiceTargetPort(), signetBaseParams->OnionServiceTargetPort(), regtestBaseParams->OnionServiceTargetPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-bind=[:][=onion]", strprintf("Bind to given address and always listen on it (default: 0.0.0.0). Use [host]:port notation for IPv6. Append =onion to tag any incoming connections to that address and port as incoming Tor connections (default: 127.0.0.1:%u=onion, testnet: 127.0.0.1:%u=onion, signet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion, blsctregtest: 127.0.0.1:%u=onion)", defaultBaseParams->OnionServiceTargetPort(), testnetBaseParams->OnionServiceTargetPort(), signetBaseParams->OnionServiceTargetPort(), regtestBaseParams->OnionServiceTargetPort(), blsctRegtestBaseParams->OnionServiceTargetPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-cjdnsreachable", "If set, then this host is configured for CJDNS (connecting to fc00::/8 addresses would lead us to the CJDNS network, see doc/cjdns.md) (default: 0)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-connect=", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -496,7 +498,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); // TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from // https://github.com/bitcoin/bitcoin/pull/23542 have become widespread. - argsman.AddArg("-port=", strprintf("Listen for connections on . Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-port=", strprintf("Listen for connections on . Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u, blsctregtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort(), blsctRegtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-proxy=", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION); argsman.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-seednode=", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -555,9 +557,9 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-checkblocks=", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-checklevel=", strprintf("How thorough the block verification of -checkblocks is: %s (0-4, default: %u)", Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); - argsman.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures occasionally. (default: %u, regtest: %u, blsctregtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks(), blsctRegtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-checkaddrman=", strprintf("Run addrman consistency checks every operations. Use 0 to disable. (default: %u)", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); - argsman.AddArg("-checkmempool=", strprintf("Run mempool consistency checks every transactions. Use 0 to disable. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-checkmempool=", strprintf("Run mempool consistency checks every transactions. Use 0 to disable. (default: %u, regtest: %u, blsctregtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks(), blsctRegtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block %s (default: %u)", defaultChainParams->Checkpoints().GetHeight(), DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-deprecatedrpc=", "Allows deprecated RPC method(s) to be used", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); @@ -579,7 +581,7 @@ void SetupServerArgs(ArgsManager& argsman) SetupChainParamsBaseOptions(argsman); - argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY); + argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest/blsctregtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY); argsman.AddArg("-incrementalrelayfee=", strprintf("Fee rate (in %s/kvB) used to define cost of relay, used for mempool limiting and replacement policy. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY); argsman.AddArg("-dustrelayfee=", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY); argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); @@ -605,7 +607,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-rpcdoccheck", strprintf("Throw a non-fatal error at runtime if the documentation for an RPC is incorrect (default: %u)", DEFAULT_RPC_DOC_CHECK), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); argsman.AddArg("-rpccookiefile=", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rpcpassword=", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); - argsman.AddArg("-rpcport=", strprintf("Listen for JSON-RPC connections on (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); + argsman.AddArg("-rpcport=", strprintf("Listen for JSON-RPC connections on (default: %u, testnet: %u, signet: %u, regtest: %u, blsctregtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort(), blsctRegtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); argsman.AddArg("-rpcserialversion", strprintf("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)", DEFAULT_RPC_SERIALIZE_VERSION), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rpcservertimeout=", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); argsman.AddArg("-rpcthreads=", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 0d916bbdf944e..11b343a9241b1 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -5,6 +5,8 @@ #include +#include +#include #include #include #include @@ -25,6 +27,60 @@ #include #include +static CBlock CreateBLSCTGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const blsct::UnsignedOutput& out) +{ + CMutableTransaction txNew; + txNew.nVersion = CTransaction::BLSCT_MARKER; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799; + txNew.vout[0] = out.out; + txNew.txSig = out.GetSignature(); + + CBlock genesis; + genesis.nTime = nTime; + genesis.nBits = nBits; + genesis.nNonce = nNonce; + genesis.nVersion = nVersion; + genesis.vtx.push_back(MakeTransactionRef(std::move(txNew))); + genesis.hashPrevBlock.SetNull(); + genesis.hashMerkleRoot = BlockMerkleRoot(genesis); + + bool fNegative; + bool fOverflow; + arith_uint256 bnTarget; + + bnTarget.SetCompact(genesis.nBits, &fNegative, &fOverflow); + + if (fNegative || bnTarget == 0 || fOverflow) + assert(0); + + while (UintToArith256(genesis.GetHash()) > bnTarget) { + ++genesis.nNonce; + } + + return genesis; +} + +/*static CBlock CreateBLSCTGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const blsct::DoublePublicKey& dest, const CAmount& genesisReward) +{ + auto out = blsct::CreateOutput(dest, genesisReward, "Reward", TokenId(), 1); + + CDataStream ss{0, 0}; + ss << out; + + return CreateBLSCTGenesisBlock(nTime, nNonce, nBits, nVersion, out); +}*/ + +static CBlock CreateBLSCTGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const std::string& outHex) +{ + blsct::UnsignedOutput out; + CDataStream ss(MakeByteSpan(ParseHex(outHex)), 0, 0); + ss >> out; + + return CreateBLSCTGenesisBlock(nTime, nNonce, nBits, nVersion, out); +} + static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) { CMutableTransaction txNew; @@ -36,9 +92,9 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi txNew.vout[0].scriptPubKey = genesisOutputScript; CBlock genesis; - genesis.nTime = nTime; - genesis.nBits = nBits; - genesis.nNonce = nNonce; + genesis.nTime = nTime; + genesis.nBits = nBits; + genesis.nNonce = nNonce; genesis.nVersion = nVersion; genesis.vtx.push_back(MakeTransactionRef(std::move(txNew))); genesis.hashPrevBlock.SetNull(); @@ -64,26 +120,30 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); } -/** - * Main network on which people trade goods and services. - */ -class CMainParams : public CChainParams { -public: - CMainParams() { + /** + * Main network on which people trade goods and services. + */ + class CMainParams : public CChainParams + { + public: + CMainParams() + { m_chain_type = ChainType::MAIN; consensus.signet_blocks = false; consensus.signet_challenge.clear(); consensus.nSubsidyHalvingInterval = 210000; + consensus.nBLSCTBlockReward = 50 * COIN; consensus.script_flag_exceptions.emplace( // BIP16 exception uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22"), SCRIPT_VERIFY_NONE); consensus.script_flag_exceptions.emplace( // Taproot exception uint256S("0x0000000000000000000f14c35b2d841e986ab5441de8c585d5ffe55ea1e395ad"), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS); consensus.BIP34Height = 227931; consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); - consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 - consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 - consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5 + consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 + consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 + consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5 consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893 + consensus.fBLSCT = false; consensus.MinBIP9WarningHeight = 483840; // segwit activation height + miner confirmation window consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -91,7 +151,7 @@ class CMainParams : public CChainParams { consensus.fPowAllowMinDifficultyBlocks = false; consensus.fPowNoRetargeting = false; consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016 - consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; @@ -99,8 +159,8 @@ class CMainParams : public CChainParams { // Deployment of Taproot (BIPs 340-342) consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021 - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021 + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021 consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000044a50fe819c39ad624021859"); @@ -130,19 +190,20 @@ class CMainParams : public CChainParams { // This is fine at runtime as we'll fall back to using them as an addrfetch if they don't support the // service bits we want, but we should get them updated to support all service bits wanted by any // release ASAP to avoid it where possible. - vSeeds.emplace_back("seed.bitcoin.sipa.be."); // Pieter Wuille, only supports x1, x5, x9, and xd - vSeeds.emplace_back("dnsseed.bluematt.me."); // Matt Corallo, only supports x9 - vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org."); // Luke Dashjr - vSeeds.emplace_back("seed.bitcoinstats.com."); // Christian Decker, supports x1 - xf + vSeeds.emplace_back("seed.bitcoin.sipa.be."); // Pieter Wuille, only supports x1, x5, x9, and xd + vSeeds.emplace_back("dnsseed.bluematt.me."); // Matt Corallo, only supports x9 + vSeeds.emplace_back("dnsseed.bitcoin.dashjr.org."); // Luke Dashjr + vSeeds.emplace_back("seed.bitcoinstats.com."); // Christian Decker, supports x1 - xf vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch."); // Jonas Schnelli, only supports x1, x5, x9, and xd - vSeeds.emplace_back("seed.btc.petertodd.org."); // Peter Todd, only supports x1, x5, x9, and xd - vSeeds.emplace_back("seed.bitcoin.sprovoost.nl."); // Sjors Provoost - vSeeds.emplace_back("dnsseed.emzy.de."); // Stephan Oeste - vSeeds.emplace_back("seed.bitcoin.wiz.biz."); // Jason Maurice - - base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,0); - base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,5); - base58Prefixes[SECRET_KEY] = std::vector(1,128); + vSeeds.emplace_back("seed.btc.petertodd.org."); // Peter Todd, only supports x1, x5, x9, and xd + vSeeds.emplace_back("seed.bitcoin.sprovoost.nl."); // Sjors Provoost + vSeeds.emplace_back("dnsseed.emzy.de."); // Stephan Oeste + vSeeds.emplace_back("seed.bitcoin.wiz.biz."); // Jason Maurice + + base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, 0); + base58Prefixes[BLSCT_ADDRESS] = {73, 33}; + base58Prefixes[SCRIPT_ADDRESS] = std::vector(1, 5); + base58Prefixes[SECRET_KEY] = std::vector(1, 128); base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; @@ -158,9 +219,9 @@ class CMainParams : public CChainParams { checkpointData = { { - { 11111, uint256S("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")}, - { 33333, uint256S("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")}, - { 74000, uint256S("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")}, + {11111, uint256S("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")}, + {33333, uint256S("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")}, + {74000, uint256S("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")}, {105000, uint256S("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")}, {134444, uint256S("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")}, {168000, uint256S("0x000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")}, @@ -171,40 +232,43 @@ class CMainParams : public CChainParams { {250000, uint256S("0x000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")}, {279000, uint256S("0x0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")}, {295000, uint256S("0x00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")}, - } - }; + }}; m_assumeutxo_data = MapAssumeutxo{ - // TODO to be specified in a future patch. + // TODO to be specified in a future patch. }; chainTxData = ChainTxData{ // Data from RPC: getchaintxstats 4096 000000000000000000035c3f0d31e71a5ee24c5aaf3354689f65bd7b07dee632 - .nTime = 1680665245, + .nTime = 1680665245, .nTxCount = 820876044, - .dTxRate = 3.672283614033389, + .dTxRate = 3.672283614033389, }; - } -}; + } + }; -/** - * Testnet (v3): public test network which is reset from time to time. - */ -class CTestNetParams : public CChainParams { -public: - CTestNetParams() { + /** + * Testnet (v3): public test network which is reset from time to time. + */ + class CTestNetParams : public CChainParams + { + public: + CTestNetParams() + { m_chain_type = ChainType::TESTNET; consensus.signet_blocks = false; consensus.signet_challenge.clear(); consensus.nSubsidyHalvingInterval = 210000; + consensus.nBLSCTBlockReward = 50 * COIN; consensus.script_flag_exceptions.emplace( // BIP16 exception uint256S("0x00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105"), SCRIPT_VERIFY_NONE); consensus.BIP34Height = 21111; consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); - consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 - consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 - consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb + consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 + consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 + consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb consensus.SegwitHeight = 834624; // 00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca + consensus.fBLSCT = false; consensus.MinBIP9WarningHeight = 836640; // segwit activation height + miner confirmation window consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -212,7 +276,7 @@ class CTestNetParams : public CChainParams { consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = false; consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains - consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; @@ -220,8 +284,8 @@ class CTestNetParams : public CChainParams { // Deployment of Taproot (BIPs 340-342) consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021 - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021 + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000977edb0244170858d07"); @@ -249,9 +313,10 @@ class CTestNetParams : public CChainParams { vSeeds.emplace_back("seed.testnet.bitcoin.sprovoost.nl."); vSeeds.emplace_back("testnet-seed.bluematt.me."); // Just a static list of stable node(s), only supports x9 - base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,111); - base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,196); - base58Prefixes[SECRET_KEY] = std::vector(1,239); + base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, 111); + base58Prefixes[BLSCT_ADDRESS] = {73, 33}; + 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}; @@ -268,8 +333,7 @@ class CTestNetParams : public CChainParams { checkpointData = { { {546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")}, - } - }; + }}; m_assumeutxo_data = MapAssumeutxo{ // TODO to be specified in a future patch. @@ -277,260 +341,382 @@ class CTestNetParams : public CChainParams { chainTxData = ChainTxData{ // Data from RPC: getchaintxstats 4096 0000000000000021bc50a89cde4870d4a81ffe0153b3c8de77b435a2fd3f6761 - .nTime = 1681542696, + .nTime = 1681542696, .nTxCount = 65345929, - .dTxRate = 0.09855282814711661, + .dTxRate = 0.09855282814711661, }; - } -}; + } + }; -/** - * Signet: test network with an additional consensus parameter (see BIP325). - */ -class SigNetParams : public CChainParams { -public: - explicit SigNetParams(const SigNetOptions& options) + /** + * Signet: test network with an additional consensus parameter (see BIP325). + */ + class SigNetParams : public CChainParams { - std::vector bin; - vSeeds.clear(); + public: + explicit SigNetParams(const SigNetOptions& options) + { + std::vector bin; + vSeeds.clear(); + + if (!options.challenge) { + bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"); + vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl."); + + // Hardcoded nodes can be removed once there are more DNS seeds + vSeeds.emplace_back("178.128.221.177"); + vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333"); + + consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000001899d8142b0"); + consensus.defaultAssumeValid = uint256S("0x0000004429ef154f7e00b4f6b46bfbe2d2678ecd351d95bbfca437ab9a5b84ec"); // 138000 + m_assumed_blockchain_size = 1; + m_assumed_chain_state_size = 0; + chainTxData = ChainTxData{ + // Data from RPC: getchaintxstats 4096 0000004429ef154f7e00b4f6b46bfbe2d2678ecd351d95bbfca437ab9a5b84ec + .nTime = 1681127428, + .nTxCount = 2226359, + .dTxRate = 0.006424463050600656, + }; + } else { + bin = *options.challenge; + consensus.nMinimumChainWork = uint256{}; + consensus.defaultAssumeValid = uint256{}; + m_assumed_blockchain_size = 0; + m_assumed_chain_state_size = 0; + chainTxData = ChainTxData{ + 0, + 0, + 0, + }; + LogPrintf("Signet with challenge %s\n", HexStr(bin)); + } - if (!options.challenge) { - bin = ParseHex("512103ad5e0edad18cb1f0fc0d28a3d4f1f3e445640337489abb10404f2d1e086be430210359ef5021964fe22d6f8e05b2463c9540ce96883fe3b278760f048f5189f2e6c452ae"); - vSeeds.emplace_back("seed.signet.bitcoin.sprovoost.nl."); + if (options.seeds) { + vSeeds = *options.seeds; + } - // Hardcoded nodes can be removed once there are more DNS seeds - vSeeds.emplace_back("178.128.221.177"); - vSeeds.emplace_back("v7ajjeirttkbnt32wpy3c6w3emwnfr3fkla7hpxcfokr3ysd3kqtzmqd.onion:38333"); + m_chain_type = ChainType::SIGNET; + consensus.signet_blocks = true; + consensus.signet_challenge.assign(bin.begin(), bin.end()); + consensus.nSubsidyHalvingInterval = 210000; + consensus.nBLSCTBlockReward = 50 * COIN; + consensus.BIP34Height = 1; + consensus.BIP34Hash = uint256{}; + consensus.BIP65Height = 1; + consensus.BIP66Height = 1; + consensus.CSVHeight = 1; + consensus.SegwitHeight = 1; + consensus.fBLSCT = false; + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = false; + consensus.fPowNoRetargeting = false; + consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016 + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.MinBIP9WarningHeight = 0; + consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000"); + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay + + // Activation of Taproot (BIPs 340-342) + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + + // message start is defined as the first 4 bytes of the sha256d of the block script + HashWriter h{}; + h << consensus.signet_challenge; + uint256 hash = h.GetHash(); + memcpy(pchMessageStart, hash.begin(), 4); + + nDefaultPort = 38333; + nPruneAfterHeight = 1000; + + 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[BLSCT_ADDRESS] = {73, 33}; + 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"; + bech32_mod_hrp = "nv"; + + fDefaultConsistencyChecks = false; + fRequireStandard = true; + m_is_test_chain = true; + m_is_mockable_chain = false; + } + }; + + /** + * Regression test: intended for private networks only. Has minimal difficulty to ensure that + * blocks can be found instantly. + */ + class CRegTestParams : public CChainParams + { + public: + explicit CRegTestParams(const RegTestOptions& opts) + { + m_chain_type = ChainType::REGTEST; + consensus.signet_blocks = false; + consensus.signet_challenge.clear(); + consensus.nSubsidyHalvingInterval = 150; + consensus.nBLSCTBlockReward = 50 * COIN; + consensus.BIP34Height = 1; // Always active unless overridden + consensus.BIP34Hash = uint256(); + consensus.BIP65Height = 1; // Always active unless overridden + consensus.BIP66Height = 1; // Always active unless overridden + consensus.CSVHeight = 1; // Always active unless overridden + consensus.SegwitHeight = 0; // Always active unless overridden + consensus.fBLSCT = false; + consensus.MinBIP9WarningHeight = 0; + consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = true; + consensus.fPowNoRetargeting = true; + consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains + consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) + + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay + + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay - consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000001899d8142b0"); - consensus.defaultAssumeValid = uint256S("0x0000004429ef154f7e00b4f6b46bfbe2d2678ecd351d95bbfca437ab9a5b84ec"); // 138000 - m_assumed_blockchain_size = 1; - m_assumed_chain_state_size = 0; - chainTxData = ChainTxData{ - // Data from RPC: getchaintxstats 4096 0000004429ef154f7e00b4f6b46bfbe2d2678ecd351d95bbfca437ab9a5b84ec - .nTime = 1681127428, - .nTxCount = 2226359, - .dTxRate = 0.006424463050600656, - }; - } else { - bin = *options.challenge; consensus.nMinimumChainWork = uint256{}; consensus.defaultAssumeValid = uint256{}; + + pchMessageStart[0] = 0xfd; + pchMessageStart[1] = 0xbf; + pchMessageStart[2] = 0x9f; + pchMessageStart[3] = 0xfb; + nDefaultPort = 18444; + nPruneAfterHeight = opts.fastprune ? 100 : 1000; m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; - chainTxData = ChainTxData{ - 0, - 0, - 0, - }; - LogPrintf("Signet with challenge %s\n", HexStr(bin)); - } - - if (options.seeds) { - vSeeds = *options.seeds; - } - m_chain_type = ChainType::SIGNET; - consensus.signet_blocks = true; - consensus.signet_challenge.assign(bin.begin(), bin.end()); - consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP34Height = 1; - consensus.BIP34Hash = uint256{}; - 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 = 1815; // 90% of 2016 - consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing - consensus.MinBIP9WarningHeight = 0; - consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000"); - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay - - // Activation of Taproot (BIPs 340-342) - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay - - // message start is defined as the first 4 bytes of the sha256d of the block script - HashWriter h{}; - h << consensus.signet_challenge; - uint256 hash = h.GetHash(); - memcpy(pchMessageStart, hash.begin(), 4); - - nDefaultPort = 38333; - nPruneAfterHeight = 1000; + for (const auto& [dep, height] : opts.activation_heights) { + switch (dep) { + case Consensus::BuriedDeployment::DEPLOYMENT_SEGWIT: + consensus.SegwitHeight = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_HEIGHTINCB: + consensus.BIP34Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_DERSIG: + consensus.BIP66Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CLTV: + consensus.BIP65Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CSV: + consensus.CSVHeight = int{height}; + break; + } + } - genesis = CreateGenesisBlock(1598918400, 52613770, 0x1e0377ae, 1, 50 * COIN); - consensus.hashGenesisBlock = genesis.GetHash(); - assert(consensus.hashGenesisBlock == uint256S("0x00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6")); - assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + for (const auto& [deployment_pos, version_bits_params] : opts.version_bits_parameters) { + consensus.vDeployments[deployment_pos].nStartTime = version_bits_params.start_time; + consensus.vDeployments[deployment_pos].nTimeout = version_bits_params.timeout; + consensus.vDeployments[deployment_pos].min_activation_height = version_bits_params.min_activation_height; + } - vFixedSeeds.clear(); + genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); + assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + + vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds. + vSeeds.clear(); + vSeeds.emplace_back("dummySeed.invalid."); + + fDefaultConsistencyChecks = true; + fRequireStandard = true; + m_is_test_chain = true; + m_is_mockable_chain = true; + + checkpointData = { + { + {0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")}, + }}; + + m_assumeutxo_data = MapAssumeutxo{ + { + 110, + {AssumeutxoHash{uint256S("0x1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618")}, 110}, + }, + { + 200, + {AssumeutxoHash{uint256S("0x51c8d11d8b5c1de51543c579736e786aa2736206d1e11e627568029ce092cf62")}, 200}, + }, + }; - 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}; + chainTxData = ChainTxData{ + 0, + 0, + 0}; - bech32_hrp = "tb"; - bech32_mod_hrp = "tn"; + base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, 111); + base58Prefixes[BLSCT_ADDRESS] = {73, 33}; + 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}; - fDefaultConsistencyChecks = false; - fRequireStandard = true; - m_is_test_chain = true; - m_is_mockable_chain = false; - } -}; + bech32_hrp = "bcrt"; + bech32_mod_hrp = "nr"; + } + }; -/** - * Regression test: intended for private networks only. Has minimal difficulty to ensure that - * blocks can be found instantly. - */ -class CRegTestParams : public CChainParams -{ -public: - explicit CRegTestParams(const RegTestOptions& opts) + /** + * Regression test: intended for private networks only. Has minimal difficulty to ensure that + * blocks can be found instantly. + */ + class CBLSCTRegTestParams : public CChainParams { - m_chain_type = ChainType::REGTEST; - consensus.signet_blocks = false; - consensus.signet_challenge.clear(); - consensus.nSubsidyHalvingInterval = 150; - consensus.BIP34Height = 1; // Always active unless overridden - consensus.BIP34Hash = uint256(); - consensus.BIP65Height = 1; // Always active unless overridden - consensus.BIP66Height = 1; // Always active unless overridden - consensus.CSVHeight = 1; // Always active unless overridden - consensus.SegwitHeight = 0; // Always active unless overridden - consensus.MinBIP9WarningHeight = 0; - consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); - consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks - consensus.nPowTargetSpacing = 10 * 60; - consensus.fPowAllowMinDifficultyBlocks = true; - consensus.fPowNoRetargeting = true; - consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains - consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) + public: + explicit CBLSCTRegTestParams(const BLSCTRegTestOptions& opts) + { + m_chain_type = ChainType::BLSCTREGTEST; + consensus.signet_blocks = false; + consensus.signet_challenge.clear(); + consensus.nSubsidyHalvingInterval = 150; + consensus.nBLSCTBlockReward = 50 * COIN; + consensus.BIP34Height = 1; // Always active unless overridden + consensus.BIP34Hash = uint256(); + consensus.BIP65Height = 1; // Always active unless overridden + consensus.BIP66Height = 1; // Always active unless overridden + consensus.CSVHeight = 1; // Always active unless overridden + consensus.SegwitHeight = 0; // Always active unless overridden + consensus.fBLSCT = true; + consensus.MinBIP9WarningHeight = 0; + consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = true; + consensus.fPowNoRetargeting = true; + consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains + consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) + + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay + + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay + consensus.nMinimumChainWork = uint256{}; + consensus.defaultAssumeValid = uint256{}; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + pchMessageStart[0] = 0xfd; + pchMessageStart[1] = 0xbf; + pchMessageStart[2] = 0x9f; + pchMessageStart[3] = 0xfb; + nDefaultPort = 18444; + nPruneAfterHeight = opts.fastprune ? 100 : 1000; + m_assumed_blockchain_size = 0; + m_assumed_chain_state_size = 0; - consensus.nMinimumChainWork = uint256{}; - consensus.defaultAssumeValid = uint256{}; - - pchMessageStart[0] = 0xfd; - pchMessageStart[1] = 0xbf; - pchMessageStart[2] = 0x9f; - pchMessageStart[3] = 0xfb; - nDefaultPort = 18444; - nPruneAfterHeight = opts.fastprune ? 100 : 1000; - m_assumed_blockchain_size = 0; - m_assumed_chain_state_size = 0; - - for (const auto& [dep, height] : opts.activation_heights) { - switch (dep) { - case Consensus::BuriedDeployment::DEPLOYMENT_SEGWIT: - consensus.SegwitHeight = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_HEIGHTINCB: - consensus.BIP34Height = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_DERSIG: - consensus.BIP66Height = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_CLTV: - consensus.BIP65Height = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_CSV: - consensus.CSVHeight = int{height}; - break; + for (const auto& [dep, height] : opts.activation_heights) { + switch (dep) { + case Consensus::BuriedDeployment::DEPLOYMENT_SEGWIT: + consensus.SegwitHeight = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_HEIGHTINCB: + consensus.BIP34Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_DERSIG: + consensus.BIP66Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CLTV: + consensus.BIP65Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CSV: + consensus.CSVHeight = int{height}; + break; + } } - } - for (const auto& [deployment_pos, version_bits_params] : opts.version_bits_parameters) { - consensus.vDeployments[deployment_pos].nStartTime = version_bits_params.start_time; - consensus.vDeployments[deployment_pos].nTimeout = version_bits_params.timeout; - consensus.vDeployments[deployment_pos].min_activation_height = version_bits_params.min_activation_height; - } + for (const auto& [deployment_pos, version_bits_params] : opts.version_bits_parameters) { + consensus.vDeployments[deployment_pos].nStartTime = version_bits_params.start_time; + consensus.vDeployments[deployment_pos].nTimeout = version_bits_params.timeout; + consensus.vDeployments[deployment_pos].min_activation_height = version_bits_params.min_activation_height; + } - genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); - consensus.hashGenesisBlock = genesis.GetHash(); - assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); - assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + genesis = CreateBLSCTGenesisBlock(1296688602, 2, 0x207fffff, 0x40000000UL, "ffffffffffffff7f0100000000000000015101a99ce23eabde4685106dfe66c9d5116c591a995d83f7c0ad0e551c1bae7b479b0475883553724c3cff9d8108f1f4c7ee06b3581950c813cdbb310dc80aa57f779c263b58f47760e7fc70ddc8cd923998519455d3ea983ec0309330fb7168180f9aa96f90180d17664842332b96ad3ee9944890f8d7bade481c073f47f95db8b987bac393a06ee380aafdbe18890e09c0c7ac255ac0699a19e1b5b0925eca8aad640e6d86274d624c186b8c4ff1d9b0fb80c9c4a0ce9363be86fb5667bc6eda74ab8cdd8a1b39269fda563b08f0e227b31f0eaf39cab020692a40225ce21694fc85f39305b0b77827b3cd24e8e5cf89782c8708998a9a211d76bc8e69489220c9548cb95a3d5f673fb0f647867ecb3f9e0528b2d9f3bcb04cfb7e0cd78278f20e5b8f82aef4c89cdd688944200ba4fa5481560d28f4e6fd6a7ec6721dc2878d9ba5e502701407edc9c70f3689b5c2ec8e1a06ab46037c03ae42a9c7b2027569e32d2aee2850a76214a98b74d0bfce59697c102984fa6f1adb3df5bd1e68c98b2afab586711aa5906f2f66dd8546fc12618fc034cb6370c96527dff837cd9c7cfc80a452b85ef0f9f432185c4f56b20439e085b9d51e8997290becaee8a7cfab02f6e04e593be4b38d7e911f46e73cce7abdfb10de60e1b49aa89856e8f377d2e80e428817651099e60da5e05ef10adfb6b31bdd60ade0204323f30713b61025ab5d980f93a9bae138036d33227116d7faa4b7919e134012c01b4ed46d934374a7641c129c9e3b13cfcd1da59947dcecf7bf7c6126ae743232d412918ce3d59290c097836517881d09fdbc2801eaf881c975a43609fcb71961c9ba0bc1446f65e230c9b9a67c2d780049afb7f32b124e9da5b70000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffae5bc62112e887afb5ece64a7853daee1d6e591ce1c3a57fa7374b63d6bcde6720b549447b76b80e230acb9d81548235ae6c6c44b740178a6263bd10a7912ebf1207409205690ef02ee46b7ca84446bbc884f593125402cadb2b623ba736f1218313907051f5af0c8fa648277afc39e3a34fd7285fb819157cd396746ccce3cce284601e77242ca95b9cb4f30acdd735b5c1a3d0269d1b7c05578ac345b804295cfb4428e29008667c5e3574d69968c7277cf65c3ae8d8283ff4cc6b10c1589935d2329e7159da6e0145d5c7ed277a97568c3b3f6b52de3ac9da14ac28bbe2ac44b99ce77ed713aa7534e1f82dfaf1a7f7bb20b70c1eb049e7268f21c5a3d550480e6cfe80f3f3a47d9865dbfac2be704d018d6b5453b7c39a14e4302b87e2ce711e438bd5d2acef5cb7f823e34378034d03e830f0b67f46ef350f2e420b00bb19df4075cf639fa4201c94f62312852261b4879078b711ab1961adec2f587964987232c232f64c69aabdcc972fa53ee6348070dd95485d87e83381c51bf3390342473c27308293d2abe5f43660e38e5dc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb79550000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000012a05f2005820674b986160bb75605931023c4a8777136bec4ff116113184c7ed5802c796"); + consensus.hashGenesisBlock = genesis.GetHash(); - vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds. - vSeeds.clear(); - vSeeds.emplace_back("dummySeed.invalid."); + vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds. + vSeeds.clear(); + vSeeds.emplace_back("dummySeed.invalid."); - fDefaultConsistencyChecks = true; - fRequireStandard = true; - m_is_test_chain = true; - m_is_mockable_chain = true; + fDefaultConsistencyChecks = true; + fRequireStandard = true; + m_is_test_chain = true; + m_is_mockable_chain = true; - checkpointData = { - { - {0, uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")}, - } - }; + checkpointData = { + { + {0, consensus.hashGenesisBlock}, + }}; - m_assumeutxo_data = MapAssumeutxo{ - { - 110, - {AssumeutxoHash{uint256S("0x1ebbf5850204c0bdb15bf030f47c7fe91d45c44c712697e4509ba67adb01c618")}, 110}, - }, - { - 200, - {AssumeutxoHash{uint256S("0x51c8d11d8b5c1de51543c579736e786aa2736206d1e11e627568029ce092cf62")}, 200}, - }, - }; + chainTxData = ChainTxData{ + 0, + 0, + 0}; - chainTxData = ChainTxData{ - 0, - 0, - 0 - }; + base58Prefixes[PUBKEY_ADDRESS] = std::vector(1, 111); + base58Prefixes[BLSCT_ADDRESS] = {73, 33}; + 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}; - 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 = "bcrt"; + bech32_mod_hrp = "nr"; + } + }; - bech32_hrp = "bcrt"; - bech32_mod_hrp = "tn"; + std::unique_ptr CChainParams::SigNet(const SigNetOptions& options) + { + return std::make_unique(options); } -}; -std::unique_ptr CChainParams::SigNet(const SigNetOptions& options) -{ - return std::make_unique(options); -} + std::unique_ptr CChainParams::RegTest(const RegTestOptions& options) + { + return std::make_unique(options); + } -std::unique_ptr CChainParams::RegTest(const RegTestOptions& options) -{ - return std::make_unique(options); -} + std::unique_ptr CChainParams::BLSCTRegTest(const BLSCTRegTestOptions& options) + { + return std::make_unique(options); + } -std::unique_ptr CChainParams::Main() -{ - return std::make_unique(); -} + std::unique_ptr CChainParams::Main() + { + return std::make_unique(); + } -std::unique_ptr CChainParams::TestNet() -{ - return std::make_unique(); -} + std::unique_ptr CChainParams::TestNet() + { + return std::make_unique(); + } diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h index 7145921052a58..6ceaea6d1f67d 100644 --- a/src/kernel/chainparams.h +++ b/src/kernel/chainparams.h @@ -82,6 +82,7 @@ class CChainParams SECRET_KEY, EXT_PUBLIC_KEY, EXT_SECRET_KEY, + BLSCT_ADDRESS, MAX_BASE58_TYPES }; @@ -159,7 +160,17 @@ class CChainParams bool fastprune{false}; }; + /** + * BLSCTRegTestOptions holds configurations for creating a regtest BLSCT CChainParams. + */ + struct BLSCTRegTestOptions { + std::unordered_map version_bits_parameters{}; + std::unordered_map activation_heights{}; + bool fastprune{false}; + }; + static std::unique_ptr RegTest(const RegTestOptions& options); + static std::unique_ptr BLSCTRegTest(const BLSCTRegTestOptions& options); static std::unique_ptr SigNet(const SigNetOptions& options); static std::unique_ptr Main(); static std::unique_ptr TestNet(); diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index f60ff83a0dca4..02d91f2bd6ba1 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -67,7 +67,7 @@ static ChainstateLoadResult CompleteChainstateInitialization( } if (!chainman.BlockIndex().empty() && - !chainman.m_blockman.LookupBlockIndex(chainman.GetConsensus().hashGenesisBlock)) { + !chainman.m_blockman.LookupBlockIndex(chainman.GetConsensus().hashGenesisBlock)) { // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way around). return {ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB, _("Incorrect or no genesis block found. Wrong datadir for network?")}; diff --git a/src/node/miner.cpp b/src/node/miner.cpp index aa1a9a155c6c7..7f7ef12b35d91 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -5,6 +5,7 @@ #include +#include #include #include #include @@ -117,7 +118,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc // Add dummy coinbase tx as first transaction pblock->vtx.emplace_back(); - pblocktemplate->vTxFees.push_back(-1); // updated at end + pblocktemplate->vTxFees.push_back(-1); // updated at end pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end LOCK(::cs_main); @@ -162,15 +163,15 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); // Fill in header - pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); - pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); - pblock->nNonce = 0; + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); + pblock->nNonce = 0; pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); BlockValidationState state; if (m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, - GetAdjustedTime, /*fCheckPOW=*/false, /*fCheckMerkleRoot=*/false)) { + GetAdjustedTime, /*fCheckPOW=*/false, /*fCheckMerkleRoot=*/false)) { throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString())); } const auto time_2{SteadyClock::now()}; @@ -183,6 +184,115 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc return std::move(pblocktemplate); } + +std::unique_ptr BlockAssembler::CreateNewBLSCTPOWBlock(const blsct::SubAddress& destination, CAmount nReward, const std::vector& txns) +{ + const auto time_start{SteadyClock::now()}; + + resetBlock(); + + pblocktemplate.reset(new CBlockTemplate()); + + if (!pblocktemplate.get()) { + return nullptr; + } + CBlock* const pblock = &pblocktemplate->block; // pointer for convenience + + // Add dummy coinbase tx as first transaction + pblock->vtx.emplace_back(); + pblocktemplate->vTxFees.push_back(-1); // updated at end + pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end + + LOCK(::cs_main); + CBlockIndex* pindexPrev = m_chainstate.m_chain.Tip(); + assert(pindexPrev != nullptr); + nHeight = pindexPrev->nHeight + 1; + + pblock->nVersion = m_chainstate.m_chainman.m_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + // -regtest only: allow overriding block.nVersion with + // -blockversion=N to test forking scenarios + if (chainparams.MineBlocksOnDemand()) { + pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion); + } + + pblock->nTime = TicksSinceEpoch(GetAdjustedTime()); + m_lock_time_cutoff = pindexPrev->GetMedianTimePast(); + + int nPackagesSelected = 0; + int nDescendantsUpdated = 0; + if (m_mempool) { + LOCK(m_mempool->cs); + addPackageTxs(*m_mempool, nPackagesSelected, nDescendantsUpdated); + } + + for (const CMutableTransaction& tx : txns) { + for (auto& out : tx.vout) { + if (out.scriptPubKey.IsFee()) { + nFees += out.nValue; + } + } + pblock->vtx.push_back(MakeTransactionRef(tx)); + } + + const auto time_1{SteadyClock::now()}; + + m_last_block_num_txs = nBlockTx; + m_last_block_weight = nBlockWeight; + + // Create coinbase transaction. + CMutableTransaction coinbaseTx; + + std::vector txSigs; + + auto out = blsct::CreateOutput(destination.GetKeys(), nReward + nFees, "Reward"); + + txSigs.push_back(blsct::PrivateKey(out.blindingKey).Sign(out.out.GetHash())); + txSigs.push_back(blsct::PrivateKey(out.gamma.Negate()).SignBalance()); + + + coinbaseTx.nVersion = CTransaction::BLSCT_MARKER; + coinbaseTx.txSig = blsct::Signature::Aggregate(txSigs); + coinbaseTx.vin.resize(1); + coinbaseTx.vin[0].prevout.SetNull(); + coinbaseTx.vout.resize(1); + coinbaseTx.vout[0] = out.out; + coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); + if (pblock->vtx.size() > 1) { + std::vector vToAggregate(pblock->vtx.begin() + 1, pblock->vtx.begin() + pblock->vtx.size()); + auto aggregatedTx = blsct::AggregateTransactions(vToAggregate); + pblock->vtx.resize(1); + pblock->vtx.push_back(aggregatedTx); + } + Assert(pblock->vtx.size() <= 2); + + pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev); + pblocktemplate->vTxFees[0] = -nFees; + + LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); + + // Fill in header + pblock->hashPrevBlock = pindexPrev->GetBlockHash(); + UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); + pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); + pblock->nNonce = 0; + pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]); + + BlockValidationState state; + if (m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, + GetAdjustedTime, /*fCheckPOW=*/false, /*fCheckMerkleRoot=*/false)) { + throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString())); + } + const auto time_2{SteadyClock::now()}; + + LogPrint(BCLog::BENCH, "CreateNewBLSCTPOWBlock() packages: %.2fms (%d packages, %d updated descendants), validity: %.2fms (total %.2fms)\n", + Ticks(time_1 - time_start), nPackagesSelected, nDescendantsUpdated, + Ticks(time_2 - time_1), + Ticks(time_2 - time_start)); + + return std::move(pblocktemplate); +} + void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet) { for (CTxMemPool::setEntries::iterator iit = testSet.begin(); iit != testSet.end(); ) { @@ -227,7 +337,14 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) nBlockWeight += iter->GetTxWeight(); ++nBlockTx; nBlockSigOpsCost += iter->GetSigOpCost(); - nFees += iter->GetFee(); + if (iter->GetTx().IsBLSCT()) { + for (auto& out : iter->GetTx().vout) { + if (out.scriptPubKey.IsFee()) + nFees += out.nValue; + } + } else { + nFees += iter->GetFee(); + } inBlock.insert(iter); bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); diff --git a/src/node/miner.h b/src/node/miner.h index f1ccffff55c68..ffc31afbaf4ed 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_NODE_MINER_H #define BITCOIN_NODE_MINER_H +#include #include #include #include @@ -162,6 +163,7 @@ class BlockAssembler /** Construct a new block template with coinbase to scriptPubKeyIn */ std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn); + std::unique_ptr CreateNewBLSCTPOWBlock(const blsct::SubAddress& destination, CAmount nReward, const std::vector& txns); inline static std::optional m_last_block_num_txs{}; inline static std::optional m_last_block_weight{}; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 41b5b2d0f1898..673fb54d515f1 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -131,6 +131,9 @@ bool IsStandardTx(const CTransaction& tx, const std::optional& max_dat unsigned int nDataOut = 0; TxoutType whichType; for (const CTxOut& txout : tx.vout) { + if (txout.IsBLSCT()) + continue; + if (!::IsStandard(txout.scriptPubKey, max_datacarrier_bytes, whichType)) { reason = "scriptpubkey"; return false; @@ -183,6 +186,8 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxOut& prev = mapInputs.AccessCoin(tx.vin[i].prevout).out; + if (prev.IsBLSCT()) continue; + std::vector > vSolutions; TxoutType whichType = Solver(prev.scriptPubKey, vSolutions); if (whichType == TxoutType::NONSTANDARD || whichType == TxoutType::WITNESS_UNKNOWN) { diff --git a/src/policy/policy.h b/src/policy/policy.h index 394fb342306d8..dcee6505b9802 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -111,7 +111,7 @@ bool IsStandard(const CScript& scriptPubKey, const std::optional& max_ // Changing the default transaction version requires a two step process: first // adapting relay policy by bumping TX_MAX_STANDARD_VERSION, and then later // allowing the new transaction version in the wallet/RPC. -static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{2}; +static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{1 << 6}; /** * Check for standard transaction types diff --git a/src/primitives/block.h b/src/primitives/block.h index bd11279a6e018..cdc10f35de786 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -51,6 +51,11 @@ class CBlockHeader return (nBits == 0); } + bool IsBLSCT() const + { + return (nVersion & 0x40000000UL); + } + uint256 GetHash() const; NodeSeconds Time() const diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 3b32e50a998c3..00838512b017f 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -51,6 +51,11 @@ std::string CTxIn::ToString() const return str; } +uint256 CTxIn::GetHash() const +{ + return SerializeHash(*this, SER_GETHASH); +} + CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, TokenId tokenIdIn) { nValue = nValueIn; @@ -60,9 +65,15 @@ CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, TokenId tokenIdI std::string CTxOut::ToString() const { - return strprintf("CTxOut(scriptPubKey=%s, spendingKey=%s, blindingKey=%s, ephemeralKey=%s%s)", HexStr(scriptPubKey).substr(0, 30), - HexStr(blsctData.spendingKey.GetVch()), HexStr(blsctData.blindingKey.GetVch()), HexStr(blsctData.ephemeralKey.GetVch()), - tokenId.IsNull() ? "" : strprintf(", tokenId=%s", tokenId.ToString())); + return strprintf("CTxOut(scriptPubKey=%s%s%s%s)", HexStr(scriptPubKey).substr(0, 30), + IsBLSCT() ? strprintf(", spendingKey=%s, blindingKey=%s, ephemeralKey=%s", HexStr(blsctData.spendingKey.GetVch()), HexStr(blsctData.blindingKey.GetVch()), HexStr(blsctData.ephemeralKey.GetVch())) : "", + tokenId.IsNull() ? "" : strprintf(", tokenId=%s", tokenId.ToString()), + IsBLSCT() ? "" : strprintf(", nAmount=%s", FormatMoney(nValue))); +} + +uint256 CTxOut::GetHash() const +{ + return SerializeHash(*this, SER_GETHASH); } CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 42469e24ba7d5..c2353ccbeb863 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -16,6 +16,7 @@ #include