Skip to content

Commit e7e57f8

Browse files
add extra currencies support to emulator (#1494)
1 parent da5644e commit e7e57f8

5 files changed

+149
-2
lines changed

emulator/emulator-emscripten.cpp

+29-2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct GetMethodParams {
6565
std::string address;
6666
uint32_t unixtime;
6767
uint64_t balance;
68+
std::string extra_currencies;
6869
std::string rand_seed_hex;
6970
int64_t gas_limit;
7071
int method_id;
@@ -108,6 +109,32 @@ td::Result<GetMethodParams> decode_get_method_params(const char* json) {
108109
TRY_RESULT(balance, td::to_integer_safe<td::uint64>(balance_field.get_string()));
109110
params.balance = balance;
110111

112+
TRY_RESULT(ec_field, td::get_json_object_field(obj, "extra_currencies", td::JsonValue::Type::Object, true));
113+
if (ec_field.type() != td::JsonValue::Type::Null) {
114+
if (ec_field.type() != td::JsonValue::Type::Object) {
115+
return td::Status::Error("EC must be of type Object");
116+
}
117+
td::StringBuilder ec_builder;
118+
auto ec_obj = ec_field.get_object();
119+
bool is_first = true;
120+
for (auto &field_value : ec_obj) {
121+
auto currency_id = field_value.first;
122+
if (field_value.second.type() != td::JsonValue::Type::String) {
123+
return td::Status::Error(PSLICE() << "EC amount must be of type String");
124+
}
125+
auto amount = field_value.second.get_string();
126+
if (!is_first) {
127+
ec_builder << " ";
128+
is_first = false;
129+
}
130+
ec_builder << currency_id << "=" << amount;
131+
}
132+
if (ec_builder.is_error()) {
133+
return td::Status::Error(PSLICE() << "Error building extra currencies string");
134+
}
135+
params.extra_currencies = ec_builder.as_cslice().str();
136+
}
137+
111138
TRY_RESULT(rand_seed_str, td::get_json_object_string_field(obj, "rand_seed", false));
112139
params.rand_seed_hex = rand_seed_str;
113140

@@ -228,8 +255,8 @@ const char *run_get_method(const char *params, const char* stack, const char* co
228255
if ((decoded_params.libs && !tvm_emulator_set_libraries(tvm, decoded_params.libs.value().c_str())) ||
229256
!tvm_emulator_set_c7(tvm, decoded_params.address.c_str(), decoded_params.unixtime, decoded_params.balance,
230257
decoded_params.rand_seed_hex.c_str(), config) ||
231-
(decoded_params.prev_blocks_info &&
232-
!tvm_emulator_set_prev_blocks_info(tvm, decoded_params.prev_blocks_info.value().c_str())) ||
258+
(decoded_params.extra_currencies.size() > 0 && !tvm_emulator_set_extra_currencies(tvm, decoded_params.extra_currencies.c_str())) ||
259+
(decoded_params.prev_blocks_info && !tvm_emulator_set_prev_blocks_info(tvm, decoded_params.prev_blocks_info.value().c_str())) ||
233260
(decoded_params.gas_limit > 0 && !tvm_emulator_set_gas_limit(tvm, decoded_params.gas_limit)) ||
234261
!tvm_emulator_set_debug_enabled(tvm, decoded_params.debug_enabled)) {
235262
tvm_emulator_destroy(tvm);

emulator/emulator-extern.cpp

+53
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,59 @@ bool tvm_emulator_set_c7(void *tvm_emulator, const char *address, uint32_t unixt
496496
return true;
497497
}
498498

499+
bool tvm_emulator_set_extra_currencies(void *tvm_emulator, const char *extra_currencies) {
500+
auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
501+
vm::Dictionary dict{32};
502+
td::Slice extra_currencies_str{extra_currencies};
503+
while (true) {
504+
auto next_space_pos = extra_currencies_str.find(' ');
505+
auto currency_id_amount = next_space_pos == td::Slice::npos ?
506+
extra_currencies_str.substr(0) : extra_currencies_str.substr(0, next_space_pos);
507+
508+
if (!currency_id_amount.empty()) {
509+
auto delim_pos = currency_id_amount.find('=');
510+
if (delim_pos == td::Slice::npos) {
511+
LOG(ERROR) << "Invalid extra currency format, missing '='";
512+
return false;
513+
}
514+
515+
auto currency_id_str = currency_id_amount.substr(0, delim_pos);
516+
auto amount_str = currency_id_amount.substr(delim_pos + 1);
517+
518+
auto currency_id = td::to_integer_safe<uint32_t>(currency_id_str);
519+
if (currency_id.is_error()) {
520+
LOG(ERROR) << "Invalid extra currency id: " << currency_id_str;
521+
return false;
522+
}
523+
auto amount = td::dec_string_to_int256(amount_str);
524+
if (amount.is_null()) {
525+
LOG(ERROR) << "Invalid extra currency amount: " << amount_str;
526+
return false;
527+
}
528+
if (amount == 0) {
529+
continue;
530+
}
531+
if (amount < 0) {
532+
LOG(ERROR) << "Negative extra currency amount: " << amount_str;
533+
return false;
534+
}
535+
536+
vm::CellBuilder cb;
537+
block::tlb::t_VarUInteger_32.store_integer_value(cb, *amount);
538+
if (!dict.set_builder(td::BitArray<32>(currency_id.ok()), cb, vm::DictionaryBase::SetMode::Add)) {
539+
LOG(ERROR) << "Duplicate extra currency id";
540+
return false;
541+
}
542+
}
543+
if (next_space_pos == td::Slice::npos) {
544+
break;
545+
}
546+
extra_currencies_str.remove_prefix(next_space_pos + 1);
547+
}
548+
emulator->set_extra_currencies(std::move(dict).extract_root_cell());
549+
return true;
550+
}
551+
499552
bool tvm_emulator_set_config_object(void* tvm_emulator, void* config) {
500553
auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
501554
auto global_config = std::shared_ptr<block::Config>(static_cast<block::Config *>(config), config_deleter);

emulator/emulator-extern.h

+8
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ EMULATOR_EXPORT bool tvm_emulator_set_libraries(void *tvm_emulator, const char *
182182
*/
183183
EMULATOR_EXPORT bool tvm_emulator_set_c7(void *tvm_emulator, const char *address, uint32_t unixtime, uint64_t balance, const char *rand_seed_hex, const char *config);
184184

185+
/**
186+
* @brief Set extra currencies balance
187+
* @param tvm_emulator Pointer to TVM emulator
188+
* @param extra_currencies String with extra currencies balance in format "currency_id1=balance1 currency_id2=balance2 ..."
189+
* @return true in case of success, false in case of error
190+
*/
191+
EMULATOR_EXPORT bool tvm_emulator_set_extra_currencies(void *tvm_emulator, const char *extra_currencies);
192+
185193
/**
186194
* @brief Set config for TVM emulator
187195
* @param tvm_emulator Pointer to TVM emulator

emulator/test/emulator-tests.cpp

+55
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,58 @@ TEST(Emulator, tvm_emulator) {
400400
CHECK(stack_res->depth() == 1);
401401
CHECK(stack_res.write().pop_int()->to_long() == init_data.seqno);
402402
}
403+
404+
TEST(Emulator, tvm_emulator_extra_currencies) {
405+
void *tvm_emulator = tvm_emulator_create("te6cckEBBAEAHgABFP8A9KQT9LzyyAsBAgFiAgMABtBfBAAJofpP8E8XmGlj", "te6cckEBAQEAAgAAAEysuc0=", 1);
406+
std::string addr = "0:" + std::string(64, 'F');
407+
tvm_emulator_set_c7(tvm_emulator, addr.c_str(), 1337, 1000, std::string(64, 'F').c_str(), nullptr);
408+
CHECK(tvm_emulator_set_extra_currencies(tvm_emulator, "100=20000 200=1"));
409+
unsigned method_crc = td::crc16("get_balance");
410+
unsigned method_id = (method_crc & 0xffff) | 0x10000;
411+
412+
auto stack = td::make_ref<vm::Stack>();
413+
vm::CellBuilder stack_cb;
414+
CHECK(stack->serialize(stack_cb));
415+
auto stack_cell = stack_cb.finalize();
416+
auto stack_boc = td::base64_encode(std_boc_serialize(stack_cell).move_as_ok());
417+
418+
std::string tvm_res = tvm_emulator_run_get_method(tvm_emulator, method_id, stack_boc.c_str());
419+
420+
auto result_json = td::json_decode(td::MutableSlice(tvm_res));
421+
auto result = result_json.move_as_ok();
422+
auto& result_obj = result.get_object();
423+
424+
auto success_field = td::get_json_object_field(result_obj, "success", td::JsonValue::Type::Boolean, false);
425+
auto success = success_field.move_as_ok().get_boolean();
426+
CHECK(success);
427+
428+
auto stack_field = td::get_json_object_field(result_obj, "stack", td::JsonValue::Type::String, false);
429+
auto stack_val = stack_field.move_as_ok();
430+
auto& stack_obj = stack_val.get_string();
431+
auto stack_res_boc = td::base64_decode(stack_obj);
432+
auto stack_res_cell = vm::std_boc_deserialize(stack_res_boc.move_as_ok());
433+
td::Ref<vm::Stack> stack_res;
434+
auto stack_res_cs = vm::load_cell_slice(stack_res_cell.move_as_ok());
435+
CHECK(vm::Stack::deserialize_to(stack_res_cs, stack_res));
436+
CHECK(stack_res->depth() == 1);
437+
auto tuple = stack_res.write().pop_tuple();
438+
CHECK(tuple->size() == 2);
439+
440+
auto ton_balance = tuple->at(0).as_int();
441+
CHECK(ton_balance == 1000);
442+
443+
auto cell = tuple->at(1).as_cell();
444+
auto dict = vm::Dictionary{cell, 32};
445+
auto it = dict.begin();
446+
std::map<uint32_t, td::RefInt256> ec_balance;
447+
while (!it.eof()) {
448+
auto id = td::BitArray<32>(it.cur_pos()).to_ulong();
449+
auto value_cs = it.cur_value();
450+
auto value = block::tlb::t_VarUInteger_32.as_integer(value_cs);
451+
ec_balance[id] = value;
452+
++it;
453+
}
454+
CHECK(ec_balance.size() == 2);
455+
CHECK(ec_balance[100] == 20000);
456+
CHECK(ec_balance[200] == 1);
457+
}

emulator/tvm-emulator.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ class TvmEmulator {
3333
}
3434
}
3535

36+
void set_extra_currencies(td::Ref<vm::Cell> extra_currencies) {
37+
args_.set_extra_currencies(std::move(extra_currencies));
38+
}
39+
3640
void set_c7_raw(td::Ref<vm::Tuple> c7) {
3741
args_.set_c7(std::move(c7));
3842
}

0 commit comments

Comments
 (0)