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

integrating the existing state of TON Storage / TON Payments / CPS Fift development branches

This commit is contained in:
ton 2020-05-27 22:10:46 +04:00
parent 040df63c98
commit 4e2624459b
153 changed files with 10760 additions and 1695 deletions

View file

@ -46,7 +46,8 @@ bool unpack_grams(td::Ref<vm::CellSlice> cs, td::uint64& amount) {
}
} // namespace smc
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept {
td::Ref<vm::Cell> GenericAccount::get_init_state(const td::Ref<vm::Cell>& code,
const td::Ref<vm::Cell>& data) noexcept {
return vm::CellBuilder()
.store_zeroes(2)
.store_ones(2)
@ -136,4 +137,23 @@ td::Result<td::Ed25519::PublicKey> GenericAccount::get_public_key(const SmartCon
};
return TRY_VM(do_get_public_key());
}
td::Result<td::uint32> GenericAccount::get_seqno(const SmartContract& sc) {
return TRY_VM([&]() -> td::Result<td::uint32> {
auto answer = sc.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> GenericAccount::get_wallet_id(const SmartContract& sc) {
return TRY_VM([&]() -> td::Result<td::uint32> {
auto answer = sc.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()));
}());
}
} // namespace ton

View file

@ -29,12 +29,17 @@ 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;
static td::Ref<vm::Cell> get_init_state(const td::Ref<vm::Cell>& code, const td::Ref<vm::Cell>& data) noexcept;
static td::Ref<vm::Cell> get_init_state(const SmartContract::State& state) noexcept {
return get_init_state(state.code, state.data);
}
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) noexcept;
static td::Ref<vm::Cell> create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body) noexcept;
static void store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms);
static td::Result<td::Ed25519::PublicKey> get_public_key(const SmartContract& sc);
static td::Result<td::uint32> get_seqno(const SmartContract& sc);
static td::Result<td::uint32> get_wallet_id(const SmartContract& sc);
};
} // namespace ton

View file

