diff --git a/.cirrus.yml b/.cirrus.yml index 022f0c3609326..89d81e705a556 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -72,10 +72,9 @@ compute_credits_template: &CREDITS_TEMPLATE task: name: 'lint' << : *BASE_TEMPLATE - container: - image: debian:bookworm - cpu: 1 - memory: 1G + persistent_worker: + labels: + type: small # For faster CI feedback, immediately schedule the linters << : *CREDITS_TEMPLATE test_runner_cache: diff --git a/.gitignore b/.gitignore index 1bcd56736aed2..af9afa9f269e8 100644 --- a/.gitignore +++ b/.gitignore @@ -147,3 +147,4 @@ compile_commands.json # others null.d +libnavioconsensus.pc diff --git a/doc/bech32-mod-gen-poly.md b/doc/bech32-mod-gen-poly.md index 7117937570bbe..d92b8e082f8c4 100644 --- a/doc/bech32-mod-gen-poly.md +++ b/doc/bech32-mod-gen-poly.md @@ -5,7 +5,7 @@ We made modification to bech32 implementation of Bitcoin so that it worked with To accomplish that, we replaced the 6-degree generator polynomial originally used by bech32 by an 8-degree one as [Bitcoin Cash's cashaddr implementation](https://github.com/bitcoin-cash-node/bitcoin-cash-node/blob/master/src/cashaddr.cpp) and [Jamtis](https://gist.github.com/tevador/50160d160d24cfc6c52ae02eb3d17024) of Monero have done. -In order to find an 8-degree polynomial for our need, we followed the Jamtis polynomial search procedure which is explained in detail in [this document](https://gist.github.com/tevador/5b3fbbd0877a3412ede07263c6b2663d) with a little modificaiton to meet our requirements. +In order to find an 8-degree polynomial for our need, we followed the Jamtis polynomial search procedure which is explained in detail in [this document](https://gist.github.com/tevador/5b3fbbd0877a3412ede07263c6b2663d) with a little modification to meet our requirements. Here are the requirements we had: diff --git a/src/Makefile.am b/src/Makefile.am index 907a83aea8c92..9fb4d3c97b5b0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,7 @@ BENCHMARKS = BLS_INCLUDES=-I$(srcdir)/bls/include -I$(srcdir)/bls/mcl/include -I$(srcdir)/bls/mcl/src -BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include $(BLS_INCLUDES) -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) +BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include $(BLS_INCLUDES) -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) -I$(srcdir)/blsct/external_api LIBBITCOIN_NODE=libbitcoin_node.a LIBBITCOIN_COMMON=libbitcoin_common.a @@ -169,6 +169,7 @@ BLSCT_H = \ blsct/double_public_key.h \ blsct/eip_2333/bls12_381_keygen.h \ blsct/external_api/blsct.h \ + blsct/key_io.h \ blsct/private_key.h \ blsct/public_key.h \ blsct/public_keys.h \ @@ -204,6 +205,7 @@ BLSCT_H = \ blsct/wallet/keyman.h \ blsct/wallet/keyring.h \ blsct/wallet/txfactory.h \ + blsct/wallet/txfactory_base.h \ blsct/wallet/txfactory_global.h \ blsct/wallet/verification.h @@ -211,6 +213,7 @@ BLSCT_CPP = \ blsct/arith/elements.cpp \ blsct/arith/mcl/mcl_g1point.cpp \ blsct/arith/mcl/mcl_scalar.cpp \ + blsct/bech32_mod.cpp \ blsct/building_block/g_h_gi_hi_zero_verifier.cpp \ blsct/building_block/generator_deriver.cpp \ blsct/building_block/imp_inner_prod_arg.cpp \ @@ -221,6 +224,7 @@ BLSCT_CPP = \ blsct/double_public_key.cpp \ blsct/eip_2333/bls12_381_keygen.cpp \ blsct/external_api/blsct.cpp \ + blsct/key_io.cpp \ blsct/pos/helpers.cpp \ blsct/pos/pos.cpp \ blsct/pos/proof_logic.cpp \ @@ -247,9 +251,15 @@ BLSCT_CPP = \ blsct/set_mem_proof/set_mem_proof_prover.cpp \ blsct/set_mem_proof/set_mem_proof_setup.cpp \ blsct/signature.cpp \ + blsct/wallet/address.cpp \ + blsct/wallet/helpers.cpp \ + blsct/wallet/keyman.cpp \ + blsct/wallet/keyring.cpp \ + blsct/wallet/txfactory.cpp \ + blsct/wallet/txfactory_base.cpp \ + blsct/wallet/txfactory_global.cpp \ blsct/wallet/verification.cpp - .PHONY: FORCE check-symbols check-security # navio core # BITCOIN_CORE_H = \ @@ -717,6 +727,7 @@ libbitcoin_wallet_a_SOURCES = \ blsct/wallet/keyman.cpp \ blsct/wallet/keyring.cpp \ blsct/wallet/txfactory.cpp \ + blsct/wallet/txfactory_base.cpp \ blsct/wallet/txfactory_global.cpp \ blsct/wallet/verification.cpp \ wallet/coincontrol.cpp \ @@ -933,6 +944,7 @@ libbitcoin_common_a_SOURCES = \ blsct/building_block/weighted_inner_prod_arg.cpp \ blsct/common.cpp \ blsct/double_public_key.cpp \ + blsct/key_io.cpp \ blsct/private_key.cpp \ blsct/public_key.cpp \ blsct/public_keys.cpp \ @@ -1423,16 +1435,65 @@ if BUILD_LIBBLSCT_ONLY LIBBLSCT = libblsct.a noinst_LIBRARIES = $(LIBBLSCT) libblsct_a_SOURCES = \ + blsct/external_api/blsct.cpp \ + common/args.cpp \ + common/system.cpp \ + common/url.cpp \ + crypto/hmac_sha256.cpp \ + crypto/ripemd160.cpp \ crypto/sha256_arm_shani.cpp \ crypto/sha256_avx2.cpp \ crypto/sha256_sse4.cpp \ crypto/sha256_sse41.cpp \ crypto/sha256_x86_shani.cpp \ crypto/sha256.cpp \ - blsct/external_api/blsct.cpp \ + primitives/transaction.cpp \ + rpc/util.cpp \ + script/interpreter.cpp \ + script/script.cpp \ + script/sign.cpp \ + script/signingprovider.cpp \ + support/cleanse.cpp \ + support/lockedpool.cpp \ + uint256.cpp \ + util/rbf.cpp \ + util/strencodings.cpp \ + wallet/coincontrol.cpp \ + wallet/context.cpp \ + wallet/crypter.cpp \ + wallet/db.cpp \ + wallet/dump.cpp \ + wallet/external_signer_scriptpubkeyman.cpp \ + wallet/feebumper.cpp \ + wallet/fees.cpp \ + wallet/interfaces.cpp \ + wallet/load.cpp \ + wallet/receive.cpp \ + wallet/rpc/addresses.cpp \ + wallet/rpc/backup.cpp \ + wallet/rpc/coins.cpp \ + wallet/rpc/encrypt.cpp \ + wallet/rpc/spend.cpp \ + wallet/rpc/signmessage.cpp \ + wallet/rpc/transactions.cpp \ + wallet/rpc/util.cpp \ + wallet/rpc/wallet.cpp \ + wallet/scriptpubkeyman.cpp \ + wallet/spend.cpp \ + wallet/transaction.cpp \ + wallet/wallet.cpp \ + wallet/walletdb.cpp \ + wallet/walletutil.cpp \ + wallet/coinselection.cpp \ $(BLSCT_CPP) + +LIBUNIVALUE_BLSCT = libunivalue_blsct.a +noinst_LIBRARIES += $(LIBUNIVALUE_BLSCT) +libunivalue_blsct_a_SOURCES = $(UNIVALUE_LIB_SOURCES_INT) $(UNIVALUE_DIST_HEADERS_INT) $(UNIVALUE_LIB_HEADERS_INT) $(UNIVALUE_TEST_FILES_INT) +libunivalue_blsct_a_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) + libblsct_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -libblsct_a_CPPFLAGS = $(AM_CPPFLAGS) $(BLS_INCLUDES) $(BOOST_CPPFLAGS) +libblsct_a_CPPFLAGS = $(AM_CPPFLAGS) $(BLS_INCLUDES) $(BOOST_CPPFLAGS) -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) if ENABLE_SSE41 libblsct_a_CXXFLAGS += $(SSE41_CXXFLAGS) @@ -1454,7 +1515,7 @@ libblsct_a_CXXFLAGS += $(ARM_SHANI_CXXFLAGS) libblsct_a_CPPFLAGS += -DENABLE_ARM_SHANI endif -$(LIBBLSCT): $(LIBBLS) $(LIBMCL) +$(LIBBLSCT): $(LIBBLS) $(LIBMCL) $(LIBUNIVALUE_BLSCT) CLEANFILES += $(LIBBLSCT) blsct/include/libblsct_a-blsct.o endif diff --git a/src/blsct/arith/mcl/mcl.h b/src/blsct/arith/mcl/mcl.h index 00ba3d7e92c39..68769af1cf48e 100644 --- a/src/blsct/arith/mcl/mcl.h +++ b/src/blsct/arith/mcl/mcl.h @@ -10,6 +10,7 @@ #define NAVIO_BLSCT_ARITH_MCL_MCL_H #include +#include #include #include @@ -21,6 +22,7 @@ struct Mcl { using Scalar = MclScalar; using Point = MclG1Point; using Util = MclUtil; + using Init = MclInit; }; #endif // NAVIO_BLSCT_ARITH_MCL_MCL_H diff --git a/src/blsct/external_api/blsct.cpp b/src/blsct/external_api/blsct.cpp index f0bef0eb7c8e7..be8624a5bc9de 100644 --- a/src/blsct/external_api/blsct.cpp +++ b/src/blsct/external_api/blsct.cpp @@ -1,24 +1,1277 @@ +#include +#include +#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include #include +#include +#include +#include +#include +#include + +static std::string g_chain; +static std::mutex g_init_mutex; +static std::mutex g_set_chain_mutex; +static bulletproofs::RangeProofLogic* g_rpl; +static bool g_is_little_endian; + +const std::function G_TRANSLATION_FUN = nullptr; +UrlDecodeFn* const URL_DECODE = urlDecode; extern "C" { -void BlsctInit() { - MclInit for_side_effect_only; +static bool is_little_endian() { + uint16_t n = 1; + uint8_t* p = (uint8_t*) &n; + return *p == 1; +} + +void init() +{ + std::lock_guard lock(g_init_mutex); + + Mcl::Init for_side_effect_only; + + g_chain = blsct::bech32_hrp::Main; + g_is_little_endian = is_little_endian(); + g_rpl = new(std::nothrow) bulletproofs::RangeProofLogic(); +} + +bool set_chain(enum Chain chain) +{ + std::lock_guard lock(g_set_chain_mutex); + if (!g_chain.empty()) { + return false; + } + + switch (chain) { + case MainNet: + g_chain = blsct::bech32_hrp::Main; + break; + + case TestNet: + g_chain = blsct::bech32_hrp::TestNet; + break; + + case SigNet: + g_chain = blsct::bech32_hrp::SigNet; + break; + + case RegTest: + g_chain = blsct::bech32_hrp::RegTest; + break; + } + return true; +} + +BlsctRetVal* succ( + void* value, + size_t value_size +) { + MALLOC(BlsctRetVal, p); + RETURN_IF_MEM_ALLOC_FAILED(p); + + p->result = BLSCT_SUCCESS; + p->value = value; + p->value_size = value_size; + return p; +} + +BlsctRetVal* err( + BLSCT_RESULT result +) { + MALLOC(BlsctRetVal, p); + RETURN_IF_MEM_ALLOC_FAILED(p); + + p->result = result; + p->value = nullptr; + return p; +} + +BlsctBoolRetVal* succ_bool( + const bool value +) { + MALLOC(BlsctBoolRetVal, p); + RETURN_IF_MEM_ALLOC_FAILED(p); + + p->result = BLSCT_SUCCESS; + p->value = value; + return p; +} + +BlsctBoolRetVal* err_bool( + const BLSCT_RESULT result +) { + MALLOC(BlsctBoolRetVal, p); + RETURN_IF_MEM_ALLOC_FAILED(p); + + p->result = result; + p->value = false; + return p; +} + +void free_obj(void* x) { + if (x != nullptr) free(x); +} + +void free_amounts_ret_val(BlsctAmountsRetVal* rv) { + auto result_vec = static_cast*>(rv->value); + + for(auto res: *result_vec) { + free(res.msg); + } + delete result_vec; + free(rv); +} + +BlsctRetVal* gen_random_point() { + MALLOC(BlsctPoint, blsct_point); + RETURN_IF_MEM_ALLOC_FAILED(blsct_point); + + auto x = Point::Rand(); + SERIALIZE_AND_COPY(x, blsct_point); + + return succ(blsct_point, POINT_SIZE); +} + +BlsctRetVal* gen_random_scalar() { + MALLOC(BlsctScalar, blsct_scalar); + RETURN_IF_MEM_ALLOC_FAILED(blsct_scalar); + + auto x = Scalar::Rand(true); + SERIALIZE_AND_COPY(x, blsct_scalar); + + return succ(blsct_scalar, SCALAR_SIZE); +} + +BlsctRetVal* gen_scalar( + const uint64_t n +) { + Scalar scalar(n); + MALLOC(BlsctScalar, blsct_scalar); + RETURN_IF_MEM_ALLOC_FAILED(blsct_scalar); + SERIALIZE_AND_COPY(scalar, blsct_scalar); + + return succ(blsct_scalar, SCALAR_SIZE); +} + +uint64_t scalar_to_uint64(const BlsctScalar* blsct_scalar) +{ + Scalar scalar; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_scalar, SCALAR_SIZE, scalar); + return scalar.GetUint64(); +} + +BlsctRetVal* gen_random_public_key() { + auto vec = Point::Rand().GetVch(); + blsct::PublicKey pub_key(vec); + + MALLOC(BlsctPubKey, blsct_pub_key); + RETURN_IF_MEM_ALLOC_FAILED(blsct_pub_key); + SERIALIZE_AND_COPY(pub_key, blsct_pub_key); + + return succ(blsct_pub_key, PUBLIC_KEY_SIZE); +} + +const char* point_to_hex(const BlsctPoint* blsct_point) { + Point point; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_point, POINT_SIZE, point); + auto hex = point.GetString(); + + size_t BUF_SIZE = hex.size() + 1; + MALLOC_BYTES(char, hex_buf, BUF_SIZE); + RETURN_ERR_IF_MEM_ALLOC_FAILED(hex_buf); + std::memcpy(hex_buf, hex.c_str(), BUF_SIZE); // also copies null at the end + + return hex_buf; +} + +const char* scalar_to_hex(const BlsctScalar* blsct_scalar) { + Scalar scalar; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_scalar, SCALAR_SIZE, scalar); + auto hex = scalar.GetString(); + + size_t BUF_SIZE = hex.size() + 1; + MALLOC_BYTES(char, hex_buf, BUF_SIZE); + RETURN_ERR_IF_MEM_ALLOC_FAILED(hex_buf); + std::memcpy(hex_buf, hex.c_str(), BUF_SIZE); // also copies null at the end + + return hex_buf; +} + +BlsctRetVal* decode_address( + const char* blsct_enc_addr +) { + try { + if (strlen(blsct_enc_addr) != ENCODED_DPK_STR_SIZE) { + return err(BLSCT_BAD_DPK_SIZE); + } + std::string enc_addr(blsct_enc_addr); + auto maybe_dpk = blsct::DecodeDoublePublicKey(g_chain, enc_addr); + if (maybe_dpk) { + auto dpk = maybe_dpk.value(); + if (dpk.IsValid()) { + auto buf = dpk.GetVch(); + MALLOC(BlsctDoublePubKey, dec_addr); + RETURN_ERR_IF_MEM_ALLOC_FAILED(dec_addr); + std::memcpy(dec_addr, &buf[0], DOUBLE_PUBLIC_KEY_SIZE); + + return succ(dec_addr, DOUBLE_PUBLIC_KEY_SIZE); + } + } + } catch(...) {} + + return err(BLSCT_EXCEPTION); +} + +BlsctRetVal* encode_address( + const void* void_blsct_dpk, + const enum AddressEncoding encoding +) { + if (encoding != Bech32 && encoding != Bech32M) { + return err(BLSCT_UNKNOWN_ENCODING); + } + try { + UNVOID(BlsctDoublePubKey, blsct_dpk); + + auto blsct_dpk_u8 = U8C(blsct_dpk); + std::vector dpk_vec(blsct_dpk_u8, blsct_dpk_u8 + sizeof(BlsctDoublePubKey)); + auto dpk = blsct::DoublePublicKey(dpk_vec); + + auto bech32_encoding = encoding == Bech32 ? + bech32_mod::Encoding::BECH32 : bech32_mod::Encoding::BECH32M; + auto enc_dpk_str = EncodeDoublePublicKey(g_chain, bech32_encoding, dpk); + size_t BUF_SIZE = enc_dpk_str.size() + 1; + MALLOC_BYTES(char, enc_addr, BUF_SIZE); + RETURN_ERR_IF_MEM_ALLOC_FAILED(enc_addr); + std::memcpy(enc_addr, enc_dpk_str.c_str(), BUF_SIZE); // also copies null at the end + + return succ(enc_addr, BUF_SIZE); + + } catch(...) {} + + return err(BLSCT_EXCEPTION); +} + +BlsctRetVal* gen_double_pub_key( + const BlsctPubKey* blsct_pk1, + const BlsctPubKey* blsct_pk2 +) { + auto blsct_pk1_u8 = U8C(blsct_pk1); + auto blsct_pk2_u8 = U8C(blsct_pk2); + + blsct::PublicKey pk1, pk2; + std::vector blsct_pk1_vec { + blsct_pk1_u8, + blsct_pk1_u8 + blsct::PublicKey::SIZE + }; + std::vector blsct_pk2_vec { + blsct_pk2_u8, + blsct_pk2_u8 + blsct::PublicKey::SIZE + }; + pk1.SetVch(blsct_pk1_vec); + pk2.SetVch(blsct_pk2_vec); + + MALLOC(BlsctDoublePubKey, blsct_dpk); + RETURN_ERR_IF_MEM_ALLOC_FAILED(blsct_dpk); + blsct::DoublePublicKey dpk(pk1, pk2); + SERIALIZE_AND_COPY(dpk, blsct_dpk); + + return succ(blsct_dpk, sizeof(BlsctDoublePubKey)); +} + +BlsctRetVal* gen_token_id_with_subid( + const uint64_t token, + const uint64_t subid +) { + uint256 token_uint256; + auto data = token_uint256.data(); + uint64_t n = token; + for (size_t i=0; i<8; i++) { + data[i] = n & 0xFF; + n >>= 8; // Shift the value right by 8 bits to process the next byte + } + TokenId token_id(token_uint256, subid); + MALLOC(BlsctTokenId, blsct_token_id); + RETURN_IF_MEM_ALLOC_FAILED(blsct_token_id); + SERIALIZE_AND_COPY_WITH_STREAM(token_id, blsct_token_id); + + return succ(blsct_token_id, TOKEN_ID_SIZE); +} + +BlsctRetVal* gen_token_id( + const uint64_t token +) { + return gen_token_id_with_subid( + token, + UINT64_MAX + ); } -void BlsctTest() { +BlsctRetVal* gen_default_token_id() { + TokenId token_id; + MALLOC(BlsctTokenId, blsct_token_id); + RETURN_IF_MEM_ALLOC_FAILED(blsct_token_id); + SERIALIZE_AND_COPY_WITH_STREAM(token_id, blsct_token_id); - Mcl::Scalar a(1); - Mcl::Scalar b(2); + return succ(blsct_token_id, TOKEN_ID_SIZE); +} + +uint64_t get_token_id_token(const BlsctTokenId* blsct_token_id) { + TokenId token_id; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_token_id, TOKEN_ID_SIZE, token_id); + return token_id.token.GetUint64(0); +} + +uint64_t get_token_id_subid(const BlsctTokenId* blsct_token_id) { + TokenId token_id; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_token_id, TOKEN_ID_SIZE, token_id); + return token_id.subid; +} + +BlsctRetVal* build_range_proof( + const void* vp_uint64_vec, + const BlsctPoint* blsct_nonce, + const char* blsct_msg, + const BlsctTokenId* blsct_token_id +) { + try { + auto uint64_vec = static_cast*>(vp_uint64_vec); + // uint64_t to Scalar + Scalars vs; + for (uint64_t v : *uint64_vec) { + if (v > INT64_MAX) { + return err(BLSCT_VALUE_OUTSIDE_THE_RANGE); + } + Mcl::Scalar x(static_cast(v)); + vs.Add(x); + } + + // blsct_nonce to nonce + Mcl::Point nonce = Mcl::Point::GetBasePoint(); + auto blsct_nonce_u8 = U8C(blsct_nonce); + std::vector ser_point( + blsct_nonce_u8, blsct_nonce_u8 + POINT_SIZE + ); + nonce.SetVch(ser_point); + + // blsct_message to message + std::string msg(blsct_msg); + std::vector msg_vec(msg.begin(), msg.end()); + + // blsct_token_id to token_id + TokenId token_id; + auto blsct_token_id_u8 = U8C(blsct_token_id); + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_token_id_u8, TOKEN_ID_SIZE, token_id); + + // range_proof to blsct_range_proof + auto range_proof = g_rpl->Prove( + vs, + nonce, + msg_vec, + token_id + ); + MALLOC(BlsctRangeProof, blsct_range_proof); + RETURN_ERR_IF_MEM_ALLOC_FAILED(blsct_range_proof); + SERIALIZE_AND_COPY_WITH_STREAM(range_proof, blsct_range_proof); + return succ(blsct_range_proof, RANGE_PROOF_SIZE); + + } catch(...) {} + + return err(BLSCT_EXCEPTION); +} + +BlsctBoolRetVal* verify_range_proofs( + const void* vp_range_proofs +) { + try { + auto range_proofs = static_cast>*>(vp_range_proofs); + + std::vector> range_proof_w_seeds; + + for(const auto& rp: *range_proofs) { + auto rp_w_seed = bulletproofs::RangeProofWithSeed(rp); + range_proof_w_seeds.push_back(rp_w_seed); + } + bool is_valid = g_rpl->Verify(range_proof_w_seeds); + return succ_bool(is_valid); + + } catch(...) {} + + return err_bool(BLSCT_EXCEPTION); +} + +BlsctAmountRecoveryReq* gen_recover_amount_req( + const void* vp_blsct_range_proof, + const void* vp_blsct_nonce +) { + auto req = new(std::nothrow) BlsctAmountRecoveryReq; + RETURN_IF_MEM_ALLOC_FAILED(req); + BLSCT_COPY(vp_blsct_range_proof, req->range_proof); + BLSCT_COPY(vp_blsct_nonce, req->nonce); + return req; +} + +BlsctAmountsRetVal* recover_amount( + void* vp_amt_recovery_req_vec +) { + MALLOC(BlsctAmountsRetVal, rv); + RETURN_IF_MEM_ALLOC_FAILED(rv); + try { + auto amt_recovery_req_vec = + static_cast*>(vp_amt_recovery_req_vec); + + // construct AmountRecoveryRequest vector + std::vector> reqs; + + for (auto ar_req: *amt_recovery_req_vec) { + bulletproofs::RangeProof range_proof; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(ar_req.range_proof, RANGE_PROOF_SIZE, range_proof); + + Mcl::Point nonce; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(ar_req.nonce, POINT_SIZE, nonce); + + auto req = bulletproofs::AmountRecoveryRequest::of( + range_proof, + nonce + ); + reqs.push_back(req); + } + + // try recover amount for all requests + // vector containing only the successful results is returned + auto recovery_results = g_rpl->RecoverAmounts(reqs); + + // return error if it failed in the middle + if (!recovery_results.is_completed) { + rv->result = BLSCT_DID_NOT_RUN_TO_COMPLETION; + return rv; + } + + // the vector to return has the same size as the request vector + auto result_vec = new(std::nothrow) std::vector; + RETURN_ERR_IF_MEM_ALLOC_FAILED(result_vec); + result_vec->resize(amt_recovery_req_vec->size()); + + // mark all the results as failed + for(auto result: *result_vec) { + result.is_succ = false; + } + + // write successful recovery results to the corresponding + // index of the return vector + for(size_t i=0; iat(succ_res.id); + + // mark the result as success and set the amount + result.is_succ = true; + + // write amount to the result + result.amount = succ_res.amount; + + // write message to the result + result.msg = (char*) malloc(succ_res.message.size() + 1); + std::memcpy( + result.msg, + succ_res.message.c_str(), + succ_res.message.size() + 1 + ); + + // gamma is omitted since it's a scalar + } + + rv->result = BLSCT_SUCCESS; + rv->value = TO_VOID(result_vec); + return rv; + + } catch(...) {} - auto c = a + b; + rv->result = BLSCT_EXCEPTION; + return rv; +} + +BlsctRetVal* gen_out_point( + const char* tx_id_c_str, + const uint32_t out_index +) { + MALLOC(BlsctOutPoint, blsct_out_point); + RETURN_IF_MEM_ALLOC_FAILED(blsct_out_point); + + std::string tx_id_str(tx_id_c_str, TX_ID_STR_LEN); + + auto tx_id = TxidFromString(tx_id_str); + COutPoint out_point { tx_id, out_index }; + + SERIALIZE_AND_COPY_WITH_STREAM( + out_point, + blsct_out_point + ); + return succ(blsct_out_point, OUT_POINT_SIZE); +} + +BlsctRetVal* build_tx_in( + const uint64_t amount, + const uint64_t gamma, + const BlsctScalar* spending_key, + const BlsctTokenId* token_id, + const BlsctOutPoint* out_point, + const bool rbf +) { + MALLOC(BlsctTxIn, tx_in); + RETURN_IF_MEM_ALLOC_FAILED(tx_in); + + tx_in->amount = amount; + tx_in->gamma = gamma; + BLSCT_COPY(spending_key, tx_in->spending_key); + BLSCT_COPY(token_id, tx_in->token_id); + BLSCT_COPY(out_point, tx_in->out_point); + tx_in->rbf = rbf; + + return succ(tx_in, sizeof(BlsctTxIn)); +} + +BlsctRetVal* dpk_to_sub_addr( + const void* blsct_dpk +) { + // unserialize double public key + blsct::DoublePublicKey dpk; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM( + blsct_dpk, DOUBLE_PUBLIC_KEY_SIZE, dpk + ); + + // create sub address from dpk + blsct::SubAddress sub_addr(dpk); + + // allocate memory for serialized sub address + MALLOC(BlsctSubAddr, blsct_sub_addr); + RETURN_IF_MEM_ALLOC_FAILED(blsct_sub_addr); + + // serialize sub address + SERIALIZE_AND_COPY_WITH_STREAM(sub_addr, blsct_sub_addr); + + return succ(blsct_sub_addr, sizeof(blsct::SubAddress)); +} + +BlsctRetVal* build_tx_out( + const BlsctSubAddr* blsct_dest, + const uint64_t amount, + const char* in_memo_c_str, + const BlsctTokenId* blsct_token_id, + const TxOutputType output_type, + const uint64_t min_stake +) { + MALLOC(BlsctTxOut, tx_out); + RETURN_IF_MEM_ALLOC_FAILED(tx_out); + + BLSCT_COPY(blsct_dest, tx_out->dest); + tx_out->amount = amount; + + // copy memo to tx_out + size_t in_memo_c_str_len = std::strlen(in_memo_c_str); + if (in_memo_c_str_len > MAX_MEMO_LEN) { + return err(BLSCT_MEMO_TOO_LONG); + } + std::memcpy(tx_out->memo_c_str, in_memo_c_str, in_memo_c_str_len + 1); - auto s = c.GetString(); + BLSCT_COPY(blsct_token_id, tx_out->token_id); + tx_out->output_type = output_type; + tx_out->min_stake = min_stake; - std::cout << "The answer is " << s << std::endl; + return succ(tx_out, sizeof(BlsctTxOut)); +} + +static blsct::PrivateKey blsct_scalar_to_priv_key( + const BlsctScalar* blsct_scalar +) { + // unserialize blsct_scalar to Scalar + Scalar scalar; + auto u8_blsct_scalar = U8C(blsct_scalar); + std::vector vec {u8_blsct_scalar, u8_blsct_scalar + SCALAR_SIZE}; + scalar.SetVch(vec); + + // build private key from the scalar + blsct::PrivateKey priv_key(scalar); + return priv_key; +} + +BlsctTxRetVal* build_tx( + const void* void_tx_ins, + const void* void_tx_outs +) { + UNVOID(std::vector, tx_ins); + UNVOID(std::vector, tx_outs); + + blsct::TxFactoryBase psbt; + MALLOC(BlsctTxRetVal, rv); + RETURN_IF_MEM_ALLOC_FAILED(rv); + + for (size_t i=0; isize(); ++i) { + // unserialize tx_in fields and add to TxFactoryBase + const BlsctTxIn& tx_in = tx_ins->at(i); + + // check if the amount is within the range + // amount is uint64_t and not serialized + if (tx_in.amount > std::numeric_limits::max()) { + rv->result = BLSCT_IN_AMOUNT_ERROR; + rv->in_amount_err_index = i; + return rv; + } + + // gamma is uint64_t and not serialized + Scalar gamma(tx_in.gamma); + + // unserialize spending_key + blsct::PrivateKey spending_key = + blsct_scalar_to_priv_key(&tx_in.spending_key); + + // unserialize token_id + TokenId token_id; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM( + tx_in.token_id, TOKEN_ID_SIZE, token_id + ); + + // unserialize out_point + COutPoint out_point; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM( + tx_in.out_point, OUT_POINT_SIZE, out_point + ); + + // add all to TxFactoryBase + psbt.AddInput( + tx_in.amount, + gamma, + spending_key, + token_id, + out_point + ); + } + + for (size_t i=0; isize(); ++i) { + // unserialize tx_out fields and add to TxFactoryBase + const BlsctTxOut& tx_out = tx_outs->at(i); + + // check if the amount is within the range + // amount is uint64_t and not serialized + if (tx_out.amount > std::numeric_limits::max()) { + rv->result = BLSCT_OUT_AMOUNT_ERROR; + rv->out_amount_err_index = i; + return rv; + } + + // unserialize destination + blsct::DoublePublicKey dest; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM( + tx_out.dest, DOUBLE_PUBLIC_KEY_SIZE, dest + ); + + // create memo std::string from memo c_str + std::string memo_str(tx_out.memo_c_str); + + // unserialize token_id + TokenId token_id; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM( + tx_out.token_id, TOKEN_ID_SIZE, token_id + ); + + // create out_type from blsct::TxOutputType + blsct::CreateTransactionType out_type; + if (tx_out.output_type == TxOutputType::Normal) { + out_type = blsct::CreateTransactionType::NORMAL; + } else if (tx_out.output_type == TxOutputType::StakedCommitment) { + out_type = blsct::CreateTransactionType::STAKED_COMMITMENT; + } else { + rv->result = BLSCT_BAD_OUT_TYPE; + return rv; + } + + // add all to TxFactoryBase + psbt.AddOutput( + dest, + tx_out.amount, + memo_str, + token_id, + out_type, + tx_out.min_stake + ); + } + + // build tx + blsct::DoublePublicKey change_amt_dest; + auto maybe_tx = psbt.BuildTx(change_amt_dest); + if (!maybe_tx.has_value()) { + rv->result = BLSCT_FAILURE; + return rv; + } + auto tx = maybe_tx.value(); + + // serialize tx + DataStream st{}; + TransactionSerParams params { .allow_witness = true }; + ParamsStream ps {params, st}; + tx.Serialize(ps); + + // copy serialize tx to the result + rv->result = BLSCT_SUCCESS; + rv->ser_tx_size = st.size(); + rv->ser_tx = (uint8_t*) malloc(st.size()); + std::memcpy(rv->ser_tx, st.data(), st.size()); + + return rv; +} + +CMutableTransaction* deserialize_tx( + const uint8_t* ser_tx, + const size_t ser_tx_size +) { + CMutableTransaction* tx = static_cast( + malloc(sizeof(CMutableTransaction)) + ); + DataStream st{}; + TransactionSerParams params { .allow_witness = true }; + ParamsStream ps {params, st}; + + for(size_t i=0; iUnserialize(ps); + + return tx; +} + +// tx in +const std::vector* get_tx_ins(const CMutableTransaction* tx) { + return &tx->vin; +} + +size_t get_tx_ins_size(const std::vector* tx_ins) { + return tx_ins->size(); +} + +const BlsctRetVal* get_tx_in(const std::vector* tx_ins, const size_t i) { + auto tx_in = &tx_ins->at(i); + auto tx_in_size = sizeof(*tx_in); + auto tx_in_copy = static_cast(malloc(tx_in_size)); + std::memcpy(tx_in_copy, tx_in, tx_in_size); + return succ(tx_in_copy, tx_in_size); +} + +const BlsctScript* get_tx_in_script_sig(const CTxIn* tx_in) { + auto copy = static_cast(malloc(SCRIPT_SIZE)); + std::memcpy(copy, &tx_in->scriptSig, SCRIPT_SIZE); + return copy; +} + +uint32_t get_tx_in_sequence(const CTxIn* tx_in) { + return tx_in->nSequence; +} + +const BlsctScript* get_tx_in_script_witness(const CTxIn* tx_in) { + auto copy = static_cast(malloc(SCRIPT_SIZE)); + std::memcpy(copy, &tx_in->scriptWitness, SCRIPT_SIZE); + return copy; +} + +const BlsctTxId* get_tx_in_prev_out_hash(const CTxIn* tx_in) { + auto copy = static_cast(malloc(TX_ID_SIZE)); + std::memcpy(copy, &tx_in->prevout.hash, TX_ID_SIZE); + return copy; +} + +uint32_t get_tx_in_prev_out_n(const CTxIn* tx_in) { + return tx_in->prevout.n; +} + +// tx out +const std::vector* get_tx_outs(const CMutableTransaction* tx) { + return &tx->vout; +} + +size_t get_tx_outs_size(const std::vector* tx_outs) { + return tx_outs->size(); +} + +const BlsctRetVal* get_tx_out(const std::vector* tx_outs, const size_t i) { + auto tx_out = &tx_outs->at(i); + auto tx_out_size = sizeof(*tx_out); + auto tx_out_copy = static_cast(malloc(tx_out_size)); + std::memcpy(tx_out_copy, tx_out, tx_out_size); + return succ(tx_out_copy, tx_out_size); +} + +uint64_t get_tx_out_value(const CTxOut* tx_out) { + return tx_out->nValue; +} + +const BlsctScript* get_tx_out_script_pub_key(const CTxOut* tx_out) { + auto copy = static_cast(malloc(SCRIPT_SIZE)); + std::memcpy(copy, &tx_out->scriptPubKey, SCRIPT_SIZE); + return copy; +} + +const BlsctTokenId* get_tx_out_token_id(const CTxOut* tx_out) { + auto copy = static_cast(malloc(TOKEN_ID_SIZE)); + std::memcpy(copy, &tx_out->tokenId, TOKEN_ID_SIZE); + return copy; +} + +const BlsctScript* get_tx_out_script_pubkey(const CTxOut* tx_out) { + auto copy = static_cast(malloc(SCRIPT_SIZE)); + std::memcpy(copy, &tx_out->scriptPubKey, SCRIPT_SIZE); + return copy; +} + +const BlsctPoint* get_tx_out_spending_key(const CTxOut* tx_out) { + auto copy = static_cast(malloc(POINT_SIZE)); + auto org = tx_out->blsctData.spendingKey.GetVch(); + std::memcpy(copy, &org[0], POINT_SIZE); + return copy; +} + +const BlsctPoint* get_tx_out_ephemeral_key(const CTxOut* tx_out) { + auto copy = static_cast(malloc(POINT_SIZE)); + auto org = tx_out->blsctData.ephemeralKey.GetVch(); + std::memcpy(copy, &org[0], POINT_SIZE); + return copy; +} + +const BlsctPoint* get_tx_out_blinding_key(const CTxOut* tx_out) { + auto copy = static_cast(malloc(POINT_SIZE)); + auto org = tx_out->blsctData.blindingKey.GetVch(); + std::memcpy(copy, &org[0], POINT_SIZE); + return copy; +} + +uint16_t get_tx_out_view_tag(const CTxOut* tx_out) { + return tx_out->blsctData.viewTag; +} + +//// range proof + +const BlsctPoint* get_tx_out_range_proof_A(const CTxOut* tx_out) { + auto copy = static_cast(malloc(POINT_SIZE)); + auto org = tx_out->blsctData.rangeProof.A.GetVch(); + std::memcpy(copy, &org[0], POINT_SIZE); + return copy; +} + +const BlsctPoint* get_tx_out_range_proof_S(const CTxOut* tx_out) { + auto copy = static_cast(malloc(POINT_SIZE)); + auto org = tx_out->blsctData.rangeProof.S.GetVch(); + std::memcpy(copy, &org[0], POINT_SIZE); + return copy; +} + +const BlsctPoint* get_tx_out_range_proof_T1(const CTxOut* tx_out) { + auto copy = static_cast(malloc(POINT_SIZE)); + auto org = tx_out->blsctData.rangeProof.T1.GetVch(); + std::memcpy(copy, &org[0], POINT_SIZE); + return copy; +} + +const BlsctPoint* get_tx_out_range_proof_T2(const CTxOut* tx_out) { + auto copy = static_cast(malloc(POINT_SIZE)); + auto org = tx_out->blsctData.rangeProof.T2.GetVch(); + std::memcpy(copy, &org[0], POINT_SIZE); + return copy; +} + +const BlsctScalar* get_tx_out_range_proof_mu(const CTxOut* tx_out) { + auto copy = static_cast(malloc(SCALAR_SIZE)); + auto org = tx_out->blsctData.rangeProof.mu.GetVch(); + std::memcpy(copy, &org[0], SCALAR_SIZE); + return copy; +} + +const BlsctScalar* get_tx_out_range_proof_a(const CTxOut* tx_out) { + auto copy = static_cast(malloc(SCALAR_SIZE)); + auto org = tx_out->blsctData.rangeProof.a.GetVch(); + std::memcpy(copy, &org[0], SCALAR_SIZE); + return copy; +} + +const BlsctScalar* get_tx_out_range_proof_b(const CTxOut* tx_out) { + auto copy = static_cast(malloc(SCALAR_SIZE)); + auto org = tx_out->blsctData.rangeProof.b.GetVch(); + std::memcpy(copy, &org[0], SCALAR_SIZE); + return copy; +} + +const BlsctScalar* get_tx_out_range_proof_t_hat(const CTxOut* tx_out) { + auto copy = static_cast(malloc(SCALAR_SIZE)); + auto org = tx_out->blsctData.rangeProof.t_hat.GetVch(); + std::memcpy(copy, &org[0], SCALAR_SIZE); + return copy; +} + +const BlsctSignature* sign_message( + const BlsctScalar* blsct_priv_key, + const char* blsct_msg +) { + Scalar scalar_priv_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM( + blsct_priv_key, SCALAR_SIZE, scalar_priv_key + ); + auto priv_key = blsct::PrivateKey(scalar_priv_key); + + std::string msg_str(blsct_msg); + blsct::Message msg(msg_str.begin(), msg_str.end()); + blsct::Signature sig = priv_key.Sign(msg); + + BlsctSignature* blsct_sig = static_cast( + malloc(SIGNATURE_SIZE) + ); + SERIALIZE_AND_COPY(sig, blsct_sig); + + return blsct_sig; +} + +bool verify_msg_sig( + const BlsctPubKey *blsct_pub_key, + const char* blsct_msg, + const BlsctSignature* blsct_signature +) { + blsct::PublicKey pub_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_pub_key, PUBLIC_KEY_SIZE, pub_key); + + std::string msg_str(blsct_msg); + blsct::Message msg(msg_str.begin(), msg_str.end()); + + blsct::Signature signature; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_signature, SIGNATURE_SIZE, signature); + + return pub_key.Verify(msg, signature); +} + +BlsctPubKey* scalar_to_pub_key( + const BlsctScalar* blsct_scalar +) { + Scalar scalar; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM( + blsct_scalar, SCALAR_SIZE, scalar + ); + auto priv_key = blsct::PrivateKey(scalar); + auto pub_key = priv_key.GetPublicKey(); + + BlsctPubKey* blsct_pub_key = static_cast( + malloc(PUBLIC_KEY_SIZE) + ); + SERIALIZE_AND_COPY(pub_key, blsct_pub_key); + return blsct_pub_key; +} + +BlsctScalar* from_seed_to_child_key( + const BlsctScalar* blsct_seed +) { + Scalar seed; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_seed, SCALAR_SIZE, seed); + + auto child_key = blsct::FromSeedToChildKey(seed); + BlsctScalar* blsct_child_key = static_cast( + malloc(SCALAR_SIZE) + ); + SERIALIZE_AND_COPY(child_key, blsct_child_key); + + return blsct_child_key; +} + +BlsctScalar* from_child_key_to_blinding_key( + const BlsctScalar* blsct_child_key +) { + Scalar child_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_child_key, SCALAR_SIZE, child_key); + + Scalar blinding_key = blsct::FromChildToBlindingKey(child_key); + BlsctScalar* blsct_blinding_key = static_cast( + malloc(SCALAR_SIZE) + ); + SERIALIZE_AND_COPY(blinding_key, blsct_blinding_key); + + return blsct_blinding_key; +} + +BlsctScalar* from_child_key_to_token_key( + const BlsctScalar* blsct_child_key +) { + Scalar child_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_child_key, SCALAR_SIZE, child_key); + + auto token_key = blsct::FromChildToTokenKey(child_key); + BlsctScalar* blsct_token_key = static_cast( + malloc(SCALAR_SIZE) + ); + SERIALIZE_AND_COPY(token_key, blsct_token_key); + + return blsct_token_key; +} + +BlsctScalar* from_child_key_to_tx_key( + const BlsctScalar* blsct_child_key +) { + Scalar child_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_child_key, SCALAR_SIZE, child_key); + + auto tx_key = blsct::FromChildToTransactionKey(child_key); + BlsctScalar* blsct_tx_key = static_cast( + malloc(SCALAR_SIZE) + ); + SERIALIZE_AND_COPY(tx_key, blsct_tx_key); + + return blsct_tx_key; +} + +BlsctScalar* from_tx_key_to_view_key( + const BlsctScalar* blsct_tx_key +) { + Scalar tx_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_tx_key, SCALAR_SIZE, tx_key); + + auto view_key = blsct::FromTransactionToViewKey(tx_key); + BlsctScalar* blsct_view_key = static_cast( + malloc(SCALAR_SIZE) + ); + SERIALIZE_AND_COPY(view_key, blsct_view_key); + + return blsct_view_key; +} + +BlsctScalar* from_tx_key_to_spending_key( + const BlsctScalar* blsct_tx_key +) { + Scalar tx_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_tx_key, SCALAR_SIZE, tx_key); + + auto spending_key = blsct::FromTransactionToSpendKey(tx_key); + BlsctScalar* blsct_spending_key = static_cast( + malloc(SCALAR_SIZE) + ); + SERIALIZE_AND_COPY(spending_key, blsct_spending_key); + + return blsct_spending_key; +} + +BlsctScalar* calc_priv_spending_key( + const BlsctPubKey* blsct_blinding_pub_key, + const BlsctScalar* blsct_view_key, + const BlsctScalar* blsct_spending_key, + const int64_t account, + const uint64_t address +) { + blsct::PublicKey blinding_pub_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_blinding_pub_key, PUBLIC_KEY_SIZE, blinding_pub_key); + + Scalar view_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_view_key, SCALAR_SIZE, view_key); + + Scalar spending_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_spending_key, SCALAR_SIZE, spending_key); + + auto priv_spending_key = blsct::CalculatePrivateSpendingKey( + blinding_pub_key.GetG1Point(), + view_key, + spending_key, + account, + address + ); + BlsctScalar* blsct_priv_spending_key = static_cast( + malloc(SCALAR_SIZE) + ); + SERIALIZE_AND_COPY(priv_spending_key, blsct_priv_spending_key); + + return blsct_priv_spending_key; +} + +uint64_t calc_view_tag( + const BlsctPubKey* blsct_blinding_pub_key, + const BlsctScalar* blsct_view_key +) { + blsct::PublicKey blinding_pub_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_blinding_pub_key, PUBLIC_KEY_SIZE, blinding_pub_key); + + Scalar view_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_view_key, SCALAR_SIZE, view_key); + + return blsct::CalculateViewTag( + blinding_pub_key.GetG1Point(), + view_key + ); +} + +BlsctKeyId* calc_hash_id( + const BlsctPubKey* blsct_blinding_pub_key, + const BlsctPubKey* blsct_spending_pub_key, + const BlsctScalar* blsct_view_key +) { + blsct::PublicKey blinding_pub_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_blinding_pub_key, PUBLIC_KEY_SIZE, blinding_pub_key); + + blsct::PublicKey spending_pub_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_spending_pub_key, PUBLIC_KEY_SIZE, spending_pub_key); + + Scalar view_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_view_key, SCALAR_SIZE, view_key); + + auto hash_id = blsct::CalculateHashId( + blinding_pub_key.GetG1Point(), + spending_pub_key.GetG1Point(), + view_key + ); + BlsctKeyId* blsct_hash_id = static_cast( + malloc(KEY_ID_SIZE) + ); + SERIALIZE_AND_COPY_WITH_STREAM(hash_id, blsct_hash_id); + + return blsct_hash_id; +} + +const char* get_key_id_hex( + const BlsctKeyId* blsct_key_id +) { + CKeyID key_id; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_key_id, KEY_ID_SIZE, key_id); + + auto hex = key_id.GetHex(); + + size_t BUF_SIZE = hex.size() + 1; + MALLOC_BYTES(char, hex_buf, BUF_SIZE); + RETURN_ERR_IF_MEM_ALLOC_FAILED(hex_buf); + std::memcpy(hex_buf, hex.c_str(), BUF_SIZE); // also copies null at the end + + return hex_buf; +} + +BlsctPoint* calc_nonce( + const BlsctPubKey* blsct_blinding_pub_key, + const BlsctScalar* blsct_view_key +) { + blsct::PublicKey blinding_pub_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_blinding_pub_key, PUBLIC_KEY_SIZE, blinding_pub_key); + + Scalar view_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_view_key, SCALAR_SIZE, view_key); + + auto nonce = blsct::CalculateNonce( + blinding_pub_key.GetG1Point(), + view_key + ); + BlsctPoint* blsct_nonce = static_cast( + malloc(POINT_SIZE) + ); + SERIALIZE_AND_COPY(nonce, blsct_nonce); + + return blsct_nonce; +} + +BlsctSubAddr* derive_sub_address( + const BlsctScalar* blsct_view_key, + const BlsctPubKey* blsct_spending_pub_key, + const BlsctSubAddrId* blsct_sub_addr_id +) { + Scalar view_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_view_key, SCALAR_SIZE, view_key); + + blsct::PublicKey spending_pub_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_spending_pub_key, PUBLIC_KEY_SIZE, spending_pub_key); + + blsct::SubAddressIdentifier sub_addr_id; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_sub_addr_id, SUB_ADDR_ID_SIZE, sub_addr_id); + + auto sub_addr = blsct::DeriveSubAddress(view_key, spending_pub_key, sub_addr_id); + BlsctSubAddr* blsct_sub_addr = static_cast( + malloc(SUB_ADDR_SIZE) + ); + SERIALIZE_AND_COPY_WITH_STREAM(sub_addr, blsct_sub_addr); + + return blsct_sub_addr; +} + +BlsctSubAddrId* gen_sub_addr_id( + const int64_t account, + const uint64_t address +) { + blsct::SubAddressIdentifier sub_addr_id; + sub_addr_id.account = account; + sub_addr_id.address = address; + + BlsctSubAddrId* blsct_sub_addr_id = static_cast( + malloc(SUB_ADDR_ID_SIZE) + ); + SERIALIZE_AND_COPY_WITH_STREAM(sub_addr_id, blsct_sub_addr_id); + + return blsct_sub_addr_id; +} + +int64_t get_sub_addr_id_account( + const BlsctSubAddrId* blsct_sub_addr_id +) { + blsct::SubAddressIdentifier sub_addr_id; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_sub_addr_id, SUB_ADDR_ID_SIZE, sub_addr_id); + return sub_addr_id.account; +} + +uint64_t get_sub_addr_id_address( + const BlsctSubAddrId* blsct_sub_addr_id +) { + blsct::SubAddressIdentifier sub_addr_id; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_sub_addr_id, SUB_ADDR_ID_SIZE, sub_addr_id); + return sub_addr_id.address; +} + +bool is_valid_point( + const BlsctPoint* blsct_point +) { + Point point; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_point, POINT_SIZE, point); + + return point.IsValid(); +} + +BlsctDoublePubKey* gen_dpk_with_keys_and_sub_addr_id( + const BlsctScalar* blsct_view_key, + const BlsctPubKey* blsct_spending_pub_key, + const int64_t account, + const uint64_t address +) { + Scalar view_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_view_key, SCALAR_SIZE, view_key); + + blsct::PublicKey spending_pub_key; + UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(blsct_spending_pub_key, PUBLIC_KEY_SIZE, spending_pub_key); + + blsct::SubAddressIdentifier sub_addr_id { account, address }; + blsct::SubAddress sub_addr(view_key, spending_pub_key, sub_addr_id); + + auto dpk = std::get(sub_addr.GetDestination()); + BlsctDoublePubKey* blsct_dpk = static_cast( + malloc(DOUBLE_PUBLIC_KEY_SIZE) + ); + SERIALIZE_AND_COPY_WITH_STREAM(dpk, blsct_dpk); + + return blsct_dpk; } } // extern "C" + diff --git a/src/blsct/external_api/blsct.h b/src/blsct/external_api/blsct.h index 578d22db26cc1..a418904eaccbb 100644 --- a/src/blsct/external_api/blsct.h +++ b/src/blsct/external_api/blsct.h @@ -1,6 +1,515 @@ +// Copyright (c) 2024 The Navcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef NAVCOIN_BLSCT_EXTERNAL_API_BLSCT_H +#define NAVCOIN_BLSCT_EXTERNAL_API_BLSCT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* constants */ +#define PUBLIC_KEY_SIZE 48 +#define DOUBLE_PUBLIC_KEY_SIZE PUBLIC_KEY_SIZE * 2 +#define SUB_ADDR_SIZE DOUBLE_PUBLIC_KEY_SIZE +#define SUB_ADDR_ID_SIZE 16 +#define ENCODED_DPK_STR_SIZE 165 +#define ENCODED_DPK_STR_BUF_SIZE ENCODED_DPK_STR_SIZE + 1 /* 1 for c-str null termination */ +#define KEY_ID_SIZE 20 +#define POINT_SIZE 48 +#define SCALAR_SIZE 32 +#define RANGE_PROOF_SIZE 1315 // needs to be at least 1315 +#define TOKEN_ID_SIZE 40 // uint256 + uint64_t = 32 + 8 = 40 +#define UINT256_SIZE 32 +#define VIEW_TAG_SIZE 8 +#define OUT_POINT_SIZE 36 +#define SIGNATURE_SIZE 96 +#define SCRIPT_SIZE 28 +#define MAX_MEMO_LEN 100 +#define MEMO_BUF_SIZE MAX_MEMO_LEN + 1 +#define TX_ID_SIZE UINT256_SIZE +#define TX_ID_STR_LEN TX_ID_SIZE * 2 + +/* return codes */ +#define BLSCT_RESULT uint8_t +#define BLSCT_SUCCESS 0 +#define BLSCT_FAILURE 1 +#define BLSCT_EXCEPTION 2 +#define BLSCT_BAD_DPK_SIZE 10 +#define BLSCT_UNKNOWN_ENCODING 11 +#define BLSCT_VALUE_OUTSIDE_THE_RANGE 12 +#define BLSCT_DID_NOT_RUN_TO_COMPLETION 13 +#define BLSCT_IN_AMOUNT_ERROR 14 +#define BLSCT_OUT_AMOUNT_ERROR 15 +#define BLSCT_BAD_OUT_TYPE 16 +#define BLSCT_MEMO_TOO_LONG 17 +#define BLSCT_MEM_ALLOC_FAILED 18 + +#define TRY_DEFINE_MCL_POINT_FROM(src, dest) \ + Point dest; \ + if (!from_blsct_point_to_mcl_point(src, dest)) { \ + return BLSCT_FAILURE; \ + } + +#define TRY_DEFINE_MCL_SCALAR_FROM(src, dest) \ + Scalar dest; \ + from_blsct_scalar_to_mcl_scalar(src, dest) + +#define SERIALIZE_AND_COPY(src, dest) \ +{ \ + auto src_vec = src.GetVch(); \ + std::memcpy(dest, &src_vec[0], src_vec.size()); \ +} + +#define UNSERIALIZE_FROM_BYTE_ARRAY_WITH_STREAM(src, src_size, dest) \ +{ \ + Span buf(U8C(src), src_size); \ + DataStream st{}; \ + st << buf; \ + dest.Unserialize(st); \ +} + +#define SERIALIZE_AND_COPY_WITH_STREAM(src, dest) \ +{ \ + DataStream st{}; \ + src.Serialize(st); \ + std::memcpy(dest, st.data(), st.size()); \ +} + +#define UNSERIALIZE_AND_COPY_WITH_STREAM(src, src_size, dest) \ +{ \ + DataStream st{}; \ + for (size_t i=0; i(name) + +#define TO_VOID(name) reinterpret_cast(name) + +#define UNVOID(T, name) const T* name = reinterpret_cast(void_##name) + +#ifdef __cplusplus extern "C" { +#endif + +enum Chain { + MainNet, + TestNet, + SigNet, + RegTest +}; + +enum TxOutputType { + Normal, + StakedCommitment +}; + +enum AddressEncoding { + Bech32, + Bech32M +}; + +using Point = Mcl::Point; +using Scalar = Mcl::Scalar; +using Scalars = Elements; + +typedef uint8_t BlsctKeyId[KEY_ID_SIZE]; // serialization of CKeyID which is based on uint160 +typedef uint8_t BlsctPoint[POINT_SIZE]; +typedef uint8_t BlsctPubKey[PUBLIC_KEY_SIZE]; +typedef uint8_t BlsctDoublePubKey[DOUBLE_PUBLIC_KEY_SIZE]; +typedef char BlsctAddrStr[ENCODED_DPK_STR_BUF_SIZE]; +typedef uint8_t BlsctRangeProof[RANGE_PROOF_SIZE]; +typedef uint8_t BlsctScalar[SCALAR_SIZE]; +typedef uint8_t BlsctScript[SCRIPT_SIZE]; +typedef uint8_t BlsctSubAddr[SUB_ADDR_SIZE]; +typedef uint8_t BlsctSubAddrId[SUB_ADDR_ID_SIZE]; +typedef uint8_t BlsctTokenId[TOKEN_ID_SIZE]; +typedef uint8_t BlsctUint256[UINT256_SIZE]; +typedef uint8_t BlsctTxId[TX_ID_SIZE]; +typedef uint8_t BlsctViewTag[VIEW_TAG_SIZE]; +typedef uint8_t BlsctOutPoint[OUT_POINT_SIZE]; +typedef uint8_t BlsctSignature[SIGNATURE_SIZE]; + +typedef struct { + BLSCT_RESULT result; + void* value; + size_t value_size; +} BlsctRetVal; + +typedef struct { + BLSCT_RESULT result; + bool value; +} BlsctBoolRetVal; + +typedef struct { + BLSCT_RESULT result; + void* value; // = std::vector +} BlsctAmountsRetVal; + +typedef struct { + BLSCT_RESULT result; + uint8_t* ser_tx; + size_t ser_tx_size; + + size_t in_amount_err_index; // holds the first index of the tx_in whose amount exceeds the maximum + size_t out_amount_err_index; // holds the first index of the tx_out whose amount exceeds the maximum +} BlsctTxRetVal; + +BlsctRetVal* succ( + void* value, + size_t value_size +); + +BlsctRetVal* err( + BLSCT_RESULT result +); + +BlsctBoolRetVal* succ_bool( + bool value +); + +BlsctBoolRetVal* err_bool( + BLSCT_RESULT result +); + +typedef struct { + BlsctRangeProof range_proof; + BlsctPoint nonce; +} BlsctAmountRecoveryReq; + +typedef struct { + bool is_succ; + char* msg; + uint64_t amount; +} BlsctAmountRecoveryResult; + +typedef struct { + uint64_t amount; + uint64_t gamma; + BlsctScalar spending_key; + BlsctTokenId token_id; + BlsctOutPoint out_point; + bool rbf; +} BlsctTxIn; + +typedef struct { + BlsctSubAddr dest; + uint64_t amount; + char memo_c_str[MEMO_BUF_SIZE]; + BlsctTokenId token_id; + TxOutputType output_type; + uint64_t min_stake; +} BlsctTxOut; + +// memory disposition +void free_obj(void* x); +void free_amounts_ret_val(BlsctAmountsRetVal* rv); // free attrs as well + +// library initialization +void init(); +bool set_chain(enum Chain chain); + +// point +BlsctRetVal* gen_random_point(); +const char* point_to_hex(const BlsctPoint* blsct_point); + +// scalar +BlsctRetVal* gen_random_scalar(); +BlsctRetVal* gen_scalar(const uint64_t n); +uint64_t scalar_to_uint64(const BlsctScalar* blsct_scalar); +const char* scalar_to_hex(const BlsctScalar* blsct_scalar); + +// public key generation +BlsctRetVal* gen_random_public_key(); + +// address +BlsctRetVal* decode_address( + const char* blsct_enc_addr +); + +BlsctRetVal* encode_address( + const void* void_blsct_dpk, + const enum AddressEncoding encoding +); + +// double public key +BlsctRetVal* gen_double_pub_key( + const BlsctPubKey* blsct_pk1, + const BlsctPubKey* blsct_pk2 +); + +// token id +BlsctRetVal* gen_token_id_with_subid( + const uint64_t token, + const uint64_t subid +); + +BlsctRetVal* gen_token_id( + const uint64_t token +); + +BlsctRetVal* gen_default_token_id(); + +uint64_t get_token_id_token(const BlsctTokenId* blsct_token_id); + +uint64_t get_token_id_subid(const BlsctTokenId* blsct_token_id); + +// range proof +BlsctRetVal* build_range_proof( + const void* vp_uint64_vec, + const BlsctPoint* blsct_nonce, + const char* blsct_msg, + const BlsctTokenId* blsct_token_id +); + +BlsctBoolRetVal* verify_range_proofs( + const void* vp_range_proofs +); + +// amount recovery +BlsctAmountRecoveryReq* gen_recover_amount_req( + const void* vp_blsct_range_proof, + const void* vp_blsct_nonce +); + +// returns a structure whose value field is +// a vector of the same size as the input vector +BlsctAmountsRetVal* recover_amount( + void* vp_amt_recovery_req_vec +); -void BlsctInit(); -void BlsctTest(); +// out point +// txid is 32 bytes and represented as 64-char hex str +BlsctRetVal* gen_out_point( + const char* tx_id_c_str, + const uint32_t n +); +BlsctRetVal* build_tx_in( + const uint64_t amount, + const uint64_t gamma, + const BlsctScalar* spending_key, + const BlsctTokenId* token_id, + const BlsctOutPoint* out_point, + const bool rbf +); + +BlsctRetVal* dpk_to_sub_addr( + const void* blsct_dpk +); + +BlsctRetVal* build_tx_out( + const BlsctSubAddr* blsct_dest, + const uint64_t amount, + const char* memo_c_str, + const BlsctTokenId* blsct_token_id, + const TxOutputType output_type, + const uint64_t min_stake +); + +BlsctTxRetVal* build_tx( + const void* void_tx_ins, + const void* void_tx_outs +); + +// must free the returned object after use +CMutableTransaction* deserialize_tx( + const uint8_t* ser_tx, + const size_t ser_tx_size +); + +const std::vector* get_tx_ins(const CMutableTransaction* tx); + +size_t get_tx_ins_size(const std::vector* tx_ins); + +const BlsctRetVal* get_tx_in(const std::vector* tx_ins, const size_t i); + +const std::vector* get_tx_outs(const CMutableTransaction* tx); + +size_t get_tx_outs_size(const std::vector* tx_outs); + +const BlsctRetVal* get_tx_out(const std::vector* tx_outs, const size_t i); + +// TxIn +const BlsctScript* get_tx_in_script_sig(const CTxIn* tx_in); + +uint32_t get_tx_in_sequence(const CTxIn* tx_in); + +const BlsctScript* get_tx_in_script_witness(const CTxIn* tx_in); + +const BlsctTxId* get_tx_in_prev_out_hash(const CTxIn* tx_in); + +uint32_t get_tx_in_prev_out_n(const CTxIn* tx_in); + +// TxOut +uint64_t get_tx_out_value(const CTxOut* tx_out); + +const BlsctScript* get_tx_out_script_pub_key(const CTxOut* tx_out); + +const BlsctTokenId* get_tx_out_token_id(const CTxOut* tx_out); + +const BlsctScript* get_tx_out_script_pubkey(const CTxOut* tx_out); + +const BlsctPoint* get_tx_out_spending_key(const CTxOut* tx_out); + +const BlsctPoint* get_tx_out_ephemeral_key(const CTxOut* tx_out); + +const BlsctPoint* get_tx_out_blinding_key(const CTxOut* tx_out); + +uint16_t get_tx_out_view_tag(const CTxOut* tx_out); + +const BlsctPoint* get_tx_out_range_proof_A(const CTxOut* tx_out); +const BlsctPoint* get_tx_out_range_proof_S(const CTxOut* tx_out); +const BlsctPoint* get_tx_out_range_proof_T1(const CTxOut* tx_out); +const BlsctPoint* get_tx_out_range_proof_T2(const CTxOut* tx_out); + +const BlsctScalar* get_tx_out_range_proof_mu(const CTxOut* tx_out); +const BlsctScalar* get_tx_out_range_proof_a(const CTxOut* tx_out); +const BlsctScalar* get_tx_out_range_proof_b(const CTxOut* tx_out); +const BlsctScalar* get_tx_out_range_proof_t_hat(const CTxOut* tx_out); + +const BlsctSignature* sign_message( + const BlsctScalar* blsct_priv_key, + const char* blsct_msg +); + +bool verify_msg_sig( + const BlsctPubKey* blsct_pub_key, + const char* blsct_msg, + const BlsctSignature* blsct_signature +); + +BlsctPubKey* scalar_to_pub_key( + const BlsctScalar* blsct_scalar +); + +// key derivation functions + +// seed (scalar) +// +---> child key (scalar) +// +--------> blinding key (scalar) +// +--------> token key (scalar) +// +--------> tx key (scalar) +// +----> view key (scalar) +// +----> spending key (scalar) + +// from seed +BlsctScalar* from_seed_to_child_key( + const BlsctScalar* blsct_seed +); + +// from child_key +BlsctScalar* from_child_key_to_blinding_key( + const BlsctScalar* blsct_child_key +); + +BlsctScalar* from_child_key_to_token_key( + const BlsctScalar* blsct_child_key +); + +BlsctScalar* from_child_key_to_tx_key( + const BlsctScalar* blsct_child_key +); + +// from tx key +BlsctScalar* from_tx_key_to_view_key( + const BlsctScalar* blsct_tx_key +); + +BlsctScalar* from_tx_key_to_spending_key( + const BlsctScalar* blsct_tx_key +); + +// from multiple keys and other info +BlsctScalar* calc_priv_spending_key( + const BlsctPubKey* blsct_blinding_pub_key, + const BlsctScalar* blsct_view_key, + const BlsctScalar* blsct_spending_key, + const int64_t account, + const uint64_t address +); + +// blsct/wallet/helpers delegators +uint64_t calc_view_tag( + const BlsctPubKey* blinding_pub_key, + const BlsctScalar* view_key +); + +BlsctKeyId* calc_hash_id( + const BlsctPubKey* blsct_blinding_pub_key, + const BlsctPubKey* blsct_spending_pub_key, + const BlsctScalar* blsct_view_key +); + +const char* get_key_id_hex( + const BlsctKeyId* blsct_key_id +); + +BlsctPoint* calc_nonce( + const BlsctPubKey* blsct_blinding_pub_key, + const BlsctScalar* view_key +); + +BlsctSubAddr* derive_sub_address( + const BlsctScalar* blsct_view_key, + const BlsctPubKey* blsct_spending_pub_key, + const BlsctSubAddrId* blsct_sub_addr_id +); + +BlsctSubAddrId* gen_sub_addr_id( + const int64_t account, + const uint64_t address +); + +int64_t get_sub_addr_id_account( + const BlsctSubAddrId* blsct_sub_addr_id +); + +uint64_t get_sub_addr_id_address( + const BlsctSubAddrId* blsct_sub_addr_id +); + +bool is_valid_point( + const BlsctPoint* blsct_point +); + +BlsctDoublePubKey* gen_dpk_with_keys_and_sub_addr_id( + const BlsctScalar* blsct_view_key, + const BlsctPubKey* blsct_spending_pub_key, + const int64_t account, + const uint64_t address +); + +#ifdef __cplusplus } // extern "C" +#endif + +#endif // NAVCOIN_BLSCT_EXTERNAL_API_BLSCT_H + diff --git a/src/blsct/key_io.cpp b/src/blsct/key_io.cpp new file mode 100644 index 0000000000000..f1b05f2da1593 --- /dev/null +++ b/src/blsct/key_io.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2024 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 + +#include + +namespace blsct { + +std::string EncodeDoublePublicKey( + const std::string bech32_mod_hrp, + const bech32_mod::Encoding encoding, + const blsct::DoublePublicKey& dpk +) { + std::vector dpk_v8 = dpk.GetVch(); + std::vector dpk_v5; + dpk_v5.reserve(DOUBLE_PUBKEY_ENC_SIZE); + + // ignoring the return value since this conversion always succeeds + ConvertBits<8, 5, true>([&](uint8_t c) { dpk_v5.push_back(c); }, dpk_v8.begin(), dpk_v8.end()); + + return Encode(encoding, bech32_mod_hrp, dpk_v5); +} + +std::optional DecodeDoublePublicKey( + const std::string bech32_mod_hrp, + const std::string& str +) { + const auto hrp = ToLower(str.substr(0, bech32_mod_hrp.size())); + + // str needs to be of the expected length and have the expected hrp + if (str.size() != DOUBLE_PUBKEY_ENC_SIZE + || hrp != bech32_mod_hrp + || str[bech32_mod_hrp.size()] != '1' + ) return std::nullopt; + + // decode to 5-bit based byte vector + const auto dec = bech32_mod::Decode(str); + + // check if it has expected encoding and the data is of the expected length + if ((dec.encoding != bech32_mod::Encoding::BECH32 && dec.encoding != bech32_mod::Encoding::BECH32M) + || dec.data.size() != 154 + ) return std::nullopt; + + // The data part consists of two concatenated 48-byte public keys + std::vector data; + data.reserve(blsct::DoublePublicKey::SIZE); + if (!ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, dec.data.begin(), dec.data.end())) { + return std::nullopt; + } + + blsct::DoublePublicKey dpk(data); + if (dpk.IsValid()) { + return dpk; + } else { + return std::nullopt; + } +} + +} // namespace blsct diff --git a/src/blsct/key_io.h b/src/blsct/key_io.h new file mode 100644 index 0000000000000..8ae2bd74f9444 --- /dev/null +++ b/src/blsct/key_io.h @@ -0,0 +1,47 @@ +// Copyright (c) 2024 The Navcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef NAVCOIN_BLSCT_KEY_IO_H +#define NAVCOIN_BLSCT_KEY_IO_H + +#include +#include +#include +#include + +namespace blsct { + +// ** COPIED FROM src/key_io.h ** +// +// double public key after encoding to bech32_mod is 165-byte long consisting of: +// - 2-byte hrp +// - 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; + +namespace bech32_hrp { + const std::string Main = "nv"; + const std::string TestNet = "tn"; + const std::string SigNet = "tn"; + const std::string RegTest = "nr"; +} + +/** Encode DoublePublicKey to Bech32 or Bech32m string. Encoding must be one of BECH32 or BECH32M. */ +std::string EncodeDoublePublicKey( + const std::string bech32_mod_hrp, + const bech32_mod::Encoding encoding, + const blsct::DoublePublicKey& dpk +); + +/** Decode a Bech32 or Bech32m string to a DoublePublicKey. */ +std::optional DecodeDoublePublicKey( + const std::string bech32_mod_hrp, + const std::string& str +); + +} // namespace blsct + +#endif // NAVCOIN_BLSCT_KEY_IO_H + diff --git a/src/blsct/pos/proof.cpp b/src/blsct/pos/proof.cpp index 3449ec4f6b990..17a44b4e7a246 100644 --- a/src/blsct/pos/proof.cpp +++ b/src/blsct/pos/proof.cpp @@ -119,4 +119,4 @@ uint256 ProofOfStake::CalculateMinValue(const uint256& kernel_hash, const unsign if (next_target == 0) return uint256(); return ArithToUint256(UintToArith256(kernel_hash) / arith_uint256().SetCompact(next_target)); } -} // namespace blsct \ No newline at end of file +} // namespace blsct diff --git a/src/blsct/public_key.cpp b/src/blsct/public_key.cpp index 523f76bbdf5d7..22ec308bd6d66 100644 --- a/src/blsct/public_key.cpp +++ b/src/blsct/public_key.cpp @@ -56,6 +56,11 @@ std::vector PublicKey::GetVch() const return point.GetVch(); } +bool PublicKey::SetVch(const std::vector vec) +{ + return point.SetVch(vec); +} + blsPublicKey PublicKey::ToBlsPublicKey() const { blsPublicKey bls_pk{point.GetUnderlying()}; diff --git a/src/blsct/public_key.h b/src/blsct/public_key.h index 3f26f78339962..fcaf49e050687 100644 --- a/src/blsct/public_key.h +++ b/src/blsct/public_key.h @@ -56,6 +56,7 @@ class PublicKey Point GetG1Point() const; std::vector GetVch() const; + bool SetVch(const std::vector vec); bool operator<(const PublicKey& b) const { diff --git a/src/blsct/wallet/address.h b/src/blsct/wallet/address.h index 3cf3d71463123..884b854e8808a 100644 --- a/src/blsct/wallet/address.h +++ b/src/blsct/wallet/address.h @@ -29,9 +29,15 @@ class SubAddressPool } }; -struct SubAddressIdentifier { +struct SubAddressIdentifier +{ int64_t account; uint64_t address; + + SERIALIZE_METHODS(SubAddressIdentifier, obj) + { + READWRITE(obj.account, obj.address); + } }; class SubAddress diff --git a/src/blsct/wallet/txfactory.cpp b/src/blsct/wallet/txfactory.cpp index 6ba9aef30e444..506afd7d11fa7 100644 --- a/src/blsct/wallet/txfactory.cpp +++ b/src/blsct/wallet/txfactory.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include -#include using T = Mcl; using Point = T::Point; @@ -13,198 +12,6 @@ using Scalars = Elements; namespace blsct { -void TxFactoryBase::AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id, const CreateTransactionType& type, const CAmount& minStake, const bool& fSubtractFeeFromAmount) -{ - UnsignedOutput out; - - out = CreateOutput(destination.GetKeys(), nAmount, sMemo, token_id, Scalar::Rand(), type, minStake); - - CAmount nFee = 0; - - if (fSubtractFeeFromAmount) { - nFee = GetTransactioOutputWeight(out.out) * BLSCT_DEFAULT_FEE; - out = CreateOutput(destination.GetKeys(), nAmount - nFee, sMemo, token_id, Scalar::Rand(), type, minStake); - }; - - if (nAmounts.count(token_id) == 0) - nAmounts[token_id] = {0, 0}; - - nAmounts[token_id].nFromOutputs += nAmount - nFee; - - if (vOutputs.count(token_id) == 0) - vOutputs[token_id] = std::vector(); - - vOutputs[token_id].push_back(out); -} - -bool TxFactoryBase::AddInput(const CAmount& amount, const MclScalar& gamma, const PrivateKey& spendingKey, const TokenId& token_id, const COutPoint& outpoint, const bool& stakedCommitment, const bool& rbf) -{ - if (vInputs.count(token_id) == 0) - vInputs[token_id] = std::vector(); - - vInputs[token_id].push_back({CTxIn(outpoint, CScript(), rbf ? MAX_BIP125_RBF_SEQUENCE : CTxIn::SEQUENCE_FINAL), amount, gamma, spendingKey, stakedCommitment}); - - if (nAmounts.count(token_id) == 0) - nAmounts[token_id] = {0, 0}; - - nAmounts[token_id].nFromInputs += amount; - - return true; -} - -std::optional -TxFactoryBase::BuildTx(const blsct::DoublePublicKey& changeDestination, const CAmount& minStake, const CreateTransactionType& type, const bool& fSubtractedFee) -{ - this->tx = CMutableTransaction(); - - std::vector outputSignatures; - Scalar outputGammas; - CAmount nFee = 0; - - - for (auto& out_ : vOutputs) { - for (auto& out : out_.second) { - this->tx.vout.push_back(out.out); - outputGammas = outputGammas - out.gamma; - outputSignatures.push_back(PrivateKey(out.blindingKey).Sign(out.out.GetHash())); - } - } - - while (true) { - CMutableTransaction tx = this->tx; - tx.nVersion |= CTransaction::BLSCT_MARKER; - - Scalar gammaAcc = outputGammas; - std::map mapChange; - std::map mapInputs; - std::vector txSigs = outputSignatures; - - if (type == STAKED_COMMITMENT_UNSTAKE || type == STAKED_COMMITMENT) { - for (auto& in_ : vInputs) { - for (auto& in : in_.second) { - if (!in.is_staked_commitment) continue; - - tx.vin.push_back(in.in); - gammaAcc = gammaAcc + in.gamma; - txSigs.push_back(in.sk.Sign(in.in.GetHash())); - - if (!mapInputs[in_.first]) mapInputs[in_.first] = 0; - - mapInputs[in_.first] += in.value.GetUint64(); - - if (mapInputs[in_.first] > nAmounts[in_.first].nFromOutputs + nFee) break; - } - } - } - - for (auto& in_ : vInputs) { - for (auto& in : in_.second) { - if (in.is_staked_commitment) continue; - - tx.vin.push_back(in.in); - gammaAcc = gammaAcc + in.gamma; - txSigs.push_back(in.sk.Sign(in.in.GetHash())); - - if (!mapInputs[in_.first]) mapInputs[in_.first] = 0; - - mapInputs[in_.first] += in.value.GetUint64(); - - if (mapInputs[in_.first] > nAmounts[in_.first].nFromOutputs + nFee) break; - } - } - - for (auto& amounts : nAmounts) { - auto tokenFee = (amounts.first == TokenId() ? nFee : 0); - - auto nFromInputs = mapInputs[amounts.first]; - - if (nFromInputs < amounts.second.nFromOutputs + tokenFee) return std::nullopt; - - mapChange[amounts.first] = nFromInputs - amounts.second.nFromOutputs - tokenFee; - } - - for (auto& change : mapChange) { - if (change.second == 0) continue; - - auto changeOutput = CreateOutput(changeDestination, change.second, "Change", change.first, MclScalar::Rand(), NORMAL, minStake); - - gammaAcc = gammaAcc - changeOutput.gamma; - - tx.vout.push_back(changeOutput.out); - txSigs.push_back(PrivateKey(changeOutput.blindingKey).Sign(changeOutput.out.GetHash())); - } - - if (nFee == GetTransactionWeight(CTransaction(tx)) * BLSCT_DEFAULT_FEE) { - 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 = GetTransactionWeight(CTransaction(tx)) * BLSCT_DEFAULT_FEE; - } - - return std::nullopt; -} - -std::optional TxFactoryBase::CreateTransaction(const std::vector& inputCandidates, const blsct::DoublePublicKey& changeDestination, const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id, const CreateTransactionType& type, const CAmount& minStake) -{ - auto tx = blsct::TxFactoryBase(); - - if (type == STAKED_COMMITMENT) { - CAmount inputFromStakedCommitments = 0; - - for (const auto& output : inputCandidates) { - if (output.is_staked_commitment) - inputFromStakedCommitments += output.amount; - - tx.AddInput(output.amount, output.gamma, output.spendingKey, output.token_id, COutPoint(output.outpoint.hash, output.outpoint.n), output.is_staked_commitment); - } - - if (nAmount + inputFromStakedCommitments < minStake) { - throw std::runtime_error(strprintf("A minimum of %s is required to stake", FormatMoney(minStake))); - } - - bool fSubtractFeeFromAmount = false; // nAmount == inAmount + inputFromStakedCommitments; - - tx.AddOutput(destination, nAmount + inputFromStakedCommitments, sMemo, token_id, type, minStake, fSubtractFeeFromAmount); - } else { - CAmount inputFromStakedCommitments = 0; - - for (const auto& output : inputCandidates) { - if (output.is_staked_commitment) { - if (!(type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE || type == CreateTransactionType::STAKED_COMMITMENT)) - continue; - inputFromStakedCommitments += output.amount; - } - - tx.AddInput(output.amount, output.gamma, output.spendingKey, output.token_id, COutPoint(output.outpoint.hash, output.outpoint.n), output.is_staked_commitment); - } - - if (type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE) { - if (inputFromStakedCommitments - nAmount < 0) { - throw std::runtime_error(strprintf("Not enough staked coins")); - } else if (inputFromStakedCommitments - nAmount < minStake && inputFromStakedCommitments - nAmount > 0) { - throw std::runtime_error(strprintf("A minimum of %s is required to stake", FormatMoney(minStake))); - } - - if (inputFromStakedCommitments - nAmount > 0) { - // CHANGE - tx.AddOutput(destination, inputFromStakedCommitments - nAmount, sMemo, token_id, CreateTransactionType::STAKED_COMMITMENT, minStake, false); - } - } - - bool fSubtractFeeFromAmount = false; // type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE; - - tx.AddOutput(destination, nAmount, sMemo, token_id, type, minStake, fSubtractFeeFromAmount); - } - - return tx.BuildTx(changeDestination, minStake, type); -} - bool TxFactory::AddInput(const CCoinsViewCache& cache, const COutPoint& outpoint, const bool& stakedCommitment, const bool& rbf) { Coin coin; @@ -262,39 +69,6 @@ TxFactory::BuildTx() return TxFactoryBase::BuildTx(std::get(km->GetNewDestination(-1).value())); } -void TxFactoryBase::AddAvailableCoins(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const wallet::CoinFilterParams& coins_params, std::vector& inputCandidates) -{ - AssertLockHeld(wallet->cs_wallet); - for (const wallet::COutput& output : AvailableCoins(*wallet, nullptr, std::nullopt, coins_params).All()) { - auto tx = wallet->GetWalletTx(output.outpoint.hash); - - if (tx == nullptr) - continue; - - auto out = tx->tx->vout[output.outpoint.n]; - - auto recoveredInfo = tx->GetBLSCTRecoveryData(output.outpoint.n); - inputCandidates.push_back({recoveredInfo.amount, recoveredInfo.gamma, blsct_km->GetSpendingKeyForOutput(out), out.tokenId, COutPoint(output.outpoint.hash, output.outpoint.n), out.IsStakedCommitment()}); - } -} - -void TxFactoryBase::AddAvailableCoins(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const TokenId& token_id, const CreateTransactionType& type, std::vector& inputCandidates) -{ - AssertLockHeld(wallet->cs_wallet); - - wallet::CoinFilterParams coins_params; - coins_params.min_amount = 0; - coins_params.only_blsct = true; - coins_params.token_id = token_id; - - AddAvailableCoins(wallet, blsct_km, coins_params, inputCandidates); - - if (type == CreateTransactionType::STAKED_COMMITMENT || type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE) { - coins_params.include_staked_commitment = true; - AddAvailableCoins(wallet, blsct_km, coins_params, inputCandidates); - } -} - std::optional TxFactory::CreateTransaction(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id, const CreateTransactionType& type, const CAmount& minStake) { LOCK(wallet->cs_wallet); @@ -309,4 +83,4 @@ std::optional TxFactory::CreateTransaction(wallet::CWallet* return TxFactoryBase::CreateTransaction(inputCandidates, changeAddress, destination, nAmount, sMemo, token_id, type, minStake); } -} // namespace blsct \ No newline at end of file +} // namespace blsct diff --git a/src/blsct/wallet/txfactory.h b/src/blsct/wallet/txfactory.h index b179124212ceb..91764f009054a 100644 --- a/src/blsct/wallet/txfactory.h +++ b/src/blsct/wallet/txfactory.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -15,37 +16,6 @@ namespace blsct { -struct InputCandidates { - CAmount amount; - MclScalar gamma; - blsct::PrivateKey spendingKey; - TokenId token_id; - COutPoint outpoint; - bool is_staked_commitment; -}; - -class TxFactoryBase -{ -protected: - CMutableTransaction tx; - std::map> - vOutputs; - std::map> - vInputs; - std::map - nAmounts; - -public: - TxFactoryBase(){}; - - void AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id = TokenId(), const CreateTransactionType& type = NORMAL, const CAmount& minStake = 0, const bool& fSubtractFeeFromAmount = false); - bool AddInput(const CAmount& amount, const MclScalar& gamma, const blsct::PrivateKey& spendingKey, const TokenId& token_id, const COutPoint& outpoint, const bool& stakedCommitment = false, const bool& rbf = false); - std::optional BuildTx(const blsct::DoublePublicKey& changeDestination, const CAmount& minStake = 0, const CreateTransactionType& type = NORMAL, const bool& fSubtractedFee = false); - static std::optional CreateTransaction(const std::vector& inputCandidates, const blsct::DoublePublicKey& changeDestination, const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id = TokenId(), const CreateTransactionType& type = NORMAL, const CAmount& minStake = 0); - static void AddAvailableCoins(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const wallet::CoinFilterParams& coins_params, std::vector& inputCandidates) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); - static void AddAvailableCoins(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const TokenId& token_id, const CreateTransactionType& type, std::vector& inputCandidates) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); -}; - class TxFactory : public TxFactoryBase { private: diff --git a/src/blsct/wallet/txfactory_base.cpp b/src/blsct/wallet/txfactory_base.cpp new file mode 100644 index 0000000000000..952a57ce2e4b8 --- /dev/null +++ b/src/blsct/wallet/txfactory_base.cpp @@ -0,0 +1,242 @@ +// Copyright (c) 2024 The Navio 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 TxFactoryBase::AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id, const CreateTransactionType& type, const CAmount& minStake, const bool& fSubtractFeeFromAmount) +{ + UnsignedOutput out; + + out = CreateOutput(destination.GetKeys(), nAmount, sMemo, token_id, Scalar::Rand(), type, minStake); + + CAmount nFee = 0; + + if (fSubtractFeeFromAmount) { + nFee = GetTransactioOutputWeight(out.out) * BLSCT_DEFAULT_FEE; + out = CreateOutput(destination.GetKeys(), nAmount - nFee, sMemo, token_id, Scalar::Rand(), type, minStake); + }; + + if (nAmounts.count(token_id) == 0) + nAmounts[token_id] = {0, 0}; + + nAmounts[token_id].nFromOutputs += nAmount - nFee; + + if (vOutputs.count(token_id) == 0) + vOutputs[token_id] = std::vector(); + + vOutputs[token_id].push_back(out); +} + +bool TxFactoryBase::AddInput(const CAmount& amount, const MclScalar& gamma, const PrivateKey& spendingKey, const TokenId& token_id, const COutPoint& outpoint, const bool& stakedCommitment, const bool& rbf) +{ + if (vInputs.count(token_id) == 0) + vInputs[token_id] = std::vector(); + + vInputs[token_id].push_back({CTxIn(outpoint, CScript(), rbf ? MAX_BIP125_RBF_SEQUENCE : CTxIn::SEQUENCE_FINAL), amount, gamma, spendingKey, stakedCommitment}); + + if (nAmounts.count(token_id) == 0) + nAmounts[token_id] = {0, 0}; + + nAmounts[token_id].nFromInputs += amount; + + return true; +} + +std::optional +TxFactoryBase::BuildTx(const blsct::DoublePublicKey& changeDestination, const CAmount& minStake, const CreateTransactionType& type, const bool& fSubtractedFee) +{ + this->tx = CMutableTransaction(); + + std::vector outputSignatures; + Scalar outputGammas; + CAmount nFee = 0; + + + for (auto& out_ : vOutputs) { + for (auto& out : out_.second) { + this->tx.vout.push_back(out.out); + outputGammas = outputGammas - out.gamma; + outputSignatures.push_back(PrivateKey(out.blindingKey).Sign(out.out.GetHash())); + } + } + + while (true) { + CMutableTransaction tx = this->tx; + tx.nVersion |= CTransaction::BLSCT_MARKER; + + Scalar gammaAcc = outputGammas; + std::map mapChange; + std::map mapInputs; + std::vector txSigs = outputSignatures; + + if (type == STAKED_COMMITMENT_UNSTAKE || type == STAKED_COMMITMENT) { + for (auto& in_ : vInputs) { + for (auto& in : in_.second) { + if (!in.is_staked_commitment) continue; + + tx.vin.push_back(in.in); + gammaAcc = gammaAcc + in.gamma; + txSigs.push_back(in.sk.Sign(in.in.GetHash())); + + if (!mapInputs[in_.first]) mapInputs[in_.first] = 0; + + mapInputs[in_.first] += in.value.GetUint64(); + + if (mapInputs[in_.first] > nAmounts[in_.first].nFromOutputs + nFee) break; + } + } + } + + for (auto& in_ : vInputs) { + for (auto& in : in_.second) { + if (in.is_staked_commitment) continue; + + tx.vin.push_back(in.in); + gammaAcc = gammaAcc + in.gamma; + txSigs.push_back(in.sk.Sign(in.in.GetHash())); + + if (!mapInputs[in_.first]) mapInputs[in_.first] = 0; + + mapInputs[in_.first] += in.value.GetUint64(); + + if (mapInputs[in_.first] > nAmounts[in_.first].nFromOutputs + nFee) break; + } + } + + for (auto& amounts : nAmounts) { + auto tokenFee = (amounts.first == TokenId() ? nFee : 0); + + auto nFromInputs = mapInputs[amounts.first]; + + if (nFromInputs < amounts.second.nFromOutputs + tokenFee) return std::nullopt; + + mapChange[amounts.first] = nFromInputs - amounts.second.nFromOutputs - tokenFee; + } + + for (auto& change : mapChange) { + if (change.second == 0) continue; + + auto changeOutput = CreateOutput(changeDestination, change.second, "Change", change.first, MclScalar::Rand(), NORMAL, minStake); + + gammaAcc = gammaAcc - changeOutput.gamma; + + tx.vout.push_back(changeOutput.out); + txSigs.push_back(PrivateKey(changeOutput.blindingKey).Sign(changeOutput.out.GetHash())); + } + + if (nFee == GetTransactionWeight(CTransaction(tx)) * BLSCT_DEFAULT_FEE) { + 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 = GetTransactionWeight(CTransaction(tx)) * BLSCT_DEFAULT_FEE; + } + + return std::nullopt; +} + +std::optional TxFactoryBase::CreateTransaction(const std::vector& inputCandidates, const blsct::DoublePublicKey& changeDestination, const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id, const CreateTransactionType& type, const CAmount& minStake) +{ + auto tx = blsct::TxFactoryBase(); + + if (type == STAKED_COMMITMENT) { + CAmount inputFromStakedCommitments = 0; + + for (const auto& output : inputCandidates) { + if (output.is_staked_commitment) + inputFromStakedCommitments += output.amount; + + tx.AddInput(output.amount, output.gamma, output.spendingKey, output.token_id, COutPoint(output.outpoint.hash, output.outpoint.n), output.is_staked_commitment); + } + + if (nAmount + inputFromStakedCommitments < minStake) { + throw std::runtime_error(strprintf("A minimum of %s is required to stake", FormatMoney(minStake))); + } + + bool fSubtractFeeFromAmount = false; // nAmount == inAmount + inputFromStakedCommitments; + + tx.AddOutput(destination, nAmount + inputFromStakedCommitments, sMemo, token_id, type, minStake, fSubtractFeeFromAmount); + } else { + CAmount inputFromStakedCommitments = 0; + + for (const auto& output : inputCandidates) { + if (output.is_staked_commitment) { + if (!(type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE || type == CreateTransactionType::STAKED_COMMITMENT)) + continue; + inputFromStakedCommitments += output.amount; + } + + tx.AddInput(output.amount, output.gamma, output.spendingKey, output.token_id, COutPoint(output.outpoint.hash, output.outpoint.n), output.is_staked_commitment); + } + + if (type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE) { + if (inputFromStakedCommitments - nAmount < 0) { + throw std::runtime_error(strprintf("Not enough staked coins")); + } else if (inputFromStakedCommitments - nAmount < minStake && inputFromStakedCommitments - nAmount > 0) { + throw std::runtime_error(strprintf("A minimum of %s is required to stake", FormatMoney(minStake))); + } + + if (inputFromStakedCommitments - nAmount > 0) { + // CHANGE + tx.AddOutput(destination, inputFromStakedCommitments - nAmount, sMemo, token_id, CreateTransactionType::STAKED_COMMITMENT, minStake, false); + } + } + + bool fSubtractFeeFromAmount = false; // type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE; + + tx.AddOutput(destination, nAmount, sMemo, token_id, type, minStake, fSubtractFeeFromAmount); + } + + return tx.BuildTx(changeDestination, minStake, type); +} + +void TxFactoryBase::AddAvailableCoins(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const wallet::CoinFilterParams& coins_params, std::vector& inputCandidates) +{ + AssertLockHeld(wallet->cs_wallet); + for (const wallet::COutput& output : AvailableCoins(*wallet, nullptr, std::nullopt, coins_params).All()) { + auto tx = wallet->GetWalletTx(output.outpoint.hash); + + if (tx == nullptr) + continue; + + auto out = tx->tx->vout[output.outpoint.n]; + + auto recoveredInfo = tx->GetBLSCTRecoveryData(output.outpoint.n); + inputCandidates.push_back({recoveredInfo.amount, recoveredInfo.gamma, blsct_km->GetSpendingKeyForOutput(out), out.tokenId, COutPoint(output.outpoint.hash, output.outpoint.n), out.IsStakedCommitment()}); + } +} + +void TxFactoryBase::AddAvailableCoins(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const TokenId& token_id, const CreateTransactionType& type, std::vector& inputCandidates) +{ + AssertLockHeld(wallet->cs_wallet); + + wallet::CoinFilterParams coins_params; + coins_params.min_amount = 0; + coins_params.only_blsct = true; + coins_params.token_id = token_id; + + AddAvailableCoins(wallet, blsct_km, coins_params, inputCandidates); + + if (type == CreateTransactionType::STAKED_COMMITMENT || type == CreateTransactionType::STAKED_COMMITMENT_UNSTAKE) { + coins_params.include_staked_commitment = true; + AddAvailableCoins(wallet, blsct_km, coins_params, inputCandidates); + } +} + + +} // namespace blsct diff --git a/src/blsct/wallet/txfactory_base.h b/src/blsct/wallet/txfactory_base.h new file mode 100644 index 0000000000000..0f66a9b5fb0cd --- /dev/null +++ b/src/blsct/wallet/txfactory_base.h @@ -0,0 +1,48 @@ +// Copyright (c) 2024 The Navio 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 + +namespace blsct { + +enum class CreateOutputType { + NORMAL, + STAKED_COMMITMENT +}; + +struct InputCandidates { + CAmount amount; + MclScalar gamma; + blsct::PrivateKey spendingKey; + TokenId token_id; + COutPoint outpoint; + bool is_staked_commitment; +}; + +class TxFactoryBase +{ +protected: + CMutableTransaction tx; + std::map> vOutputs; + std::map> vInputs; + std::map nAmounts; + +public: + TxFactoryBase(){}; + + void AddOutput(const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id = TokenId(), const CreateTransactionType& type = CreateTransactionType::NORMAL, const CAmount& minStake = 0, const bool& fSubtractFeeFromAmount = false); + bool AddInput(const CAmount& amount, const MclScalar& gamma, const blsct::PrivateKey& spendingKey, const TokenId& token_id, const COutPoint& outpoint, const bool& stakedCommitment = false, const bool& rbf = false); + std::optional BuildTx(const blsct::DoublePublicKey& changeDestination, const CAmount& minStake = 0, const CreateTransactionType& type = CreateTransactionType::NORMAL, const bool& fSubtractedFee = false); + static std::optional CreateTransaction(const std::vector& inputCandidates, const blsct::DoublePublicKey& changeDestination, const SubAddress& destination, const CAmount& nAmount, std::string sMemo, const TokenId& token_id = TokenId(), const CreateTransactionType& type = CreateTransactionType::NORMAL, const CAmount& minStake = 0); + static void AddAvailableCoins(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const wallet::CoinFilterParams& coins_params, std::vector& inputCandidates) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); + static void AddAvailableCoins(wallet::CWallet* wallet, blsct::KeyMan* blsct_km, const TokenId& token_id, const CreateTransactionType& type, std::vector& inputCandidates) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet); + +}; + +} // namespace blsct diff --git a/src/key_io.cpp b/src/key_io.cpp index 1d892ee967b87..015e37de98cd3 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include