-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.cpp
389 lines (313 loc) · 13.3 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/*
* Copyright (c) 2021 Vertices Network <cyril@vertices.network>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "vertices.h"
#include <vertices_log.h>
#include <unix_config.h>
#include <cstring>
#include <sodium.h>
#include <getopt.h>
#include "utils/base32.h"
#include "utils/base64.h"
typedef enum {
PAY_TX = 0,
APP_CALL_TX
} tx_type_t;
static ret_code_t
vertices_evt_handler(vtc_evt_t *evt);
static provider_info_t providers =
{.url = (char *) SERVER_URL, .port = SERVER_PORT, .header = (char *) SERVER_TOKEN_HEADER};
/// We store anything related to the account into the below structure
/// The private key is used outside of the Vertices library:
/// you don't have to pass the private key to the SDK as signing is done outside
typedef struct {
unsigned char private_key[ADDRESS_LENGTH]; //!< 32-bytes private key
account_info_t *vtc_account; //!< pointer to Vertices account data
} account_t;
// Alice's account is used to send data, keys will be retrived from config/key_files.txt
static account_t alice_account = {.private_key = {0}, .vtc_account = nullptr};
// Bob is receiving the money 😎
static account_t bob_account = {.private_key = {0}, .vtc_account = nullptr};
static vertex_t m_vertex = {
.provider = &providers,
.vertices_evt_handler = vertices_evt_handler
};
static ret_code_t
vertices_evt_handler(vtc_evt_t *evt) {
ret_code_t err_code = VTC_SUCCESS;
switch (evt->type) {
case VTC_EVT_TX_READY_TO_SIGN: {
signed_transaction_t *tx = nullptr;
err_code = vertices_event_tx_get(evt->bufid, &tx);
if (err_code == VTC_SUCCESS) {
LOG_DEBUG("About to sign tx: data length %lu", tx->payload_body_length);
// libsodium wants to have private and public keys concatenated
unsigned char keys[crypto_sign_ed25519_SECRETKEYBYTES] = {0};
memcpy(keys, alice_account.private_key, sizeof(alice_account.private_key));
memcpy(&keys[32],
alice_account.vtc_account->public_key,
ADDRESS_LENGTH);
// prepend "TX" to the payload before signing
unsigned char to_be_signed[tx->payload_body_length + 2];
to_be_signed[0] = 'T';
to_be_signed[1] = 'X';
// copy body
memcpy(&to_be_signed[2],
&tx->payload[tx->payload_header_length],
tx->payload_body_length);
// sign the payload
crypto_sign_ed25519_detached(tx->signature,
nullptr, to_be_signed, tx->payload_body_length + 2, keys);
char b64_signature[128] = {0};
size_t b64_signature_len = sizeof(b64_signature);
b64_encode((const char *) tx->signature,
sizeof(tx->signature),
b64_signature,
&b64_signature_len);
LOG_DEBUG("Signature %s (%zu bytes)", b64_signature, b64_signature_len);
// send event to send the signed TX
vtc_evt_t sched_evt = {.type = VTC_EVT_TX_SENDING, .bufid = evt->bufid};
err_code = vertices_event_schedule(&sched_evt);
}
}
break;
case VTC_EVT_TX_SENDING: {
// let's create transaction files which can then be used with `goal clerk ...`
signed_transaction_t *tx = nullptr;
err_code = vertices_event_tx_get(evt->bufid, &tx);
FILE *fstx = fopen(CONFIG_PATH "../signed_tx.bin", "wb");
if (fstx == nullptr) {
return VTC_ERROR_NOT_FOUND;
}
fwrite(tx->payload, tx->payload_header_length + tx->payload_body_length, 1, fstx);
fclose(fstx);
FILE *ftx = fopen(CONFIG_PATH "../tx.bin", "wb");
if (ftx == nullptr) {
return VTC_ERROR_NOT_FOUND;
}
// goal-generated transaction files are packed into a map of one element: `txn`.
// the one-element map takes 4 bytes into our message packed payload <=> `txn`
// we also add the `map` type before
// which results in 5-bytes to be added before the payload at `payload_offset`
char payload[tx->payload_body_length + 5];
payload[0] = (char) 0x81; // starting flag for map of one element
memcpy(&payload[1],
&tx->payload[tx->payload_header_length - 4],
tx->payload_body_length + 4);
fwrite(payload, sizeof payload, 1, ftx);
fclose(ftx);
}
break;
default:
break;
}
return err_code;
}
/// Create new random account
/// Account keys will be stored in files
static ret_code_t
create_new_account() {
ret_code_t err_code;
unsigned char seed[crypto_sign_ed25519_SEEDBYTES] = {0};
unsigned char ed25519_pk[crypto_sign_ed25519_PUBLICKEYBYTES];
LOG_WARNING("🧾 Creating new random account and storing it (path " CONFIG_PATH ")");
unsigned char ed25519_sk[crypto_sign_ed25519_SECRETKEYBYTES];
randombytes_buf(seed, sizeof(seed));
crypto_sign_ed25519_seed_keypair(ed25519_pk, ed25519_sk, seed);
memcpy(alice_account.private_key, ed25519_sk, sizeof(alice_account.private_key));
FILE *fw_priv = fopen(CONFIG_PATH "private_key.bin", "wb");
if (fw_priv == nullptr) {
LOG_ERROR("Cannot create " CONFIG_PATH "private_key.bin");
return VTC_ERROR_NOT_FOUND;
} else {
fwrite(ed25519_sk, 1, ADDRESS_LENGTH, fw_priv);
fclose(fw_priv);
}
// adding account, account address will be computed from binary public key
err_code = vertices_account_new_from_bin((char *) ed25519_pk, &alice_account.vtc_account);
VTC_ASSERT(err_code);
// we can now store the b32 address in a file
FILE *fw_pub = fopen(CONFIG_PATH "public_b32.txt", "w");
if (fw_pub != nullptr) {
size_t len = strlen(alice_account.vtc_account->public_b32);
fwrite(alice_account.vtc_account->public_b32, 1, len, fw_pub);
fwrite("\n", 1, 1, fw_pub);
fclose(fw_pub);
}
return err_code;
}
/// Source the account using private/public keys from files.
/// \return \c VTC_ERROR_NOT_FOUND account not found
static ret_code_t
load_existing_account() {
ret_code_t err_code;
char public_b32[PUBLIC_B32_STR_MAX_LENGTH] = {0};
size_t bytes_read = 0;
// we either create a new random account or load it from private and public key files.
// key files can also be generated using [`algokey`](https://developer.algorand.org/docs/reference/cli/algokey/generate/)
FILE *f_priv = fopen(CONFIG_PATH "private_key.bin", "rb");
if (f_priv != nullptr) {
LOG_INFO("🔑 Loading private key from: %s", CONFIG_PATH "private_key.bin");
bytes_read = fread(alice_account.private_key, 1, ADDRESS_LENGTH, f_priv);
fclose(f_priv);
}
if (f_priv == nullptr || bytes_read != ADDRESS_LENGTH) {
LOG_WARNING(
"🤔 private_key.bin does not exist or keys not found. You can pass the -n flag to create a new account");
return VTC_ERROR_NOT_FOUND;
}
FILE *f_pub = fopen(CONFIG_PATH "public_b32.txt", "r");
if (f_pub != nullptr) {
LOG_INFO("🔑 Loading public key from: %s", CONFIG_PATH "public_b32.txt");
bytes_read = fread(public_b32, 1, PUBLIC_B32_STR_MAX_LENGTH, f_pub);
fclose(f_pub);
size_t len = strlen(public_b32);
while (public_b32[len - 1] == '\n' || public_b32[len - 1] == '\r') {
public_b32[len - 1] = '\0';
len--;
}
}
if (f_pub == nullptr || bytes_read < ADDRESS_LENGTH) {
LOG_WARNING(
"🤔 public_b32.txt does not exist or keys not found. You can pass the -n flag to create a new account");
return VTC_ERROR_NOT_FOUND;
}
err_code = vertices_account_new_from_b32(public_b32, &alice_account.vtc_account);
VTC_ASSERT(err_code);
LOG_INFO("💳 Created Alice's account: %s", alice_account.vtc_account->public_b32);
return VTC_SUCCESS;
}
int
main(int argc, char *argv[]) {
ret_code_t err_code;
bool create_new = true; // bug fixing convert false to tru at first.
tx_type_t run_tx = PAY_TX;
int opt;
while ((opt = getopt(argc, argv, "npa")) != -1) {
switch (opt) {
case 'n': {
create_new = true;
}
break;
case 'p': {
run_tx = PAY_TX;
}
break;
case 'a': {
run_tx = APP_CALL_TX;
}
break;
default: {
fprintf(stderr,
"Usage:\n%s [-p|-a] [-n] \nSend signed transaction on the blockchain.\n-p (default)\tSend [p]ayment (Alice sends tokens to Bob)\n-a\t\t\t\tSend [a]pplication call (Alice sends integer value to application)\n-n\t\t\t\tCreate [n]ew account",
argv[0]);
exit(EXIT_FAILURE);
}
}
}
LOG_INFO("😎 Vertices SDK running on Unix-based OS");
int ret = sodium_init();
VTC_ASSERT_BOOL(ret == 0);
// create new vertex
err_code = vertices_new(&m_vertex);
VTC_ASSERT(err_code);
// making sure the provider is accessible
err_code = vertices_ping();
VTC_ASSERT(err_code);
// ask for provider version
provider_version_t version = {0};
err_code = vertices_version(&version);
if (err_code == VTC_ERROR_OFFLINE) {
LOG_WARNING("Version might not be accurate: old value is being used");
} else {
VTC_ASSERT(err_code);
}
LOG_INFO("🏎 Running on %s v.%u.%u.%u",
version.network,
version.major,
version.minor,
version.patch);
// Several ways to create/load accounts:
if (create_new) {
// 1) create new one
err_code = create_new_account();
VTC_ASSERT(err_code);
} else {
// 2) from files
err_code = load_existing_account();
VTC_ASSERT(err_code);
}
// 3) from b32 address
// Note: creating a receiver account is not mandatory to send money to the account
// but we can use it to load the public key from the account address
err_code = vertices_account_new_from_b32((char *) ACCOUNT_RECEIVER, &bob_account.vtc_account);
VTC_ASSERT(err_code);
LOG_INFO("🤑 %f Algos on Alice's account (%s)",
alice_account.vtc_account->amount / 1.e6,
alice_account.vtc_account->public_b32);
if (alice_account.vtc_account->amount < 1001000) {
LOG_ERROR(
"🙄 Amount available on account is too low to pass a transaction, consider adding Algos");
LOG_INFO("👉 Go to https://bank.testnet.algorand.network/, dispense Algos to: %s",
alice_account.vtc_account->public_b32);
LOG_INFO("😎 Then wait for a few seconds for transaction to pass...");
return 0;
}
switch (run_tx) {
case PAY_TX: {
// send assets from account 0 to account 1
char *notes = (char *) "Alice sent 1 Algo to Bob";
err_code =
vertices_transaction_pay_new(alice_account.vtc_account,
(char *) bob_account.vtc_account->public_b32 /* or ACCOUNT_RECEIVER */,
AMOUNT_SENT,
notes);
VTC_ASSERT(err_code);
}
break;
case APP_CALL_TX: {
// get application information
LOG_INFO("Application %u, global states", APP_ID);
app_values_t app_kv = {0};
err_code = vertices_application_get(APP_ID, &app_kv);
VTC_ASSERT(err_code);
for (uint32_t i = 0; i < app_kv.count; ++i) {
if (app_kv.values[i].type == VALUE_TYPE_INTEGER) {
LOG_INFO("%s: %llu", app_kv.values[i].name, (long long unsigned) app_kv.values[i].value_uint);
} else if (app_kv.values[i].type == VALUE_TYPE_BYTESLICE) {
LOG_INFO("%s: %s", app_kv.values[i].name, app_kv.values[i].value_slice);
}
}
// send application call
app_values_t kv = {0};
kv.count = 1;
kv.values[0].type = VALUE_TYPE_INTEGER;
kv.values[0].value_uint = 32;
err_code = vertices_transaction_app_call(alice_account.vtc_account, APP_ID, &kv);
VTC_ASSERT(err_code);
}
break;
default:
LOG_ERROR("Unknown action to run");
}
unsigned char *txID = nullptr;
txID = new unsigned char[TRANSACTION_HASH_STR_MAX_LENGTH];
// processing
size_t queue_size = 1;
while (queue_size && err_code == VTC_SUCCESS) {
err_code = vertices_event_process(&queue_size, txID);
VTC_ASSERT(err_code);
}
if(err_code == VTC_SUCCESS)
{
LOG_INFO("👉 Haha This is transaction ID: %s",txID);
}
free(txID);
// delete the created accounts from the Vertices wallet
err_code = vertices_account_free(alice_account.vtc_account);
VTC_ASSERT(err_code);
err_code = vertices_account_free(bob_account.vtc_account);
VTC_ASSERT(err_code);
}