forked from jannotti/cpp-algorand-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathalgorand.h
527 lines (436 loc) · 16.5 KB
/
algorand.h
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
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
#ifndef ALGORAND_H
#define ALGORAND_H
#include <memory>
#define RAPIDJSON_HAS_STDSTRING 1
#include "rapidjson/document.h"
#include <msgpack.hpp>
std::ostream& operator<<(std::ostream& os, const rapidjson::Value&);
std::string json_to_string(const rapidjson::Value&);
std::string maybe_env(std::string name, std::string def = "");
std::string require_env(std::string name);
struct JsonResponse {
int status;
std::unique_ptr<rapidjson::Document> json;
rapidjson::Value& operator[](const std::string& name) const {
return (*json)[name];
}
bool succeeded() const { return status == 200; }
};
std::ostream& operator<<(std::ostream& os, const JsonResponse& jr);
typedef std::vector<unsigned char> bytes;
class Address {
public:
Address(); // Constructs the ZERO address
Address(std::string b32form);
Address(bytes public_key);
std::string as_string;
bytes public_key;
bool is_zero() const;
private:
Address(std::string s, bytes with_csum);
Address(bytes public_key, bytes with_csum);
};
inline bool operator==(const Address& lhs, const Address& rhs) {
return lhs.as_string == rhs.as_string && lhs.public_key == rhs.public_key;
}
inline bool operator!=(const Address& lhs, const Address& rhs) {
return !(lhs == rhs);
}
inline bool operator <(const Address& lhs, const Address& rhs ) {
return lhs.as_string < rhs.as_string;
}
std::ostream& operator<<(std::ostream& os, const Address& addr);
class Account {
public:
Account(std::string address);
Account(Address address);
Account(bytes public_key, bytes secret_key);
Account(std::pair<bytes,bytes> key_pair);
static Account from_mnemonic(std::string mnemonic);
static std::pair<bytes,bytes> generate_keys();
static std::pair<bytes,bytes> generate_keys(bytes seed);
std::string mnemonic() const;
bytes seed() const;
bytes sign(std::string prefix, bytes msg) const;
bytes sign(bytes msg) const;
const bytes public_key() const { return address.public_key; }
const Address address;
const bytes secret_key; // empty() if created from an address, not key
};
std::ostream& operator<<(std::ostream& os, const Account& acct);
class SignedTransaction;
class AssetParams {
public:
uint64_t total = 0;
uint64_t decimals = 0;
bool default_frozen = false;
std::string unit_name;
std::string asset_name;
std::string url;
bytes meta_data_hash;
Address manager_addr;
Address reserve_addr;
Address freeze_addr;
Address clawback_addr;
template <typename Stream>
msgpack::packer<Stream>& pack(msgpack::packer<Stream>& o) const;
int key_count() const;
};
class StateSchema {
public:
StateSchema(int, int) : StateSchema() {}
StateSchema() : ints(0), byte_slices(0) {}
uint64_t ints = 0;
uint64_t byte_slices = 0;
template <typename Stream>
msgpack::packer<Stream>& pack(msgpack::packer<Stream>& o) const;
int key_count() const;
};
class LogicSig {
public:
LogicSig(bytes logic = {}, std::vector<bytes> args = {}, bytes sig = {}) :
logic(logic), args(args), sig(sig) {}
bool is_delegated() const { return !logic.empty(); }
template <typename Stream>
msgpack::packer<Stream>& pack(msgpack::packer<Stream>& o) const;
/* Create a new logicsig with the same program, but delegated by the Account. */
LogicSig sign(Account) const;
bytes logic;
std::vector<bytes> args;
bytes sig;
};
class Subsig {
public:
Subsig(bytes public_key, bytes signature={});
template <typename Stream>
msgpack::packer<Stream>& pack(msgpack::packer<Stream>& o) const;
bytes public_key;
bytes signature;
};
class MultiSig {
public:
MultiSig(std::vector<Address> addrs={}, uint64_t threshold=0);
template <typename Stream>
msgpack::packer<Stream>& pack(msgpack::packer<Stream>& o) const;
/* Create a new Multisig with the extra signature of Account */
MultiSig sign(Account) const;
std::vector<Subsig> sigs;
uint64_t threshold;
uint64_t version = 1;
};
/* We use a single transaction class to represent all transaction
types. While it might seem natural to have Payment, AssetCreate
and so on as subclasses, it would complicate msgpacking. Standard
msgpack does not "omitempty" (as we must to be compatible with
algod/spec), so we need to use the lower-level packing routines.
It seems easier to consolidate that in one place than to create an
interface for subclasses that would allow them to a) report how
many keys they intend to populate and b) return the pairs, and c)
sort them before packing them from the top.
We'd then also need some sort of "virtual constructor" pattern to
unpack Transactions into the right subclass.
PS. This more closely models the implementation in algod.
*/
class Transaction {
Transaction(Address sender, std::string tx_type);
public:
static Transaction payment(Address sender,
Address receiver, uint64_t amount, Address close_to,
uint64_t fee,
uint64_t first_valid, uint64_t last_valid,
std::string genesis_id, bytes genesis_hash,
bytes lease, bytes note, Address rekey_to);
static Transaction key_registration(Address sender,
bytes vote_pk,
bytes selection_pk,
uint64_t vote_first,
uint64_t vote_last,
uint64_t vote_key_dilution,
bool nonparticipation,
uint64_t fee,
uint64_t first_valid, uint64_t last_valid,
std::string genesis_id, bytes genesis_hash,
bytes lease, bytes note, Address rekey_to);
static Transaction asset_config(Address sender,
uint64_t asset_id, AssetParams asset_params,
uint64_t fee,
uint64_t first_valid, uint64_t last_valid,
std::string genesis_id, bytes genesis_hash,
bytes lease, bytes note, Address rekey_to);
static Transaction asset_transfer(Address sender,
uint64_t asset_id, uint64_t asset_amount,
Address asset_sender,
Address asset_receiver,
Address asset_close_to,
uint64_t fee,
uint64_t first_valid, uint64_t last_valid,
std::string genesis_id, bytes genesis_hash,
bytes lease, bytes note, Address rekey_to);
static Transaction asset_freeze(Address sender,
Address freeze_account,
uint64_t freeze_asset,
bool asset_frozen,
uint64_t fee,
uint64_t first_valid, uint64_t last_valid,
std::string genesis_id, bytes genesis_hash,
bytes lease, bytes note, Address rekey_to);
static Transaction app_call(Address sender,
uint64_t application_id,
uint64_t on_complete,
std::vector<Address> accounts,
bytes approval_program, bytes clear_state_program,
std::vector<bytes> app_arguments,
std::vector<uint64_t> foreign_apps,
std::vector<uint64_t> foreign_assets,
StateSchema globals, StateSchema locals,
uint64_t fee,
uint64_t first_valid, uint64_t last_valid,
std::string genesis_id, bytes genesis_hash,
bytes lease, bytes note, Address rekey_to);
SignedTransaction sign(Account) const;
SignedTransaction sign(LogicSig) const;
// Field names and sections are taken from:
// https://developer.algorand.org/docs/reference/transactions/
// Header
uint64_t fee = 1000; // required parameter, but a nice safety
uint64_t first_valid;
bytes genesis_hash;
uint64_t last_valid;
Address sender;
std::string tx_type;
std::string genesis_id;
bytes group;
bytes lease;
bytes note;
Address rekey_to;
// Payment
Address receiver;
uint64_t amount = 0;
Address close_to;
// Key Registration
bytes vote_pk;
bytes selection_pk;
uint64_t vote_first = 0;
uint64_t vote_last = 0;
uint64_t vote_key_dilution = 0;
bool nonparticipation = false;
// Asset Config
uint64_t config_asset = 0;
AssetParams asset_params;
// Asset Transfer
uint64_t xfer_asset = 0;
uint64_t asset_amount = 0;
Address asset_sender;
Address asset_receiver;
Address asset_close_to;
// Asset Freeze
uint64_t freeze_asset = 0;
Address freeze_account;
bool asset_frozen = false;
// Application Call
uint64_t application_id = 0;
uint64_t on_complete = 0;
std::vector<Address> accounts;
bytes approval_program;
bytes clear_state_program;
std::vector<bytes> app_arguments;
std::vector<uint64_t> foreign_apps;
std::vector<uint64_t> foreign_assets;
StateSchema globals;
StateSchema locals;
template <typename Stream>
msgpack::packer<Stream>& pack(msgpack::packer<Stream>& o) const;
int key_count() const;
bytes encode() const;
};
std::ostream& operator<<(std::ostream& os, const Transaction& txn);
class SignedTransaction {
public:
SignedTransaction(const Transaction& txn, bytes signature);
SignedTransaction(const Transaction& txn, LogicSig logic);
SignedTransaction(const Transaction& txn, MultiSig multi);
bytes encode() const;
template <typename Stream>
msgpack::packer<Stream>& pack(msgpack::packer<Stream>& o) const;
// Reconsider macro use once we do unpack for Transaction
// MSGPACK_DEFINE_MAP(sig, txn);
private:
bytes sig;
LogicSig lsig;
MultiSig msig;
Address signer;
Transaction txn;
};
namespace msgpack {
MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {
namespace adaptor {
template<>
struct pack<Address> {
template <typename Stream>
packer<Stream>&
operator()(msgpack::packer<Stream>& o, Address const& v) const {
// We can't use MSGPACK_DEFINE because we don't want to
// encode an "outer" object here, we just want an Address to
// encode the public_key as if that was the whole object.
return o.pack(v.public_key);
}
};
template<>
struct pack<LogicSig> {
template <typename Stream>
packer<Stream>&
operator()(msgpack::packer<Stream>& o, LogicSig const& v) const {
return v.pack<Stream>(o);
}
};
template<>
struct pack<Subsig> {
template <typename Stream>
packer<Stream>&
operator()(msgpack::packer<Stream>& o, Subsig const& v) const {
return v.pack<Stream>(o);
}
};
template<>
struct pack<MultiSig> {
template <typename Stream>
packer<Stream>&
operator()(msgpack::packer<Stream>& o, MultiSig const& v) const {
return v.pack<Stream>(o);
}
};
template<>
struct pack<Transaction> {
template <typename Stream>
packer<Stream>&
operator()(msgpack::packer<Stream>& o, Transaction const& v) const {
// We don't use the MSGPACK_DEFINE_MAP macro because we need
// to "omitempty" for compatibility. That requires counting
// keys first, to size the map, and then packing them (in
// lexographical order).
return v.pack<Stream>(o);
}
};
template<>
struct pack<SignedTransaction> {
template <typename Stream>
packer<Stream>&
operator()(msgpack::packer<Stream>& o, SignedTransaction const& v) const {
// We don't use the MSGPACK_DEFINE_MAP macro because
// Transaction has no unpacking support yet.
return v.pack<Stream>(o);
}
};
template<>
struct pack<AssetParams> {
template <typename Stream>
packer<Stream>&
operator()(msgpack::packer<Stream>& o, AssetParams const& v) const {
// "omitempty" problem, and special Address handling
return v.pack<Stream>(o);
}
};
template<>
struct pack<StateSchema> {
template <typename Stream>
packer<Stream>&
operator()(msgpack::packer<Stream>& o, StateSchema const& v) const {
// "omitempty" problem
return v.pack<Stream>(o);
}
};
} // namespace adaptor
} // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
}
class RestClient {
public:
RestClient(std::string prefix, std::string authorization) :
prefix(prefix), authorization(authorization) { }
/**
* @brief Return the requested information from the API using method
* @param route API route.
* @param method HTTP method to make the request with
* @param request_body raw bytes to be sent as body of request
* @return JsonResponse with the status code and JSON value from response
*/
JsonResponse api(const std::string& route,
const std::string& method,
const std::string& request_body = "");
/**
* @brief Return the requested information from the API using a GET
* @param route API route.
* @return string containing the requested information.
*/
JsonResponse get(const std::string& route);
/**
* @brief Return the requested information from the API using a POST
* @param route API route.
* @param body Raw bytes to send as body. "" means no body.
* @return string containing the requested information.
*/
JsonResponse post(const std::string& route, const std::string& body = "");
protected:
std::string prefix;
std::string authorization;
};
class AlgodClient : public RestClient {
public:
/**
* @brief Initialize the client. Reads ALGOD_ADDRESS, ALGOD_TOKEN
* from environment.
*/
AlgodClient();
/**
* @brief Initialize the client with passed address for algod and API token.
*/
AlgodClient(std::string address, std::string token);
JsonResponse genesis(void);
bool healthy(void);
std::string metrics(void);
virtual std::string account_url(std::string address);
JsonResponse account(std::string address);
JsonResponse account(const Address& addr) { return account(addr.as_string); }
JsonResponse account(const Account& acct) { return account(acct.address); }
JsonResponse transactions_pending(std::string address, unsigned max = 0);
JsonResponse application(std::string id);
JsonResponse application(uint64_t id) { return asset(std::to_string(id)); }
virtual std::string asset_url(std::string id);
JsonResponse asset(std::string id);
JsonResponse asset(uint64_t id) { return asset(std::to_string(id)); }
JsonResponse block(uint64_t round);
JsonResponse catchup(std::string catchpoint);
JsonResponse abort_catchup(std::string catchpoint);
JsonResponse supply();
JsonResponse register_participation_key(std::string address, uint64_t fee, uint64_t key_dilution, bool no_wait, uint64_t lv);
JsonResponse status();
JsonResponse status_after(uint64_t block);
JsonResponse teal_compile(std::string source);
JsonResponse teal_dryrun(rapidjson::Value& request);
virtual std::string submit_url();
JsonResponse submit(std::string raw);
JsonResponse submit(const SignedTransaction& stxn);
JsonResponse submit(const std::vector<SignedTransaction> stxn);
JsonResponse transaction_pending(std::string txid = "");
virtual std::string params_url();
JsonResponse params();
};
class IndexerClient : RestClient {
public:
/**
* @brief Initialize the client. Reads INDEXER_ADDRESS, INDEXER_TOKEN
* from environment.
*/
IndexerClient();
/**
* @brief Initialize the client with passed address for indexer and API token.
*/
IndexerClient(std::string address, std::string token);
bool healthy(void);
JsonResponse accounts(uint64_t limit=20, std::string next_page="",
uint64_t held_asset=0, uint64_t min_bal=0, uint64_t max_bal=0,
uint64_t optedin_app=0,
Address auth_addr=Address(), uint64_t as_of=0);
JsonResponse account(Address addr, uint64_t round=0);
JsonResponse block(uint64_t round=0);
};
#endif