-
Notifications
You must be signed in to change notification settings - Fork 953
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add infrastructure for precompiled smartcontracts (#927)
* Utils for writing precompiled contracts * Precompiled contracts in config, override gas_usage for them * Add base class for precompiled contracts * Improve utils for precompiled smc * Implement GETPRECOMPILEDGAS * Enable precompiles by flag * Process null data in PrecompiledSmartContract * Fix ton_block wasm build * Fix vm::util::store_(u)long --------- Co-authored-by: SpyCheese <[email protected]>
- Loading branch information
1 parent
b09f910
commit 9d05696
Showing
21 changed files
with
991 additions
and
95 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
170 changes: 170 additions & 0 deletions
170
crypto/block/precompiled-smc/PrecompiledSmartContract.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/* | ||
This file is part of TON Blockchain Library. | ||
TON Blockchain Library is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU Lesser General Public License as published by | ||
the Free Software Foundation, either version 2 of the License, or | ||
(at your option) any later version. | ||
TON Blockchain Library is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU Lesser General Public License for more details. | ||
You should have received a copy of the GNU Lesser General Public License | ||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
#include "common.h" | ||
#include <memory> | ||
#include "vm/memo.h" | ||
|
||
namespace block::precompiled { | ||
|
||
using namespace vm; | ||
|
||
Result PrecompiledSmartContract::run(td::Ref<vm::CellSlice> my_address, ton::UnixTime now, ton::LogicalTime cur_lt, | ||
CurrencyCollection balance, td::Ref<vm::Cell> c4, vm::CellSlice msg_body, | ||
td::Ref<vm::Cell> msg, CurrencyCollection msg_balance, bool is_external, | ||
std::vector<td::Ref<vm::Cell>> libraries, int global_version, | ||
td::uint16 max_data_depth, td::Ref<vm::Cell> my_code, | ||
td::Ref<vm::Tuple> unpacked_config, td::RefInt256 due_payment, | ||
td::uint64 precompiled_gas_usage) { | ||
my_address_ = std::move(my_address); | ||
now_ = now; | ||
cur_lt_ = cur_lt; | ||
balance_ = std::move(balance); | ||
c4_ = (c4.not_null() ? std::move(c4) : CellBuilder().finalize()); | ||
in_msg_body_ = std::move(msg_body); | ||
in_msg_ = std::move(msg); | ||
in_msg_balance_ = std::move(msg_balance); | ||
is_external_ = is_external; | ||
my_code_ = std::move(my_code); | ||
unpacked_config_ = std::move(unpacked_config); | ||
due_payment_ = std::move(due_payment); | ||
precompiled_gas_usage_ = precompiled_gas_usage; | ||
|
||
vm::DummyVmState vm_state{std::move(libraries), global_version}; | ||
vm::VmStateInterface::Guard guard{&vm_state}; | ||
|
||
Result result; | ||
try { | ||
result = do_run(); | ||
} catch (vm::VmError &e) { | ||
result = Result::error(e.get_errno(), e.get_arg()); | ||
} catch (Result &r) { | ||
result = std::move(r); | ||
} | ||
|
||
if (result.exit_code != 0 && result.exit_code != 1) { | ||
// see VmState::try_commit() | ||
if (c4_.is_null() || c4_->get_depth() > max_data_depth || c4_->get_level() != 0 || c5_.is_null() || | ||
c5_->get_depth() > max_data_depth || c5_->get_level() != 0) { | ||
result = Result::error(Excno::cell_ov, 0); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
void PrecompiledSmartContract::send_raw_message(const td::Ref<Cell> &msg, int mode) { | ||
CellBuilder cb; | ||
if (!(cb.store_ref_bool(c5_) // out_list$_ {n:#} prev:^(OutList n) | ||
&& cb.store_long_bool(0x0ec3c86d, 32) // action_send_msg#0ec3c86d | ||
&& cb.store_long_bool(mode, 8) // mode:(## 8) | ||
&& cb.store_ref_bool(msg))) { | ||
throw VmError{Excno::cell_ov, "cannot serialize raw output message into an output action cell"}; | ||
} | ||
c5_ = cb.finalize_novm(); | ||
} | ||
|
||
void PrecompiledSmartContract::raw_reserve(const td::RefInt256 &amount, int mode) { | ||
if (amount->sgn() < 0) { | ||
throw VmError{Excno::range_chk, "amount of nanograms must be non-negative"}; | ||
} | ||
CellBuilder cb; | ||
if (!(cb.store_ref_bool(c5_) // out_list$_ {n:#} prev:^(OutList n) | ||
&& cb.store_long_bool(0x36e6b809, 32) // action_reserve_currency#36e6b809 | ||
&& cb.store_long_bool(mode, 8) // mode:(## 8) | ||
&& util::store_coins(cb, std::move(amount), true) // | ||
&& cb.store_maybe_ref({}))) { | ||
throw VmError{Excno::cell_ov, "cannot serialize raw reserved currency amount into an output action cell"}; | ||
} | ||
c5_ = cb.finalize_novm(); | ||
} | ||
|
||
td::RefInt256 PrecompiledSmartContract::get_compute_fee(ton::WorkchainId wc, td::uint64 gas_used) { | ||
if (gas_used >= (1ULL << 63)) { | ||
throw VmError{Excno::range_chk}; | ||
} | ||
block::GasLimitsPrices prices = util::get_gas_prices(unpacked_config_, wc); | ||
return util::check_finite(prices.compute_gas_price(gas_used)); | ||
} | ||
|
||
td::RefInt256 PrecompiledSmartContract::get_forward_fee(ton::WorkchainId wc, td::uint64 bits, td::uint64 cells) { | ||
if (bits >= (1ULL << 63) || cells >= (1ULL << 63)) { | ||
throw VmError{Excno::range_chk}; | ||
} | ||
block::MsgPrices prices = util::get_msg_prices(unpacked_config_, wc); | ||
return util::check_finite(prices.compute_fwd_fees256(cells, bits)); | ||
} | ||
|
||
td::RefInt256 PrecompiledSmartContract::get_storage_fee(ton::WorkchainId wc, td::uint64 duration, td::uint64 bits, | ||
td::uint64 cells) { | ||
if (bits >= (1ULL << 63) || cells >= (1ULL << 63) || duration >= (1ULL << 63)) { | ||
throw VmError{Excno::range_chk}; | ||
} | ||
td::optional<block::StoragePrices> maybe_prices = util::get_storage_prices(unpacked_config_); | ||
return util::check_finite(util::calculate_storage_fee(maybe_prices, wc, duration, bits, cells)); | ||
} | ||
|
||
td::RefInt256 PrecompiledSmartContract::get_simple_compute_fee(ton::WorkchainId wc, td::uint64 gas_used) { | ||
if (gas_used >= (1ULL << 63)) { | ||
throw VmError{Excno::range_chk}; | ||
} | ||
block::GasLimitsPrices prices = util::get_gas_prices(unpacked_config_, wc); | ||
return util::check_finite(td::rshift(td::make_refint(prices.gas_price) * gas_used, 16, 1)); | ||
} | ||
|
||
td::RefInt256 PrecompiledSmartContract::get_simple_forward_fee(ton::WorkchainId wc, td::uint64 bits, td::uint64 cells) { | ||
if (bits >= (1ULL << 63) || cells >= (1ULL << 63)) { | ||
throw VmError{Excno::range_chk}; | ||
} | ||
block::MsgPrices prices = util::get_msg_prices(unpacked_config_, wc); | ||
return util::check_finite( | ||
td::rshift(td::make_refint(prices.bit_price) * bits + td::make_refint(prices.cell_price) * cells, 16, 1)); | ||
} | ||
|
||
td::RefInt256 PrecompiledSmartContract::get_original_fwd_fee(ton::WorkchainId wc, const td::RefInt256 &x) { | ||
if (x->sgn() < 0) { | ||
throw VmError{Excno::range_chk, "fwd_fee is negative"}; | ||
} | ||
block::MsgPrices prices = util::get_msg_prices(unpacked_config_, wc); | ||
return util::check_finite(td::muldiv(x, td::make_refint(1 << 16), td::make_refint((1 << 16) - prices.first_frac))); | ||
} | ||
|
||
static std::atomic_bool precompiled_execution_enabled{false}; | ||
|
||
std::unique_ptr<PrecompiledSmartContract> get_implementation(td::Bits256 code_hash) { | ||
if (!precompiled_execution_enabled) { | ||
return nullptr; | ||
} | ||
static std::map<td::Bits256, std::unique_ptr<PrecompiledSmartContract> (*)()> map = []() { | ||
auto from_hex = [](td::Slice s) -> td::Bits256 { | ||
td::Bits256 x; | ||
CHECK(x.from_hex(s) == 256); | ||
return x; | ||
}; | ||
std::map<td::Bits256, std::unique_ptr<PrecompiledSmartContract> (*)()> map; | ||
#define CONTRACT(hash, cls) \ | ||
map[from_hex(hash)] = []() -> std::unique_ptr<PrecompiledSmartContract> { return std::make_unique<cls>(); }; | ||
// CONTRACT("CODE_HASH_HEX", ClassName); | ||
return map; | ||
}(); | ||
auto it = map.find(code_hash); | ||
return it == map.end() ? nullptr : it->second(); | ||
} | ||
|
||
void set_precompiled_execution_enabled(bool value) { | ||
precompiled_execution_enabled = value; | ||
} | ||
|
||
} // namespace block::precompiled |
122 changes: 122 additions & 0 deletions
122
crypto/block/precompiled-smc/PrecompiledSmartContract.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* | ||
This file is part of TON Blockchain Library. | ||
TON Blockchain Library is free software: you can redistribute it and/or modify | ||
it under the terms of the GNU Lesser General Public License as published by | ||
the Free Software Foundation, either version 2 of the License, or | ||
(at your option) any later version. | ||
TON Blockchain Library is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU Lesser General Public License for more details. | ||
You should have received a copy of the GNU Lesser General Public License | ||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>. | ||
*/ | ||
#pragma once | ||
#include "common/refcnt.hpp" | ||
#include "common/refint.h" | ||
#include "vm/cells.h" | ||
#include "vm/cellslice.h" | ||
#include "vm/dict.h" | ||
#include "vm/boc.h" | ||
#include <ostream> | ||
#include "tl/tlblib.hpp" | ||
#include "td/utils/bits.h" | ||
#include "ton/ton-types.h" | ||
#include "block/block.h" | ||
#include "block/mc-config.h" | ||
|
||
namespace block::precompiled { | ||
|
||
struct Result { | ||
int exit_code = 0; | ||
td::optional<long long> exit_arg; | ||
bool accepted = true; | ||
bool committed = false; | ||
|
||
static Result error(int code, long long arg = 0) { | ||
Result res; | ||
res.exit_code = code; | ||
res.exit_arg = arg; | ||
return res; | ||
} | ||
|
||
static Result error(vm::Excno code, long long arg = 0) { | ||
Result res; | ||
res.exit_code = (int)code; | ||
res.exit_arg = arg; | ||
return res; | ||
} | ||
|
||
static Result not_accepted(int code = 0) { | ||
Result res; | ||
res.exit_code = code; | ||
res.accepted = false; | ||
return res; | ||
} | ||
|
||
static Result success() { | ||
Result res; | ||
res.committed = true; | ||
return res; | ||
} | ||
}; | ||
|
||
class PrecompiledSmartContract { | ||
public: | ||
virtual ~PrecompiledSmartContract() = default; | ||
|
||
virtual std::string get_name() const = 0; | ||
|
||
virtual int required_version() const { | ||
return 6; | ||
} | ||
|
||
Result run(td::Ref<vm::CellSlice> my_address, ton::UnixTime now, ton::LogicalTime cur_lt, CurrencyCollection balance, | ||
td::Ref<vm::Cell> c4, vm::CellSlice msg_body, td::Ref<vm::Cell> msg, CurrencyCollection msg_balance, | ||
bool is_external, std::vector<td::Ref<vm::Cell>> libraries, int global_version, td::uint16 max_data_depth, | ||
td::Ref<vm::Cell> my_code, td::Ref<vm::Tuple> unpacked_config, td::RefInt256 due_payment, td::uint64 precompiled_gas_usage); | ||
|
||
td::Ref<vm::Cell> get_c4() const { | ||
return c4_; | ||
} | ||
td::Ref<vm::Cell> get_c5() const { | ||
return c5_; | ||
} | ||
|
||
protected: | ||
td::Ref<vm::CellSlice> my_address_; | ||
ton::UnixTime now_; | ||
ton::LogicalTime cur_lt_; | ||
CurrencyCollection balance_; | ||
vm::CellSlice in_msg_body_; | ||
td::Ref<vm::Cell> in_msg_; | ||
CurrencyCollection in_msg_balance_; | ||
bool is_external_; | ||
td::Ref<vm::Cell> my_code_; | ||
td::Ref<vm::Tuple> unpacked_config_; | ||
td::RefInt256 due_payment_; | ||
td::uint64 precompiled_gas_usage_; | ||
|
||
td::Ref<vm::Cell> c4_; | ||
td::Ref<vm::Cell> c5_ = vm::CellBuilder().finalize_novm(); | ||
|
||
void send_raw_message(const td::Ref<vm::Cell>& msg, int mode); | ||
void raw_reserve(const td::RefInt256& amount, int mode); | ||
|
||
td::RefInt256 get_compute_fee(ton::WorkchainId wc, td::uint64 gas_used); | ||
td::RefInt256 get_forward_fee(ton::WorkchainId wc, td::uint64 bits, td::uint64 cells); | ||
td::RefInt256 get_storage_fee(ton::WorkchainId wc, td::uint64 duration, td::uint64 bits, td::uint64 cells); | ||
td::RefInt256 get_simple_compute_fee(ton::WorkchainId wc, td::uint64 gas_used); | ||
td::RefInt256 get_simple_forward_fee(ton::WorkchainId wc, td::uint64 bits, td::uint64 cells); | ||
td::RefInt256 get_original_fwd_fee(ton::WorkchainId wc, const td::RefInt256& x); | ||
|
||
virtual Result do_run() = 0; | ||
}; | ||
|
||
std::unique_ptr<PrecompiledSmartContract> get_implementation(td::Bits256 code_hash); | ||
void set_precompiled_execution_enabled(bool value); // disabled by default | ||
|
||
} // namespace block::precompiled |
Oops, something went wrong.