1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

updated submodules, bugfixes

- added new fift/func code for validator complaint creation
- bugfixes in validator
- updates in tonlib
- new versions of rocksdb/abseil
- hardfork support
This commit is contained in:
ton 2020-04-27 16:01:46 +04:00
parent 16a4566091
commit 9f008b129f
129 changed files with 8438 additions and 879 deletions

View file

@ -21,6 +21,31 @@
#include "block/block-auto.h"
#include "block/block-parse.h"
namespace ton {
namespace smc {
td::Ref<vm::CellSlice> pack_grams(td::uint64 amount) {
vm::CellBuilder cb;
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(amount));
return vm::load_cell_slice_ref(cb.finalize());
}
bool unpack_grams(td::Ref<vm::CellSlice> cs, td::uint64& amount) {
td::RefInt256 got;
if (!block::tlb::t_Grams.as_integer_to(cs, got)) {
return false;
}
if (!got->unsigned_fits_bits(63)) {
return false;
}
auto x = got->to_long();
if (x < 0) {
return false;
}
amount = x;
return true;
}
} // namespace smc
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept {
return vm::CellBuilder()
.store_zeroes(2)
@ -47,7 +72,7 @@ void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddr
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
cb.store_zeroes(9 + 64 + 32 + 1 + 1);
cb.store_zeroes(9 + 64 + 32);
}
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,

View file

@ -23,6 +23,10 @@
#include "SmartContract.h"
namespace ton {
namespace smc {
td::Ref<vm::CellSlice> pack_grams(td::uint64 amount);
bool unpack_grams(td::Ref<vm::CellSlice> cs, td::uint64& amount);
} // namespace smc
class GenericAccount {
public:
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept;

View file

@ -73,17 +73,11 @@ td::Ref<vm::Cell> HighloadWallet::make_a_gift_message(const td::Ed25519::Private
for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i];
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
auto message_inner = create_int_message(gift);
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, gift.destination, gramms);
cb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
auto message_inner = cb.finalize();
cb = {};
cb.store_long(send_mode, 8).store_ref(message_inner);
auto key = messages.integer_key(td::make_refint(i), 16, false);
messages.set_builder(key.bits(), 16, cb);

View file

@ -73,18 +73,11 @@ td::Ref<vm::Cell> HighloadWalletV2::make_a_gift_message(const td::Ed25519::Priva
for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i];
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, gift.destination, gramms);
cb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
auto message_inner = cb.finalize();
cb = {};
cb.store_long(send_mode, 8).store_ref(message_inner);
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
auto key = messages.integer_key(td::make_refint(i), 16, false);
messages.set_builder(key.bits(), 16, cb);
}

View file

