diff --git a/src/Makefile.am b/src/Makefile.am index 8f7c3fe1fada6..4d2b25b9a1860 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -205,6 +205,7 @@ BLSCT_H = \ blsct/tokens/predicate_parser.h \ blsct/tokens/rpc.h \ blsct/wallet/address.h \ + blsct/wallet/balance_proof.h \ blsct/wallet/hdchain.h \ blsct/wallet/helpers.h \ blsct/wallet/import_wallet_type.h \ @@ -214,6 +215,7 @@ BLSCT_H = \ blsct/wallet/txfactory.h \ blsct/wallet/txfactory_base.h \ blsct/wallet/txfactory_global.h \ + blsct/wallet/unsigned_transaction.h \ blsct/wallet/verification.h BLSCT_CPP = \ @@ -268,6 +270,7 @@ BLSCT_CPP = \ blsct/wallet/txfactory.cpp \ blsct/wallet/txfactory_base.cpp \ blsct/wallet/txfactory_global.cpp \ + blsct/wallet/unsigned_transaction.cpp \ blsct/wallet/verification.cpp .PHONY: FORCE check-symbols check-security @@ -576,12 +579,13 @@ libbitcoin_node_a_SOURCES = \ blsct/range_proof/generators.cpp \ blsct/range_proof/msg_amt_cipher.cpp \ blsct/range_proof/proof_base.cpp \ + blsct/range_proof/rpc.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/tokens/info.cpp \ blsct/tokens/rpc.cpp \ - blsct/wallet/rpc.cpp \ + blsct/wallet/unsigned_transaction.cpp \ blsct/wallet/verification.cpp \ blsct/signature.cpp \ chain.cpp \ @@ -736,9 +740,11 @@ libbitcoin_wallet_a_SOURCES = \ blsct/set_mem_proof/set_mem_proof_setup.cpp \ blsct/signature.cpp \ blsct/wallet/address.cpp \ + blsct/wallet/balance_proof.cpp \ blsct/wallet/helpers.cpp \ blsct/wallet/keyman.cpp \ blsct/wallet/keyring.cpp \ + blsct/wallet/unsigned_transaction.cpp \ blsct/wallet/rpc.cpp \ blsct/wallet/txfactory.cpp \ blsct/wallet/txfactory_base.cpp \ @@ -907,6 +913,7 @@ libbitcoin_consensus_a_SOURCES = \ blsct/set_mem_proof/set_mem_proof_prover.cpp \ blsct/pos/helpers.cpp \ blsct/pos/proof.cpp \ + blsct/public_key.cpp \ blsct/signature.cpp \ consensus/amount.h \ consensus/merkle.cpp \ @@ -987,6 +994,7 @@ libbitcoin_common_a_SOURCES = \ blsct/tokens/predicate_parser.cpp \ blsct/wallet/address.cpp \ blsct/wallet/txfactory_global.cpp \ + blsct/wallet/unsigned_transaction.cpp \ chainparams.cpp \ coins.cpp \ common/args.cpp \ @@ -1317,6 +1325,7 @@ libnaviokernel_la_SOURCES = \ blsct/tokens/predicate_exec.cpp \ blsct/tokens/predicate_parser.cpp \ blsct/wallet/txfactory_global.cpp \ + blsct/wallet/unsigned_transaction.cpp \ blsct/wallet/verification.cpp \ chain.cpp \ clientversion.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 2244cb30f9be7..4c2b9098992bf 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -109,6 +109,7 @@ BITCOIN_TESTS =\ test/blsct/set_mem_proof/set_mem_proof_tests.cpp \ test/blsct/signature_tests.cpp \ test/blsct/sign_verify_tests.cpp \ + test/blsct_signature_checker_tests.cpp \ test/bswap_tests.cpp \ test/checkqueue_tests.cpp \ test/coins_tests.cpp \ diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include index e58b8410a1b40..1e28676269e71 100644 --- a/src/Makefile.test_util.include +++ b/src/Makefile.test_util.include @@ -19,6 +19,7 @@ TEST_UTIL_H = \ blsct/public_key.h \ blsct/public_keys.h \ blsct/signature.h \ + blsct/wallet/unsigned_transaction.h \ blsct/wallet/txfactory_global.h \ test/util/blockfilter.h \ test/util/chainstate.h \ @@ -54,6 +55,7 @@ libtest_util_a_SOURCES = \ blsct/public_key.cpp \ blsct/public_keys.cpp \ blsct/signature.cpp \ + blsct/wallet/unsigned_transaction.cpp \ blsct/wallet/rpc.cpp \ blsct/wallet/txfactory_global.cpp \ test/util/blockfilter.cpp \ diff --git a/src/bench/bech32_mod.cpp b/src/bench/bech32_mod.cpp index 3ef6c904c0fb2..3525e647380b6 100644 --- a/src/bench/bech32_mod.cpp +++ b/src/bench/bech32_mod.cpp @@ -25,7 +25,7 @@ static void Bech32ModEncode(benchmark::Bench& bench) static void Bech32ModDecode(benchmark::Bench& bench) { - std::string addr = "nv1d3fqq4j2w384smpjxgm95anexe4rjwzr2pc553t0xduxuc3sd35nvatcxpnn2mjyde45sqrtfapnws3kwfc85sm3v9ekzc2r2ajnquzf09282e62xddykn25x3"; + std::string addr = "nav1d3fqq4j2w384smpjxgm95anexe4rjwzr2pc553t0xduxuc3sd35nvatcxpnn2mjyde45sqrtfapnws3kwfc85sm3v9ekzc2r2ajnquzf09282e62xddykn25x3"; bench.batch(addr.size()).unit("byte").run([&] { bech32_mod::Decode(addr); }); diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index 18a5fdbed1810..001d1b872139e 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -31,7 +31,10 @@ CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block) : void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const { DataStream stream{}; - stream << header << posProof << nonce; + stream << header; + if (header.IsProofOfStake()) + stream << posProof; + stream << nonce; CSHA256 hasher; hasher.Write((unsigned char*)&(*stream.begin()), stream.end() - stream.begin()); uint256 shorttxidhash; diff --git a/src/blsct/key_io.h b/src/blsct/key_io.h index 8ae2bd74f9444..35c8b9b484d5d 100644 --- a/src/blsct/key_io.h +++ b/src/blsct/key_io.h @@ -19,13 +19,13 @@ namespace blsct { // - 1-byte separator '1' // - 154-byte data // - 8-byte checksum -constexpr size_t DOUBLE_PUBKEY_ENC_SIZE = 2 + 1 + bech32_mod::DOUBLE_PUBKEY_DATA_ENC_SIZE + 8; +constexpr size_t DOUBLE_PUBKEY_ENC_SIZE = 3 + 1 + bech32_mod::DOUBLE_PUBKEY_DATA_ENC_SIZE + 8; namespace bech32_hrp { - const std::string Main = "nv"; - const std::string TestNet = "tn"; - const std::string SigNet = "tn"; - const std::string RegTest = "nr"; + const std::string Main = "nav"; + const std::string TestNet = "tnv"; + const std::string SigNet = "snv"; + const std::string RegTest = "rnav"; } /** Encode DoublePublicKey to Bech32 or Bech32m string. Encoding must be one of BECH32 or BECH32M. */ diff --git a/src/blsct/range_proof/rpc.cpp b/src/blsct/range_proof/rpc.cpp new file mode 100644 index 0000000000000..2c93a52a7ab23 --- /dev/null +++ b/src/blsct/range_proof/rpc.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2024 The Navio Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +RPCHelpMan verifyblsctbalanceproof() +{ + return RPCHelpMan{ + "verifyblsctbalanceproof", + "Verifies a zero-knowledge balance proof\n", + { + {"proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The serialized balance proof"}, + {"additional_commitment", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The additional commitment to use for the proof signature verification"}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", { + {RPCResult::Type::BOOL, "valid", "Whether the proof is valid"}, + {RPCResult::Type::NUM, "min_amount", "The minimum amount proven"}, + }}, + RPCExamples{HelpExampleCli("verifyblsctbalanceproof", "\"\"") + HelpExampleRpc("verifyblsctbalanceproof", "\"\"")}, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { + LOCK(cs_main); + ChainstateManager& chainman = EnsureAnyChainman(request.context); + Chainstate& active_chainstate = chainman.ActiveChainstate(); + + CCoinsViewCache* coins_view; + coins_view = &active_chainstate.CoinsTip(); + + // Deserialize the proof + std::vector proof_data = ParseHex(request.params[0].get_str()); + uint256 hash = MessageHash("BLSCT_BALANCE_PROOF_" + (!request.params[1].isNull() ? request.params[1].get_str() : "")); + blsct::Message additional_commitment(hash.begin(), hash.end()); + DataStream ss{proof_data}; + blsct::BalanceProof proof; + try { + ss >> proof; + } catch (const std::exception) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proof format"); + } + + // Verify the proof + bool valid = proof.Verify(coins_view, additional_commitment); + + UniValue result(UniValue::VOBJ); + result.pushKV("valid", valid); + result.pushKV("min_amount", ValueFromAmount(proof.GetMinAmount())); + + return result; + }, + }; +} + +void RegisterRangeProofRPCCommands(CRPCTable& t) +{ + static const CRPCCommand commands[]{ + {"blsct", &verifyblsctbalanceproof}, + }; + for (const auto& c : commands) { + t.appendCommand(c.name, &c); + } +} + +Span GetRangeProofRPCCommands() +{ + static const CRPCCommand commands[]{ + {"blsct", &verifyblsctbalanceproof}, + }; + return commands; +} \ No newline at end of file diff --git a/src/blsct/signature.h b/src/blsct/signature.h index 5c1d1f9fc6ce8..2b38c7449cbf3 100644 --- a/src/blsct/signature.h +++ b/src/blsct/signature.h @@ -21,6 +21,9 @@ class Signature { public: Signature(); + Signature(const std::vector& vch) { + SetVch(vch); + }; static Signature Aggregate(const std::vector& sigs); diff --git a/src/blsct/tokens/rpc.cpp b/src/blsct/tokens/rpc.cpp index db2c662538012..6f79ea5f9d482 100644 --- a/src/blsct/tokens/rpc.cpp +++ b/src/blsct/tokens/rpc.cpp @@ -18,7 +18,7 @@ std::vector tokenInfoResult = { RPCResult{RPCResult::Type::OBJ_DYN, "metadata", "the token metadata", {{RPCResult::Type::STR, "xxxx", "value"}}}, RPCResult{RPCResult::Type::NUM, "maxSupply", "the token max supply"}, RPCResult{RPCResult::Type::NUM, "currentSupply", true, "the token current supply"}, - RPCResult{RPCResult::Type::OBJ_DYN, "mintedNft", "the nfts already minted", {{RPCResult::Type::OBJ_DYN, "index", "metadata", {{RPCResult::Type::STR, "xxxx", "value"}}}}}, + RPCResult{RPCResult::Type::OBJ_DYN, "mintedNft", true, "the nfts already minted", {{RPCResult::Type::OBJ_DYN, "index", "metadata", {{RPCResult::Type::STR, "xxxx", "value"}}}}}, }; diff --git a/src/blsct/wallet/balance_proof.cpp b/src/blsct/wallet/balance_proof.cpp new file mode 100644 index 0000000000000..d41904e10e395 --- /dev/null +++ b/src/blsct/wallet/balance_proof.cpp @@ -0,0 +1,6 @@ +#include +#include + +namespace blsct { +// Implementation is in the header file since it's all inline +} // namespace blsct diff --git a/src/blsct/wallet/balance_proof.h b/src/blsct/wallet/balance_proof.h new file mode 100644 index 0000000000000..0ca73b6dd7700 --- /dev/null +++ b/src/blsct/wallet/balance_proof.h @@ -0,0 +1,123 @@ +// src/blsct/wallet/balance_proof.h +#ifndef BITCOIN_BLSCT_WALLET_BALANCE_PROOF_H +#define BITCOIN_BLSCT_WALLET_BALANCE_PROOF_H + +#include +#include +#include +#include +#include +#include + +namespace blsct { + +class BalanceProof +{ +private: + std::vector m_outpoints; + CAmount m_min_amount; + bulletproofs_plus::RangeProof m_proof; + blsct::Signature m_signature; + uint16_t m_index; + +public: + BalanceProof() = default; + BalanceProof(const std::vector& outpoints, CAmount min_amount, const bulletproofs_plus::RangeProof& proof, const blsct::Signature& signature) + : m_outpoints(outpoints), m_min_amount(min_amount), m_proof(proof), m_signature(signature), m_index(0) {} + + BalanceProof(const std::vector& outpoints, CAmount min_amount, const wallet::CWallet& wallet, const blsct::Message& additional_commitment) + { + m_outpoints = outpoints; + m_min_amount = min_amount; + + // Sum up all commitments from the outputs + MclScalar value = 0; + MclScalar gamma = 0; + + if (outpoints.empty()) { + throw std::runtime_error("No outpoints provided"); + } + + const auto& blsct_km = const_cast(wallet).GetOrCreateBLSCTKeyMan(); + blsct::PrivateKey private_key; + bool has_private_key = false; + uint16_t index = 0; + + for (const auto& outpoint : outpoints) { + const wallet::CWalletTx* wtx = wallet.GetWalletTx(outpoint.hash); + if (!wtx) { + throw std::runtime_error("Outpoint not found in wallet"); + } + + if (outpoint.n >= wtx->tx->vout.size()) { + throw std::runtime_error("Invalid output index"); + } + const CTxOut& txout = wtx->tx->vout[outpoint.n]; + if (!has_private_key) { + has_private_key = blsct_km->GetSpendingKeyForOutput(txout, private_key); + m_index = index; + } + if (!txout.HasBLSCTRangeProof()) { + throw std::runtime_error("Outpoint does not have BLSCT range proof"); + } + auto recoveryData = wtx->GetBLSCTRecoveryData(outpoint.n); + value = value + MclScalar(recoveryData.amount); + gamma = gamma + MclScalar(recoveryData.gamma); + index++; + } + + // Create range proof + bulletproofs_plus::RangeProofLogic prover; + range_proof::GammaSeed nonce(Elements{1, gamma}); + std::vector message; + m_proof = prover.Prove({1, value}, nonce, message, TokenId(), MclScalar(min_amount)); + m_signature = private_key.Sign(additional_commitment); + } + + const std::vector& GetOutpoints() const { return m_outpoints; } + CAmount GetMinAmount() const { return m_min_amount; } + const bulletproofs_plus::RangeProof& GetProof() const { return m_proof; } + + bool Verify(const CCoinsViewCache& view, const blsct::Message& additional_commitment) const + { + // Sum up all commitments from the outputs + MclG1Point sum_commitment; + MclG1Point public_key; + uint16_t index = 0; + for (const auto& outpoint : m_outpoints) { + Coin coin; + if (!view.GetCoin(outpoint, coin)) { + return false; + } + if (!coin.out.HasBLSCTRangeProof()) { + return false; + } + if (index == m_index) { + public_key = coin.out.blsctData.spendingKey; + } + sum_commitment = sum_commitment + coin.out.blsctData.rangeProof.Vs[0]; + index++; + } + + const_cast&>(m_proof).Vs.Clear(); + const_cast&>(m_proof).Vs.Add(sum_commitment); + + // Create a range proof with seed for verification + bulletproofs_plus::RangeProofWithSeed proof(m_proof, TokenId(), MclScalar(m_min_amount)); + std::vector> proofs; + proofs.push_back(proof); + + // Verify the range proof + bulletproofs_plus::RangeProofLogic prover; + return prover.Verify(proofs) && blsct::PublicKey(public_key).Verify(additional_commitment, m_signature); + } + + SERIALIZE_METHODS(BalanceProof, obj) + { + READWRITE(obj.m_outpoints, obj.m_min_amount, obj.m_proof, obj.m_signature, obj.m_index); + } +}; + +} // namespace blsct + +#endif // BITCOIN_BLSCT_WALLET_BALANCE_PROOF_H \ No newline at end of file diff --git a/src/blsct/wallet/keyman.cpp b/src/blsct/wallet/keyman.cpp index 19866868e954e..db4c120a93014 100644 --- a/src/blsct/wallet/keyman.cpp +++ b/src/blsct/wallet/keyman.cpp @@ -4,6 +4,7 @@ #include #include +#include