@ -27,48 +27,12 @@
#include <limits>
namespace ton {
td::optional<td::int32> HighloadWallet::guess_revision(const vm::Cell::Hash& code_hash) {
for (td::int32 i = 1; i <= 2; i++) {
if (get_init_code(i)->get_hash() == code_hash) {
return i;
}
}
return {};
}
td::optional<td::int32> HighloadWallet::guess_revision(const block::StdAddress& address,
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) {
for (td::int32 i = 1; i <= 2; i++) {
if (GenericAccount::get_address(address.workchain, get_init_state(public_key, wallet_id, i)) == address) {
return i;
}
}
return {};
}
td::Ref<vm::Cell> HighloadWallet::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
td::int32 revision) noexcept {
auto code = get_init_code(revision);
auto data = get_init_data(public_key, wallet_id);
return GenericAccount::get_init_state(std::move(code), std::move(data));
}
td::Ref<vm::Cell> HighloadWallet::get_init_message(const td::Ed25519::PrivateKey& private_key,
td::uint32 wallet_id) noexcept {
td::uint32 seqno = 0;
td::uint32 valid_until = std::numeric_limits<td::uint32>::max();
auto append_message = [&](auto&& cb) -> vm::CellBuilder& {
cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(seqno, 32);
CHECK(cb.store_maybe_ref({}));
return cb;
};
auto signature = private_key.sign(append_message(vm::CellBuilder()).finalize()->get_hash().as_slice()).move_as_ok();
return append_message(vm::CellBuilder().store_bytes(signature)).finalize();
}
td::Ref<vm::Cell> HighloadWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 seqno, td::uint32 valid_until,
td::Span<Gift> gifts) noexcept {
CHECK(gifts.size() <= max_gifts_size);
td::Result<td::Ref<vm::Cell>> HighloadWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
td::uint32 valid_until, td::Span<Gift> gifts) const {
TRY_RESULT(wallet_id, get_wallet_id());
TRY_RESULT(seqno, get_seqno());
CHECK(gifts.size() <= get_max_gifts_size());
vm::Dictionary messages(16);
for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i];
@ -91,63 +55,36 @@ td::Ref<vm::Cell> HighloadWallet::make_a_gift_message(const td::Ed25519::Private
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Ref<vm::Cell> HighloadWallet::get_init_code(td::int32 revision) noexcept {
return SmartContractCode::get_code(SmartContractCode::HighloadWalletV1, revision);
}
vm::CellHash HighloadWallet::get_init_code_hash() noexcept {
return get_init_code(0)->get_hash();
}
td::Ref<vm::Cell> HighloadWallet::get_init_data(const td::Ed25519::PublicKey& public_key,
td::uint32 wallet_id) noexcept {
td::Ref<vm::Cell> HighloadWallet::get_init_data(const InitData& init_data) noexcept {
return vm::CellBuilder()
.store_long(0, 32)
.store_long(wallet_id, 32)
.store_bytes(public_key.as_octet_string())
.store_long(init_data.seqno, 32)
.store_long(init_data.wallet_id, 32)
.store_bytes(init_data.public_key)
.finalize();
}
td::Result<td::uint32> HighloadWallet::get_seqno() const {
return TRY_VM(get_seqno_or_throw());
}
td::Result<td::uint32> HighloadWallet::get_seqno_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
//FIXME use get method
return static_cast<td::uint32>(vm::load_cell_slice(state_.data).fetch_ulong(32));
}
td::Result<td::uint32> HighloadWallet::get_wallet_id() const {
return TRY_VM(get_wallet_id_or_throw());
}
td::Result<td::uint32> HighloadWallet::get_wallet_id_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
//FIXME use get method
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(32);
return static_cast<td::uint32>(cs.fetch_ulong(32));
return TRY_VM([&]() -> td::Result<td::uint32> {
if (state_.data.is_null()) {
return 0;
}
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(32);
return static_cast<td::uint32>(cs.fetch_ulong(32));
}());
}
td::Result<td::Ed25519::PublicKey> HighloadWallet::get_public_key() const {
return TRY_VM(get_public_key_or_throw());
}
td::Result<td::Ed25519::PublicKey> HighloadWallet::get_public_key_or_throw() const {
if (state_.data.is_null()) {
return td::Status::Error("data is null");
}
//FIXME use get method
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(64);
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
return td::Ed25519::PublicKey(std::move(res));
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
if (state_.data.is_null()) {
return td::Status::Error("data is null");
}
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(64);
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
return td::Ed25519::PublicKey(std::move(res));
}());
}
} // namespace ton

View file

@ -26,41 +26,24 @@
#include "vm/cells/CellString.h"
namespace ton {
class HighloadWallet : public ton::SmartContract, public WalletInterface {
public:
explicit HighloadWallet(State state) : ton::SmartContract(std::move(state)) {
}
struct HighloadWalletTraits {
using InitData = WalletInterface::DefaultInitData;
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static constexpr unsigned max_gifts_size = 254;
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
td::int32 revision) noexcept;
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id) noexcept;
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 seqno, td::uint32 valid_until, td::Span<Gift> gifts) noexcept;
static td::Ref<vm::Cell> get_init_code(td::int32 revision) noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept;
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
static td::optional<td::int32> guess_revision(const block::StdAddress& address,
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id);
td::Result<td::uint32> get_seqno() const;
td::Result<td::uint32> get_wallet_id() const;
static constexpr auto code_type = SmartContractCode::HighloadWalletV1;
};
class HighloadWallet : public WalletBase<HighloadWallet, HighloadWalletTraits> {
public:
explicit HighloadWallet(State state) : WalletBase(std::move(state)) {
}
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 {
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
return make_a_gift_message(private_key, wallet_id, seqno, valid_until, gifts);
}
size_t get_max_gifts_size() const override {
return max_gifts_size;
}
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
td::Span<Gift> gifts) const override;
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) noexcept;
private:
td::Result<td::uint32> get_seqno_or_throw() const;
td::Result<td::uint32> get_wallet_id_or_throw() const;
td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const;
// can't use get methods for compatibility with old revisions
td::Result<td::uint32> get_wallet_id() const override;
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
};
} // namespace ton