@ -0,0 +1,264 @@
#include "PaymentChannel.h"
#include "GenericAccount.h"
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "Ed25519.h"
#include "block/block-auto.h"
#include "block/block-parse.h"
#include "SmartContract.h"
#include "SmartContractCode.h"
namespace ton {
using smc::pack_grams;
using smc::unpack_grams;
namespace pchan {
td::Ref<vm::Cell> Config::serialize() const {
block::gen::ChanConfig::Record rec;
vm::CellBuilder a_addr_cb;
block::tlb::t_MsgAddressInt.store_std_address(a_addr_cb, a_addr);
rec.a_addr = a_addr_cb.finalize_novm();
vm::CellBuilder b_addr_cb;
block::tlb::t_MsgAddressInt.store_std_address(b_addr_cb, b_addr);
rec.b_addr = b_addr_cb.finalize_novm();
rec.a_key.as_slice().copy_from(a_key);
rec.b_key.as_slice().copy_from(b_key);
rec.init_timeout = init_timeout;
rec.close_timeout = close_timeout;
rec.channel_id = channel_id;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> MsgInit::serialize() const {
block::gen::ChanMsg::Record_chan_msg_init rec;
rec.min_A = pack_grams(min_A);
rec.min_B = pack_grams(min_B);
rec.inc_A = pack_grams(inc_A);
rec.inc_B = pack_grams(inc_B);
rec.channel_id = channel_id;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> Promise::serialize() const {
block::gen::ChanPromise::Record rec;
rec.channel_id = channel_id;
rec.promise_A = pack_grams(promise_A);
rec.promise_B = pack_grams(promise_B);
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::SecureString sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key) {
return key->sign(msg->get_hash().as_slice()).move_as_ok();
}
td::Ref<vm::Cell> maybe_sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key) {
if (!key) {
return {};
}
return vm::CellBuilder().store_bytes(sign(msg, key).as_slice()).finalize();
}
td::Ref<vm::CellSlice> maybe_ref(td::Ref<vm::Cell> msg) {
vm::CellBuilder cb;
CHECK(cb.store_maybe_ref(msg));
return vm::load_cell_slice_ref(cb.finalize());
}
td::Ref<vm::Cell> MsgClose::serialize() const {
block::gen::ChanMsg::Record_chan_msg_close rec;
rec.extra_A = pack_grams(extra_A);
rec.extra_B = pack_grams(extra_B);
rec.promise = signed_promise;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> MsgTimeout::serialize() const {
block::gen::ChanMsg::Record_chan_msg_timeout rec;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::SecureString SignedPromise::signature(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise) {
return sign(promise, key);
}
td::Ref<vm::Cell> SignedPromise::create_and_serialize(td::Slice signature, const td::Ref<vm::Cell>& promise) {
block::gen::ChanSignedPromise::Record rec;
rec.promise = vm::load_cell_slice_ref(promise);
LOG(ERROR) << "signature.size() = " << signature.size();
rec.sig = maybe_ref(vm::CellBuilder().store_bytes(signature).finalize());
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> SignedPromise::create_and_serialize(const td::Ed25519::PrivateKey* key,
const td::Ref<vm::Cell>& promise) {
block::gen::ChanSignedPromise::Record rec;
rec.promise = vm::load_cell_slice_ref(promise);
rec.sig = maybe_ref(maybe_sign(promise, key));
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
bool SignedPromise::unpack(td::Ref<vm::Cell> cell) {
block::gen::ChanSignedPromise::Record rec;
if (!tlb::unpack_cell(cell, rec)) {
return false;
}
block::gen::ChanPromise::Record rec_promise;
if (!tlb::csr_unpack(rec.promise, rec_promise)) {
return false;
}
promise.channel_id = rec_promise.channel_id;
if (!unpack_grams(rec_promise.promise_A, promise.promise_A)) {
return false;
}
if (!unpack_grams(rec_promise.promise_B, promise.promise_B)) {
return false;
}
td::Ref<vm::Cell> sig_cell;
if (!rec.sig->prefetch_maybe_ref(sig_cell)) {
return false;
}
td::SecureString signature(64);
vm::CellSlice cs = vm::load_cell_slice(sig_cell);
if (!cs.prefetch_bytes(signature.as_mutable_slice())) {
return false;
}
o_signature = std::move(signature);
return true;
}
td::Ref<vm::Cell> StateInit::serialize() const {
block::gen::ChanState::Record_chan_state_init rec;
rec.expire_at = expire_at;
rec.min_A = pack_grams(min_A);
rec.min_B = pack_grams(min_B);
rec.A = pack_grams(A);
rec.B = pack_grams(B);
rec.signed_A = signed_A;
rec.signed_B = signed_B;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> Data::serialize() const {
block::gen::ChanData::Record rec;
rec.config = config;
rec.state = state;
td::Ref<vm::Cell> res;
CHECK(block::gen::t_ChanData.cell_pack(res, rec));
return res;
}
td::Ref<vm::Cell> Data::init_state() {
return StateInit().serialize();
}
} // namespace pchan
td::Result<PaymentChannel::Info> PaymentChannel::get_info() const {
block::gen::ChanData::Record data_rec;
if (!tlb::unpack_cell(get_state().data, data_rec)) {
return td::Status::Error("Can't unpack data");
}
block::gen::ChanConfig::Record config_rec;
if (!tlb::unpack_cell(data_rec.config, config_rec)) {
return td::Status::Error("Can't unpack config");
}
pchan::Config config;
config.a_key = td::SecureString(config_rec.a_key.as_slice());
config.b_key = td::SecureString(config_rec.b_key.as_slice());
block::tlb::t_MsgAddressInt.extract_std_address(vm::load_cell_slice_ref(config_rec.a_addr), config.a_addr);
block::tlb::t_MsgAddressInt.extract_std_address(vm::load_cell_slice_ref(config_rec.b_addr), config.b_addr);
config.init_timeout = static_cast<td::int32>(config_rec.init_timeout);
config.close_timeout = static_cast<td::int32>(config_rec.close_timeout);
config.channel_id = static_cast<td::int64>(config_rec.channel_id);
auto state_cs = vm::load_cell_slice(data_rec.state);
Info res;
switch (block::gen::t_ChanState.check_tag(state_cs)) {
case block::gen::ChanState::chan_state_init: {
pchan::StateInit state;
block::gen::ChanState::Record_chan_state_init state_rec;
if (!tlb::unpack_cell(data_rec.state, state_rec)) {
return td::Status::Error("Can't unpack state");
}
bool ok = unpack_grams(state_rec.A, state.A) && unpack_grams(state_rec.B, state.B) &&
unpack_grams(state_rec.min_A, state.min_A) && unpack_grams(state_rec.min_B, state.min_B);
state.expire_at = state_rec.expire_at;
state.signed_A = state_rec.signed_A;
state.signed_B = state_rec.signed_B;
if (!ok) {
return td::Status::Error("Can't unpack state");
}
res.state = std::move(state);
break;
}
case block::gen::ChanState::chan_state_close: {
pchan::StateClose state;
block::gen::ChanState::Record_chan_state_close state_rec;
if (!tlb::unpack_cell(data_rec.state, state_rec)) {
return td::Status::Error("Can't unpack state");
}
bool ok = unpack_grams(state_rec.A, state.A) && unpack_grams(state_rec.B, state.B) &&
unpack_grams(state_rec.promise_A, state.promise_A) &&
unpack_grams(state_rec.promise_B, state.promise_B);
state.expire_at = state_rec.expire_at;
state.signed_A = state_rec.signed_A;
state.signed_B = state_rec.signed_B;
if (!ok) {
return td::Status::Error("Can't unpack state");
}
res.state = std::move(state);
break;
}
case block::gen::ChanState::chan_state_payout: {
pchan::StatePayout state;
block::gen::ChanState::Record_chan_state_payout state_rec;
if (!tlb::unpack_cell(data_rec.state, state_rec)) {
return td::Status::Error("Can't unpack state");
}
bool ok = unpack_grams(state_rec.A, state.A) && unpack_grams(state_rec.B, state.B);
if (!ok) {
return td::Status::Error("Can't unpack state");
}
res.state = std::move(state);
break;
}
default:
return td::Status::Error("Can't unpack state");
}
res.config = std::move(config);
res.description = block::gen::t_ChanState.as_string_ref(data_rec.state);
return std::move(res);
} // namespace ton
td::optional<td::int32> PaymentChannel::guess_revision(const vm::Cell::Hash& code_hash) {
for (auto i : ton::SmartContractCode::get_revisions(ton::SmartContractCode::PaymentChannel)) {
auto code = SmartContractCode::get_code(SmartContractCode::PaymentChannel, i);
if (code->get_hash() == code_hash) {
return i;
}
}
return {};
}
} // namespace ton

View file

@ -0,0 +1,251 @@
#pragma once
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "Ed25519.h"
#include "block/block-auto.h"
#include "block/block-parse.h"
#include "td/utils/Variant.h"
#include "SmartContract.h"
#include "SmartContractCode.h"
namespace ton {
namespace pchan {
//
// Payment channels
//
struct Config {
td::uint32 init_timeout{0};
td::uint32 close_timeout{0};
td::SecureString a_key;
td::SecureString b_key;
block::StdAddress a_addr;
block::StdAddress b_addr;
td::uint64 channel_id{0};
td::Ref<vm::Cell> serialize() const;
};
struct MsgInit {
td::uint64 inc_A{0};
td::uint64 inc_B{0};
td::uint64 min_A{0};
td::uint64 min_B{0};
td::uint64 channel_id{0};
td::Ref<vm::Cell> serialize() const;
};
struct Promise {
td::uint64 channel_id;
td::uint64 promise_A{0};
td::uint64 promise_B{0};
td::Ref<vm::Cell> serialize() const;
};
td::Ref<vm::Cell> maybe_sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key);
td::Ref<vm::CellSlice> maybe_ref(td::Ref<vm::Cell> msg);
struct MsgClose {
td::uint64 extra_A{0};
td::uint64 extra_B{0};
td::Ref<vm::CellSlice> signed_promise;
td::Ref<vm::Cell> serialize() const;
};
struct MsgTimeout {
td::Ref<vm::Cell> serialize() const;
};
struct SignedPromise {
Promise promise;
td::optional<td::SecureString> o_signature;
bool unpack(td::Ref<vm::Cell> cell);
static td::SecureString signature(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise);
static td::Ref<vm::Cell> create_and_serialize(td::Slice signature, const td::Ref<vm::Cell>& promise);
static td::Ref<vm::Cell> create_and_serialize(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise);
};
struct StateInit {
bool signed_A{false};
bool signed_B{false};
td::uint64 min_A{0};
td::uint64 min_B{0};
td::uint64 A{0};
td::uint64 B{0};
td::uint32 expire_at{0};
td::Ref<vm::Cell> serialize() const;
};
struct StateClose {
bool signed_A{false};
bool signed_B{false};
td::uint64 promise_A{0};
td::uint64 promise_B{0};
td::uint64 A{0};
td::uint64 B{0};
td::uint32 expire_at{0};
};
struct StatePayout {
td::uint64 A{0};
td::uint64 B{0};
};
struct Data {
td::Ref<vm::Cell> config;
td::Ref<vm::Cell> state;
static td::Ref<vm::Cell> init_state();
td::Ref<vm::Cell> serialize() const;
};
template <class T>
struct MsgBuilder {
td::Ed25519::PrivateKey* a_key{nullptr};
td::Ed25519::PrivateKey* b_key{nullptr};
T&& with_a_key(td::Ed25519::PrivateKey* key) && {
a_key = key;
return static_cast<T&&>(*this);
}
T&& with_b_key(td::Ed25519::PrivateKey* key) && {
b_key = key;
return static_cast<T&&>(*this);
}
td::Ref<vm::Cell> finalize() && {
block::gen::ChanSignedMsg::Record rec;
auto msg = static_cast<T&&>(*this).msg.serialize();
rec.msg = vm::load_cell_slice_ref(msg);
rec.sig_A = maybe_ref(maybe_sign(msg, a_key));
rec.sig_B = maybe_ref(maybe_sign(msg, b_key));
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
};
struct MsgInitBuilder : public MsgBuilder<MsgInitBuilder> {
MsgInit msg;
MsgInitBuilder&& min_A(td::uint64 value) && {
msg.min_A = value;
return std::move(*this);
}
MsgInitBuilder&& min_B(td::uint64 value) && {
msg.min_B = value;
return std::move(*this);
}
MsgInitBuilder&& inc_A(td::uint64 value) && {
msg.inc_A = value;
return std::move(*this);
}
MsgInitBuilder&& inc_B(td::uint64 value) && {
msg.inc_B = value;
return std::move(*this);
}
MsgInitBuilder&& channel_id(td::uint64 value) && {
msg.channel_id = value;
return std::move(*this);
}
};
struct MsgTimeoutBuilder : public MsgBuilder<MsgTimeoutBuilder> {
MsgTimeout msg;
};
struct MsgCloseBuilder : public MsgBuilder<MsgCloseBuilder> {
MsgClose msg;
MsgCloseBuilder&& extra_A(td::uint64 value) && {
msg.extra_A = value;
return std::move(*this);
}
MsgCloseBuilder&& extra_B(td::uint64 value) && {
msg.extra_B = value;
return std::move(*this);
}
MsgCloseBuilder&& signed_promise(td::Ref<vm::Cell> signed_promise) && {
msg.signed_promise = vm::load_cell_slice_ref(signed_promise);
return std::move(*this);
}
};
struct SignedPromiseBuilder {
Promise promise;
td::optional<td::SecureString> o_signature;
td::Ed25519::PrivateKey* key{nullptr};
SignedPromiseBuilder& with_key(td::Ed25519::PrivateKey* key) {
this->key = key;
return *this;
}
SignedPromiseBuilder& promise_A(td::uint64 value) {
promise.promise_A = value;
return *this;
}
SignedPromiseBuilder& promise_B(td::uint64 value) {
promise.promise_B = value;
return *this;
}
SignedPromiseBuilder& channel_id(td::uint64 value) {
promise.channel_id = value;
return *this;
}
SignedPromiseBuilder& signature(td::SecureString signature) {
o_signature = std::move(signature);
return *this;
}
bool check_signature(td::Slice signature, const td::Ed25519::PublicKey& pk) {
return pk.verify_signature(promise.serialize()->get_hash().as_slice(), signature).is_ok();
}
td::SecureString calc_signature() {
CHECK(key);
return SignedPromise::signature(key, promise.serialize());
}
td::Ref<vm::Cell> finalize() {
if (o_signature) {
return SignedPromise::create_and_serialize(o_signature.value().copy(), promise.serialize());
} else {
return SignedPromise::create_and_serialize(key, promise.serialize());
}
}
};
} // namespace pchan
class PaymentChannel : public SmartContract {
public:
PaymentChannel(State state) : SmartContract(std::move(state)) {
}
struct Info {
pchan::Config config;
td::Variant<pchan::StateInit, pchan::StateClose, pchan::StatePayout> state;
std::string description;
};
td::Result<Info> get_info() const;
static td::Ref<PaymentChannel> create(State state) {
return td::Ref<PaymentChannel>(true, std::move(state));
}
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
static td::Ref<PaymentChannel> create(const pchan::Config& config, td::int32 revision) {
State state;
state.code = SmartContractCode::get_code(SmartContractCode::PaymentChannel, revision);
pchan::Data data;
data.config = config.serialize();
pchan::StateInit init;
data.state = init.serialize();
state.data = data.serialize();
return create(std::move(state));
}
};
} // namespace ton

View file

@ -32,19 +32,19 @@
namespace ton {
namespace {
td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) {
td::Ref<vm::Stack> prepare_vm_stack(td::RefInt256 amount, td::Ref<vm::CellSlice> body) {
td::Ref<vm::Stack> stack_ref{true};
td::RefInt256 acc_addr{true};
//CHECK(acc_addr.write().import_bits(account.addr.cbits(), 256));
vm::Stack& stack = stack_ref.write();
stack.push_int(td::make_refint(10000000000));
stack.push_int(td::make_refint(10000000000));
stack.push_int(std::move(amount));
stack.push_cell(vm::CellBuilder().finalize());
stack.push_cellslice(std::move(body));
return stack_ref;
}
td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now, td::uint64 balance) {
// TODO: fix initialization of c7
td::BitArray<256> rand_seed;
rand_seed.as_slice().fill(0);
@ -58,7 +58,7 @@ td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
td::make_refint(0), // block_lt:Integer
td::make_refint(0), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer
block::CurrencyCollection(1000000000).as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
block::CurrencyCollection(balance).as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
vm::load_cell_slice_ref(vm::CellBuilder().finalize()) // myself:MsgAddressInt
//vm::StackEntry::maybe(td::Ref<vm::Cell>())
); // global_config:(Maybe Cell) ] = SmartContractInfo;
@ -66,6 +66,15 @@ td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
return vm::make_tuple_ref(std::move(tuple));
}
static int output_actions_count(td::Ref<vm::Cell> list) {
int i = -1;
do {
++i;
list = load_cell_slice(std::move(list)).prefetch_ref();
} while (list.not_null());
return i;
}
SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stack> stack, td::Ref<vm::Tuple> c7,
vm::GasLimits gas, bool ignore_chksig) {
auto gas_credit = gas.gas_credit;
@ -123,6 +132,8 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
if (res.success) {
res.new_state.data = vm.get_c4();
res.actions = vm.get_d(5);
LOG(DEBUG) << "output actions:\n"
<< block::gen::OutList{output_actions_count(res.actions)}.as_string_ref(res.actions);
}
LOG_IF(ERROR, gas_credit != 0 && (res.accepted && !res.success))
<< "Accepted but failed with code " << res.code << "\n"
@ -171,10 +182,13 @@ SmartContract::Answer SmartContract::run_method(Args args) {
now = args.now.unwrap();
}
if (!args.c7) {
args.c7 = prepare_vm_c7(now);
args.c7 = prepare_vm_c7(now, args.balance);
}
if (!args.limits) {
args.limits = vm::GasLimits{(long long)0, (long long)1000000, (long long)10000};
bool is_internal = args.get_method_id().ok() == 0;
args.limits = vm::GasLimits{is_internal ? (long long)args.amount * 1000 : (long long)0, (long long)1000000,
is_internal ? 0 : (long long)10000};
}
CHECK(args.stack);
CHECK(args.method_id);
@ -191,7 +205,7 @@ SmartContract::Answer SmartContract::run_get_method(Args args) const {
now = args.now.unwrap();
}
if (!args.c7) {
args.c7 = prepare_vm_c7(now);
args.c7 = prepare_vm_c7(now, args.balance);
}
if (!args.limits) {
args.limits = vm::GasLimits{1000000};
@ -209,6 +223,11 @@ SmartContract::Answer SmartContract::run_get_method(td::Slice method, Args args)
}
SmartContract::Answer SmartContract::send_external_message(td::Ref<vm::Cell> cell, Args args) {
return run_method(args.set_stack(prepare_vm_stack(vm::load_cell_slice_ref(cell))).set_method_id(-1));
return run_method(
args.set_stack(prepare_vm_stack(td::make_refint(0), vm::load_cell_slice_ref(cell))).set_method_id(-1));
}
SmartContract::Answer SmartContract::send_internal_message(td::Ref<vm::Cell> cell, Args args) {
return run_method(
args.set_stack(prepare_vm_stack(td::make_refint(args.amount), vm::load_cell_slice_ref(cell))).set_method_id(0));
}
} // namespace ton

View file

@ -57,6 +57,8 @@ class SmartContract : public td::CntObject {
td::optional<td::Ref<vm::Stack>> stack;
td::optional<td::int32> now;
bool ignore_chksig{false};
td::uint64 amount{0};
td::uint64 balance{0};
Args() {
}
@ -95,6 +97,14 @@ class SmartContract : public td::CntObject {
this->ignore_chksig = ignore_chksig;
return std::move(*this);
}
Args&& set_amount(td::uint64 amount) {
this->amount = amount;
return std::move(*this);
}
Args&& set_balance(td::uint64 balance) {
this->balance = balance;
return std::move(*this);
}
td::Result<td::int32> get_method_id() const {
if (!method_id) {
@ -109,6 +119,7 @@ class SmartContract : public td::CntObject {
Answer run_get_method(Args args = {}) const;
Answer run_get_method(td::Slice method, Args args = {}) const;
Answer send_external_message(td::Ref<vm::Cell> cell, Args args = {});
Answer send_internal_message(td::Ref<vm::Cell> cell, Args args = {});
size_t code_size() const;
size_t data_size() const;
@ -122,6 +133,9 @@ class SmartContract : public td::CntObject {
const State& get_state() const {
return state_;
}
CntObject* make_copy() const override {
return new SmartContract(state_);
}
protected:
State state_;

View file

@ -44,6 +44,8 @@ const auto& get_map() {
#include "smartcont/auto/highload-wallet-code.cpp"
#include "smartcont/auto/highload-wallet-v2-code.cpp"
#include "smartcont/auto/dns-manual-code.cpp"
#include "smartcont/auto/payment-channel-code.cpp"
#include "smartcont/auto/restricted-wallet3-code.cpp"
with_tvm_code("highload-wallet-r1",
"te6ccgEBBgEAhgABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQC88oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/"
@ -96,6 +98,14 @@ const auto& get_map() {
"FwCEMQLTAAHAAZPUAdCY0wUBqgLXGAHiINdJwg/"
"ypiB41yLXCwfyaHBTEddJqTYCmNMHAcAAEqEB5DDIywYBzxbJ0FADACBZ9KhvpSCUAvQEMJIybeICACg0A4AQ9FqZECOECUBE8AEBkjAx4gBmM"
"SLAFZwy9AQQI4QJUELwAQHgIsAWmDIChAn0czAB4DAyIMAfkzD0BODAIJJtAeDyLG0B");
with_tvm_code(
"restricted-wallet3-r1",
"te6ccgECEgEAAUsAART/APSkE/S88sgLAQIBIAIDAgFIBAUD+PKDCNcYINMf0x/THwL4I7vyY+1E0NMf0x/T/"
"1NDuvKhUWK68qIG+QFUEHb5EPKkAY4fMwHT/9EB0x/0BNH4AAOkyMsfFMsfy/8Syx/0AMntVOEC0x/"
"0BNH4ACH4I9s8IYAg9HtvpTGW+gAwcvsCkTDiApMg10qK6NECpMgPEBEABNAwAgEgBgcCASAICQIBSAwNAgFuCgsAEbjJftRNDXCx+"
"AAXrc52omhpn5jrhf/AABesePaiaGmPmOuFj8ABDbbYHwR7Z5AOAQm1B1tnkA4BTu1E0IEBQNch0x/"
"0BNEC2zz4J28QAoAg9HtvpTGX+gAwoXC2CZEw4g8AOiGOETGA8/gzIG6SMHCU0NcLH+IB3yGSAaGSW3/iAAzTB9QC+wAAHssfFMsfEsv/yx/"
"0AMntVA==");
return map;
}();
return map;
@ -103,7 +113,6 @@ const auto& get_map() {
} // namespace
td::Result<td::Ref<vm::Cell>> SmartContractCode::load(td::Slice name) {
LOG(ERROR) << "LOAD " << name;
auto& map = get_map();
auto it = map.find(name);
if (it == map.end()) {
@ -146,6 +155,14 @@ td::Span<int> SmartContractCode::get_revisions(Type type) {
static int res[] = {-1, 1};
return res;
}
case Type::PaymentChannel: {
static int res[] = {-1};
return res;
}
case Type::RestrictedWallet: {
static int res[] = {-1, 1};
return res;
}
}
UNREACHABLE();
return {};
@ -190,6 +207,10 @@ td::Ref<vm::Cell> SmartContractCode::get_code(Type type, int ext_revision) {
return "multisig";
case Type::ManualDns:
return "dns-manual";
case Type::PaymentChannel:
return "payment-channel";
case Type::RestrictedWallet:
return "restricted-wallet3";
}
UNREACHABLE();
return "";

View file

@ -16,6 +16,7 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "vm/cells.h"
#include "td/utils/Span.h"
@ -25,7 +26,18 @@ class SmartContractCode {
public:
static td::Result<td::Ref<vm::Cell>> load(td::Slice name);
enum Type { WalletV1 = 1, WalletV1Ext, WalletV2, WalletV3, HighloadWalletV1, HighloadWalletV2, ManualDns, Multisig };
enum Type {
WalletV1 = 1,
WalletV1Ext,
WalletV2,
WalletV3,
HighloadWalletV1,
HighloadWalletV2,
ManualDns,
Multisig,
PaymentChannel,
RestrictedWallet
};
static td::Span<int> get_revisions(Type type);
static td::Result<int> validate_revision(Type type, int revision);
static td::Ref<vm::Cell> get_code(Type type, int revision = 0);

View file

@ -43,12 +43,7 @@ td::Ref<vm::Cell> TestGiver::make_a_gift_message_static(td::uint32 seqno, td::Sp
for (auto& gift : gifts) {
td::int32 send_mode = 1;
auto gramms = gift.gramms;
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
return cb.finalize();

View file

@ -46,18 +46,11 @@ td::Ref<vm::Cell> TestWallet::make_a_gift_message_static(const td::Ed25519::Priv
for (auto& gift : gifts) {
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();

View file

@ -52,16 +52,10 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
for (auto& gift : gifts) {
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.finalize();

View file

@ -24,6 +24,7 @@
#include "vm/cells/CellString.h"
#include "SmartContract.h"
#include "GenericAccount.h"
namespace ton {
class WalletInterface {
@ -36,6 +37,7 @@ class WalletInterface {
std::string message;
td::Ref<vm::Cell> body;
td::Ref<vm::Cell> init_state;
};
virtual ~WalletInterface() {
@ -48,15 +50,29 @@ class WalletInterface {
return td::Status::Error("Unsupported");
}
td::Result<td::Ref<vm::Cell>> get_init_message(const td::Ed25519::PrivateKey &private_key,
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) {
td::Result<td::Ref<vm::Cell>> get_init_message(
const td::Ed25519::PrivateKey &private_key,
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const {
return make_a_gift_message(private_key, valid_until, {});
}
static td::Ref<vm::Cell> create_int_message(const Gift &gift) {
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms);
if (gift.init_state.not_null()) {
cbi.store_ones(2);
cbi.store_ref(gift.init_state);
} else {
cbi.store_zeroes(1);
}
cbi.store_zeroes(1);
store_gift_message(cbi, gift);
return cbi.finalize();
}
static void store_gift_message(vm::CellBuilder &cb, const Gift &gift) {
if (gift.body.not_null()) {
auto body = vm::load_cell_slice(gift.body);
//TODO: handle error
cb.append_cellslice_bool(body);
CHECK(cb.append_cellslice_bool(body));
return;
}

View file

@ -62,16 +62,10 @@ td::Ref<vm::Cell> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& p
for (auto& gift : gifts) {
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.finalize();

View file

@ -70,3 +70,209 @@ class WalletV3 : public ton::SmartContract, public WalletInterface {
td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const;
};
} // namespace ton
#include "smc-envelope/SmartContractCode.h"
#include "smc-envelope/GenericAccount.h"
#include "block/block-parse.h"
#include <algorithm>
namespace ton {
template <class WalletT, class TraitsT>
class WalletBase : public SmartContract, public WalletInterface {
public:
using Traits = TraitsT;
using InitData = typename Traits::InitData;
explicit WalletBase(State state) : SmartContract(std::move(state)) {
}
static td::Ref<WalletT> create(State state) {
return td::Ref<WalletT>(true, std::move(state));
}
static td::Ref<vm::Cell> get_init_code(int revision) {
return SmartContractCode::get_code(get_code_type(), revision);
};
size_t get_max_gifts_size() const override {
return Traits::max_gifts_size;
}
static SmartContractCode::Type get_code_type() {
return Traits::code_type;
}
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash) {
for (auto i : ton::SmartContractCode::get_revisions(get_code_type())) {
auto code = SmartContractCode::get_code(get_code_type(), i);
if (code->get_hash() == code_hash) {
return i;
}
}
return {};
}
static td::Ref<WalletT> create(const InitData& init_data, int revision) {
return td::Ref<WalletT>(true, State{get_init_code(revision), WalletT::get_init_data(init_data)});
}
td::Result<td::uint32> get_seqno() const {
return TRY_VM([&]() -> td::Result<td::uint32> {
Answer answer = this->run_get_method("seqno");
if (!answer.success) {
return td::Status::Error("seqno get method failed");
}
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
}());
}
td::Result<td::uint32> get_wallet_id() const {
return TRY_VM([&]() -> td::Result<td::uint32> {
Answer answer = this->run_get_method("wallet_id");
if (!answer.success) {
return td::Status::Error("seqno get method failed");
}
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
}());
}
td::Result<td::uint64> get_balance(td::uint64 account_balance, td::uint32 now) const {
return TRY_VM([&]() -> td::Result<td::uint64> {
Answer answer = this->run_get_method(Args().set_method_id("balance").set_balance(account_balance).set_now(now));
if (!answer.success) {
return td::Status::Error("balance get method failed");
}
return static_cast<td::uint64>(answer.stack.write().pop_long());
}());
}
td::Result<td::Ed25519::PublicKey> get_public_key() const override {
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
Answer answer = this->run_get_method("get_public_key");
if (!answer.success) {
return td::Status::Error("get_public_key get method failed");
}
auto key_int = answer.stack.write().pop_int();
LOG(ERROR) << key_int->bit_size(false);
td::SecureString bytes(32);
if (!key_int->export_bytes(bytes.as_mutable_slice().ubegin(), bytes.size(), false)) {
return td::Status::Error("not a public key");
}
return td::Ed25519::PublicKey(std::move(bytes));
}());
};
};
struct RestrictedWalletTraits {
struct InitData {
td::SecureString init_key;
td::SecureString main_key;
td::uint32 wallet_id{0};
};
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static constexpr unsigned max_gifts_size = 4;
static constexpr auto code_type = SmartContractCode::RestrictedWallet;
};
class RestrictedWallet : public WalletBase<RestrictedWallet, RestrictedWalletTraits> {
public:
struct Config {
td::uint32 start_at{0};
std::vector<std::pair<td::int32, td::uint64>> limits;
};
explicit RestrictedWallet(State state) : WalletBase(std::move(state)) {
}
td::Result<Config> get_config() const {
return TRY_VM([this]() -> td::Result<Config> {
auto cs = vm::load_cell_slice(get_state().data);
Config config;
td::Ref<vm::Cell> dict_root;
auto ok = cs.advance(32 + 32 + 256) && cs.fetch_uint_to(32, config.start_at) && cs.fetch_maybe_ref(dict_root);
vm::Dictionary dict(std::move(dict_root), 32);
dict.check_for_each([&](auto cs, auto ptr, auto ptr_bits) {
auto r_seconds = td::narrow_cast_safe<td::int32>(dict.key_as_integer(ptr, true)->to_long());
if (r_seconds.is_error()) {
ok = false;
return ok;
}
td::uint64 value;
ok &= smc::unpack_grams(cs, value);
config.limits.emplace_back(r_seconds.ok(), value);
return ok;
});
if (!ok) {
return td::Status::Error("Can't parse config");
}
std::sort(config.limits.begin(), config.limits.end());
return config;
}());
}
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) {
vm::CellBuilder cb;
cb.store_long(0, 32);
cb.store_long(init_data.wallet_id, 32);
CHECK(init_data.init_key.size() == 32);
CHECK(init_data.main_key.size() == 32);
cb.store_bytes(init_data.init_key.as_slice());
cb.store_bytes(init_data.main_key.as_slice());
return cb.finalize();
}
td::Result<td::Ref<vm::Cell>> get_init_message(const td::Ed25519::PrivateKey& init_private_key,
td::uint32 valid_until, const Config& config) const {
vm::CellBuilder cb;
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
LOG(ERROR) << "seqno: " << seqno << " wallet_id: " << wallet_id;
if (seqno != 0) {
return td::Status::Error("Wallet is already inited");
}
cb.store_long(wallet_id, 32);
cb.store_long(valid_until, 32);
cb.store_long(seqno, 32);
cb.store_long(config.start_at, 32);
vm::Dictionary dict(32);
auto add = [&](td::int32 till, td::uint64 value) {
auto key = dict.integer_key(td::make_refint(till), 32, true);
vm::CellBuilder gcb;
block::tlb::t_Grams.store_integer_value(gcb, td::BigInt256(value));
dict.set_builder(key.bits(), 32, gcb);
};
for (auto limit : config.limits) {
add(limit.first, limit.second);
}
cb.store_maybe_ref(dict.get_root_cell());
auto message_outer = cb.finalize();
auto signature = init_private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Result<td::Ref<vm::Cell>> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until,
td::Span<Gift> gifts) const override {
CHECK(gifts.size() <= Traits::max_gifts_size);
vm::CellBuilder cb;
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
if (seqno == 0) {
return td::Status::Error("Wallet is not inited yet");
}
cb.store_long(wallet_id, 32);
cb.store_long(valid_until, 32);
cb.store_long(seqno, 32);
for (auto& gift : gifts) {
td::int32 send_mode = 3;
if (gift.gramms == -1) {
send_mode += 128;
}
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
};
} // namespace ton