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:
parent
16a4566091
commit
9f008b129f
129 changed files with 8438 additions and 879 deletions
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
264
crypto/smc-envelope/PaymentChannel.cpp
Normal file
264
crypto/smc-envelope/PaymentChannel.cpp
Normal 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
|
251
crypto/smc-envelope/PaymentChannel.h
Normal file
251
crypto/smc-envelope/PaymentChannel.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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 "";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue