Skip to content

Commit

Permalink
add extra currencies support to emulator (#1494)
Browse files Browse the repository at this point in the history
  • Loading branch information
dungeon-master-666 authored Jan 26, 2025
1 parent da5644e commit e7e57f8
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 2 deletions.
31 changes: 29 additions & 2 deletions emulator/emulator-emscripten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct GetMethodParams {
std::string address;
uint32_t unixtime;
uint64_t balance;
std::string extra_currencies;
std::string rand_seed_hex;
int64_t gas_limit;
int method_id;
Expand Down Expand Up @@ -108,6 +109,32 @@ td::Result<GetMethodParams> decode_get_method_params(const char* json) {
TRY_RESULT(balance, td::to_integer_safe<td::uint64>(balance_field.get_string()));
params.balance = balance;

TRY_RESULT(ec_field, td::get_json_object_field(obj, "extra_currencies", td::JsonValue::Type::Object, true));
if (ec_field.type() != td::JsonValue::Type::Null) {
if (ec_field.type() != td::JsonValue::Type::Object) {
return td::Status::Error("EC must be of type Object");
}
td::StringBuilder ec_builder;
auto ec_obj = ec_field.get_object();
bool is_first = true;
for (auto &field_value : ec_obj) {
auto currency_id = field_value.first;
if (field_value.second.type() != td::JsonValue::Type::String) {
return td::Status::Error(PSLICE() << "EC amount must be of type String");
}
auto amount = field_value.second.get_string();
if (!is_first) {
ec_builder << " ";
is_first = false;
}
ec_builder << currency_id << "=" << amount;
}
if (ec_builder.is_error()) {
return td::Status::Error(PSLICE() << "Error building extra currencies string");
}
params.extra_currencies = ec_builder.as_cslice().str();
}

TRY_RESULT(rand_seed_str, td::get_json_object_string_field(obj, "rand_seed", false));
params.rand_seed_hex = rand_seed_str;

Expand Down Expand Up @@ -228,8 +255,8 @@ const char *run_get_method(const char *params, const char* stack, const char* co
if ((decoded_params.libs && !tvm_emulator_set_libraries(tvm, decoded_params.libs.value().c_str())) ||
!tvm_emulator_set_c7(tvm, decoded_params.address.c_str(), decoded_params.unixtime, decoded_params.balance,
decoded_params.rand_seed_hex.c_str(), config) ||
(decoded_params.prev_blocks_info &&
!tvm_emulator_set_prev_blocks_info(tvm, decoded_params.prev_blocks_info.value().c_str())) ||
(decoded_params.extra_currencies.size() > 0 && !tvm_emulator_set_extra_currencies(tvm, decoded_params.extra_currencies.c_str())) ||
(decoded_params.prev_blocks_info && !tvm_emulator_set_prev_blocks_info(tvm, decoded_params.prev_blocks_info.value().c_str())) ||
(decoded_params.gas_limit > 0 && !tvm_emulator_set_gas_limit(tvm, decoded_params.gas_limit)) ||
!tvm_emulator_set_debug_enabled(tvm, decoded_params.debug_enabled)) {
tvm_emulator_destroy(tvm);
Expand Down
53 changes: 53 additions & 0 deletions emulator/emulator-extern.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,59 @@ bool tvm_emulator_set_c7(void *tvm_emulator, const char *address, uint32_t unixt
return true;
}

bool tvm_emulator_set_extra_currencies(void *tvm_emulator, const char *extra_currencies) {
auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
vm::Dictionary dict{32};
td::Slice extra_currencies_str{extra_currencies};
while (true) {
auto next_space_pos = extra_currencies_str.find(' ');
auto currency_id_amount = next_space_pos == td::Slice::npos ?
extra_currencies_str.substr(0) : extra_currencies_str.substr(0, next_space_pos);

if (!currency_id_amount.empty()) {
auto delim_pos = currency_id_amount.find('=');
if (delim_pos == td::Slice::npos) {
LOG(ERROR) << "Invalid extra currency format, missing '='";
return false;
}

auto currency_id_str = currency_id_amount.substr(0, delim_pos);
auto amount_str = currency_id_amount.substr(delim_pos + 1);

auto currency_id = td::to_integer_safe<uint32_t>(currency_id_str);
if (currency_id.is_error()) {
LOG(ERROR) << "Invalid extra currency id: " << currency_id_str;
return false;
}
auto amount = td::dec_string_to_int256(amount_str);
if (amount.is_null()) {
LOG(ERROR) << "Invalid extra currency amount: " << amount_str;
return false;
}
if (amount == 0) {
continue;
}
if (amount < 0) {
LOG(ERROR) << "Negative extra currency amount: " << amount_str;
return false;
}

vm::CellBuilder cb;
block::tlb::t_VarUInteger_32.store_integer_value(cb, *amount);
if (!dict.set_builder(td::BitArray<32>(currency_id.ok()), cb, vm::DictionaryBase::SetMode::Add)) {
LOG(ERROR) << "Duplicate extra currency id";
return false;
}
}
if (next_space_pos == td::Slice::npos) {
break;
}
extra_currencies_str.remove_prefix(next_space_pos + 1);
}
emulator->set_extra_currencies(std::move(dict).extract_root_cell());
return true;
}

bool tvm_emulator_set_config_object(void* tvm_emulator, void* config) {
auto emulator = static_cast<emulator::TvmEmulator *>(tvm_emulator);
auto global_config = std::shared_ptr<block::Config>(static_cast<block::Config *>(config), config_deleter);
Expand Down
8 changes: 8 additions & 0 deletions emulator/emulator-extern.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,14 @@ EMULATOR_EXPORT bool tvm_emulator_set_libraries(void *tvm_emulator, const char *
*/
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);

/**
* @brief Set extra currencies balance
* @param tvm_emulator Pointer to TVM emulator
* @param extra_currencies String with extra currencies balance in format "currency_id1=balance1 currency_id2=balance2 ..."
* @return true in case of success, false in case of error
*/
EMULATOR_EXPORT bool tvm_emulator_set_extra_currencies(void *tvm_emulator, const char *extra_currencies);

/**
* @brief Set config for TVM emulator
* @param tvm_emulator Pointer to TVM emulator
Expand Down
55 changes: 55 additions & 0 deletions emulator/test/emulator-tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,58 @@ TEST(Emulator, tvm_emulator) {
CHECK(stack_res->depth() == 1);
CHECK(stack_res.write().pop_int()->to_long() == init_data.seqno);
}

TEST(Emulator, tvm_emulator_extra_currencies) {
void *tvm_emulator = tvm_emulator_create("te6cckEBBAEAHgABFP8A9KQT9LzyyAsBAgFiAgMABtBfBAAJofpP8E8XmGlj", "te6cckEBAQEAAgAAAEysuc0=", 1);
std::string addr = "0:" + std::string(64, 'F');
tvm_emulator_set_c7(tvm_emulator, addr.c_str(), 1337, 1000, std::string(64, 'F').c_str(), nullptr);
CHECK(tvm_emulator_set_extra_currencies(tvm_emulator, "100=20000 200=1"));
unsigned method_crc = td::crc16("get_balance");
unsigned method_id = (method_crc & 0xffff) | 0x10000;

auto stack = td::make_ref<vm::Stack>();
vm::CellBuilder stack_cb;
CHECK(stack->serialize(stack_cb));
auto stack_cell = stack_cb.finalize();
auto stack_boc = td::base64_encode(std_boc_serialize(stack_cell).move_as_ok());

std::string tvm_res = tvm_emulator_run_get_method(tvm_emulator, method_id, stack_boc.c_str());

auto result_json = td::json_decode(td::MutableSlice(tvm_res));
auto result = result_json.move_as_ok();
auto& result_obj = result.get_object();

auto success_field = td::get_json_object_field(result_obj, "success", td::JsonValue::Type::Boolean, false);
auto success = success_field.move_as_ok().get_boolean();
CHECK(success);

auto stack_field = td::get_json_object_field(result_obj, "stack", td::JsonValue::Type::String, false);
auto stack_val = stack_field.move_as_ok();
auto& stack_obj = stack_val.get_string();
auto stack_res_boc = td::base64_decode(stack_obj);
auto stack_res_cell = vm::std_boc_deserialize(stack_res_boc.move_as_ok());
td::Ref<vm::Stack> stack_res;
auto stack_res_cs = vm::load_cell_slice(stack_res_cell.move_as_ok());
CHECK(vm::Stack::deserialize_to(stack_res_cs, stack_res));
CHECK(stack_res->depth() == 1);
auto tuple = stack_res.write().pop_tuple();
CHECK(tuple->size() == 2);

auto ton_balance = tuple->at(0).as_int();
CHECK(ton_balance == 1000);

auto cell = tuple->at(1).as_cell();
auto dict = vm::Dictionary{cell, 32};
auto it = dict.begin();
std::map<uint32_t, td::RefInt256> ec_balance;
while (!it.eof()) {
auto id = td::BitArray<32>(it.cur_pos()).to_ulong();
auto value_cs = it.cur_value();
auto value = block::tlb::t_VarUInteger_32.as_integer(value_cs);
ec_balance[id] = value;
++it;
}
CHECK(ec_balance.size() == 2);
CHECK(ec_balance[100] == 20000);
CHECK(ec_balance[200] == 1);
}
4 changes: 4 additions & 0 deletions emulator/tvm-emulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class TvmEmulator {
}
}

void set_extra_currencies(td::Ref<vm::Cell> extra_currencies) {
args_.set_extra_currencies(std::move(extra_currencies));
}

void set_c7_raw(td::Ref<vm::Tuple> c7) {
args_.set_c7(std::move(c7));
}
Expand Down

0 comments on commit e7e57f8

Please sign in to comment.