8#include "bech32/bech32.h"
9#include "bech32/util/strencodings.h"
10#include "crypto/sha256.h"
27 auto decode(
const std::string& addr_str)
28 -> std::optional<cbdc::hash_t> {
32 const auto [hrp, enc_data] = bech32::Decode(addr_str);
33 if(hrp != cbdc::config::bech32_hrp) {
34 std::cout <<
"Invalid address encoding" << std::endl;
37 auto data = std::vector<uint8_t>();
38 ConvertBits<bech32_bits_per_symbol, bits_per_byte, false>(
47 !=
static_cast<uint8_t
>(
49 || data.size() != pubkey.size() + 1) {
50 std::cout <<
"Address is not a supported type" << std::endl;
54 data.erase(data.begin());
55 std::copy_n(data.begin(), pubkey.size(), pubkey.begin());
62 std::shared_ptr<logging::log> logger,
63 std::string wallet_file,
64 std::string client_file)
65 : m_opts(std::move(opts)),
66 m_logger(std::move(logger)),
67 m_sentinel_client(m_opts.m_sentinel_endpoints, m_logger),
68 m_client_file(std::move(client_file)),
69 m_wallet_file(std::move(wallet_file)) {}
72 if(std::filesystem::exists(m_wallet_file)) {
73 m_wallet.load(m_wallet_file);
75 m_logger->warn(
"Existing wallet file not found");
80 if(!m_sentinel_client.init()) {
81 m_logger->error(
"Failed to initialize sentinel client.");
85 return init_derived();
90 ss << config::currency_symbol << std::fixed << std::setprecision(2)
91 <<
static_cast<double>(val) / 100.0;
97 auto mint_tx = m_wallet.mint_new_coins(n_outputs, output_val);
98 import_transaction(mint_tx);
101 if(!send_mint_tx(mint_tx)) {
102 m_logger->error(
"Failed to send mint tx");
115 m_pending_spend.insert({in.hash(), in});
122 -> std::optional<transaction::full_tx> {
123 auto tx = m_wallet.send_to(value, payee,
true);
124 if(!tx.has_value()) {
128 register_pending_tx(tx.value());
134 -> std::pair<std::optional<transaction::full_tx>,
135 std::optional<cbdc::sentinel::execute_response>> {
136 static constexpr auto null_return
137 = std::make_pair(std::nullopt, std::nullopt);
139 auto spend_tx = create_transaction(value, payee);
140 if(!spend_tx.has_value()) {
141 m_logger->error(
"Failed to generate wallet spend tx.");
145 auto res = send_transaction(spend_tx.value());
146 if(!res.has_value()) {
150 return std::make_pair(spend_tx.value(), res.value());
154 -> std::pair<std::optional<transaction::full_tx>,
155 std::optional<cbdc::sentinel::execute_response>> {
156 static constexpr auto null_return
157 = std::make_pair(std::nullopt, std::nullopt);
159 auto tx = m_wallet.fan(count, value, payee,
true);
160 if(!tx.has_value()) {
161 m_logger->error(
"Failed to generate wallet fan tx");
165 register_pending_tx(tx.value());
167 auto res = send_transaction(tx.value());
168 if(!res.has_value()) {
172 return std::make_pair(tx.value(), res.value());
176 -> std::optional<cbdc::sentinel::execute_response> {
177 import_transaction(tx);
179 auto res = m_sentinel_client.execute_transaction(tx);
180 if(!res.has_value()) {
181 m_logger->error(
"Failed to send transaction to sentinel.");
185 m_logger->info(
"Sentinel responded:",
199 -> std::vector<transaction::input> {
208 m_logger->warn(
"Ignoring non-spendable input");
213 auto addr = m_wallet.generate_key();
219 return m_wallet.balance();
223 return m_wallet.count();
227 return m_pending_txs.size();
231 return m_pending_inputs.size();
243 for(
auto& it : m_pending_txs) {
244 for(
auto& in : it.second.m_inputs) {
255 const auto it = m_pending_txs.find(tx_id);
256 if(it != m_pending_txs.end()) {
257 auto tx = it->second;
258 m_pending_txs.erase(it);
263 const auto ps_it = m_pending_spend.find(i.hash());
264 if(ps_it != m_pending_spend.end()) {
265 auto pending = check_pending(i);
267 m_pending_spend.erase(ps_it);
268 m_wallet.confirm_inputs({i});
284 const auto it = m_pending_txs.find(tx_id);
285 if(it != m_pending_txs.end()) {
286 m_wallet.confirm_transaction(it->second);
287 for(
auto& i : it->second.m_inputs) {
288 const auto ps_it = m_pending_spend.find(i.hash());
289 if(ps_it != m_pending_spend.end()) {
290 m_pending_spend.erase(ps_it);
293 m_pending_txs.erase(it);
297 const auto pi_it = m_pending_inputs.find(tx_id);
298 if(pi_it != m_pending_inputs.end()) {
299 m_wallet.confirm_inputs({pi_it->second});
300 m_pending_inputs.erase(pi_it);
309 void client::load_client_state() {
310 std::ifstream client_file(m_client_file,
311 std::ios::binary | std::ios::in);
312 if(client_file.good()) {
314 if(!(deser >> m_pending_txs >> m_pending_inputs
315 >> m_pending_spend)) {
316 m_logger->fatal(
"Error deserializing client file");
319 m_logger->warn(
"Existing client file not found");
323 void client::save_client_state() {
324 std::ofstream client_file(m_client_file,
325 std::ios::binary | std::ios::trunc
327 if(!client_file.good()) {
328 m_logger->fatal(
"Failed to open client file for saving");
331 if(!(ser << m_pending_txs << m_pending_inputs << m_pending_spend)) {
332 m_logger->fatal(
"Failed to write client data");
336 void client::save() {
338 m_wallet.
save(m_wallet_file);
342 -> std::unordered_map<
hash_t, transaction::full_tx, hashing::null> {
343 return m_pending_txs;
347 -> std::unordered_map<
hash_t, transaction::input, hashing::null> {
348 return m_pending_inputs;
auto pending_inputs() const -> std::unordered_map< hash_t, transaction::input, hashing::null >
Returns the set of imported inputs from senders.
auto fan(uint32_t count, uint32_t value, const pubkey_t &payee) -> std::pair< std::optional< transaction::full_tx >, std::optional< cbdc::sentinel::execute_response > >
Send a specified number of fixed-value outputs from this client's wallet to a target address.
auto init() -> bool
Initializes the client.
auto pending_tx_count() -> size_t
Returns the number of unconfirmed transactions.
auto confirm_transaction(const hash_t &tx_id) -> bool
Confirms the transaction with the given ID.
client(cbdc::config::options opts, std::shared_ptr< logging::log > logger, std::string wallet_file, std::string client_file)
Constructor.
auto balance() -> uint64_t
Returns the balance in this client's wallet.
auto new_address() -> pubkey_t
Generates a new wallet address that other clients can use to send money to this client using send.
auto abandon_transaction(const hash_t &tx_id) -> bool
Abandons a transaction currently awaiting confirmation.
void sign_transaction(transaction::full_tx &tx)
Signs the given transaction for as far as client's wallet contains the transaction's keys.
auto pending_txs() const -> std::unordered_map< hash_t, transaction::full_tx, hashing::null >
Returns the set of transactions pending confirmation.
void import_send_input(const transaction::input &in)
Imports transaction data from a sender.
auto send_transaction(const transaction::full_tx &tx) -> std::optional< cbdc::sentinel::execute_response >
Send the given transaction to the sentinel.
auto create_transaction(uint32_t value, const pubkey_t &payee) -> std::optional< transaction::full_tx >
Create a new transaction.
static auto export_send_inputs(const transaction::full_tx &send_tx, const pubkey_t &payee) -> std::vector< transaction::input >
Extracts the transaction data that recipients need from senders to confirm pending transfers.
auto utxo_count() -> size_t
Returns the number of UTXOs in this client's wallet.
@ public_key
Pay-to-Public-Key (P2PK) address data.
auto check_pending(const transaction::input &inp) -> bool
Checks the client's pending transaction set for the specified transaction.
auto pending_input_count() -> size_t
Returns the number of pending received inputs.
auto send(uint32_t value, const pubkey_t &payee) -> std::pair< std::optional< transaction::full_tx >, std::optional< cbdc::sentinel::execute_response > >
Send a specified amount from this client's wallet to a target address.
auto mint(size_t n_outputs, uint32_t output_val) -> transaction::full_tx
Creates the specified number spendable outputs each with the specified value.
static auto print_amount(uint64_t val) -> std::string
Format a value given in currency base units as USD.
Implementation of serializer for reading from a std::istream.
Implementation of serializer for writing to a std::ostream.
static auto export_send_inputs(const full_tx &send_tx, const pubkey_t &payee) -> std::vector< input >
Extracts the transaction data that recipients need from senders to confirm pending transfers.
void sign(full_tx &tx) const
Signs each of the transaction's inputs using Schnorr signatures.
void save(const std::string &wallet_file) const
Save the state of the wallet to a binary data file.
auto is_spendable(const input &in) const -> bool
Checks if the input is spendable by the current wallet.
Tools for reading options from a configuration file and building application-specific parameter sets ...
auto decode(const std::string &addr_str) -> std::optional< cbdc::hash_t >
auto to_string(tx_status status) -> std::string
Return a human-readable string describing a tx_status.
@ confirmed
Executed to completion.
auto tx_id(const full_tx &tx) noexcept -> hash_t
Calculates the unique hash of a full transaction.
std::array< unsigned char, cbdc::hash_size > hash_t
SHA256 hash container.
auto to_string(const hash_t &val) -> std::string
Converts a hash to a hexadecimal string.
std::array< unsigned char, pubkey_len > pubkey_t
A public key of a public/private keypair.
Project-wide configuration options.
std::vector< input > m_inputs
The set of inputs for the transaction.
hash_t m_tx_id
The hash of the transaction which created the out_point.