View file

@ -27,33 +27,10 @@
#include <limits>
namespace ton {
td::optional<td::int32> HighloadWalletV2::guess_revision(const vm::Cell::Hash& code_hash) {
for (td::int32 i = 1; i <= 2; i++) {
if (get_init_code(i)->get_hash() == code_hash) {
return i;
}
}
return {};
}
td::optional<td::int32> HighloadWalletV2::guess_revision(const block::StdAddress& address,
const td::Ed25519::PublicKey& public_key,
td::uint32 wallet_id) {
for (td::int32 i = 1; i <= 2; i++) {
if (GenericAccount::get_address(address.workchain, get_init_state(public_key, wallet_id, i)) == address) {
return i;
}
}
return {};
}
td::Ref<vm::Cell> HighloadWalletV2::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
td::int32 revision) noexcept {
auto code = get_init_code(revision);
auto data = get_init_data(public_key, wallet_id);
return GenericAccount::get_init_state(std::move(code), std::move(data));
}
td::Ref<vm::Cell> HighloadWalletV2::get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 valid_until) noexcept {
td::Result<td::Ref<vm::Cell>> HighloadWalletV2::get_init_message(const td::Ed25519::PrivateKey& private_key,
td::uint32 valid_until) const noexcept {
TRY_RESULT(wallet_id, get_wallet_id());
td::uint32 id = -1;
auto append_message = [&](auto&& cb) -> vm::CellBuilder& {
cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(id, 32);
@ -65,10 +42,11 @@ td::Ref<vm::Cell> HighloadWalletV2::get_init_message(const td::Ed25519::PrivateK
return append_message(vm::CellBuilder().store_bytes(signature)).finalize();
}
td::Ref<vm::Cell> HighloadWalletV2::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
td::uint32 wallet_id, td::uint32 valid_until,
td::Span<Gift> gifts) noexcept {
CHECK(gifts.size() <= max_gifts_size);
td::Result<td::Ref<vm::Cell>> HighloadWalletV2::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
td::uint32 valid_until,
td::Span<Gift> gifts) const {
TRY_RESULT(wallet_id, get_wallet_id());
CHECK(gifts.size() <= get_max_gifts_size());
vm::Dictionary messages(16);
for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i];
@ -96,49 +74,34 @@ td::Ref<vm::Cell> HighloadWalletV2::make_a_gift_message(const td::Ed25519::Priva
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Ref<vm::Cell> HighloadWalletV2::get_init_code(td::int32 revision) noexcept {
return SmartContractCode::get_code(SmartContractCode::HighloadWalletV2, revision);
}
vm::CellHash HighloadWalletV2::get_init_code_hash() noexcept {
return get_init_code(0)->get_hash();
}
td::Ref<vm::Cell> HighloadWalletV2::get_init_data(const td::Ed25519::PublicKey& public_key,
td::uint32 wallet_id) noexcept {
td::Ref<vm::Cell> HighloadWalletV2::get_init_data(const InitData& init_data) noexcept {
vm::CellBuilder cb;
cb.store_long(wallet_id, 32).store_long(0, 64).store_bytes(public_key.as_octet_string());
cb.store_long(init_data.wallet_id, 32).store_long(init_data.seqno, 64).store_bytes(init_data.public_key);
CHECK(cb.store_maybe_ref({}));
return cb.finalize();
}
td::Result<td::uint32> HighloadWalletV2::get_wallet_id() const {
return TRY_VM(get_wallet_id_or_throw());
}
td::Result<td::uint32> HighloadWalletV2::get_wallet_id_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
//FIXME use get method
auto cs = vm::load_cell_slice(state_.data);
return static_cast<td::uint32>(cs.fetch_ulong(32));
return TRY_VM([&]() -> td::Result<td::uint32> {
if (state_.data.is_null()) {
return 0;
}
auto cs = vm::load_cell_slice(state_.data);
return static_cast<td::uint32>(cs.fetch_ulong(32));
}());
}
td::Result<td::Ed25519::PublicKey> HighloadWalletV2::get_public_key() const {
return TRY_VM(get_public_key_or_throw());
}
td::Result<td::Ed25519::PublicKey> HighloadWalletV2::get_public_key_or_throw() const {
if (state_.data.is_null()) {
return td::Status::Error("data is null");
}
//FIXME use get method
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(96);
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
return td::Ed25519::PublicKey(std::move(res));
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
if (state_.data.is_null()) {
return td::Status::Error("data is null");
}
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(96);
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
return td::Ed25519::PublicKey(std::move(res));
}());
}
} // namespace ton

