mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-12 11:12:16 +00:00
add extra currencies support to emulator (#1494)
This commit is contained in:
parent
da5644e758
commit
e7e57f8e6d
5 changed files with 149 additions and 2 deletions
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -33,6 +33,10 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue