OpenCBDC Transaction Processor
Loading...
Searching...
No Matches
client-cli.cpp
Go to the documentation of this file.
1// Copyright (c) 2021 MIT Digital Currency Initiative,
2// Federal Reserve Bank of Boston
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include "atomizer_client.hpp"
7#include "bech32/bech32.h"
8#include "bech32/util/strencodings.h"
9#include "client.hpp"
10#include "crypto/sha256.h"
11#include "twophase_client.hpp"
15
16#include <future>
17#include <iostream>
18
19auto mint_command(cbdc::client& client, const std::vector<std::string>& args)
20 -> bool {
21 static constexpr auto min_mint_arg_count = 7;
22 if(args.size() < min_mint_arg_count) {
23 std::cerr << "Mint requires args <n outputs> <output value>"
24 << std::endl;
25 return false;
26 }
27
28 const auto n_outputs = std::stoull(args[5]);
29 const auto output_val = std::stoul(args[6]);
30
31 const auto mint_tx
32 = client.mint(n_outputs, static_cast<uint32_t>(output_val));
33 std::cout << cbdc::to_string(cbdc::transaction::tx_id(mint_tx))
34 << std::endl;
35 return true;
36}
37
39 const std::optional<cbdc::transaction::full_tx>& tx,
40 const std::optional<cbdc::sentinel::execute_response>& resp,
41 const cbdc::hash_t& pubkey) {
42 std::cout << "tx_id:" << std::endl
44 << std::endl;
45 const auto inputs = cbdc::client::export_send_inputs(tx.value(), pubkey);
46 for(const auto& inp : inputs) {
47 auto buf = cbdc::make_buffer(inp);
48 std::cout << "Data for recipient importinput:" << std::endl
49 << buf.to_hex() << std::endl;
50 }
51
52 if(resp.has_value()) {
53 std::cout << "Sentinel responded: "
54 << cbdc::sentinel::to_string(resp.value().m_tx_status)
55 << std::endl;
56 if(resp.value().m_tx_error.has_value()) {
57 std::cout << "Validation error: "
59 resp.value().m_tx_error.value())
60 << std::endl;
61 }
62 }
63}
64
65auto send_command(cbdc::client& client, const std::vector<std::string>& args)
66 -> bool {
67 static constexpr auto min_send_arg_count = 7;
68 if(args.size() < min_send_arg_count) {
69 std::cerr << "Send requires args <value> <pubkey>" << std::endl;
70 return false;
71 }
72
73 const auto value = std::stoul(args[5]);
74 static constexpr auto address_arg_idx = 6;
75 auto pubkey = cbdc::address::decode(args[address_arg_idx]);
76 if(!pubkey.has_value()) {
77 std::cout << "Could not decode address" << std::endl;
78 return false;
79 }
80
81 const auto [tx, resp]
82 = client.send(static_cast<uint32_t>(value), pubkey.value());
83 if(!tx.has_value()) {
84 std::cout << "Could not generate valid send tx." << std::endl;
85 return false;
86 }
87
88 print_tx_result(tx, resp, pubkey.value());
89 return true;
90}
91
92auto fan_command(cbdc::client& client, const std::vector<std::string>& args)
93 -> bool {
94 static constexpr auto min_fan_arg_count = 8;
95 if(args.size() < min_fan_arg_count) {
96 std::cerr << "Fan requires args <count> <value> <pubkey>" << std::endl;
97 return false;
98 }
99
100 const auto value = std::stoul(args[6]);
101 const auto count = std::stoul(args[5]);
102
103 static constexpr auto address_arg_idx = 7;
104 auto pubkey = cbdc::address::decode(args[address_arg_idx]);
105 if(!pubkey.has_value()) {
106 std::cout << "Could not decode address" << std::endl;
107 return false;
108 }
109
110 const auto [tx, resp] = client.fan(static_cast<uint32_t>(count),
111 static_cast<uint32_t>(value),
112 pubkey.value());
113 if(!tx.has_value()) {
114 std::cout << "Could not generate valid send tx." << std::endl;
115 return false;
116 }
117
118 print_tx_result(tx, resp, pubkey.value());
119 return true;
120}
121
123 const auto addr = client.new_address();
124 auto addr_vec
125 = std::vector<uint8_t>(sizeof(cbdc::client::address_type::public_key)
126 + std::tuple_size<decltype(addr)>::value);
127 addr_vec[0] = static_cast<uint8_t>(cbdc::client::address_type::public_key);
128 std::copy_n(addr.begin(),
129 addr.size(),
130 addr_vec.begin()
132 auto data = std::vector<uint8_t>();
133 ConvertBits<cbdc::address::bits_per_byte,
134 cbdc::address::bech32_bits_per_symbol,
135 true>(
136 [&](uint8_t c) {
137 data.push_back(c);
138 },
139 addr_vec.begin(),
140 addr_vec.end());
141 std::cout << bech32::Encode(cbdc::config::bech32_hrp, data) << std::endl;
142}
143
145 const std::vector<std::string>& args) -> bool {
146 static constexpr auto input_arg_idx = 5;
147 auto buffer = cbdc::buffer::from_hex(args[input_arg_idx]);
148 if(!buffer.has_value()) {
149 std::cout << "Invalid input encoding." << std::endl;
150 return false;
151 }
152
153 auto in = cbdc::from_buffer<cbdc::transaction::input>(buffer.value());
154 if(!in.has_value()) {
155 std::cout << "Invalid input" << std::endl;
156 return false;
157 }
158 client.import_send_input(in.value());
159 return true;
160}
161
163 const std::vector<std::string>& args) -> bool {
164 const auto tx_id = cbdc::hash_from_hex(args[5]);
165 auto success = client.confirm_transaction(tx_id);
166 if(!success) {
167 std::cout << "Unknown TXID" << std::endl;
168 return false;
169 }
170 std::cout << "Confirmed. Balance: "
171 << cbdc::client::print_amount(client.balance())
172 << " UTXOs: " << client.utxo_count() << std::endl;
173 return true;
174}
175
176// LCOV_EXCL_START
177auto main(int argc, char** argv) -> int {
178 auto args = cbdc::config::get_args(argc, argv);
179 static constexpr auto min_arg_count = 5;
180 if(args.size() < min_arg_count) {
181 std::cerr << "Usage: " << args[0]
182 << " <config file> <client file> <wallet file> <command>"
183 << " <args...>" << std::endl;
184 return 0;
185 }
186
187 auto cfg_or_err = cbdc::config::load_options(args[1]);
188 if(std::holds_alternative<std::string>(cfg_or_err)) {
189 std::cerr << "Error loading config file: "
190 << std::get<std::string>(cfg_or_err) << std::endl;
191 return -1;
192 }
193
194 auto opts = std::get<cbdc::config::options>(cfg_or_err);
195
196 SHA256AutoDetect();
197
198 const auto wallet_file = args[3];
199 const auto client_file = args[2];
200
201 auto logger = std::make_shared<cbdc::logging::log>(
202 cbdc::config::defaults::log_level);
203
204 auto client = std::unique_ptr<cbdc::client>();
205 if(opts.m_twophase_mode) {
206 client = std::make_unique<cbdc::twophase_client>(opts,
207 logger,
208 wallet_file,
209 client_file);
210 } else {
211 client = std::make_unique<cbdc::atomizer_client>(opts,
212 logger,
213 wallet_file,
214 client_file);
215 }
216
217 if(!client->init()) {
218 return -1;
219 }
220
221 const auto command = std::string(args[4]);
222 if(command == "mint") {
223 if(!mint_command(*client, args)) {
224 return -1;
225 }
226 } else if(command == "send") {
227 if(!send_command(*client, args)) {
228 return -1;
229 }
230 } else if(command == "fan") {
231 if(!fan_command(*client, args)) {
232 return -1;
233 }
234 } else if(command == "sync") {
235 client->sync();
236 } else if(command == "newaddress") {
237 newaddress_command(*client);
238 } else if(command == "info") {
239 const auto balance = client->balance();
240 const auto n_txos = client->utxo_count();
241 std::cout << "Balance: " << cbdc::client::print_amount(balance)
242 << ", UTXOs: " << n_txos
243 << ", pending TXs: " << client->pending_tx_count()
244 << std::endl;
245 } else if(command == "importinput") {
246 if(!importinput_command(*client, args)) {
247 return -1;
248 }
249 } else if(command == "confirmtx") {
250 if(!confirmtx_command(*client, args)) {
251 return -1;
252 }
253 } else {
254 std::cerr << "Unknown command" << std::endl;
255 }
256
257 // TODO: check that the send queue has drained before closing
258 // the network. For now, just sleep.
259 static constexpr auto shutdown_delay = std::chrono::milliseconds(100);
260 std::this_thread::sleep_for(shutdown_delay);
261
262 return 0;
263}
264// LCOV_EXCL_STOP
static auto from_hex(const std::string &hex) -> std::optional< cbdc::buffer >
Creates a new buffer from the provided hex string.
Definition buffer.cpp:85
External client for sending new transactions to the system.
auto new_address() -> pubkey_t
Generates a new wallet address that other clients can use to send money to this client using send.
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.
@ public_key
Pay-to-Public-Key (P2PK) address data.
static auto print_amount(uint64_t val) -> std::string
Format a value given in currency base units as USD.
auto main(int argc, char **argv) -> int
auto send_command(cbdc::client &client, const std::vector< std::string > &args) -> bool
auto confirmtx_command(cbdc::client &client, const std::vector< std::string > &args) -> bool
auto mint_command(cbdc::client &client, const std::vector< std::string > &args) -> bool
void newaddress_command(cbdc::client &client)
auto importinput_command(cbdc::client &client, const std::vector< std::string > &args) -> bool
auto fan_command(cbdc::client &client, const std::vector< std::string > &args) -> bool
void print_tx_result(const std::optional< cbdc::transaction::full_tx > &tx, const std::optional< cbdc::sentinel::execute_response > &resp, const cbdc::hash_t &pubkey)
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 load_options(const std::string &config_file) -> std::variant< options, std::string >
Loads options from the given config file and check for invariants.
Definition config.cpp:668
auto get_args(int argc, char **argv) -> std::vector< std::string >
Converts c-args from an executable's main function into a vector of strings.
Definition config.cpp:751
auto to_string(tx_status status) -> std::string
Return a human-readable string describing a tx_status.
auto to_string(cbdc::transaction::validation::tx_error_code err) -> std::string
auto tx_id(const full_tx &tx) noexcept -> hash_t
Calculates the unique hash of a full transaction.
auto from_buffer(nuraft::buffer &buf) -> std::optional< T >
Deserialize object of given type from a nuraft::buffer.
std::array< unsigned char, cbdc::hash_size > hash_t
SHA256 hash container.
auto hash_from_hex(const std::string &val) -> hash_t
Parses a hexadecimal representation of a hash.
auto to_string(const hash_t &val) -> std::string
Converts a hash to a hexadecimal string.
auto make_buffer(const T &obj) -> std::enable_if_t< std::is_same_v< B, nuraft::ptr< nuraft::buffer > >, nuraft::ptr< nuraft::buffer > >
Serialize object into nuraft::buffer using a cbdc::nuraft_serializer.