View file

@ -26,41 +26,26 @@
#include "vm/cells/CellString.h"
namespace ton {
class HighloadWalletV2 : public ton::SmartContract, public WalletInterface {
public:
explicit HighloadWalletV2(State state) : ton::SmartContract(std::move(state)) {
}
struct HighloadWalletV2Traits {
using InitData = WalletInterface::DefaultInitData;
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static constexpr unsigned max_gifts_size = 254;
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
td::int32 revision) noexcept;
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 valid_until) noexcept;
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 valid_until, td::Span<Gift> gifts) noexcept;
static td::Ref<vm::Cell> get_init_code(td::int32 revision) noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept;
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
static td::optional<td::int32> guess_revision(const block::StdAddress& address,
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id);
td::Result<td::uint32> get_wallet_id() const;
static constexpr auto code_type = SmartContractCode::HighloadWalletV2;
};
class HighloadWalletV2 : public WalletBase<HighloadWalletV2, HighloadWalletV2Traits> {
public:
explicit HighloadWalletV2(State state) : WalletBase(std::move(state)) {
}
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 {
TRY_RESULT(wallet_id, get_wallet_id());
return make_a_gift_message(private_key, wallet_id, valid_until, gifts);
}
size_t get_max_gifts_size() const override {
return max_gifts_size;
}
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
td::Span<Gift> gifts) const override;
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) noexcept;
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
noexcept;
private:
td::Result<td::uint32> get_wallet_id_or_throw() const;
td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const;
// can't use get methods for compatibility with old revisions
td::Result<td::uint32> get_wallet_id() const override;
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
};
} // namespace ton

View file

@ -30,6 +30,14 @@
#include "td/utils/crypto.h"
namespace ton {
int SmartContract::Answer::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;
}
namespace {
td::Ref<vm::Stack> prepare_vm_stack(td::RefInt256 amount, td::Ref<vm::CellSlice> body) {
@ -66,15 +74,6 @@ td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now, td::uint64 balance) {
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;
@ -133,7 +132,7 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
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);
<< block::gen::OutList{res.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"

View file

@ -48,6 +48,7 @@ class SmartContract : public td::CntObject {
td::Ref<vm::Cell> actions;
td::int32 code;
td::int64 gas_used;
static int output_actions_count(td::Ref<vm::Cell> list);
};
struct Args {

View file

@ -38,8 +38,6 @@ const auto& get_map() {
map[name] = vm::std_boc_deserialize(td::base64_decode(code_str).move_as_ok()).move_as_ok();
};
#include "smartcont/auto/multisig-code.cpp"
#include "smartcont/auto/simple-wallet-ext-code.cpp"
#include "smartcont/auto/simple-wallet-code.cpp"
#include "smartcont/auto/wallet-code.cpp"
#include "smartcont/auto/highload-wallet-code.cpp"
#include "smartcont/auto/highload-wallet-v2-code.cpp"
@ -65,18 +63,6 @@ const auto& get_map() {
"QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44WIYAQ9HhvpSCYAtMH1DAB+wCRMuIBs+"
"ZbgyWhyEA0gED0Q4rmMcgSyx8Tyz/L//QAye1UCAAE0DACASAGBwAXvZznaiaGmvmOuF/8AEG+X5dqJoaY+Y6Z/p/"
"5j6AmipEEAgegc30JjJLb/JXdHxQANCCAQPSWb6UyURCUMFMDud4gkzM2AZIyMOKz");
with_tvm_code("simple-wallet-r1",
"te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/"
"0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA==");
with_tvm_code("simple-wallet-r2",
"te6ccgEBAQEAXwAAuv8AIN0gggFMl7ohggEznLqxnHGw7UTQ0x/XC//jBOCk8mCBAgDXGCDXCx/tRNDTH9P/"
"0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA==");
with_tvm_code("wallet-r1",
"te6ccgEBAQEAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/0VExuvKhA/"
"kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=");
with_tvm_code("wallet-r2",
"te6ccgEBAQEAYwAAwv8AIN0gggFMl7ohggEznLqxnHGw7UTQ0x/XC//jBOCk8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/"
"0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=");
with_tvm_code("wallet3-r1",
"te6ccgEBAQEAYgAAwP8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/T/"
"9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA==");
@ -123,22 +109,10 @@ td::Result<td::Ref<vm::Cell>> SmartContractCode::load(td::Slice name) {
td::Span<int> SmartContractCode::get_revisions(Type type) {
switch (type) {
case Type::WalletV1: {
static int res[] = {1, 2};
return res;
}
case Type::WalletV2: {
static int res[] = {1, 2};
return res;
}
case Type::WalletV3: {
static int res[] = {1, 2};
return res;
}
case Type::WalletV1Ext: {
static int res[] = {-1};
return res;
}
case Type::HighloadWalletV1: {
static int res[] = {-1, 1, 2};
return res;
@ -191,14 +165,8 @@ td::Ref<vm::Cell> SmartContractCode::get_code(Type type, int ext_revision) {
auto revision = validate_revision(type, ext_revision).move_as_ok();
auto basename = [](Type type) -> td::Slice {
switch (type) {
case Type::WalletV1:
return "simple-wallet";
case Type::WalletV2:
return "wallet";
case Type::WalletV3:
return "wallet3";
case Type::WalletV1Ext:
return "simple-wallet-ext";
case Type::HighloadWalletV1:
return "highload-wallet";
case Type::HighloadWalletV2:

View file

@ -26,18 +26,7 @@ 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,
PaymentChannel,
RestrictedWallet
};
enum Type { WalletV3 = 4, 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

@ -0,0 +1,77 @@
/*
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/>.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "WalletInterface.h"
namespace ton {
td::Result<td::uint64> WalletInterface::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> WalletInterface::get_public_key() const {
return GenericAccount::get_public_key(*this);
};
td::Result<td::uint32> WalletInterface::get_seqno() const {
return GenericAccount::get_seqno(*this);
}
td::Result<td::uint32> WalletInterface::get_wallet_id() const {
return GenericAccount::get_wallet_id(*this);
}
td::Result<td::Ref<vm::Cell>> WalletInterface::get_init_message(const td::Ed25519::PrivateKey &private_key,
td::uint32 valid_until) const {
return make_a_gift_message(private_key, valid_until, {});
}
td::Ref<vm::Cell> WalletInterface::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();
}
void WalletInterface::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
CHECK(cb.append_cellslice_bool(body));
return;
}
if (gift.is_encrypted) {
cb.store_long(1, 32);
} else {
cb.store_long(0, 32);
}
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
}
} // namespace ton

View file

@ -21,13 +21,17 @@
#include "td/utils/common.h"
#include "Ed25519.h"
#include "block/block.h"
#include "block/block-parse.h"
#include "vm/cells/CellString.h"
#include "SmartContract.h"
#include "SmartContractCode.h"
#include "GenericAccount.h"
#include <algorithm>
namespace ton {
class WalletInterface {
class WalletInterface : public SmartContract {
public:
struct Gift {
block::StdAddress destination;
@ -39,49 +43,91 @@ class WalletInterface {
td::Ref<vm::Cell> body;
td::Ref<vm::Cell> init_state;
};
struct DefaultInitData {
td::SecureString public_key;
td::uint32 wallet_id{0};
td::uint32 seqno{0};
DefaultInitData() = default;
DefaultInitData(td::Slice key, td::uint32 wallet_id) : public_key(key), wallet_id(wallet_id) {
}
};
WalletInterface(State state) : SmartContract(std::move(state)) {
}
virtual ~WalletInterface() {
}
virtual size_t get_max_gifts_size() const = 0;
virtual size_t get_max_message_size() const = 0;
virtual 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 = 0;
virtual td::Result<td::Ed25519::PublicKey> get_public_key() const {
return td::Status::Error("Unsupported");
virtual td::Result<td::uint32> get_seqno() const;
virtual td::Result<td::uint32> get_wallet_id() const;
virtual td::Result<td::uint64> get_balance(td::uint64 account_balance, td::uint32 now) const;
virtual td::Result<td::Ed25519::PublicKey> get_public_key() const;
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;
static td::Ref<vm::Cell> create_int_message(const Gift &gift);
static void store_gift_message(vm::CellBuilder &cb, const Gift &gift);
};
template <class WalletT, class TraitsT>
class WalletBase : public WalletInterface {
public:
using Traits = TraitsT;
using InitData = typename Traits::InitData;
explicit WalletBase(State state) : WalletInterface(std::move(state)) {
}
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, {});
size_t get_max_gifts_size() const override {
return Traits::max_gifts_size;
}
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();
size_t get_max_message_size() const override {
return Traits::max_message_size;
}
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
CHECK(cb.append_cellslice_bool(body));
return;
}
if (gift.is_encrypted) {
cb.store_long(1, 32);
} else {
cb.store_long(0, 32);
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);
};
static State get_init_state(int revision, const InitData &init_data) {
return {get_init_code(revision), WalletT::get_init_data(init_data)};
}
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 revision : ton::SmartContractCode::get_revisions(get_code_type())) {
auto code = get_init_code(revision);
if (code->get_hash() == code_hash) {
return revision;
}
}
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
return {};
}
static td::Span<td::int32> get_revisions() {
return ton::SmartContractCode::get_revisions(get_code_type());
}
static td::optional<td::int32> guess_revision(block::StdAddress &address, const InitData &init_data) {
for (auto revision : get_revisions()) {
if (WalletT(get_init_state(revision, init_data)).get_address(address.workchain) == address) {
return revision;
}
}
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)});
}
CntObject *make_copy() const override {
return new WalletT(get_state());
}
};

View file

@ -27,36 +27,11 @@
#include <limits>
namespace ton {
td::Ref<vm::Cell> WalletV3::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
td::int32 revision) noexcept {
auto code = get_init_code(revision);
auto data = get_init_data(public_key, wallet_id);
return GenericAccount::get_init_state(std::move(code), std::move(data));
}
td::optional<td::int32> WalletV3::guess_revision(const vm::Cell::Hash& code_hash) {
for (td::int32 i = 1; i <= 2; i++) {
if (get_init_code(i)->get_hash() == code_hash) {
return i;
}
}
return {};
}
td::optional<td::int32> WalletV3::guess_revision(const block::StdAddress& address,
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) {
for (td::int32 i = 1; i <= 2; i++) {
if (GenericAccount::get_address(address.workchain, get_init_state(public_key, wallet_id, i)) == address) {
return i;
}
}
return {};
}
td::Ref<vm::Cell> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 seqno, td::uint32 valid_until,
td::Span<Gift> gifts) noexcept {
CHECK(gifts.size() <= max_gifts_size);
td::Result<td::Ref<vm::Cell>> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
td::uint32 valid_until, td::Span<Gift> gifts) const {
CHECK(gifts.size() <= get_max_gifts_size());
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
vm::CellBuilder cb;
cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(seqno, 32);
@ -73,63 +48,36 @@ td::Ref<vm::Cell> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& p
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Ref<vm::Cell> WalletV3::get_init_code(td::int32 revision) noexcept {
return SmartContractCode::get_code(ton::SmartContractCode::WalletV3, revision);
}
vm::CellHash WalletV3::get_init_code_hash() noexcept {
return get_init_code()->get_hash();
}
td::Ref<vm::Cell> WalletV3::get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
td::uint32 seqno) noexcept {
td::Ref<vm::Cell> WalletV3::get_init_data(const InitData& init_data) noexcept {
return vm::CellBuilder()
.store_long(seqno, 32)
.store_long(wallet_id, 32)
.store_bytes(public_key.as_octet_string())
.store_long(init_data.seqno, 32)
.store_long(init_data.wallet_id, 32)
.store_bytes(init_data.public_key)
.finalize();
}
td::Result<td::uint32> WalletV3::get_seqno() const {
return TRY_VM(get_seqno_or_throw());
}
td::Result<td::uint32> WalletV3::get_seqno_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
//FIXME use get method
return static_cast<td::uint32>(vm::load_cell_slice(state_.data).fetch_ulong(32));
}
td::Result<td::uint32> WalletV3::get_wallet_id() const {
return TRY_VM(get_wallet_id_or_throw());
}
td::Result<td::uint32> WalletV3::get_wallet_id_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
//FIXME use get method
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(32);
return static_cast<td::uint32>(cs.fetch_ulong(32));
return TRY_VM([&]() -> td::Result<td::uint32> {
if (state_.data.is_null()) {
return 0;
}
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(32);
return static_cast<td::uint32>(cs.fetch_ulong(32));
}());
}
td::Result<td::Ed25519::PublicKey> WalletV3::get_public_key() const {
return TRY_VM(get_public_key_or_throw());
}
td::Result<td::Ed25519::PublicKey> WalletV3::get_public_key_or_throw() const {
if (state_.data.is_null()) {
return td::Status::Error("data is null");
}
//FIXME use get method
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(64);
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
return td::Ed25519::PublicKey(std::move(res));
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
if (state_.data.is_null()) {
return td::Status::Error("data is null");
}
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(64);
td::SecureString res(td::Ed25519::PublicKey::LENGTH);
cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast<td::int32>(res.size()));
return td::Ed25519::PublicKey(std::move(res));
}());
}
} // namespace ton

View file

@ -26,135 +26,30 @@
#include "vm/cells/CellString.h"
namespace ton {
class WalletV3 : public ton::SmartContract, public WalletInterface {
public:
explicit WalletV3(State state) : ton::SmartContract(std::move(state)) {
}
explicit WalletV3(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, td::uint32 seqno = 0)
: WalletV3(State{get_init_code(), get_init_data(public_key, wallet_id, seqno)}) {
}
struct WalletV3Traits {
using InitData = WalletInterface::DefaultInitData;
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static constexpr unsigned max_gifts_size = 4;
static constexpr auto code_type = SmartContractCode::WalletV3;
};
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
static td::optional<td::int32> guess_revision(const block::StdAddress& address,
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id);
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
td::int32 revision = 0) noexcept;
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 seqno, td::uint32 valid_until, td::Span<Gift> gifts) noexcept;
static td::Ref<vm::Cell> get_init_code(td::int32 revision = 0) noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id,
td::uint32 seqno = 0) noexcept;
td::Result<td::uint32> get_seqno() const;
td::Result<td::uint32> get_wallet_id() const;
using WalletInterface::get_init_message;
class WalletV3 : public WalletBase<WalletV3, WalletV3Traits> {
public:
explicit WalletV3(State state) : WalletBase(std::move(state)) {
}
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 {
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
return make_a_gift_message(private_key, wallet_id, seqno, valid_until, gifts);
}
size_t get_max_gifts_size() const override {
return max_gifts_size;
}
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
td::Span<Gift> gifts) const override;
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) noexcept;
private:
td::Result<td::uint32> get_seqno_or_throw() const;
td::Result<td::uint32> get_wallet_id_or_throw() const;
td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const;
// can't use get methods for compatibility with old revisions
td::Result<td::uint32> get_wallet_id() const override;
td::Result<td::Ed25519::PublicKey> get_public_key() const override;
};
} // 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 {