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

updated vm (breaking compatibility)

- updated vm
- new actor scheduler
- updated tonlib
- updated DNS smartcontract
This commit is contained in:
ton 2020-02-28 14:28:47 +04:00
parent 9e4816e7f6
commit e27fb1e09c
100 changed files with 3692 additions and 1299 deletions

View file

@ -519,7 +519,7 @@ if (NOT TON_ONLY_TONLIB)
add_test(test-adnl test-adnl) add_test(test-adnl test-adnl)
add_test(test-dht test-dht) add_test(test-dht test-dht)
add_test(test-rldp test-rldp) add_test(test-rldp test-rldp)
add_test(test-validator-session-state test-validator-session-state) #add_test(test-validator-session-state test-validator-session-state)
add_test(test-catchain test-catchain) add_test(test-catchain test-catchain)
add_test(test-fec test-fec) add_test(test-fec test-fec)

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "td/utils/int_types.h" #include "td/utils/int_types.h"

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "td/utils/bits.h" #include "td/utils/bits.h"
#include "block/block-parse.h" #include "block/block-parse.h"
@ -80,7 +80,7 @@ bool Maybe_Anycast::skip_get_depth(vm::CellSlice& cs, int& depth) const {
const Maybe_Anycast t_Maybe_Anycast; const Maybe_Anycast t_Maybe_Anycast;
bool MsgAddressInt::validate_skip(vm::CellSlice& cs, bool weak) const { bool MsgAddressInt::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
if (!cs.have(3)) { if (!cs.have(3)) {
return false; return false;
} }
@ -265,14 +265,14 @@ Ref<vm::CellSlice> MsgAddressInt::pack_std_address(ton::WorkchainId workchain, c
const MsgAddressInt t_MsgAddressInt; const MsgAddressInt t_MsgAddressInt;
bool MsgAddress::validate_skip(vm::CellSlice& cs, bool weak) const { bool MsgAddress::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
switch (get_tag(cs)) { switch (get_tag(cs)) {
case addr_none: case addr_none:
case addr_ext: case addr_ext:
return t_MsgAddressExt.validate_skip(cs, weak); return t_MsgAddressExt.validate_skip(ops, cs, weak);
case addr_std: case addr_std:
case addr_var: case addr_var:
return t_MsgAddressInt.validate_skip(cs, weak); return t_MsgAddressInt.validate_skip(ops, cs, weak);
} }
return false; return false;
} }
@ -284,7 +284,7 @@ bool VarUInteger::skip(vm::CellSlice& cs) const {
return len >= 0 && len < n && cs.advance(len * 8); return len >= 0 && len < n && cs.advance(len * 8);
} }
bool VarUInteger::validate_skip(vm::CellSlice& cs, bool weak) const { bool VarUInteger::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int len = (int)cs.fetch_ulong(ln); int len = (int)cs.fetch_ulong(ln);
return len >= 0 && len < n && (!len || cs.prefetch_ulong(8)) && cs.advance(len * 8); return len >= 0 && len < n && (!len || cs.prefetch_ulong(8)) && cs.advance(len * 8);
} }
@ -325,7 +325,7 @@ bool VarUIntegerPos::skip(vm::CellSlice& cs) const {
return len > 0 && len < n && cs.advance(len * 8); return len > 0 && len < n && cs.advance(len * 8);
} }
bool VarUIntegerPos::validate_skip(vm::CellSlice& cs, bool weak) const { bool VarUIntegerPos::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int len = (int)cs.fetch_ulong(ln); int len = (int)cs.fetch_ulong(ln);
return len > 0 && len < n && cs.prefetch_ulong(8) && cs.advance(len * 8); return len > 0 && len < n && cs.prefetch_ulong(8) && cs.advance(len * 8);
} }
@ -360,7 +360,7 @@ bool VarInteger::skip(vm::CellSlice& cs) const {
return len >= 0 && len < n && cs.advance(len * 8); return len >= 0 && len < n && cs.advance(len * 8);
} }
bool VarInteger::validate_skip(vm::CellSlice& cs, bool weak) const { bool VarInteger::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int len = (int)cs.fetch_ulong(ln); int len = (int)cs.fetch_ulong(ln);
return len >= 0 && len < n && (!len || !redundant_int(cs)) && cs.advance(len * 8); return len >= 0 && len < n && (!len || !redundant_int(cs)) && cs.advance(len * 8);
} }
@ -386,7 +386,7 @@ bool VarIntegerNz::skip(vm::CellSlice& cs) const {
return len > 0 && len < n && cs.advance(len * 8); return len > 0 && len < n && cs.advance(len * 8);
} }
bool VarIntegerNz::validate_skip(vm::CellSlice& cs, bool weak) const { bool VarIntegerNz::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int len = (int)cs.fetch_ulong(ln); int len = (int)cs.fetch_ulong(ln);
return len > 0 && len < n && !redundant_int(cs) && cs.advance(len * 8); return len > 0 && len < n && !redundant_int(cs) && cs.advance(len * 8);
} }
@ -409,8 +409,8 @@ bool VarIntegerNz::store_integer_value(vm::CellBuilder& cb, const td::BigInt256&
cb.store_int256_bool(value, (k + 7) & -8, true); cb.store_int256_bool(value, (k + 7) & -8, true);
} }
bool Grams::validate_skip(vm::CellSlice& cs, bool weak) const { bool Grams::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_16.validate_skip(cs, weak); return t_VarUInteger_16.validate_skip(ops, cs, weak);
} }
td::RefInt256 Grams::as_integer_skip(vm::CellSlice& cs) const { td::RefInt256 Grams::as_integer_skip(vm::CellSlice& cs) const {
@ -464,15 +464,15 @@ bool HashmapNode::skip(vm::CellSlice& cs) const {
return n ? cs.advance_refs(2) : value_type.skip(cs); return n ? cs.advance_refs(2) : value_type.skip(cs);
} }
bool HashmapNode::validate_skip(vm::CellSlice& cs, bool weak) const { bool HashmapNode::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
assert(n >= 0); assert(n >= 0);
if (!n) { if (!n) {
// hmn_leaf // hmn_leaf
return value_type.validate_skip(cs, weak); return value_type.validate_skip(ops, cs, weak);
} else { } else {
// hmn_fork // hmn_fork
Hashmap branch_type{n - 1, value_type}; Hashmap branch_type{n - 1, value_type};
return branch_type.validate_ref(cs.fetch_ref(), weak) && branch_type.validate_ref(cs.fetch_ref(), weak); return branch_type.validate_ref(ops, cs.fetch_ref(), weak) && branch_type.validate_ref(ops, cs.fetch_ref(), weak);
} }
} }
@ -481,9 +481,9 @@ bool Hashmap::skip(vm::CellSlice& cs) const {
return HmLabel{n}.skip(cs, l) && HashmapNode{n - l, value_type}.skip(cs); return HmLabel{n}.skip(cs, l) && HashmapNode{n - l, value_type}.skip(cs);
} }
bool Hashmap::validate_skip(vm::CellSlice& cs, bool weak) const { bool Hashmap::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int l; int l;
return HmLabel{n}.validate_skip(cs, weak, l) && HashmapNode{n - l, value_type}.validate_skip(cs, weak); return HmLabel{n}.validate_skip(cs, weak, l) && HashmapNode{n - l, value_type}.validate_skip(ops, cs, weak);
} }
int HashmapE::get_size(const vm::CellSlice& cs) const { int HashmapE::get_size(const vm::CellSlice& cs) const {
@ -491,9 +491,9 @@ int HashmapE::get_size(const vm::CellSlice& cs) const {
return (tag >= 0 ? (tag > 0 ? 0x10001 : 1) : -1); return (tag >= 0 ? (tag > 0 ? 0x10001 : 1) : -1);
} }
bool HashmapE::validate(const vm::CellSlice& cs, bool weak) const { bool HashmapE::validate(int* ops, const vm::CellSlice& cs, bool weak) const {
int tag = get_tag(cs); int tag = get_tag(cs);
return tag <= 0 ? !tag : root_type.validate_ref(cs.prefetch_ref(), weak); return tag <= 0 ? !tag : root_type.validate_ref(ops, cs.prefetch_ref(), weak);
} }
bool HashmapE::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { bool HashmapE::add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const {
@ -583,8 +583,8 @@ bool HashmapE::store_ref(vm::CellBuilder& cb, Ref<vm::Cell> arg) const {
const ExtraCurrencyCollection t_ExtraCurrencyCollection; const ExtraCurrencyCollection t_ExtraCurrencyCollection;
bool CurrencyCollection::validate_skip(vm::CellSlice& cs, bool weak) const { bool CurrencyCollection::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_Grams.validate_skip(cs, weak) && t_ExtraCurrencyCollection.validate_skip(cs, weak); return t_Grams.validate_skip(ops, cs, weak) && t_ExtraCurrencyCollection.validate_skip(ops, cs, weak);
} }
bool CurrencyCollection::skip(vm::CellSlice& cs) const { bool CurrencyCollection::skip(vm::CellSlice& cs) const {
@ -641,24 +641,24 @@ bool CurrencyCollection::pack(vm::CellBuilder& cb, const block::CurrencyCollecti
const CurrencyCollection t_CurrencyCollection; const CurrencyCollection t_CurrencyCollection;
bool CommonMsgInfo::validate_skip(vm::CellSlice& cs, bool weak) const { bool CommonMsgInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int tag = get_tag(cs); int tag = get_tag(cs);
switch (tag) { switch (tag) {
case int_msg_info: case int_msg_info:
return cs.advance(4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool return cs.advance(4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
&& t_MsgAddressInt.validate_skip(cs, weak) // src && t_MsgAddressInt.validate_skip(ops, cs, weak) // src
&& t_MsgAddressInt.validate_skip(cs, weak) // dest && t_MsgAddressInt.validate_skip(ops, cs, weak) // dest
&& t_CurrencyCollection.validate_skip(cs, weak) // value && t_CurrencyCollection.validate_skip(ops, cs, weak) // value
&& t_Grams.validate_skip(cs, weak) // ihr_fee && t_Grams.validate_skip(ops, cs, weak) // ihr_fee
&& t_Grams.validate_skip(cs, weak) // fwd_fee && t_Grams.validate_skip(ops, cs, weak) // fwd_fee
&& cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32
case ext_in_msg_info: case ext_in_msg_info:
return cs.advance(2) && t_MsgAddressExt.validate_skip(cs, weak) // src return cs.advance(2) && t_MsgAddressExt.validate_skip(ops, cs, weak) // src
&& t_MsgAddressInt.validate_skip(cs, weak) // dest && t_MsgAddressInt.validate_skip(ops, cs, weak) // dest
&& t_Grams.validate_skip(cs, weak); // import_fee && t_Grams.validate_skip(ops, cs, weak); // import_fee
case ext_out_msg_info: case ext_out_msg_info:
return cs.advance(2) && t_MsgAddressInt.validate_skip(cs, weak) // src return cs.advance(2) && t_MsgAddressInt.validate_skip(ops, cs, weak) // src
&& t_MsgAddressExt.validate_skip(cs, weak) // dest && t_MsgAddressExt.validate_skip(ops, cs, weak) // dest
&& cs.advance(64 + 32); // created_lt:uint64 created_at:uint32 && cs.advance(64 + 32); // created_lt:uint64 created_at:uint32
} }
return false; return false;
@ -721,28 +721,29 @@ const CommonMsgInfo t_CommonMsgInfo;
const TickTock t_TickTock; const TickTock t_TickTock;
const RefAnything t_RefCell; const RefAnything t_RefCell;
bool StateInit::validate_skip(vm::CellSlice& cs, bool weak) const { bool StateInit::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return Maybe<UInt>{5}.validate_skip(cs, weak) // split_depth:(Maybe (## 5)) return Maybe<UInt>{5}.validate_skip(ops, cs, weak) // split_depth:(Maybe (## 5))
&& Maybe<TickTock>{}.validate_skip(cs, weak) // special:(Maybe TickTock) && Maybe<TickTock>{}.validate_skip(ops, cs, weak) // special:(Maybe TickTock)
&& Maybe<RefAnything>{}.validate_skip(cs, weak) // code:(Maybe ^Cell) && Maybe<RefAnything>{}.validate_skip(ops, cs, weak) // code:(Maybe ^Cell)
&& Maybe<RefAnything>{}.validate_skip(cs, weak) // data:(Maybe ^Cell) && Maybe<RefAnything>{}.validate_skip(ops, cs, weak) // data:(Maybe ^Cell)
&& Maybe<RefAnything>{}.validate_skip(cs, weak); // library:(Maybe ^Cell) && Maybe<RefAnything>{}.validate_skip(ops, cs, weak); // library:(Maybe ^Cell)
} }
bool StateInit::get_ticktock(vm::CellSlice& cs, int& ticktock) const { bool StateInit::get_ticktock(vm::CellSlice& cs, int& ticktock) const {
bool have_tt; bool have_tt;
ticktock = 0; ticktock = 0;
return Maybe<UInt>{5}.validate_skip(cs) && cs.fetch_bool_to(have_tt) && (!have_tt || cs.fetch_uint_to(2, ticktock)); return Maybe<UInt>{5}.validate_skip_upto(1, cs) && cs.fetch_bool_to(have_tt) &&
(!have_tt || cs.fetch_uint_to(2, ticktock));
} }
const StateInit t_StateInit; const StateInit t_StateInit;
bool Message::validate_skip(vm::CellSlice& cs, bool weak) const { bool Message::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
static const Maybe<Either<StateInit, RefTo<StateInit>>> init_type; static const Maybe<Either<StateInit, RefTo<StateInit>>> init_type;
static const Either<Anything, RefAnything> body_type; static const Either<Anything, RefAnything> body_type;
return t_CommonMsgInfo.validate_skip(cs, weak) // info:CommonMsgInfo return t_CommonMsgInfo.validate_skip(ops, cs, weak) // info:CommonMsgInfo
&& init_type.validate_skip(cs, weak) // init:(Maybe (Either StateInit ^StateInit)) && init_type.validate_skip(ops, cs, weak) // init:(Maybe (Either StateInit ^StateInit))
&& body_type.validate_skip(cs, weak); // body:(Either X ^X) && body_type.validate_skip(ops, cs, weak); // body:(Either X ^X)
} }
bool Message::extract_info(vm::CellSlice& cs) const { bool Message::extract_info(vm::CellSlice& cs) const {
@ -760,7 +761,7 @@ bool Message::is_internal(Ref<vm::Cell> ref) const {
const Message t_Message; const Message t_Message;
const RefTo<Message> t_Ref_Message; const RefTo<Message> t_Ref_Message;
bool IntermediateAddress::validate_skip(vm::CellSlice& cs, bool weak) const { bool IntermediateAddress::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
switch (get_tag(cs)) { switch (get_tag(cs)) {
case interm_addr_regular: case interm_addr_regular:
return cs.advance(1) && cs.fetch_ulong(7) <= 96U; return cs.advance(1) && cs.fetch_ulong(7) <= 96U;
@ -795,12 +796,12 @@ int IntermediateAddress::get_size(const vm::CellSlice& cs) const {
const IntermediateAddress t_IntermediateAddress; const IntermediateAddress t_IntermediateAddress;
bool MsgEnvelope::validate_skip(vm::CellSlice& cs, bool weak) const { bool MsgEnvelope::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.fetch_ulong(4) == 4 // msg_envelope#4 return cs.fetch_ulong(4) == 4 // msg_envelope#4
&& t_IntermediateAddress.validate_skip(cs, weak) // cur_addr:IntermediateAddress && t_IntermediateAddress.validate_skip(ops, cs, weak) // cur_addr:IntermediateAddress
&& t_IntermediateAddress.validate_skip(cs, weak) // next_addr:IntermediateAddress && t_IntermediateAddress.validate_skip(ops, cs, weak) // next_addr:IntermediateAddress
&& t_Grams.validate_skip(cs, weak) // fwd_fee_remaining:Grams && t_Grams.validate_skip(ops, cs, weak) // fwd_fee_remaining:Grams
&& t_Ref_Message.validate_skip(cs, weak); // msg:^Message && t_Ref_Message.validate_skip(ops, cs, weak); // msg:^Message
} }
bool MsgEnvelope::skip(vm::CellSlice& cs) const { bool MsgEnvelope::skip(vm::CellSlice& cs) const {
@ -849,10 +850,10 @@ bool MsgEnvelope::get_created_lt(const vm::CellSlice& cs, unsigned long long& cr
const MsgEnvelope t_MsgEnvelope; const MsgEnvelope t_MsgEnvelope;
const RefTo<MsgEnvelope> t_Ref_MsgEnvelope; const RefTo<MsgEnvelope> t_Ref_MsgEnvelope;
bool StorageUsed::validate_skip(vm::CellSlice& cs, bool weak) const { bool StorageUsed::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_7.validate_skip(cs, weak) // cells:(VarUInteger 7) return t_VarUInteger_7.validate_skip(ops, cs, weak) // cells:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(cs, weak) // bits:(VarUInteger 7) && t_VarUInteger_7.validate_skip(ops, cs, weak) // bits:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(cs, weak); // public_cells:(VarUInteger 7) && t_VarUInteger_7.validate_skip(ops, cs, weak); // public_cells:(VarUInteger 7)
} }
bool StorageUsed::skip(vm::CellSlice& cs) const { bool StorageUsed::skip(vm::CellSlice& cs) const {
@ -863,9 +864,9 @@ bool StorageUsed::skip(vm::CellSlice& cs) const {
const StorageUsed t_StorageUsed; const StorageUsed t_StorageUsed;
bool StorageUsedShort::validate_skip(vm::CellSlice& cs, bool weak) const { bool StorageUsedShort::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_7.validate_skip(cs, weak) // cells:(VarUInteger 7) return t_VarUInteger_7.validate_skip(ops, cs, weak) // cells:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(cs, weak); // bits:(VarUInteger 7) && t_VarUInteger_7.validate_skip(ops, cs, weak); // bits:(VarUInteger 7)
} }
bool StorageUsedShort::skip(vm::CellSlice& cs) const { bool StorageUsedShort::skip(vm::CellSlice& cs) const {
@ -883,22 +884,22 @@ bool StorageInfo::skip(vm::CellSlice& cs) const {
&& t_Maybe_Grams.skip(cs); // due_payment:(Maybe Grams) && t_Maybe_Grams.skip(cs); // due_payment:(Maybe Grams)
} }
bool StorageInfo::validate_skip(vm::CellSlice& cs, bool weak) const { bool StorageInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_StorageUsed.validate_skip(cs, weak) // used:StorageUsed return t_StorageUsed.validate_skip(ops, cs, weak) // used:StorageUsed
&& cs.advance(32) // last_paid:uint32 && cs.advance(32) // last_paid:uint32
&& t_Maybe_Grams.validate_skip(cs, weak); // due_payment:(Maybe Grams) && t_Maybe_Grams.validate_skip(ops, cs, weak); // due_payment:(Maybe Grams)
} }
const StorageInfo t_StorageInfo; const StorageInfo t_StorageInfo;
bool AccountState::validate_skip(vm::CellSlice& cs, bool weak) const { bool AccountState::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
switch (get_tag(cs)) { switch (get_tag(cs)) {
case account_uninit: case account_uninit:
return cs.advance(2); return cs.advance(2);
case account_frozen: case account_frozen:
return cs.advance(2 + 256); return cs.advance(2 + 256);
case account_active: case account_active:
return cs.advance(1) && t_StateInit.validate_skip(cs, weak); return cs.advance(1) && t_StateInit.validate_skip(ops, cs, weak);
} }
return false; return false;
} }
@ -921,8 +922,9 @@ bool AccountStorage::skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) c
return cs.advance(64) && t_CurrencyCollection.skip_copy(cb, cs) && t_AccountState.skip(cs); return cs.advance(64) && t_CurrencyCollection.skip_copy(cb, cs) && t_AccountState.skip(cs);
} }
bool AccountStorage::validate_skip(vm::CellSlice& cs, bool weak) const { bool AccountStorage::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.advance(64) && t_CurrencyCollection.validate_skip(cs, weak) && t_AccountState.validate_skip(cs, weak); return cs.advance(64) && t_CurrencyCollection.validate_skip(ops, cs, weak) &&
t_AccountState.validate_skip(ops, cs, weak);
} }
const AccountStorage t_AccountStorage; const AccountStorage t_AccountStorage;
@ -940,15 +942,15 @@ bool Account::skip(vm::CellSlice& cs) const {
return false; return false;
} }
bool Account::validate_skip(vm::CellSlice& cs, bool weak) const { bool Account::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
switch (get_tag(cs)) { switch (get_tag(cs)) {
case account_none: case account_none:
return allow_empty && cs.advance(1); return allow_empty && cs.advance(1);
case account: case account:
return cs.advance(1) // account$1 return cs.advance(1) // account$1
&& t_MsgAddressInt.validate_skip(cs, weak) // addr:MsgAddressInt && t_MsgAddressInt.validate_skip(ops, cs, weak) // addr:MsgAddressInt
&& t_StorageInfo.validate_skip(cs, weak) // storage_stat:StorageInfo && t_StorageInfo.validate_skip(ops, cs, weak) // storage_stat:StorageInfo
&& t_AccountStorage.validate_skip(cs, weak); // storage:AccountStorage && t_AccountStorage.validate_skip(ops, cs, weak); // storage:AccountStorage
} }
return false; return false;
} }
@ -1032,19 +1034,19 @@ bool HashmapAugNode::skip(vm::CellSlice& cs) const {
} }
} }
bool HashmapAugNode::validate_skip(vm::CellSlice& cs, bool weak) const { bool HashmapAugNode::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
if (n < 0) { if (n < 0) {
return false; return false;
} }
if (!n) { if (!n) {
// ahmn_leaf // ahmn_leaf
vm::CellSlice cs_extra{cs}; vm::CellSlice cs_extra{cs};
if (!aug.extra_type.validate_skip(cs, weak)) { if (!aug.extra_type.validate_skip(ops, cs, weak)) {
return false; return false;
} }
cs_extra.cut_tail(cs); cs_extra.cut_tail(cs);
vm::CellSlice cs_value{cs}; vm::CellSlice cs_value{cs};
if (!aug.value_type.validate_skip(cs, weak)) { if (!aug.value_type.validate_skip(ops, cs, weak)) {
return false; return false;
} }
cs_value.cut_tail(cs); cs_value.cut_tail(cs);
@ -1055,13 +1057,14 @@ bool HashmapAugNode::validate_skip(vm::CellSlice& cs, bool weak) const {
return false; return false;
} }
HashmapAug branch_type{n - 1, aug}; HashmapAug branch_type{n - 1, aug};
if (!branch_type.validate_ref(cs.prefetch_ref(0), weak) || !branch_type.validate_ref(cs.prefetch_ref(1), weak)) { if (!branch_type.validate_ref(ops, cs.prefetch_ref(0), weak) ||
!branch_type.validate_ref(ops, cs.prefetch_ref(1), weak)) {
return false; return false;
} }
auto cs_left = load_cell_slice(cs.fetch_ref()); auto cs_left = load_cell_slice(cs.fetch_ref());
auto cs_right = load_cell_slice(cs.fetch_ref()); auto cs_right = load_cell_slice(cs.fetch_ref());
vm::CellSlice cs_extra{cs}; vm::CellSlice cs_extra{cs};
if (!aug.extra_type.validate_skip(cs, weak)) { if (!aug.extra_type.validate_skip(ops, cs, weak)) {
return false; return false;
} }
cs_extra.cut_tail(cs); cs_extra.cut_tail(cs);
@ -1074,9 +1077,9 @@ bool HashmapAug::skip(vm::CellSlice& cs) const {
return HmLabel{n}.skip(cs, l) && HashmapAugNode{n - l, aug}.skip(cs); return HmLabel{n}.skip(cs, l) && HashmapAugNode{n - l, aug}.skip(cs);
} }
bool HashmapAug::validate_skip(vm::CellSlice& cs, bool weak) const { bool HashmapAug::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int l; int l;
return HmLabel{n}.validate_skip(cs, weak, l) && HashmapAugNode{n - l, aug}.validate_skip(cs, weak); return HmLabel{n}.validate_skip(cs, weak, l) && HashmapAugNode{n - l, aug}.validate_skip(ops, cs, weak);
} }
bool HashmapAug::extract_extra(vm::CellSlice& cs) const { bool HashmapAug::extract_extra(vm::CellSlice& cs) const {
@ -1084,20 +1087,20 @@ bool HashmapAug::extract_extra(vm::CellSlice& cs) const {
return HmLabel{n}.skip(cs, l) && (l == n || cs.advance_refs(2)) && aug.extra_type.extract(cs); return HmLabel{n}.skip(cs, l) && (l == n || cs.advance_refs(2)) && aug.extra_type.extract(cs);
} }
bool HashmapAugE::validate_skip(vm::CellSlice& cs, bool weak) const { bool HashmapAugE::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
Ref<vm::CellSlice> extra; Ref<vm::CellSlice> extra;
switch (get_tag(cs)) { switch (get_tag(cs)) {
case ahme_empty: case ahme_empty:
return cs.advance(1) && (extra = root_type.aug.extra_type.validate_fetch(cs, weak)).not_null() && return cs.advance(1) && (extra = root_type.aug.extra_type.validate_fetch(ops, cs, weak)).not_null() &&
root_type.aug.check_empty(extra.unique_write()); root_type.aug.check_empty(extra.unique_write());
case ahme_root: case ahme_root:
if (cs.advance(1) && root_type.validate_ref(cs.prefetch_ref(), weak)) { if (cs.advance(1) && root_type.validate_ref(ops, cs.prefetch_ref(), weak)) {
bool special; bool special;
auto cs_root = load_cell_slice_special(cs.fetch_ref(), special); auto cs_root = load_cell_slice_special(cs.fetch_ref(), special);
if (special) { if (special) {
return weak; return weak;
} }
return (extra = root_type.aug.extra_type.validate_fetch(cs, weak)).not_null() && return (extra = root_type.aug.extra_type.validate_fetch(ops, cs, weak)).not_null() &&
root_type.extract_extra(cs_root) && extra->contents_equal(cs_root); root_type.extract_extra(cs_root) && extra->contents_equal(cs_root);
} }
break; break;
@ -1121,9 +1124,10 @@ bool DepthBalanceInfo::skip(vm::CellSlice& cs) const {
cs); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection = DepthBalanceInfo; cs); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection = DepthBalanceInfo;
} }
bool DepthBalanceInfo::validate_skip(vm::CellSlice& cs, bool weak) const { bool DepthBalanceInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.fetch_ulong(5) <= 30 && t_CurrencyCollection.validate_skip( return cs.fetch_ulong(5) <= 30 &&
cs, weak); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection t_CurrencyCollection.validate_skip(ops, cs,
weak); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection
} }
bool DepthBalanceInfo::null_value(vm::CellBuilder& cb) const { bool DepthBalanceInfo::null_value(vm::CellBuilder& cb) const {
@ -1159,10 +1163,10 @@ bool TrStoragePhase::skip(vm::CellSlice& cs) const {
&& t_AccStatusChange.skip(cs); // status_change:AccStatusChange && t_AccStatusChange.skip(cs); // status_change:AccStatusChange
} }
bool TrStoragePhase::validate_skip(vm::CellSlice& cs, bool weak) const { bool TrStoragePhase::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_Grams.validate_skip(cs, weak) // storage_fees_collected:Grams return t_Grams.validate_skip(ops, cs, weak) // storage_fees_collected:Grams
&& t_Maybe_Grams.validate_skip(cs, weak) // storage_fees_due:Grams && t_Maybe_Grams.validate_skip(ops, cs, weak) // storage_fees_due:Grams
&& t_AccStatusChange.validate_skip(cs, weak); // status_change:AccStatusChange && t_AccStatusChange.validate_skip(ops, cs, weak); // status_change:AccStatusChange
} }
bool TrStoragePhase::get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const { bool TrStoragePhase::get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const {
@ -1186,9 +1190,9 @@ bool TrCreditPhase::skip(vm::CellSlice& cs) const {
&& t_CurrencyCollection.skip(cs); // credit:CurrencyCollection && t_CurrencyCollection.skip(cs); // credit:CurrencyCollection
} }
bool TrCreditPhase::validate_skip(vm::CellSlice& cs, bool weak) const { bool TrCreditPhase::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_Maybe_Grams.validate_skip(cs, weak) // due_fees_collected:(Maybe Grams) return t_Maybe_Grams.validate_skip(ops, cs, weak) // due_fees_collected:(Maybe Grams)
&& t_CurrencyCollection.validate_skip(cs, weak); // credit:CurrencyCollection && t_CurrencyCollection.validate_skip(ops, cs, weak); // credit:CurrencyCollection
} }
const TrCreditPhase t_TrCreditPhase; const TrCreditPhase t_TrCreditPhase;
@ -1204,12 +1208,12 @@ bool TrComputeInternal1::skip(vm::CellSlice& cs) const {
// vm_final_state_hash:uint256 // vm_final_state_hash:uint256
} }
bool TrComputeInternal1::validate_skip(vm::CellSlice& cs, bool weak) const { bool TrComputeInternal1::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_7.validate_skip(cs, weak) // gas_used:(VarUInteger 7) return t_VarUInteger_7.validate_skip(ops, cs, weak) // gas_used:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(cs, weak) // gas_limit:(VarUInteger 7) && t_VarUInteger_7.validate_skip(ops, cs, weak) // gas_limit:(VarUInteger 7)
&& Maybe<VarUInteger>{3}.validate_skip(cs, weak) // gas_credit:(Maybe (VarUInteger 3)) && Maybe<VarUInteger>{3}.validate_skip(ops, cs, weak) // gas_credit:(Maybe (VarUInteger 3))
&& cs.advance(8 + 32) // mode:int8 exit_code:int32 && cs.advance(8 + 32) // mode:int8 exit_code:int32
&& Maybe<Int>{32}.validate_skip(cs, weak) // exit_arg:(Maybe int32) && Maybe<Int>{32}.validate_skip(ops, cs, weak) // exit_arg:(Maybe int32)
&& cs.advance(32 + 256 + 256); // vm_steps:uint32 && cs.advance(32 + 256 + 256); // vm_steps:uint32
// vm_init_state_hash:uint256 // vm_init_state_hash:uint256
// vm_final_state_hash:uint256 // vm_final_state_hash:uint256
@ -1231,14 +1235,14 @@ bool TrComputePhase::skip(vm::CellSlice& cs) const {
return false; return false;
} }
bool TrComputePhase::validate_skip(vm::CellSlice& cs, bool weak) const { bool TrComputePhase::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
switch (get_tag(cs)) { switch (get_tag(cs)) {
case tr_phase_compute_skipped: case tr_phase_compute_skipped:
return cs.advance(1) && t_ComputeSkipReason.validate_skip(cs, weak); return cs.advance(1) && t_ComputeSkipReason.validate_skip(ops, cs, weak);
case tr_phase_compute_vm: case tr_phase_compute_vm:
return cs.advance(1 + 3) // tr_phase_compute_vm$1 success:Bool msg_state_used:Bool account_activated:Bool return cs.advance(1 + 3) // tr_phase_compute_vm$1 success:Bool msg_state_used:Bool account_activated:Bool
&& t_Grams.validate_skip(cs, weak) // gas_fees:Grams && t_Grams.validate_skip(ops, cs, weak) // gas_fees:Grams
&& t_Ref_TrComputeInternal1.validate_skip(cs, weak); // ^[ gas_used:(..) .. ] && t_Ref_TrComputeInternal1.validate_skip(ops, cs, weak); // ^[ gas_used:(..) .. ]
} }
return false; return false;
} }
@ -1258,17 +1262,17 @@ bool TrActionPhase::skip(vm::CellSlice& cs) const {
&& t_StorageUsedShort.skip(cs); // tot_msg_size:StorageUsedShort && t_StorageUsedShort.skip(cs); // tot_msg_size:StorageUsedShort
} }
bool TrActionPhase::validate_skip(vm::CellSlice& cs, bool weak) const { bool TrActionPhase::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.advance(3) // success:Bool valid:Bool no_funds:Bool return cs.advance(3) // success:Bool valid:Bool no_funds:Bool
&& t_AccStatusChange.validate_skip(cs, weak) // status_change:AccStatusChange && t_AccStatusChange.validate_skip(ops, cs, weak) // status_change:AccStatusChange
&& t_Maybe_Grams.validate_skip(cs, weak) // total_fwd_fees:(Maybe Grams) && t_Maybe_Grams.validate_skip(ops, cs, weak) // total_fwd_fees:(Maybe Grams)
&& t_Maybe_Grams.validate_skip(cs, weak) // total_action_fees:(Maybe Grams) && t_Maybe_Grams.validate_skip(ops, cs, weak) // total_action_fees:(Maybe Grams)
&& cs.advance(32) // result_code:int32 && cs.advance(32) // result_code:int32
&& Maybe<Int>{32}.validate_skip(cs, weak) // result_arg:(Maybe int32) && Maybe<Int>{32}.validate_skip(ops, cs, weak) // result_arg:(Maybe int32)
&& cs.advance(16 * 4 + 256) // tot_actions:uint16 spec_actions:uint16 && cs.advance(16 * 4 + 256) // tot_actions:uint16 spec_actions:uint16
// skipped_actions:uint16 msgs_created:uint16 // skipped_actions:uint16 msgs_created:uint16
// action_list_hash:uint256 // action_list_hash:uint256
&& t_StorageUsedShort.validate_skip(cs, weak); // tot_msg_size:StorageUsed && t_StorageUsedShort.validate_skip(ops, cs, weak); // tot_msg_size:StorageUsed
} }
const TrActionPhase t_TrActionPhase; const TrActionPhase t_TrActionPhase;
@ -1290,19 +1294,19 @@ bool TrBouncePhase::skip(vm::CellSlice& cs) const {
return false; return false;
} }
bool TrBouncePhase::validate_skip(vm::CellSlice& cs, bool weak) const { bool TrBouncePhase::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
switch (get_tag(cs)) { switch (get_tag(cs)) {
case tr_phase_bounce_negfunds: case tr_phase_bounce_negfunds:
return cs.advance(2); // tr_phase_bounce_negfunds$00 return cs.advance(2); // tr_phase_bounce_negfunds$00
case tr_phase_bounce_nofunds: case tr_phase_bounce_nofunds:
return cs.advance(2) // tr_phase_bounce_nofunds$01 return cs.advance(2) // tr_phase_bounce_nofunds$01
&& t_StorageUsedShort.validate_skip(cs, weak) // msg_size:StorageUsedShort && t_StorageUsedShort.validate_skip(ops, cs, weak) // msg_size:StorageUsedShort
&& t_Grams.validate_skip(cs, weak); // req_fwd_fees:Grams && t_Grams.validate_skip(ops, cs, weak); // req_fwd_fees:Grams
case tr_phase_bounce_ok: case tr_phase_bounce_ok:
return cs.advance(1) // tr_phase_bounce_ok$1 return cs.advance(1) // tr_phase_bounce_ok$1
&& t_StorageUsedShort.validate_skip(cs, weak) // msg_size:StorageUsedShort && t_StorageUsedShort.validate_skip(ops, cs, weak) // msg_size:StorageUsedShort
&& t_Grams.validate_skip(cs, weak) // msg_fees:Grams && t_Grams.validate_skip(ops, cs, weak) // msg_fees:Grams
&& t_Grams.validate_skip(cs, weak); // fwd_fees:Grams && t_Grams.validate_skip(ops, cs, weak); // fwd_fees:Grams
} }
return false; return false;
} }
@ -1322,7 +1326,7 @@ bool SplitMergeInfo::skip(vm::CellSlice& cs) const {
return cs.advance(6 + 6 + 256 + 256); return cs.advance(6 + 6 + 256 + 256);
} }
bool SplitMergeInfo::validate_skip(vm::CellSlice& cs, bool weak) const { bool SplitMergeInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
if (!cs.have(6 + 6 + 256 + 256)) { if (!cs.have(6 + 6 + 256 + 256)) {
return false; return false;
} }
@ -1392,51 +1396,51 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
return false; return false;
} }
bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const { bool TransactionDescr::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
switch (get_tag(cs)) { switch (get_tag(cs)) {
case trans_ord: case trans_ord:
return cs.advance(4 + 1) // trans_ord$0000 credit_first:Bool return cs.advance(4 + 1) // trans_ord$0000 credit_first:Bool
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) && Maybe<TrStoragePhase>{}.validate_skip(ops, cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase) && Maybe<TrCreditPhase>{}.validate_skip(ops, cs, weak) // credit_ph:(Maybe TrCreditPhase)
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase && t_TrComputePhase.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase) && Maybe<RefTo<TrActionPhase>>{}.validate_skip(ops, cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(1) // aborted:Bool && cs.advance(1) // aborted:Bool
&& Maybe<TrBouncePhase>{}.validate_skip(cs, weak) // bounce:(Maybe TrBouncePhase) && Maybe<TrBouncePhase>{}.validate_skip(ops, cs, weak) // bounce:(Maybe TrBouncePhase)
&& cs.advance(1); // destroyed:Bool && cs.advance(1); // destroyed:Bool
case trans_storage: case trans_storage:
return cs.advance(4) // trans_storage$0001 return cs.advance(4) // trans_storage$0001
&& t_TrStoragePhase.validate_skip(cs, weak); // storage_ph:TrStoragePhase && t_TrStoragePhase.validate_skip(ops, cs, weak); // storage_ph:TrStoragePhase
case trans_tick_tock: case trans_tick_tock:
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
&& t_TrStoragePhase.validate_skip(cs, weak) // storage_ph:TrStoragePhase && t_TrStoragePhase.validate_skip(ops, cs, weak) // storage_ph:TrStoragePhase
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase && t_TrComputePhase.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase) && Maybe<RefTo<TrActionPhase>>{}.validate_skip(ops, cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool && cs.advance(2); // aborted:Bool destroyed:Bool
case trans_split_prepare: case trans_split_prepare:
return cs.advance(4) // trans_split_prepare$0100 return cs.advance(4) // trans_split_prepare$0100
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo && t_SplitMergeInfo.validate_skip(ops, cs, weak) // split_info:SplitMergeInfo
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) && Maybe<TrStoragePhase>{}.validate_skip(ops, cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase && t_TrComputePhase.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase) && Maybe<RefTo<TrActionPhase>>{}.validate_skip(ops, cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool && cs.advance(2); // aborted:Bool destroyed:Bool
case trans_split_install: case trans_split_install:
return cs.advance(4) // trans_split_install$0101 return cs.advance(4) // trans_split_install$0101
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo && t_SplitMergeInfo.validate_skip(ops, cs, weak) // split_info:SplitMergeInfo
&& t_Ref_Transaction.validate_skip(cs, weak) // prepare_transaction:^Transaction && t_Ref_Transaction.validate_skip(ops, cs, weak) // prepare_transaction:^Transaction
&& cs.advance(1); // installed:Bool && cs.advance(1); // installed:Bool
case trans_merge_prepare: case trans_merge_prepare:
return cs.advance(4) // trans_merge_prepare$0110 return cs.advance(4) // trans_merge_prepare$0110
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo && t_SplitMergeInfo.validate_skip(ops, cs, weak) // split_info:SplitMergeInfo
&& t_TrStoragePhase.validate_skip(cs, weak) // storage_ph:TrStoragePhase && t_TrStoragePhase.validate_skip(ops, cs, weak) // storage_ph:TrStoragePhase
&& cs.advance(1); // aborted:Bool && cs.advance(1); // aborted:Bool
case trans_merge_install: case trans_merge_install:
return cs.advance(4) // trans_merge_install$0111 return cs.advance(4) // trans_merge_install$0111
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo && t_SplitMergeInfo.validate_skip(ops, cs, weak) // split_info:SplitMergeInfo
&& t_Ref_Transaction.validate_skip(cs, weak) // prepare_transaction:^Transaction && t_Ref_Transaction.validate_skip(ops, cs, weak) // prepare_transaction:^Transaction
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) && Maybe<TrStoragePhase>{}.validate_skip(ops, cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase) && Maybe<TrCreditPhase>{}.validate_skip(ops, cs, weak) // credit_ph:(Maybe TrCreditPhase)
&& Maybe<TrComputePhase>{}.validate_skip(cs, weak) // compute_ph:TrComputePhase && Maybe<TrComputePhase>{}.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase) && Maybe<RefTo<TrActionPhase>>{}.validate_skip(ops, cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool && cs.advance(2); // aborted:Bool destroyed:Bool
} }
return false; return false;
@ -1501,9 +1505,9 @@ bool Transaction_aux::skip(vm::CellSlice& cs) const {
&& HashmapE{15, t_Ref_Message}.skip(cs); // out_msgs:(HashmapE 15 ^Message) && HashmapE{15, t_Ref_Message}.skip(cs); // out_msgs:(HashmapE 15 ^Message)
} }
bool Transaction_aux::validate_skip(vm::CellSlice& cs, bool weak) const { bool Transaction_aux::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return Maybe<RefTo<Message>>{}.validate_skip(cs, weak) // in_msg:(Maybe ^Message) return Maybe<RefTo<Message>>{}.validate_skip(ops, cs, weak) // in_msg:(Maybe ^Message)
&& HashmapE{15, t_Ref_Message}.validate_skip(cs, weak); // out_msgs:(HashmapE 15 ^Message) && HashmapE{15, t_Ref_Message}.validate_skip(ops, cs, weak); // out_msgs:(HashmapE 15 ^Message)
} }
const Transaction_aux t_Transaction_aux; const Transaction_aux t_Transaction_aux;
@ -1520,18 +1524,18 @@ bool Transaction::skip(vm::CellSlice& cs) const {
&& RefTo<TransactionDescr>{}.skip(cs); // description:^TransactionDescr && RefTo<TransactionDescr>{}.skip(cs); // description:^TransactionDescr
} }
bool Transaction::validate_skip(vm::CellSlice& cs, bool weak) const { bool Transaction::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.fetch_ulong(4) == 7 // transaction$0111 return cs.fetch_ulong(4) == 7 // transaction$0111
&& &&
cs.advance( cs.advance(
256 + 64 + 256 + 64 + 32 + 256 + 64 + 256 + 64 + 32 +
15) // account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15 15) // account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15
&& t_AccountStatus.validate_skip(cs, weak) // orig_status:AccountStatus && t_AccountStatus.validate_skip(ops, cs, weak) // orig_status:AccountStatus
&& t_AccountStatus.validate_skip(cs, weak) // end_status:AccountStatus && t_AccountStatus.validate_skip(ops, cs, weak) // end_status:AccountStatus
&& RefTo<Transaction_aux>{}.validate_skip(cs, weak) // ^[ in_msg:... out_msgs:... ] && RefTo<Transaction_aux>{}.validate_skip(ops, cs, weak) // ^[ in_msg:... out_msgs:... ]
&& t_CurrencyCollection.validate_skip(cs, weak) // total_fees:CurrencyCollection && t_CurrencyCollection.validate_skip(ops, cs, weak) // total_fees:CurrencyCollection
&& t_Ref_HashUpdate.validate_skip(cs, weak) // state_update:^(HASH_UPDATE Account) && t_Ref_HashUpdate.validate_skip(ops, cs, weak) // state_update:^(HASH_UPDATE Account)
&& RefTo<TransactionDescr>{}.validate_skip(cs, weak); // description:^TransactionDescr && RefTo<TransactionDescr>{}.validate_skip(ops, cs, weak); // description:^TransactionDescr
} }
bool Transaction::get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const { bool Transaction::get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const {
@ -1595,12 +1599,12 @@ bool AccountBlock::skip(vm::CellSlice& cs) const {
&& cs.advance_refs(1); // state_update:^(HASH_UPDATE Account) && cs.advance_refs(1); // state_update:^(HASH_UPDATE Account)
} }
bool AccountBlock::validate_skip(vm::CellSlice& cs, bool weak) const { bool AccountBlock::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.fetch_ulong(4) == 5 // acc_trans#5 return cs.fetch_ulong(4) == 5 // acc_trans#5
&& cs.advance(256) // account_addr:bits256 && cs.advance(256) // account_addr:bits256
&& && t_AccountTransactions.validate_skip(ops, cs,
t_AccountTransactions.validate_skip(cs, weak) // transactions:(HashmapAug 64 ^Transaction CurrencyCollection) weak) // transactions:(HashmapAug 64 ^Transaction CurrencyCollection)
&& t_Ref_HashUpdate.validate_skip(cs, weak); // state_update:^(HASH_UPDATE Account) && t_Ref_HashUpdate.validate_skip(ops, cs, weak); // state_update:^(HASH_UPDATE Account)
} }
bool AccountBlock::get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const { bool AccountBlock::get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const {
@ -1620,8 +1624,8 @@ const Aug_ShardAccountBlocks aug_ShardAccountBlocks;
const HashmapAugE t_ShardAccountBlocks{256, const HashmapAugE t_ShardAccountBlocks{256,
aug_ShardAccountBlocks}; // (HashmapAugE 256 AccountBlock CurrencyCollection) aug_ShardAccountBlocks}; // (HashmapAugE 256 AccountBlock CurrencyCollection)
bool ImportFees::validate_skip(vm::CellSlice& cs, bool weak) const { bool ImportFees::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_Grams.validate_skip(cs, weak) && t_CurrencyCollection.validate_skip(cs, weak); return t_Grams.validate_skip(ops, cs, weak) && t_CurrencyCollection.validate_skip(ops, cs, weak);
} }
bool ImportFees::skip(vm::CellSlice& cs) const { bool ImportFees::skip(vm::CellSlice& cs) const {
@ -1676,44 +1680,44 @@ bool InMsg::skip(vm::CellSlice& cs) const {
return false; return false;
} }
bool InMsg::validate_skip(vm::CellSlice& cs, bool weak) const { bool InMsg::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
switch (get_tag(cs)) { switch (get_tag(cs)) {
case msg_import_ext: case msg_import_ext:
return cs.advance(3) // msg_import_ext$000 return cs.advance(3) // msg_import_ext$000
&& t_Ref_Message.validate_skip(cs, weak) // msg:^Message && t_Ref_Message.validate_skip(ops, cs, weak) // msg:^Message
&& t_Ref_Transaction.validate_skip(cs, weak); // transaction:^Transaction && t_Ref_Transaction.validate_skip(ops, cs, weak); // transaction:^Transaction
case msg_import_ihr: case msg_import_ihr:
return cs.advance(3) // msg_import_ihr$010 return cs.advance(3) // msg_import_ihr$010
&& t_Ref_Message.validate_skip(cs, weak) // msg:^Message && t_Ref_Message.validate_skip(ops, cs, weak) // msg:^Message
&& t_Ref_Transaction.validate_skip(cs, weak) // transaction:^Transaction && t_Ref_Transaction.validate_skip(ops, cs, weak) // transaction:^Transaction
&& t_Grams.validate_skip(cs, weak) // ihr_fee:Grams && t_Grams.validate_skip(ops, cs, weak) // ihr_fee:Grams
&& t_RefCell.validate_skip(cs, weak); // proof_created:^Cell && t_RefCell.validate_skip(ops, cs, weak); // proof_created:^Cell
case msg_import_imm: case msg_import_imm:
return cs.advance(3) // msg_import_imm$011 return cs.advance(3) // msg_import_imm$011
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(cs, weak) // transaction:^Transaction && t_Ref_Transaction.validate_skip(ops, cs, weak) // transaction:^Transaction
&& t_Grams.validate_skip(cs, weak); // fwd_fee:Grams && t_Grams.validate_skip(ops, cs, weak); // fwd_fee:Grams
case msg_import_fin: case msg_import_fin:
return cs.advance(3) // msg_import_fin$100 return cs.advance(3) // msg_import_fin$100
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(cs, weak) // transaction:^Transaction && t_Ref_Transaction.validate_skip(ops, cs, weak) // transaction:^Transaction
&& t_Grams.validate_skip(cs, weak); // fwd_fee:Grams && t_Grams.validate_skip(ops, cs, weak); // fwd_fee:Grams
case msg_import_tr: case msg_import_tr:
return cs.advance(3) // msg_import_tr$101 return cs.advance(3) // msg_import_tr$101
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& t_Grams.validate_skip(cs, weak); // transit_fee:Grams && t_Grams.validate_skip(ops, cs, weak); // transit_fee:Grams
case msg_discard_fin: case msg_discard_fin:
return cs.advance(3) // msg_discard_fin$110 return cs.advance(3) // msg_discard_fin$110
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& cs.advance(64) // transaction_id:uint64 && cs.advance(64) // transaction_id:uint64
&& t_Grams.validate_skip(cs, weak); // fwd_fee:Grams && t_Grams.validate_skip(ops, cs, weak); // fwd_fee:Grams
case msg_discard_tr: case msg_discard_tr:
return cs.advance(3) // msg_discard_tr$111 return cs.advance(3) // msg_discard_tr$111
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& cs.advance(64) // transaction_id:uint64 && cs.advance(64) // transaction_id:uint64
&& t_Grams.validate_skip(cs, weak) // fwd_fee:Grams && t_Grams.validate_skip(ops, cs, weak) // fwd_fee:Grams
&& t_RefCell.validate_skip(cs, weak); // proof_delivered:^Cell && t_RefCell.validate_skip(ops, cs, weak); // proof_delivered:^Cell
} }
return false; return false;
} }
@ -1851,37 +1855,37 @@ bool OutMsg::skip(vm::CellSlice& cs) const {
return false; return false;
} }
bool OutMsg::validate_skip(vm::CellSlice& cs, bool weak) const { bool OutMsg::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
switch (get_tag(cs)) { switch (get_tag(cs)) {
case msg_export_ext: case msg_export_ext:
return cs.advance(3) // msg_export_ext$000 return cs.advance(3) // msg_export_ext$000
&& t_Ref_Message.validate_skip(cs, weak) // msg:^Message && t_Ref_Message.validate_skip(ops, cs, weak) // msg:^Message
&& t_Ref_Transaction.validate_skip(cs, weak); // transaction:^Transaction && t_Ref_Transaction.validate_skip(ops, cs, weak); // transaction:^Transaction
case msg_export_imm: case msg_export_imm:
return cs.advance(3) // msg_export_imm$010 return cs.advance(3) // msg_export_imm$010
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(cs, weak) // transaction:^Transaction && t_Ref_Transaction.validate_skip(ops, cs, weak) // transaction:^Transaction
&& RefTo<InMsg>{}.validate_skip(cs, weak); // reimport:^InMsg && RefTo<InMsg>{}.validate_skip(ops, cs, weak); // reimport:^InMsg
case msg_export_new: case msg_export_new:
return cs.advance(3) // msg_export_new$001 return cs.advance(3) // msg_export_new$001
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(cs, weak); // transaction:^Transaction && t_Ref_Transaction.validate_skip(ops, cs, weak); // transaction:^Transaction
case msg_export_tr: case msg_export_tr:
return cs.advance(3) // msg_export_tr$011 return cs.advance(3) // msg_export_tr$011
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& RefTo<InMsg>{}.validate_skip(cs, weak); // imported:^InMsg && RefTo<InMsg>{}.validate_skip(ops, cs, weak); // imported:^InMsg
case msg_export_deq_imm: case msg_export_deq_imm:
return cs.advance(3) // msg_export_deq_imm$100 return cs.advance(3) // msg_export_deq_imm$100
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& RefTo<InMsg>{}.validate_skip(cs, weak); // reimport:^InMsg && RefTo<InMsg>{}.validate_skip(ops, cs, weak); // reimport:^InMsg
case msg_export_deq: case msg_export_deq:
return cs.advance(3) // msg_export_deq$110 return cs.advance(3) // msg_export_deq$110
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& cs.advance(64); // import_block_lt:uint64 && cs.advance(64); // import_block_lt:uint64
case msg_export_tr_req: case msg_export_tr_req:
return cs.advance(3) // msg_export_tr_req$111 return cs.advance(3) // msg_export_tr_req$111
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& RefTo<InMsg>{}.validate_skip(cs, weak); // imported:^InMsg && RefTo<InMsg>{}.validate_skip(ops, cs, weak); // imported:^InMsg
} }
return false; return false;
} }
@ -1954,8 +1958,8 @@ const OutMsg t_OutMsg;
const Aug_OutMsgDescr aug_OutMsgDescr; const Aug_OutMsgDescr aug_OutMsgDescr;
const OutMsgDescr t_OutMsgDescr; const OutMsgDescr t_OutMsgDescr;
bool EnqueuedMsg::validate_skip(vm::CellSlice& cs, bool weak) const { bool EnqueuedMsg::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.advance(64) && t_Ref_MsgEnvelope.validate_skip(cs, weak); return cs.advance(64) && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak);
} }
const EnqueuedMsg t_EnqueuedMsg; const EnqueuedMsg t_EnqueuedMsg;
@ -1989,9 +1993,9 @@ bool OutMsgQueueInfo::skip(vm::CellSlice& cs) const {
return t_OutMsgQueue.skip(cs) && t_ProcessedInfo.skip(cs) && t_IhrPendingInfo.skip(cs); return t_OutMsgQueue.skip(cs) && t_ProcessedInfo.skip(cs) && t_IhrPendingInfo.skip(cs);
} }
bool OutMsgQueueInfo::validate_skip(vm::CellSlice& cs, bool weak) const { bool OutMsgQueueInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_OutMsgQueue.validate_skip(cs, weak) && t_ProcessedInfo.validate_skip(cs, weak) && return t_OutMsgQueue.validate_skip(ops, cs, weak) && t_ProcessedInfo.validate_skip(ops, cs, weak) &&
t_IhrPendingInfo.validate_skip(cs, weak); t_IhrPendingInfo.validate_skip(ops, cs, weak);
} }
const OutMsgQueueInfo t_OutMsgQueueInfo; const OutMsgQueueInfo t_OutMsgQueueInfo;
@ -2047,7 +2051,7 @@ bool ExtBlkRef::pack_to(Ref<vm::Cell>& cell, const ton::BlockIdExt& blkid, ton::
const ExtBlkRef t_ExtBlkRef; const ExtBlkRef t_ExtBlkRef;
const BlkMasterInfo t_BlkMasterInfo; const BlkMasterInfo t_BlkMasterInfo;
bool ShardIdent::validate_skip(vm::CellSlice& cs, bool weak) const { bool ShardIdent::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int shard_pfx_len, workchain_id; int shard_pfx_len, workchain_id;
unsigned long long shard_pfx; unsigned long long shard_pfx;
if (cs.fetch_ulong(2) == 0 && cs.fetch_uint_to(6, shard_pfx_len) && cs.fetch_int_to(32, workchain_id) && if (cs.fetch_ulong(2) == 0 && cs.fetch_uint_to(6, shard_pfx_len) && cs.fetch_int_to(32, workchain_id) &&
@ -2112,8 +2116,8 @@ bool ShardIdent::pack(vm::CellBuilder& cb, ton::ShardIdFull data) const {
const ShardIdent t_ShardIdent; const ShardIdent t_ShardIdent;
bool BlockIdExt::validate_skip(vm::CellSlice& cs, bool weak) const { bool BlockIdExt::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_ShardIdent.validate_skip(cs, weak) && cs.advance(32 + 256 * 2); return t_ShardIdent.validate_skip(ops, cs, weak) && cs.advance(32 + 256 * 2);
} }
bool BlockIdExt::unpack(vm::CellSlice& cs, ton::BlockIdExt& data) const { bool BlockIdExt::unpack(vm::CellSlice& cs, ton::BlockIdExt& data) const {
@ -2146,21 +2150,21 @@ bool ShardState::skip(vm::CellSlice& cs) const {
&& Maybe<RefTo<McStateExtra>>{}.skip(cs); // custom:(Maybe ^McStateExtra) && Maybe<RefTo<McStateExtra>>{}.skip(cs); // custom:(Maybe ^McStateExtra)
} }
bool ShardState::validate_skip(vm::CellSlice& cs, bool weak) const { bool ShardState::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int seq_no; int seq_no;
return get_tag(cs) == shard_state && cs.advance(64) // shard_state#9023afe2 blockchain_id:int32 return get_tag(cs) == shard_state && cs.advance(64) // shard_state#9023afe2 blockchain_id:int32
&& t_ShardIdent.validate_skip(cs, weak) // shard_id:ShardIdent && t_ShardIdent.validate_skip(ops, cs, weak) // shard_id:ShardIdent
&& cs.fetch_int_to(32, seq_no) // seq_no:int32 && cs.fetch_int_to(32, seq_no) // seq_no:int32
&& seq_no >= -1 // { seq_no >= -1 } && seq_no >= -1 // { seq_no >= -1 }
&& cs.advance(32 + 32 + 64 + 32) // vert_seq_no:# gen_utime:uint32 gen_lt:uint64 min_ref_mc_seqno:uint32 && cs.advance(32 + 32 + 64 + 32) // vert_seq_no:# gen_utime:uint32 gen_lt:uint64 min_ref_mc_seqno:uint32
&& t_Ref_OutMsgQueueInfo.validate_skip(cs, weak) // out_msg_queue_info:^OutMsgQueueInfo && t_Ref_OutMsgQueueInfo.validate_skip(ops, cs, weak) // out_msg_queue_info:^OutMsgQueueInfo
&& cs.advance(1) // before_split:Bool && cs.advance(1) // before_split:Bool
&& t_ShardAccounts.validate_skip_ref(cs, weak) // accounts:^ShardAccounts && t_ShardAccounts.validate_skip_ref(ops, cs, weak) // accounts:^ShardAccounts
&& &&
t_ShardState_aux.validate_skip_ref( t_ShardState_aux.validate_skip_ref(
cs, ops, cs,
weak) // ^[ total_balance:CurrencyCollection total_validator_fees:CurrencyCollection libraries:(HashmapE 256 LibDescr) master_ref:(Maybe BlkMasterInfo) ] weak) // ^[ total_balance:CurrencyCollection total_validator_fees:CurrencyCollection libraries:(HashmapE 256 LibDescr) master_ref:(Maybe BlkMasterInfo) ]
&& Maybe<RefTo<McStateExtra>>{}.validate_skip(cs, weak); // custom:(Maybe ^McStateExtra) && Maybe<RefTo<McStateExtra>>{}.validate_skip(ops, cs, weak); // custom:(Maybe ^McStateExtra)
} }
const ShardState t_ShardState; const ShardState t_ShardState;
@ -2173,12 +2177,12 @@ bool ShardState_aux::skip(vm::CellSlice& cs) const {
&& Maybe<BlkMasterInfo>{}.skip(cs); // master_ref:(Maybe BlkMasterInfo) && Maybe<BlkMasterInfo>{}.skip(cs); // master_ref:(Maybe BlkMasterInfo)
} }
bool ShardState_aux::validate_skip(vm::CellSlice& cs, bool weak) const { bool ShardState_aux::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.advance(128) // overload_history:uint64 underload_history:uint64 return cs.advance(128) // overload_history:uint64 underload_history:uint64
&& t_CurrencyCollection.validate_skip(cs, weak) // total_balance:CurrencyCollection && t_CurrencyCollection.validate_skip(ops, cs, weak) // total_balance:CurrencyCollection
&& t_CurrencyCollection.validate_skip(cs, weak) // total_validator_fees:CurrencyCollection && t_CurrencyCollection.validate_skip(ops, cs, weak) // total_validator_fees:CurrencyCollection
&& HashmapE{256, t_LibDescr}.validate_skip(cs, weak) // libraries:(HashmapE 256 LibDescr) && HashmapE{256, t_LibDescr}.validate_skip(ops, cs, weak) // libraries:(HashmapE 256 LibDescr)
&& Maybe<BlkMasterInfo>{}.validate_skip(cs, weak); // master_ref:(Maybe BlkMasterInfo) && Maybe<BlkMasterInfo>{}.validate_skip(ops, cs, weak); // master_ref:(Maybe BlkMasterInfo)
} }
const ShardState_aux t_ShardState_aux; const ShardState_aux t_ShardState_aux;
@ -2189,10 +2193,10 @@ bool LibDescr::skip(vm::CellSlice& cs) const {
&& Hashmap{256, t_True}.skip(cs); // publishers:(Hashmap 256 False) && Hashmap{256, t_True}.skip(cs); // publishers:(Hashmap 256 False)
} }
bool LibDescr::validate_skip(vm::CellSlice& cs, bool weak) const { bool LibDescr::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return get_tag(cs) == shared_lib_descr && cs.advance(2) // shared_lib_descr$00 return get_tag(cs) == shared_lib_descr && cs.advance(2) // shared_lib_descr$00
&& cs.fetch_ref().not_null() // lib:^Cell && cs.fetch_ref().not_null() // lib:^Cell
&& Hashmap{256, t_True}.validate_skip(cs, weak); // publishers:(Hashmap 256 False) && Hashmap{256, t_True}.validate_skip(ops, cs, weak); // publishers:(Hashmap 256 False)
} }
const LibDescr t_LibDescr; const LibDescr t_LibDescr;
@ -2202,9 +2206,9 @@ bool BlkPrevInfo::skip(vm::CellSlice& cs) const {
&& (!merged || t_ExtBlkRef.skip(cs)); // prev_alt:merged?ExtBlkRef && (!merged || t_ExtBlkRef.skip(cs)); // prev_alt:merged?ExtBlkRef
} }
bool BlkPrevInfo::validate_skip(vm::CellSlice& cs, bool weak) const { bool BlkPrevInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_ExtBlkRef.validate_skip(cs, weak) // prev_blk_info$_ {merged:#} prev:ExtBlkRef return t_ExtBlkRef.validate_skip(ops, cs, weak) // prev_blk_info$_ {merged:#} prev:ExtBlkRef
&& (!merged || t_ExtBlkRef.validate_skip(cs, weak)); // prev_alt:merged?ExtBlkRef && (!merged || t_ExtBlkRef.validate_skip(ops, cs, weak)); // prev_alt:merged?ExtBlkRef
} }
const BlkPrevInfo t_BlkPrevInfo_0{0}; const BlkPrevInfo t_BlkPrevInfo_0{0};
@ -2213,8 +2217,8 @@ bool McStateExtra::skip(vm::CellSlice& cs) const {
return block::gen::t_McStateExtra.skip(cs); return block::gen::t_McStateExtra.skip(cs);
} }
bool McStateExtra::validate_skip(vm::CellSlice& cs, bool weak) const { bool McStateExtra::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return block::gen::t_McStateExtra.validate_skip(cs, weak); // ?? return block::gen::t_McStateExtra.validate_skip(ops, cs, weak); // ??
} }
const McStateExtra t_McStateExtra; const McStateExtra t_McStateExtra;
@ -2241,8 +2245,8 @@ bool ShardFeeCreated::skip(vm::CellSlice& cs) const {
return t_CurrencyCollection.skip(cs) && t_CurrencyCollection.skip(cs); return t_CurrencyCollection.skip(cs) && t_CurrencyCollection.skip(cs);
} }
bool ShardFeeCreated::validate_skip(vm::CellSlice& cs, bool weak) const { bool ShardFeeCreated::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_CurrencyCollection.validate_skip(cs, weak) && t_CurrencyCollection.validate_skip(cs, weak); return t_CurrencyCollection.validate_skip(ops, cs, weak) && t_CurrencyCollection.validate_skip(ops, cs, weak);
} }
bool ShardFeeCreated::null_value(vm::CellBuilder& cb) const { bool ShardFeeCreated::null_value(vm::CellBuilder& cb) const {

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
#include "common/refcnt.hpp" #include "common/refcnt.hpp"
@ -59,7 +59,7 @@ struct VarUInteger final : TLB_Complex {
ln = 32 - td::count_leading_zeroes32(n - 1); ln = 32 - td::count_leading_zeroes32(n - 1);
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override;
unsigned long long as_uint(const vm::CellSlice& cs) const override; unsigned long long as_uint(const vm::CellSlice& cs) const override;
bool null_value(vm::CellBuilder& cb) const override { bool null_value(vm::CellBuilder& cb) const override {
@ -78,7 +78,7 @@ struct VarUIntegerPos final : TLB_Complex {
ln = 32 - td::count_leading_zeroes32(n - 1); ln = 32 - td::count_leading_zeroes32(n - 1);
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override;
unsigned long long as_uint(const vm::CellSlice& cs) const override; unsigned long long as_uint(const vm::CellSlice& cs) const override;
bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override;
@ -92,7 +92,7 @@ struct VarInteger final : TLB_Complex {
ln = 32 - td::count_leading_zeroes32(n - 1); ln = 32 - td::count_leading_zeroes32(n - 1);
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override;
long long as_int(const vm::CellSlice& cs) const override; long long as_int(const vm::CellSlice& cs) const override;
bool null_value(vm::CellBuilder& cb) const override { bool null_value(vm::CellBuilder& cb) const override {
@ -107,7 +107,7 @@ struct VarIntegerNz final : TLB_Complex {
ln = 32 - td::count_leading_zeroes32(n - 1); ln = 32 - td::count_leading_zeroes32(n - 1);
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override;
long long as_int(const vm::CellSlice& cs) const override; long long as_int(const vm::CellSlice& cs) const override;
bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override;
@ -123,13 +123,13 @@ struct Unary final : TLB {
bool skip(vm::CellSlice& cs, int& n) const { bool skip(vm::CellSlice& cs, int& n) const {
return validate_skip(cs, false, n); return validate_skip(cs, false, n);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return cs.advance(get_size(cs)); return skip(cs);
} }
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return validate_skip(cs); return cs.advance(get_size(cs));
} }
bool validate(const vm::CellSlice& cs, bool weak = false) const override { bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return cs.have(get_size(cs)); return cs.have(get_size(cs));
} }
}; };
@ -149,7 +149,7 @@ struct HmLabel final : TLB_Complex {
int n; int n;
return skip(cs, n); return skip(cs, n);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
int n; int n;
return validate_skip(cs, weak, n); return validate_skip(cs, weak, n);
} }
@ -162,7 +162,7 @@ struct Hashmap final : TLB_Complex {
Hashmap(int _n, const TLB& _val_type) : value_type(_val_type), n(_n) { Hashmap(int _n, const TLB& _val_type) : value_type(_val_type), n(_n) {
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
struct HashmapNode final : TLB_Complex { struct HashmapNode final : TLB_Complex {
@ -173,7 +173,7 @@ struct HashmapNode final : TLB_Complex {
} }
int get_size(const vm::CellSlice& cs) const override; int get_size(const vm::CellSlice& cs) const override;
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return n > 0 ? hmn_fork : n; return n > 0 ? hmn_fork : n;
} }
@ -185,7 +185,7 @@ struct HashmapE final : TLB {
HashmapE(int _n, const TLB& _val_type) : root_type(_n, _val_type) { HashmapE(int _n, const TLB& _val_type) : root_type(_n, _val_type) {
} }
int get_size(const vm::CellSlice& cs) const override; int get_size(const vm::CellSlice& cs) const override;
bool validate(const vm::CellSlice& cs, bool weak = false) const override; bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(1); return (int)cs.prefetch_ulong(1);
} }
@ -221,7 +221,7 @@ struct HashmapAug final : TLB_Complex {
HashmapAug(int _n, const AugmentationCheckData& _aug) : aug(_aug), n(_n) { HashmapAug(int _n, const AugmentationCheckData& _aug) : aug(_aug), n(_n) {
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool extract_extra(vm::CellSlice& cs) const; bool extract_extra(vm::CellSlice& cs) const;
}; };
@ -232,7 +232,7 @@ struct HashmapAugNode final : TLB_Complex {
HashmapAugNode(int _n, const AugmentationCheckData& _aug) : aug(_aug), n(_n) { HashmapAugNode(int _n, const AugmentationCheckData& _aug) : aug(_aug), n(_n) {
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return n > 0 ? ahmn_fork : n; return n > 0 ? ahmn_fork : n;
} }
@ -244,7 +244,7 @@ struct HashmapAugE final : TLB_Complex {
HashmapAugE(int _n, const AugmentationCheckData& _aug) : root_type(_n, std::move(_aug)) { HashmapAugE(int _n, const AugmentationCheckData& _aug) : root_type(_n, std::move(_aug)) {
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool extract_extra(vm::CellSlice& cs) const; bool extract_extra(vm::CellSlice& cs) const;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(1); return (int)cs.prefetch_ulong(1);
@ -252,7 +252,7 @@ struct HashmapAugE final : TLB_Complex {
}; };
struct Grams final : TLB_Complex { struct Grams final : TLB_Complex {
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override;
bool null_value(vm::CellBuilder& cb) const override; bool null_value(vm::CellBuilder& cb) const override;
bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override; bool store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const override;
@ -264,7 +264,7 @@ extern const Grams t_Grams;
struct MsgAddressInt final : TLB_Complex { struct MsgAddressInt final : TLB_Complex {
enum { addr_std = 2, addr_var = 3 }; enum { addr_std = 2, addr_var = 3 };
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(2); return (int)cs.prefetch_ulong(2);
} }
@ -303,7 +303,7 @@ extern const MsgAddressExt t_MsgAddressExt;
struct MsgAddress final : TLB_Complex { struct MsgAddress final : TLB_Complex {
enum { addr_none = 0, addr_ext = 1, addr_std = 2, addr_var = 3 }; enum { addr_none = 0, addr_ext = 1, addr_std = 2, addr_var = 3 };
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(2); return (int)cs.prefetch_ulong(2);
} }
@ -318,8 +318,8 @@ struct ExtraCurrencyCollection final : TLB {
int get_size(const vm::CellSlice& cs) const override { int get_size(const vm::CellSlice& cs) const override {
return dict_type.get_size(cs); return dict_type.get_size(cs);
} }
bool validate(const vm::CellSlice& cs, bool weak) const override { bool validate(int* ops, const vm::CellSlice& cs, bool weak) const override {
return dict_type.validate(cs, weak); return dict_type.validate(ops, cs, weak);
} }
bool null_value(vm::CellBuilder& cb) const override { bool null_value(vm::CellBuilder& cb) const override {
return cb.store_zeroes_bool(1); return cb.store_zeroes_bool(1);
@ -348,7 +348,7 @@ extern const ExtraCurrencyCollection t_ExtraCurrencyCollection;
struct CurrencyCollection final : TLB_Complex { struct CurrencyCollection final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override; td::RefInt256 as_integer_skip(vm::CellSlice& cs) const override;
bool null_value(vm::CellBuilder& cb) const override { bool null_value(vm::CellBuilder& cb) const override {
return cb.store_bits_same_bool(1 + 4, false); return cb.store_bits_same_bool(1 + 4, false);
@ -371,7 +371,7 @@ extern const CurrencyCollection t_CurrencyCollection;
struct CommonMsgInfo final : TLB_Complex { struct CommonMsgInfo final : TLB_Complex {
enum { int_msg_info = 0, ext_in_msg_info = 2, ext_out_msg_info = 3 }; enum { int_msg_info = 0, ext_in_msg_info = 2, ext_out_msg_info = 3 };
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
int v = (int)cs.prefetch_ulong(2); int v = (int)cs.prefetch_ulong(2);
return v == 1 ? int_msg_info : v; return v == 1 ? int_msg_info : v;
@ -402,14 +402,14 @@ struct TickTock final : TLB {
extern const TickTock t_TickTock; extern const TickTock t_TickTock;
struct StateInit final : TLB_Complex { struct StateInit final : TLB_Complex {
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool get_ticktock(vm::CellSlice& cs, int& ticktock) const; bool get_ticktock(vm::CellSlice& cs, int& ticktock) const;
}; };
extern const StateInit t_StateInit; extern const StateInit t_StateInit;
struct Message final : TLB_Complex { struct Message final : TLB_Complex {
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool extract_info(vm::CellSlice& cs) const; bool extract_info(vm::CellSlice& cs) const;
bool get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const; bool get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) const;
bool is_internal(const vm::CellSlice& cs) const { bool is_internal(const vm::CellSlice& cs) const {
@ -425,7 +425,7 @@ struct IntermediateAddress final : TLB_Complex {
enum { interm_addr_regular = 0, interm_addr_simple = 2, interm_addr_ext = 3 }; enum { interm_addr_regular = 0, interm_addr_simple = 2, interm_addr_ext = 3 };
int get_size(const vm::CellSlice& cs) const override; int get_size(const vm::CellSlice& cs) const override;
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool fetch_regular(vm::CellSlice& cs, int& use_dst_bits) const { bool fetch_regular(vm::CellSlice& cs, int& use_dst_bits) const {
return cs.fetch_uint_to(8, use_dst_bits) && use_dst_bits <= 96; return cs.fetch_uint_to(8, use_dst_bits) && use_dst_bits <= 96;
} }
@ -439,7 +439,7 @@ extern const IntermediateAddress t_IntermediateAddress;
struct MsgEnvelope final : TLB_Complex { struct MsgEnvelope final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool extract_fwd_fees_remaining(vm::CellSlice& cs) const; bool extract_fwd_fees_remaining(vm::CellSlice& cs) const;
struct Record { struct Record {
typedef MsgEnvelope type_class; typedef MsgEnvelope type_class;
@ -463,28 +463,28 @@ extern const RefTo<MsgEnvelope> t_Ref_MsgEnvelope;
struct StorageUsed final : TLB_Complex { struct StorageUsed final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const StorageUsed t_StorageUsed; extern const StorageUsed t_StorageUsed;
struct StorageUsedShort final : TLB_Complex { struct StorageUsedShort final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const StorageUsedShort t_StorageUsedShort; extern const StorageUsedShort t_StorageUsedShort;
struct StorageInfo final : TLB_Complex { struct StorageInfo final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const StorageInfo t_StorageInfo; extern const StorageInfo t_StorageInfo;
struct AccountState final : TLB_Complex { struct AccountState final : TLB_Complex {
enum { account_uninit = 0, account_frozen = 1, account_active = 2 }; enum { account_uninit = 0, account_frozen = 1, account_active = 2 };
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
int t = (int)cs.prefetch_ulong(2); int t = (int)cs.prefetch_ulong(2);
return t == 3 ? account_active : t; return t == 3 ? account_active : t;
@ -496,7 +496,7 @@ extern const AccountState t_AccountState;
struct AccountStorage final : TLB_Complex { struct AccountStorage final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const; bool skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const;
}; };
@ -508,7 +508,7 @@ struct Account final : TLB_Complex {
Account(bool _allow_empty = false) : allow_empty(_allow_empty) { Account(bool _allow_empty = false) : allow_empty(_allow_empty) {
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
// Ref<vm::CellSlice> get_balance(const vm::CellSlice& cs) const; // Ref<vm::CellSlice> get_balance(const vm::CellSlice& cs) const;
bool skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const; bool skip_copy_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const;
bool skip_copy_depth_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const; bool skip_copy_depth_balance(vm::CellBuilder& cb, vm::CellSlice& cs) const;
@ -553,8 +553,8 @@ struct ShardAccount final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return cs.advance_ext(0x140, 1); return cs.advance_ext(0x140, 1);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return cs.advance(0x140) && t_Ref_Account.validate_skip(cs, weak); return cs.advance(0x140) && t_Ref_Account.validate_skip(ops, cs, weak);
} }
static bool unpack(vm::CellSlice& cs, Record& info) { static bool unpack(vm::CellSlice& cs, Record& info) {
return info.unpack(cs); return info.unpack(cs);
@ -569,7 +569,7 @@ extern const ShardAccount t_ShardAccount;
struct DepthBalanceInfo final : TLB_Complex { struct DepthBalanceInfo final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool null_value(vm::CellBuilder& cb) const override; bool null_value(vm::CellBuilder& cb) const override;
bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override;
}; };
@ -590,8 +590,8 @@ struct ShardAccounts final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return dict_type.skip(cs); return dict_type.skip(cs);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(cs, weak); return dict_type.validate_skip(ops, cs, weak);
} }
}; };
@ -615,7 +615,7 @@ extern const AccStatusChange t_AccStatusChange;
struct TrStoragePhase final : TLB_Complex { struct TrStoragePhase final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const; bool get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const;
bool maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const; bool maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const;
}; };
@ -624,14 +624,14 @@ extern const TrStoragePhase t_TrStoragePhase;
struct TrCreditPhase final : TLB_Complex { struct TrCreditPhase final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const TrCreditPhase t_TrCreditPhase; extern const TrCreditPhase t_TrCreditPhase;
struct TrComputeInternal1 final : TLB_Complex { struct TrComputeInternal1 final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
struct ComputeSkipReason final : TLB { struct ComputeSkipReason final : TLB {
@ -639,7 +639,7 @@ struct ComputeSkipReason final : TLB {
int get_size(const vm::CellSlice& cs) const override { int get_size(const vm::CellSlice& cs) const override {
return 2; return 2;
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return get_tag(cs) >= 0 && cs.advance(2); return get_tag(cs) >= 0 && cs.advance(2);
} }
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
@ -653,7 +653,7 @@ extern const ComputeSkipReason t_ComputeSkipReason;
struct TrComputePhase final : TLB_Complex { struct TrComputePhase final : TLB_Complex {
enum { tr_phase_compute_skipped = 0, tr_phase_compute_vm = 1 }; enum { tr_phase_compute_skipped = 0, tr_phase_compute_vm = 1 };
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(1); return (int)cs.prefetch_ulong(1);
} }
@ -663,7 +663,7 @@ extern const TrComputePhase t_TrComputePhase;
struct TrActionPhase final : TLB_Complex { struct TrActionPhase final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const TrActionPhase t_TrActionPhase; extern const TrActionPhase t_TrActionPhase;
@ -671,7 +671,7 @@ extern const TrActionPhase t_TrActionPhase;
struct TrBouncePhase final : TLB_Complex { struct TrBouncePhase final : TLB_Complex {
enum { tr_phase_bounce_negfunds = 0, tr_phase_bounce_nofunds = 1, tr_phase_bounce_ok = 2 }; enum { tr_phase_bounce_negfunds = 0, tr_phase_bounce_nofunds = 1, tr_phase_bounce_ok = 2 };
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override; int get_tag(const vm::CellSlice& cs) const override;
}; };
@ -679,7 +679,7 @@ extern const TrBouncePhase t_TrBouncePhase;
struct SplitMergeInfo final : TLB_Complex { struct SplitMergeInfo final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const SplitMergeInfo t_SplitMergeInfo; extern const SplitMergeInfo t_SplitMergeInfo;
@ -695,7 +695,7 @@ struct TransactionDescr final : TLB_Complex {
trans_merge_install = 7 trans_merge_install = 7
}; };
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override; int get_tag(const vm::CellSlice& cs) const override;
bool skip_to_storage_phase(vm::CellSlice& cs, bool& found) const; bool skip_to_storage_phase(vm::CellSlice& cs, bool& found) const;
bool get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const; bool get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const;
@ -705,14 +705,14 @@ extern const TransactionDescr t_TransactionDescr;
struct Transaction_aux final : TLB_Complex { struct Transaction_aux final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const Transaction_aux t_Transaction_aux; extern const Transaction_aux t_Transaction_aux;
struct Transaction final : TLB_Complex { struct Transaction final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const; bool get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const;
bool get_descr(Ref<vm::Cell> cell, Ref<vm::Cell>& tdescr) const; bool get_descr(Ref<vm::Cell> cell, Ref<vm::Cell>& tdescr) const;
bool get_descr(vm::CellSlice& cs, Ref<vm::Cell>& tdescr) const; bool get_descr(vm::CellSlice& cs, Ref<vm::Cell>& tdescr) const;
@ -735,7 +735,7 @@ struct HashUpdate final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return cs.advance(8 + 256 * 2); return cs.advance(8 + 256 * 2);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return cs.fetch_ulong(8) == 0x72 && cs.advance(256 * 2); return cs.fetch_ulong(8) == 0x72 && cs.advance(256 * 2);
} }
}; };
@ -745,7 +745,7 @@ extern const RefTo<HashUpdate> t_Ref_HashUpdate;
struct AccountBlock final : TLB_Complex { struct AccountBlock final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const; bool get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const;
}; };
@ -762,7 +762,7 @@ extern const HashmapAugE t_ShardAccountBlocks; // (HashmapAugE 256 AccountBlock
struct ImportFees final : TLB_Complex { struct ImportFees final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool null_value(vm::CellBuilder& cb) const override { bool null_value(vm::CellBuilder& cb) const override {
return cb.store_bits_same_bool(4 + 4 + 1, false); return cb.store_bits_same_bool(4 + 4 + 1, false);
} }
@ -782,7 +782,7 @@ struct InMsg final : TLB_Complex {
msg_discard_tr = 7 msg_discard_tr = 7
}; };
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(3); return (int)cs.prefetch_ulong(3);
} }
@ -802,7 +802,7 @@ struct OutMsg final : TLB_Complex {
msg_export_tr_req = 7 msg_export_tr_req = 7
}; };
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(3); return (int)cs.prefetch_ulong(3);
} }
@ -830,8 +830,8 @@ struct InMsgDescr final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return dict_type.skip(cs); return dict_type.skip(cs);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(cs, weak); return dict_type.validate_skip(ops, cs, weak);
} }
}; };
@ -853,8 +853,8 @@ struct OutMsgDescr final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return dict_type.skip(cs); return dict_type.skip(cs);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(cs, weak); return dict_type.validate_skip(ops, cs, weak);
} }
}; };
@ -867,7 +867,7 @@ struct EnqueuedMsg final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return cs.advance_ext(0x10040); return cs.advance_ext(0x10040);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, EnqueuedMsgDescr& descr) const { bool unpack(vm::CellSlice& cs, EnqueuedMsgDescr& descr) const {
return descr.unpack(cs); return descr.unpack(cs);
} }
@ -891,8 +891,8 @@ struct OutMsgQueue final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return dict_type.skip(cs); return dict_type.skip(cs);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(cs, weak); return dict_type.validate_skip(ops, cs, weak);
} }
}; };
@ -910,7 +910,7 @@ extern const HashmapE t_IhrPendingInfo;
struct OutMsgQueueInfo final : TLB_Complex { struct OutMsgQueueInfo final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const OutMsgQueueInfo t_OutMsgQueueInfo; extern const OutMsgQueueInfo t_OutMsgQueueInfo;
@ -946,7 +946,7 @@ struct ShardIdent final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return cs.advance(get_size(cs)); return cs.advance(get_size(cs));
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return 0; return 0;
} }
@ -985,7 +985,7 @@ struct BlockIdExt final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return cs.advance(get_size(cs)); return cs.advance(get_size(cs));
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, ton::BlockIdExt& data) const; bool unpack(vm::CellSlice& cs, ton::BlockIdExt& data) const;
bool pack(vm::CellBuilder& cb, const ton::BlockIdExt& data) const; bool pack(vm::CellBuilder& cb, const ton::BlockIdExt& data) const;
}; };
@ -995,7 +995,7 @@ extern const BlockIdExt t_BlockIdExt;
struct ShardState final : TLB_Complex { struct ShardState final : TLB_Complex {
enum { shard_state = (int)0x9023afe2, split_state = 0x5f327da5 }; enum { shard_state = (int)0x9023afe2, split_state = 0x5f327da5 };
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(32) == shard_state ? shard_state : -1; return (int)cs.prefetch_ulong(32) == shard_state ? shard_state : -1;
} }
@ -1005,7 +1005,7 @@ extern const ShardState t_ShardState;
struct ShardState_aux final : TLB_Complex { struct ShardState_aux final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return 0; return 0;
} }
@ -1016,7 +1016,7 @@ extern const ShardState_aux t_ShardState_aux;
struct LibDescr final : TLB_Complex { struct LibDescr final : TLB_Complex {
enum { shared_lib_descr = 0 }; enum { shared_lib_descr = 0 };
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(2); return (int)cs.prefetch_ulong(2);
} }
@ -1029,7 +1029,7 @@ struct BlkPrevInfo final : TLB_Complex {
BlkPrevInfo(bool _merged) : merged(_merged) { BlkPrevInfo(bool _merged) : merged(_merged) {
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const BlkPrevInfo t_BlkPrevInfo_0; extern const BlkPrevInfo t_BlkPrevInfo_0;
@ -1037,7 +1037,7 @@ extern const BlkPrevInfo t_BlkPrevInfo_0;
struct McStateExtra final : TLB_Complex { struct McStateExtra final : TLB_Complex {
enum { masterchain_state_extra = 0xcc26 }; enum { masterchain_state_extra = 0xcc26 };
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
}; };
extern const McStateExtra t_McStateExtra; extern const McStateExtra t_McStateExtra;
@ -1074,7 +1074,7 @@ extern const Aug_OldMcBlocksInfo aug_OldMcBlocksInfo;
struct ShardFeeCreated final : TLB_Complex { struct ShardFeeCreated final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool null_value(vm::CellBuilder& cb) const override; bool null_value(vm::CellBuilder& cb) const override;
bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override; bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const override;
}; };

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "td/utils/bits.h" #include "td/utils/bits.h"
#include "block/block.h" #include "block/block.h"
@ -813,11 +813,11 @@ td::Status ShardState::unpack_out_msg_queue_info(Ref<vm::Cell> out_msg_queue_inf
LOG(DEBUG) << "unpacking ProcessedUpto of our previous block " << id_.to_str(); LOG(DEBUG) << "unpacking ProcessedUpto of our previous block " << id_.to_str();
block::gen::t_ProcessedInfo.print(std::cerr, qinfo.proc_info); block::gen::t_ProcessedInfo.print(std::cerr, qinfo.proc_info);
} }
if (!block::gen::t_ProcessedInfo.validate_csr(qinfo.proc_info)) { if (!block::gen::t_ProcessedInfo.validate_csr(1024, qinfo.proc_info)) {
return td::Status::Error( return td::Status::Error(
-666, "ProcessedInfo in the state of "s + id_.to_str() + " is invalid according to automated validity checks"); -666, "ProcessedInfo in the state of "s + id_.to_str() + " is invalid according to automated validity checks");
} }
if (!block::gen::t_IhrPendingInfo.validate_csr(qinfo.ihr_pending)) { if (!block::gen::t_IhrPendingInfo.validate_csr(1024, qinfo.ihr_pending)) {
return td::Status::Error( return td::Status::Error(
-666, "IhrPendingInfo in the state of "s + id_.to_str() + " is invalid according to automated validity checks"); -666, "IhrPendingInfo in the state of "s + id_.to_str() + " is invalid according to automated validity checks");
} }
@ -1036,7 +1036,7 @@ td::Status ShardState::split(ton::ShardIdFull subshard) {
LOG(DEBUG) << "splitting total_balance"; LOG(DEBUG) << "splitting total_balance";
auto old_total_balance = total_balance_; auto old_total_balance = total_balance_;
auto accounts_extra = account_dict_->get_root_extra(); auto accounts_extra = account_dict_->get_root_extra();
if (!(accounts_extra.write().advance(5) && total_balance_.validate_unpack(accounts_extra))) { if (!(accounts_extra.write().advance(5) && total_balance_.validate_unpack(accounts_extra, 1024))) {
LOG(ERROR) << "cannot unpack CurrencyCollection from the root of newly-split accounts dictionary"; LOG(ERROR) << "cannot unpack CurrencyCollection from the root of newly-split accounts dictionary";
return td::Status::Error( return td::Status::Error(
-666, "error splitting total balance in account dictionary of shardchain state "s + id_.to_str()); -666, "error splitting total balance in account dictionary of shardchain state "s + id_.to_str());
@ -1085,16 +1085,16 @@ int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull ol
}); });
} }
bool CurrencyCollection::validate() const { bool CurrencyCollection::validate(int max_cells) const {
return is_valid() && td::sgn(grams) >= 0 && validate_extra(); return is_valid() && td::sgn(grams) >= 0 && validate_extra(max_cells);
} }
bool CurrencyCollection::validate_extra() const { bool CurrencyCollection::validate_extra(int max_cells) const {
if (extra.is_null()) { if (extra.is_null()) {
return true; return true;
} }
vm::CellBuilder cb; vm::CellBuilder cb;
return cb.store_maybe_ref(extra) && block::tlb::t_ExtraCurrencyCollection.validate_ref(cb.finalize()); return cb.store_maybe_ref(extra) && block::tlb::t_ExtraCurrencyCollection.validate_ref(max_cells, cb.finalize());
} }
bool CurrencyCollection::add(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c) { bool CurrencyCollection::add(const CurrencyCollection& a, const CurrencyCollection& b, CurrencyCollection& c) {
@ -1265,8 +1265,8 @@ bool CurrencyCollection::unpack(Ref<vm::CellSlice> csr) {
return unpack_CurrencyCollection(std::move(csr), grams, extra) || invalidate(); return unpack_CurrencyCollection(std::move(csr), grams, extra) || invalidate();
} }
bool CurrencyCollection::validate_unpack(Ref<vm::CellSlice> csr) { bool CurrencyCollection::validate_unpack(Ref<vm::CellSlice> csr, int max_cells) {
return (csr.not_null() && block::tlb::t_CurrencyCollection.validate(*csr) && return (csr.not_null() && block::tlb::t_CurrencyCollection.validate_upto(max_cells, *csr) &&
unpack_CurrencyCollection(std::move(csr), grams, extra)) || unpack_CurrencyCollection(std::move(csr), grams, extra)) ||
invalidate(); invalidate();
} }
@ -1593,7 +1593,7 @@ bool check_one_config_param(Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, td::
} else if (idx < 0) { } else if (idx < 0) {
return true; return true;
} }
bool ok = block::gen::ConfigParam{idx}.validate_ref(std::move(cell)); bool ok = block::gen::ConfigParam{idx}.validate_ref(1024, std::move(cell));
if (!ok) { if (!ok) {
LOG(ERROR) << "configuration parameter #" << idx << " is invalid"; LOG(ERROR) << "configuration parameter #" << idx << " is invalid";
} }

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
#include "common/refcnt.hpp" #include "common/refcnt.hpp"
@ -323,8 +323,8 @@ struct CurrencyCollection {
grams.clear(); grams.clear();
return false; return false;
} }
bool validate() const; bool validate(int max_cells = 1024) const;
bool validate_extra() const; bool validate_extra(int max_cells = 1024) const;
bool operator==(const CurrencyCollection& other) const; bool operator==(const CurrencyCollection& other) const;
bool operator!=(const CurrencyCollection& other) const { bool operator!=(const CurrencyCollection& other) const {
return !operator==(other); return !operator==(other);
@ -360,7 +360,7 @@ struct CurrencyCollection {
bool fetch(vm::CellSlice& cs); bool fetch(vm::CellSlice& cs);
bool fetch_exact(vm::CellSlice& cs); bool fetch_exact(vm::CellSlice& cs);
bool unpack(Ref<vm::CellSlice> csr); bool unpack(Ref<vm::CellSlice> csr);
bool validate_unpack(Ref<vm::CellSlice> csr); bool validate_unpack(Ref<vm::CellSlice> csr, int max_cells = 1024);
Ref<vm::CellSlice> pack() const; Ref<vm::CellSlice> pack() const;
bool pack_to(Ref<vm::CellSlice>& csr) const { bool pack_to(Ref<vm::CellSlice>& csr) const {
return (csr = pack()).not_null(); return (csr = pack()).not_null();

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here. from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include <cassert> #include <cassert>
#include <algorithm> #include <algorithm>
@ -697,7 +697,7 @@ void interpret_tlb_skip(vm::Stack& stack) {
void interpret_tlb_validate_skip(vm::Stack& stack) { void interpret_tlb_validate_skip(vm::Stack& stack) {
auto tp = pop_tlb_type(stack); auto tp = pop_tlb_type(stack);
auto cs = stack.pop_cellslice(); auto cs = stack.pop_cellslice();
bool ok = (*tp)->validate_skip(cs.write()); bool ok = (*tp)->validate_skip_upto(1048576, cs.write());
if (ok) { if (ok) {
stack.push(std::move(cs)); stack.push(std::move(cs));
} }

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here. from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "block/block.h" #include "block/block.h"
#include "vm/boc.h" #include "vm/boc.h"
@ -98,7 +98,7 @@ void test1() {
block::tlb::ShardIdent::Record shard_id; block::tlb::ShardIdent::Record shard_id;
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
std::cout << "ShardIdent.validate() = " << block::tlb::t_ShardIdent.validate(csl) << std::endl; std::cout << "ShardIdent.validate() = " << block::tlb::t_ShardIdent.validate_upto(1024, csl) << std::endl;
csl.print_rec(std::cerr); csl.print_rec(std::cerr);
csl.dump(std::cerr, 7); csl.dump(std::cerr, 7);
std::cout << "ShardIdent.unpack() = " << block::tlb::t_ShardIdent.unpack(csl, shard_id) << std::endl; std::cout << "ShardIdent.unpack() = " << block::tlb::t_ShardIdent.unpack(csl, shard_id) << std::endl;
@ -107,9 +107,9 @@ void test1() {
<< " shard_prefix:" << shard_id.shard_prefix << std::endl; << " shard_prefix:" << shard_id.shard_prefix << std::endl;
} }
} }
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl; std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip_upto(1024, csl) << std::endl;
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl; std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip_upto(1024, csl) << std::endl;
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl; std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip_upto(1024, csl) << std::endl;
using namespace td::literals; using namespace td::literals;
std::cout << "Grams.store_intval(239) = " << block::tlb::t_Grams.store_integer_value(cb, "239"_i256) << std::endl; std::cout << "Grams.store_intval(239) = " << block::tlb::t_Grams.store_integer_value(cb, "239"_i256) << std::endl;
std::cout << "Grams.store_intval(17239) = " << block::tlb::t_Grams.store_integer_value(cb, "17239"_i256) << std::endl; std::cout << "Grams.store_intval(17239) = " << block::tlb::t_Grams.store_integer_value(cb, "17239"_i256) << std::endl;
@ -120,13 +120,13 @@ void test1() {
std::cout << "Grams.store_intval(666) = " << block::tlb::t_Grams.store_integer_value(cb, "666"_i256) << std::endl; std::cout << "Grams.store_intval(666) = " << block::tlb::t_Grams.store_integer_value(cb, "666"_i256) << std::endl;
std::cout << cb << std::endl; std::cout << cb << std::endl;
cs2 = td::Ref<vm::CellSlice>{true, cb.finalize()}; cs2 = td::Ref<vm::CellSlice>{true, cb.finalize()};
std::cout << "Grams.validate(cs) = " << block::tlb::t_Grams.validate(*cs) << std::endl; std::cout << "Grams.validate(cs) = " << block::tlb::t_Grams.validate_upto(1024, *cs) << std::endl;
std::cout << "Grams.validate(cs2) = " << block::tlb::t_Grams.validate(*cs2) << std::endl; std::cout << "Grams.validate(cs2) = " << block::tlb::t_Grams.validate_upto(1024, *cs2) << std::endl;
// //
block::gen::SplitMergeInfo::Record data; block::gen::SplitMergeInfo::Record data;
block::gen::Grams::Record data2; block::gen::Grams::Record data2;
std::cout << "block::gen::Grams.validate(cs) = " << block::gen::t_Grams.validate(*cs) << std::endl; std::cout << "block::gen::Grams.validate(cs) = " << block::gen::t_Grams.validate_upto(1024, *cs) << std::endl;
std::cout << "block::gen::Grams.validate(cs2) = " << block::gen::t_Grams.validate(*cs2) << std::endl; std::cout << "block::gen::Grams.validate(cs2) = " << block::gen::t_Grams.validate_upto(1024, *cs2) << std::endl;
std::cout << "[cs = " << cs << "]" << std::endl; std::cout << "[cs = " << cs << "]" << std::endl;
bool ok = tlb::csr_unpack_inexact(cs, data); bool ok = tlb::csr_unpack_inexact(cs, data);
std::cout << "block::gen::SplitMergeInfo.unpack(cs, data) = " << ok << std::endl; std::cout << "block::gen::SplitMergeInfo.unpack(cs, data) = " << ok << std::endl;
@ -182,12 +182,12 @@ void test1() {
} }
void test2(vm::CellSlice& cs) { void test2(vm::CellSlice& cs) {
std::cout << "Bool.validate() = " << block::tlb::t_Bool.validate(cs) << std::endl; std::cout << "Bool.validate() = " << block::tlb::t_Bool.validate_upto(1024, cs) << std::endl;
std::cout << "UInt16.validate() = " << block::tlb::t_uint16.validate(cs) << std::endl; std::cout << "UInt16.validate() = " << block::tlb::t_uint16.validate_upto(1024, cs) << std::endl;
std::cout << "HashmapE(32,UInt16).validate() = " << block::tlb::HashmapE(32, block::tlb::t_uint16).validate(cs) std::cout << "HashmapE(32,UInt16).validate() = "
<< std::endl; << block::tlb::HashmapE(32, block::tlb::t_uint16).validate_upto(1024, cs) << std::endl;
std::cout << "block::gen::HashmapE(32,UInt16).validate() = " std::cout << "block::gen::HashmapE(32,UInt16).validate() = "
<< block::gen::HashmapE{32, block::gen::t_uint16}.validate(cs) << std::endl; << block::gen::HashmapE{32, block::gen::t_uint16}.validate_upto(1024, cs) << std::endl;
} }
void usage() { void usage() {
@ -249,7 +249,7 @@ int main(int argc, char* const argv[]) {
} }
type->print_ref(std::cout, boc); type->print_ref(std::cout, boc);
std::cout << std::endl; std::cout << std::endl;
bool ok = type->validate_ref(boc); bool ok = type->validate_ref(1048576, boc);
std::cout << "(" << (ok ? "" : "in") << "valid " << *type << ")" << std::endl; std::cout << "(" << (ok ? "" : "in") << "valid " << *type << ")" << std::endl;
} }
} }

View file

@ -1239,7 +1239,7 @@ bool ShardConfig::new_workchain(ton::WorkchainId workchain, ton::BlockSeqno reg_
cb.store_zeroes_bool( cb.store_zeroes_bool(
1 + 5 + 1 + 5 +
5) // split_merge_at:FutureSplitMerge fees_collected:CurrencyCollection funds_created:CurrencyCollection 5) // split_merge_at:FutureSplitMerge fees_collected:CurrencyCollection funds_created:CurrencyCollection
&& cb.finalize_to(cell) && block::gen::t_BinTree_ShardDescr.validate_ref(cell) && && cb.finalize_to(cell) && block::gen::t_BinTree_ShardDescr.validate_ref(1024, cell) &&
shard_hashes_dict_->set_ref(td::BitArray<32>{workchain}, std::move(cell), vm::Dictionary::SetMode::Add); shard_hashes_dict_->set_ref(td::BitArray<32>{workchain}, std::move(cell), vm::Dictionary::SetMode::Add);
} }
@ -1469,7 +1469,7 @@ static bool btree_set(Ref<vm::Cell>& root, ton::ShardId shard, Ref<vm::Cell> val
} }
bool ShardConfig::set_shard_info(ton::ShardIdFull shard, Ref<vm::Cell> value) { bool ShardConfig::set_shard_info(ton::ShardIdFull shard, Ref<vm::Cell> value) {
if (!gen::t_BinTree_ShardDescr.validate_ref(value)) { if (!gen::t_BinTree_ShardDescr.validate_ref(1024, value)) {
LOG(ERROR) << "attempting to store an invalid (BinTree ShardDescr) at shard configuration position " LOG(ERROR) << "attempting to store an invalid (BinTree ShardDescr) at shard configuration position "
<< shard.to_str(); << shard.to_str();
gen::t_BinTree_ShardDescr.print_ref(std::cerr, value); gen::t_BinTree_ShardDescr.print_ref(std::cerr, value);

View file

@ -150,7 +150,7 @@ bool Account::unpack_storage_info(vm::CellSlice& cs) {
return false; return false;
} }
} else { } else {
due_payment = td::RefInt256{true, 0}; due_payment = td::zero_refint();
} }
unsigned long long u = 0; unsigned long long u = 0;
u |= storage_stat.cells = block::tlb::t_VarUInteger_7.as_uint(*used.cells); u |= storage_stat.cells = block::tlb::t_VarUInteger_7.as_uint(*used.cells);
@ -369,7 +369,7 @@ bool Account::init_new(ton::UnixTime now) {
now_ = now; now_ = now;
last_paid = 0; last_paid = 0;
storage_stat.clear(); storage_stat.clear();
due_payment = td::RefInt256{true, 0}; due_payment = td::zero_refint();
balance.set_zero(); balance.set_zero();
if (my_addr_exact.is_null()) { if (my_addr_exact.is_null()) {
vm::CellBuilder cb; vm::CellBuilder cb;
@ -473,6 +473,9 @@ Transaction::Transaction(const Account& _account, int ttype, ton::LogicalTime re
start_lt = std::max(req_start_lt, account.last_trans_end_lt_); start_lt = std::max(req_start_lt, account.last_trans_end_lt_);
end_lt = start_lt + 1; end_lt = start_lt + 1;
acc_status = (account.status == Account::acc_nonexist ? Account::acc_uninit : account.status); acc_status = (account.status == Account::acc_nonexist ? Account::acc_uninit : account.status);
if (acc_status == Account::acc_frozen) {
frozen_hash = account.state_hash;
}
} }
bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig* cfg) { bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig* cfg) {
@ -504,7 +507,7 @@ bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig*
if (ihr_delivered) { if (ihr_delivered) {
in_fwd_fee = std::move(ihr_fee); in_fwd_fee = std::move(ihr_fee);
} else { } else {
in_fwd_fee = td::RefInt256{true, 0}; in_fwd_fee = td::zero_refint();
msg_balance_remaining += std::move(ihr_fee); msg_balance_remaining += std::move(ihr_fee);
} }
if (info.created_lt >= start_lt) { if (info.created_lt >= start_lt) {
@ -544,7 +547,7 @@ bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig*
LOG(DEBUG) << "computed fwd fees set to zero for special account"; LOG(DEBUG) << "computed fwd fees set to zero for special account";
fees_c.first = fees_c.second = 0; fees_c.first = fees_c.second = 0;
} }
in_fwd_fee = td::RefInt256{true, fees_c.first}; in_fwd_fee = td::make_refint(fees_c.first);
if (balance.grams < in_fwd_fee) { if (balance.grams < in_fwd_fee) {
LOG(DEBUG) << "cannot pay for importing this external message"; LOG(DEBUG) << "cannot pay for importing this external message";
return false; return false;
@ -616,19 +619,19 @@ bool Transaction::prepare_storage_phase(const StoragePhaseConfig& cfg, bool forc
res->is_special = account.is_special; res->is_special = account.is_special;
last_paid = res->last_paid_updated = (res->is_special ? 0 : now); last_paid = res->last_paid_updated = (res->is_special ? 0 : now);
if (to_pay.is_null() || sgn(to_pay) == 0) { if (to_pay.is_null() || sgn(to_pay) == 0) {
res->fees_collected = res->fees_due = td::RefInt256{true, 0}; res->fees_collected = res->fees_due = td::zero_refint();
} else if (to_pay <= balance.grams) { } else if (to_pay <= balance.grams) {
res->fees_collected = to_pay; res->fees_collected = to_pay;
res->fees_due = td::RefInt256{true, 0}; res->fees_due = td::zero_refint();
balance -= std::move(to_pay); balance -= std::move(to_pay);
} else if (acc_status == Account::acc_frozen && !force_collect && to_pay + due_payment < cfg.delete_due_limit) { } else if (acc_status == Account::acc_frozen && !force_collect && to_pay + due_payment < cfg.delete_due_limit) {
// do not collect fee // do not collect fee
res->last_paid_updated = (res->is_special ? 0 : account.last_paid); res->last_paid_updated = (res->is_special ? 0 : account.last_paid);
res->fees_collected = res->fees_due = td::RefInt256{true, 0}; res->fees_collected = res->fees_due = td::zero_refint();
} else { } else {
res->fees_collected = balance.grams; res->fees_collected = balance.grams;
res->fees_due = std::move(to_pay) - std::move(balance.grams); res->fees_due = std::move(to_pay) - std::move(balance.grams);
balance.grams = td::RefInt256{true, 0}; balance.grams = td::zero_refint();
if (!res->is_special) { if (!res->is_special) {
auto total_due = res->fees_due + due_payment; auto total_due = res->fees_due + due_payment;
switch (acc_status) { switch (acc_status) {
@ -707,8 +710,8 @@ bool ComputePhaseConfig::parse_GasLimitsPrices_internal(Ref<vm::CellSlice> cs, t
special_gas_limit = spec_limit; special_gas_limit = spec_limit;
gas_credit = r.gas_credit; gas_credit = r.gas_credit;
gas_price = r.gas_price; gas_price = r.gas_price;
freeze_due_limit = td::RefInt256{true, r.freeze_due_limit}; freeze_due_limit = td::make_refint(r.freeze_due_limit);
delete_due_limit = td::RefInt256{true, r.delete_due_limit}; delete_due_limit = td::make_refint(r.delete_due_limit);
}; };
block::gen::GasLimitsPrices::Record_gas_prices_ext rec; block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
if (tlb::csr_unpack(cs, rec)) { if (tlb::csr_unpack(cs, rec)) {
@ -728,10 +731,10 @@ bool ComputePhaseConfig::parse_GasLimitsPrices_internal(Ref<vm::CellSlice> cs, t
} }
void ComputePhaseConfig::compute_threshold() { void ComputePhaseConfig::compute_threshold() {
gas_price256 = td::RefInt256{true, gas_price}; gas_price256 = td::make_refint(gas_price);
if (gas_limit > flat_gas_limit) { if (gas_limit > flat_gas_limit) {
max_gas_threshold = max_gas_threshold =
td::rshift(gas_price256 * (gas_limit - flat_gas_limit), 16, 1) + td::make_refint(flat_gas_price); td::rshift(gas_price256 * (gas_limit - flat_gas_limit), 16, 1) + td::make_bigint(flat_gas_price);
} else { } else {
max_gas_threshold = td::make_refint(flat_gas_price); max_gas_threshold = td::make_refint(flat_gas_price);
} }
@ -828,8 +831,8 @@ Ref<vm::Tuple> Transaction::prepare_vm_c7(const ComputePhaseConfig& cfg) const {
} }
auto tuple = vm::make_tuple_ref( auto tuple = vm::make_tuple_ref(
td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // actions:Integer td::zero_refint(), // actions:Integer
td::make_refint(0), // msgs_sent:Integer td::zero_refint(), // msgs_sent:Integer
td::make_refint(now), // unixtime:Integer td::make_refint(now), // unixtime:Integer
td::make_refint(account.block_lt), // block_lt:Integer td::make_refint(account.block_lt), // block_lt:Integer
td::make_refint(start_lt), // trans_lt:Integer td::make_refint(start_lt), // trans_lt:Integer
@ -1012,7 +1015,7 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
} }
if (cp.accepted) { if (cp.accepted) {
if (account.is_special) { if (account.is_special) {
cp.gas_fees = td::RefInt256{true, 0}; cp.gas_fees = td::zero_refint();
} else { } else {
cp.gas_fees = cfg.compute_gas_price(cp.gas_used); cp.gas_fees = cfg.compute_gas_price(cp.gas_used);
total_fees += cp.gas_fees; total_fees += cp.gas_fees;
@ -1040,8 +1043,8 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
ap.action_list_hash = list->get_hash().bits(); ap.action_list_hash = list->get_hash().bits();
ap.remaining_balance = balance; ap.remaining_balance = balance;
ap.end_lt = end_lt; ap.end_lt = end_lt;
ap.total_fwd_fees = td::RefInt256{true, 0}; ap.total_fwd_fees = td::zero_refint();
ap.total_action_fees = td::RefInt256{true, 0}; ap.total_action_fees = td::zero_refint();
ap.reserved_balance.set_zero(); ap.reserved_balance.set_zero();
int n = 0; int n = 0;
@ -1429,7 +1432,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
info.ihr_disabled = true; info.ihr_disabled = true;
info.bounce = false; info.bounce = false;
info.bounced = false; info.bounced = false;
fwd_fee = ihr_fee = td::RefInt256{true, 0}; fwd_fee = ihr_fee = td::zero_refint();
} else { } else {
// int_msg_info$0 constructor // int_msg_info$0 constructor
if (!tlb::csr_unpack(msg.info, info) || !block::tlb::t_CurrencyCollection.validate_csr(info.value)) { if (!tlb::csr_unpack(msg.info, info) || !block::tlb::t_CurrencyCollection.validate_csr(info.value)) {
@ -1482,10 +1485,10 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
// set fees to computed values // set fees to computed values
if (fwd_fee->unsigned_fits_bits(63) && fwd_fee->to_long() < (long long)fees_c.first) { if (fwd_fee->unsigned_fits_bits(63) && fwd_fee->to_long() < (long long)fees_c.first) {
fwd_fee = td::RefInt256{true, fees_c.first}; fwd_fee = td::make_refint(fees_c.first);
} }
if (fees_c.second && ihr_fee->unsigned_fits_bits(63) && ihr_fee->to_long() < (long long)fees_c.second) { if (fees_c.second && ihr_fee->unsigned_fits_bits(63) && ihr_fee->to_long() < (long long)fees_c.second) {
ihr_fee = td::RefInt256{true, fees_c.second}; ihr_fee = td::make_refint(fees_c.second);
} }
Ref<vm::Cell> new_msg; Ref<vm::Cell> new_msg;
@ -1502,7 +1505,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
} }
if (info.ihr_disabled) { if (info.ihr_disabled) {
// if IHR is disabled, IHR fees will be always zero // if IHR is disabled, IHR fees will be always zero
ihr_fee = td::RefInt256{true, 0}; ihr_fee = td::zero_refint();
} }
// extract value to be carried by the message // extract value to be carried by the message
block::CurrencyCollection req; block::CurrencyCollection req;
@ -1757,10 +1760,10 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
balance -= msg_balance; balance -= msg_balance;
CHECK(balance.is_valid()); CHECK(balance.is_valid());
// debit total forwarding fees from the message's balance, then split forwarding fees into our part and remaining part // debit total forwarding fees from the message's balance, then split forwarding fees into our part and remaining part
msg_balance -= td::RefInt256{true, bp.fwd_fees}; msg_balance -= td::make_refint(bp.fwd_fees);
bp.fwd_fees_collected = msg_prices.get_first_part(bp.fwd_fees); bp.fwd_fees_collected = msg_prices.get_first_part(bp.fwd_fees);
bp.fwd_fees -= bp.fwd_fees_collected; bp.fwd_fees -= bp.fwd_fees_collected;
total_fees += td::RefInt256{true, bp.fwd_fees_collected}; total_fees += td::make_refint(bp.fwd_fees_collected);
// serialize outbound message // serialize outbound message
info.created_lt = end_lt++; info.created_lt = end_lt++;
info.created_at = now; info.created_at = now;

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
#include <vector> #include <vector>
@ -169,6 +169,8 @@ class PropagateConstSpan {
size_t size_{0}; size_t size_{0};
}; };
struct Normalize {};
template <class Tr = BigIntInfo> template <class Tr = BigIntInfo>
class AnyIntView { class AnyIntView {
public: public:
@ -308,6 +310,10 @@ class BigIntG {
explicit BigIntG(word_t x) : n(1) { explicit BigIntG(word_t x) : n(1) {
digits[0] = x; digits[0] = x;
} }
BigIntG(Normalize, word_t x) : n(1) {
digits[0] = x;
normalize_bool();
}
BigIntG(const BigIntG& x) : n(x.n) { BigIntG(const BigIntG& x) : n(x.n) {
std::memcpy(digits, x.digits, n * sizeof(word_t)); std::memcpy(digits, x.digits, n * sizeof(word_t));
///std::cout << "(BiCC " << (const void*)&x << "->" << (void*)this << ")"; ///std::cout << "(BiCC " << (const void*)&x << "->" << (void*)this << ")";
@ -2515,6 +2521,11 @@ extern template class AnyIntView<BigIntInfo>;
extern template class BigIntG<257, BigIntInfo>; extern template class BigIntG<257, BigIntInfo>;
typedef BigIntG<257, BigIntInfo> BigInt256; typedef BigIntG<257, BigIntInfo> BigInt256;
template <int n = 257>
BigIntG<n, BigIntInfo> make_bigint(long long x) {
return BigIntG<n, BigIntInfo>{Normalize(), x};
}
namespace literals { namespace literals {
extern BigInt256 operator""_i256(const char* str, std::size_t str_len); extern BigInt256 operator""_i256(const char* str, std::size_t str_len);

View file

@ -38,6 +38,11 @@ RefInt256 operator+(RefInt256 x, long long y) {
return x; return x;
} }
RefInt256 operator+(RefInt256 x, const BigInt256& y) {
(x.write() += y).normalize();
return x;
}
RefInt256 operator-(RefInt256 x, RefInt256 y) { RefInt256 operator-(RefInt256 x, RefInt256 y) {
(x.write() -= *y).normalize(); (x.write() -= *y).normalize();
return x; return x;
@ -48,6 +53,11 @@ RefInt256 operator-(RefInt256 x, long long y) {
return x; return x;
} }
RefInt256 operator-(RefInt256 x, const BigInt256& y) {
(x.write() -= y).normalize();
return x;
}
RefInt256 operator-(RefInt256 x) { RefInt256 operator-(RefInt256 x) {
x.write().negate().normalize(); x.write().negate().normalize();
return x; return x;
@ -69,6 +79,12 @@ RefInt256 operator*(RefInt256 x, long long y) {
return x; return x;
} }
RefInt256 operator*(RefInt256 x, const BigInt256& y) {
RefInt256 z{true, 0};
z.write().add_mul(*x, y).normalize();
return z;
}
RefInt256 operator/(RefInt256 x, RefInt256 y) { RefInt256 operator/(RefInt256 x, RefInt256 y) {
RefInt256 quot{true}; RefInt256 quot{true};
x.write().mod_div(*y, quot.write()); x.write().mod_div(*y, quot.write());
@ -142,6 +158,11 @@ RefInt256& operator+=(RefInt256& x, long long y) {
return x; return x;
} }
RefInt256& operator+=(RefInt256& x, const BigInt256& y) {
(x.write() += y).normalize();
return x;
}
RefInt256& operator-=(RefInt256& x, RefInt256 y) { RefInt256& operator-=(RefInt256& x, RefInt256 y) {
(x.write() -= *y).normalize(); (x.write() -= *y).normalize();
return x; return x;
@ -152,6 +173,11 @@ RefInt256& operator-=(RefInt256& x, long long y) {
return x; return x;
} }
RefInt256& operator-=(RefInt256& x, const BigInt256& y) {
(x.write() -= y).normalize();
return x;
}
RefInt256& operator*=(RefInt256& x, RefInt256 y) { RefInt256& operator*=(RefInt256& x, RefInt256 y) {
RefInt256 z{true, 0}; RefInt256 z{true, 0};
z.write().add_mul(*x, *y).normalize(); z.write().add_mul(*x, *y).normalize();
@ -163,6 +189,12 @@ RefInt256& operator*=(RefInt256& x, long long y) {
return x; return x;
} }
RefInt256& operator*=(RefInt256& x, const BigInt256& y) {
RefInt256 z{true, 0};
z.write().add_mul(*x, y).normalize();
return x = z;
}
RefInt256& operator/=(RefInt256& x, RefInt256 y) { RefInt256& operator/=(RefInt256& x, RefInt256 y) {
RefInt256 quot{true}; RefInt256 quot{true};
x.write().mod_div(*y, quot.write()); x.write().mod_div(*y, quot.write());
@ -214,9 +246,13 @@ int sgn(RefInt256 x) {
} }
RefInt256 make_refint(long long x) { RefInt256 make_refint(long long x) {
auto xx = td::RefInt256{true, x}; return td::RefInt256{true, td::Normalize(), x};
xx.unique_write().normalize(); }
return xx;
RefInt256 zero_refint() {
// static RefInt256 Zero = td::RefInt256{true, 0};
// return Zero;
return td::RefInt256{true, 0};
} }
RefInt256 bits_to_refint(td::ConstBitPtr bits, int n, bool sgnd) { RefInt256 bits_to_refint(td::ConstBitPtr bits, int n, bool sgnd) {

View file

@ -33,10 +33,13 @@ typedef Ref<CntInt256> RefInt256;
extern RefInt256 operator+(RefInt256 x, RefInt256 y); extern RefInt256 operator+(RefInt256 x, RefInt256 y);
extern RefInt256 operator+(RefInt256 x, long long y); extern RefInt256 operator+(RefInt256 x, long long y);
extern RefInt256 operator+(RefInt256 x, const BigInt256& y);
extern RefInt256 operator-(RefInt256 x, RefInt256 y); extern RefInt256 operator-(RefInt256 x, RefInt256 y);
extern RefInt256 operator-(RefInt256 x, long long y); extern RefInt256 operator-(RefInt256 x, long long y);
extern RefInt256 operator-(RefInt256 x, const BigInt256& y);
extern RefInt256 operator*(RefInt256 x, RefInt256 y); extern RefInt256 operator*(RefInt256 x, RefInt256 y);
extern RefInt256 operator*(RefInt256 x, long long y); extern RefInt256 operator*(RefInt256 x, long long y);
extern RefInt256 operator*(RefInt256 x, const BigInt256& y);
extern RefInt256 operator/(RefInt256 x, RefInt256 y); extern RefInt256 operator/(RefInt256 x, RefInt256 y);
extern RefInt256 operator%(RefInt256 x, RefInt256 y); extern RefInt256 operator%(RefInt256 x, RefInt256 y);
extern RefInt256 div(RefInt256 x, RefInt256 y, int round_mode = -1); extern RefInt256 div(RefInt256 x, RefInt256 y, int round_mode = -1);
@ -53,10 +56,13 @@ extern RefInt256 rshift(RefInt256 x, int y, int round_mode = -1);
extern RefInt256& operator+=(RefInt256& x, RefInt256 y); extern RefInt256& operator+=(RefInt256& x, RefInt256 y);
extern RefInt256& operator+=(RefInt256& x, long long y); extern RefInt256& operator+=(RefInt256& x, long long y);
extern RefInt256& operator+=(RefInt256& x, const BigInt256& y);
extern RefInt256& operator-=(RefInt256& x, RefInt256 y); extern RefInt256& operator-=(RefInt256& x, RefInt256 y);
extern RefInt256& operator-=(RefInt256& x, long long y); extern RefInt256& operator-=(RefInt256& x, long long y);
extern RefInt256& operator-=(RefInt256& x, const BigInt256& y);
extern RefInt256& operator*=(RefInt256& x, RefInt256 y); extern RefInt256& operator*=(RefInt256& x, RefInt256 y);
extern RefInt256& operator*=(RefInt256& x, long long y); extern RefInt256& operator*=(RefInt256& x, long long y);
extern RefInt256& operator*=(RefInt256& x, const BigInt256& y);
extern RefInt256& operator/=(RefInt256& x, RefInt256 y); extern RefInt256& operator/=(RefInt256& x, RefInt256 y);
extern RefInt256& operator%=(RefInt256& x, RefInt256 y); extern RefInt256& operator%=(RefInt256& x, RefInt256 y);
@ -100,7 +106,14 @@ extern int cmp(RefInt256 x, RefInt256 y);
extern int cmp(RefInt256 x, long long y); extern int cmp(RefInt256 x, long long y);
extern int sgn(RefInt256 x); extern int sgn(RefInt256 x);
template <typename... Args>
RefInt256 make_refint(Args&&... args) {
return td::RefInt256{true, std::forward<Args>(args)...};
}
extern RefInt256 make_refint(long long x); extern RefInt256 make_refint(long long x);
extern RefInt256 zero_refint();
extern RefInt256 bits_to_refint(td::ConstBitPtr bits, int n, bool sgnd = false); extern RefInt256 bits_to_refint(td::ConstBitPtr bits, int n, bool sgnd = false);
extern std::string dec_string(RefInt256 x); extern std::string dec_string(RefInt256 x);

View file

@ -180,9 +180,9 @@ void interpret_times_div(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int(); auto z = stack.pop_int();
auto y = stack.pop_int(); auto y = stack.pop_int();
auto x = stack.pop_int(); auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{0}; typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y); tmp.add_mul(*x, *y);
auto q = td::RefInt256{true}; auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode); tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize(); q.unique_write().normalize();
stack.push_int(std::move(q)); stack.push_int(std::move(q));
@ -192,26 +192,23 @@ void interpret_times_divmod(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int(); auto z = stack.pop_int();
auto y = stack.pop_int(); auto y = stack.pop_int();
auto x = stack.pop_int(); auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{0}; typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y); tmp.add_mul(*x, *y);
auto q = td::RefInt256{true}; auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode); tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize(); q.unique_write().normalize();
auto r = td::RefInt256{true, tmp};
stack.push_int(std::move(q)); stack.push_int(std::move(q));
stack.push_int(std::move(r)); stack.push_int(td::make_refint(tmp));
} }
void interpret_times_mod(vm::Stack& stack, int round_mode) { void interpret_times_mod(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int(); auto z = stack.pop_int();
auto y = stack.pop_int(); auto y = stack.pop_int();
auto x = stack.pop_int(); auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{0}; typename td::BigInt256::DoubleInt tmp{0}, q;
tmp.add_mul(*x, *y); tmp.add_mul(*x, *y);
td::BigIntG<257 * 2> q;
tmp.mod_div(*z, q, round_mode); tmp.mod_div(*z, q, round_mode);
auto r = td::RefInt256{true, tmp}; stack.push_int(td::make_refint(tmp));
stack.push_int(std::move(r));
} }
void interpret_negate(vm::Stack& stack) { void interpret_negate(vm::Stack& stack) {
@ -253,21 +250,21 @@ void interpret_fits(vm::Stack& stack, bool sgnd) {
void interpret_pow2(vm::Stack& stack) { void interpret_pow2(vm::Stack& stack) {
int x = stack.pop_smallint_range(255); int x = stack.pop_smallint_range(255);
auto r = td::RefInt256{true}; auto r = td::make_refint();
r.unique_write().set_pow2(x); r.unique_write().set_pow2(x);
stack.push_int(r); stack.push_int(r);
} }
void interpret_neg_pow2(vm::Stack& stack) { void interpret_neg_pow2(vm::Stack& stack) {
int x = stack.pop_smallint_range(256); int x = stack.pop_smallint_range(256);
auto r = td::RefInt256{true}; auto r = td::make_refint();
r.unique_write().set_pow2(x).negate().normalize(); r.unique_write().set_pow2(x).negate().normalize();
stack.push_int(r); stack.push_int(r);
} }
void interpret_pow2_minus1(vm::Stack& stack) { void interpret_pow2_minus1(vm::Stack& stack) {
int x = stack.pop_smallint_range(256); int x = stack.pop_smallint_range(256);
auto r = td::RefInt256{true}; auto r = td::make_refint();
r.unique_write().set_pow2(x).add_tiny(-1).normalize(); r.unique_write().set_pow2(x).add_tiny(-1).normalize();
stack.push_int(r); stack.push_int(r);
} }
@ -301,19 +298,18 @@ void interpret_times_rshift(vm::Stack& stack, int round_mode) {
int z = stack.pop_smallint_range(256); int z = stack.pop_smallint_range(256);
auto y = stack.pop_int(); auto y = stack.pop_int();
auto x = stack.pop_int(); auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{0}; typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y).rshift(z, round_mode).normalize(); tmp.add_mul(*x, *y).rshift(z, round_mode).normalize();
auto q = td::RefInt256{true, tmp}; stack.push_int(td::make_refint(tmp));
stack.push_int(std::move(q));
} }
void interpret_lshift_div(vm::Stack& stack, int round_mode) { void interpret_lshift_div(vm::Stack& stack, int round_mode) {
int z = stack.pop_smallint_range(256); int z = stack.pop_smallint_range(256);
auto y = stack.pop_int(); auto y = stack.pop_int();
auto x = stack.pop_int(); auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{*x}; typename td::BigInt256::DoubleInt tmp{*x};
tmp <<= z; tmp <<= z;
auto q = td::RefInt256{true}; auto q = td::make_refint();
tmp.mod_div(*y, q.unique_write(), round_mode); tmp.mod_div(*y, q.unique_write(), round_mode);
q.unique_write().normalize(); q.unique_write().normalize();
stack.push_int(std::move(q)); stack.push_int(std::move(q));
@ -1932,7 +1928,7 @@ int parse_number(std::string s, td::RefInt256& num, td::RefInt256& denom, bool a
const char* str = s.c_str(); const char* str = s.c_str();
int len = (int)s.size(); int len = (int)s.size();
int frac = -1, base, *frac_ptr = allow_frac ? &frac : nullptr; int frac = -1, base, *frac_ptr = allow_frac ? &frac : nullptr;
num = td::RefInt256{true}; num = td::make_refint();
auto& x = num.unique_write(); auto& x = num.unique_write();
if (len >= 4 && str[0] == '-' && str[1] == '0' && (str[2] == 'x' || str[2] == 'b')) { if (len >= 4 && str[0] == '-' && str[1] == '0' && (str[2] == 'x' || str[2] == 'b')) {
if (str[2] == 'x') { if (str[2] == 'x') {
@ -1974,7 +1970,7 @@ int parse_number(std::string s, td::RefInt256& num, td::RefInt256& denom, bool a
if (frac < 0) { if (frac < 0) {
return 1; return 1;
} else { } else {
denom = td::RefInt256{true, 1}; denom = td::make_refint(1);
while (frac-- > 0) { while (frac-- > 0) {
if (!denom.unique_write().mul_tiny(base).normalize_bool()) { if (!denom.unique_write().mul_tiny(base).normalize_bool()) {
if (throw_error) { if (throw_error) {

View file

@ -138,7 +138,7 @@ void VarDescr::show(std::ostream& os, const char* name) const {
} }
void VarDescr::set_const(long long value) { void VarDescr::set_const(long long value) {
return set_const(td::RefInt256{true, value}); return set_const(td::make_refint(value));
} }
void VarDescr::set_const(td::RefInt256 value) { void VarDescr::set_const(td::RefInt256 value) {
@ -169,7 +169,7 @@ void VarDescr::set_const(td::RefInt256 value) {
} }
void VarDescr::set_const_nan() { void VarDescr::set_const_nan() {
set_const(td::RefInt256{true}); set_const(td::make_refint());
} }
void VarDescr::operator|=(const VarDescr& y) { void VarDescr::operator|=(const VarDescr& y) {

View file

@ -628,7 +628,7 @@ AsmOp compile_mod(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int r
if ((*y.int_const == 1 || *y.int_const == -1) && x.always_finite()) { if ((*y.int_const == 1 || *y.int_const == -1) && x.always_finite()) {
x.unused(); x.unused();
y.unused(); y.unused();
r.set_const(td::RefInt256{true, 0}); r.set_const(td::zero_refint());
return push_const(r.int_const); return push_const(r.int_const);
} }
int k = is_pos_pow2(y.int_const); int k = is_pos_pow2(y.int_const);

View file

@ -934,7 +934,7 @@ struct AsmOp {
void out_indent_nl(std::ostream& os, bool no_nl = false) const; void out_indent_nl(std::ostream& os, bool no_nl = false) const;
std::string to_string() const; std::string to_string() const;
void compute_gconst() { void compute_gconst() {
gconst = (is_custom() && (op == "PUSHNULL" || op == "NEWC")); gconst = (is_custom() && (op == "PUSHNULL" || op == "NEWC" || op == "NEWB" || op == "TRUE" || op == "FALSE"));
} }
bool is_nop() const { bool is_nop() const {
return t == a_none && op.empty(); return t == a_none && op.empty();
@ -975,6 +975,9 @@ struct AsmOp {
*y = b; *y = b;
return is_xchg(); return is_xchg();
} }
bool is_xchg_short() const {
return is_xchg() && (a <= 1 || b <= 1);
}
bool is_swap() const { bool is_swap() const {
return is_xchg(0, 1); return is_xchg(0, 1);
} }
@ -1265,10 +1268,14 @@ struct StackTransform {
} }
bool is_xchg(int i, int j) const; bool is_xchg(int i, int j) const;
bool is_xchg(int* i, int* j) const; bool is_xchg(int* i, int* j) const;
bool is_xchg_xchg(int i, int j, int k, int l) const;
bool is_xchg_xchg(int* i, int* j, int* k, int* l) const;
bool is_push(int i) const; bool is_push(int i) const;
bool is_push(int* i) const; bool is_push(int* i) const;
bool is_pop(int i) const; bool is_pop(int i) const;
bool is_pop(int* i) const; bool is_pop(int* i) const;
bool is_pop_pop(int i, int j) const;
bool is_pop_pop(int* i, int* j) const;
bool is_rot() const; bool is_rot() const;
bool is_rotrev() const; bool is_rotrev() const;
bool is_push_rot(int i) const; bool is_push_rot(int i) const;
@ -1407,8 +1414,10 @@ struct Optimizer {
bool is_2swap(); bool is_2swap();
bool is_2over(); bool is_2over();
bool is_xchg(int* i, int* j); bool is_xchg(int* i, int* j);
bool is_xchg_xchg(int* i, int* j, int* k, int* l);
bool is_push(int* i); bool is_push(int* i);
bool is_pop(int* i); bool is_pop(int* i);
bool is_pop_pop(int* i, int* j);
bool is_nop(); bool is_nop();
bool is_push_rot(int* i); bool is_push_rot(int* i);
bool is_push_rotrev(int* i); bool is_push_rotrev(int* i);

View file

@ -393,6 +393,13 @@ bool Optimizer::is_xchg(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_xchg(i, j) && ((*i < 16 && *j < 16) || (!*i && *j < 256)); }); return is_pred([i, j](const auto& t) { return t.is_xchg(i, j) && ((*i < 16 && *j < 16) || (!*i && *j < 256)); });
} }
bool Optimizer::is_xchg_xchg(int* i, int* j, int* k, int* l) {
return is_pred([i, j, k, l](const auto& t) {
return t.is_xchg_xchg(i, j, k, l) && (*i < 2 && *j < (*i ? 16 : 256) && *k < 2 && *l < (*k ? 16 : 256));
}) &&
(!(p_ == 2 && op_[0]->is_xchg(*i, *j) && op_[1]->is_xchg(*k, *l)));
}
bool Optimizer::is_push(int* i) { bool Optimizer::is_push(int* i) {
return is_pred([i](const auto& t) { return t.is_push(i) && *i < 256; }); return is_pred([i](const auto& t) { return t.is_push(i) && *i < 256; });
} }
@ -401,6 +408,10 @@ bool Optimizer::is_pop(int* i) {
return is_pred([i](const auto& t) { return t.is_pop(i) && *i < 256; }); return is_pred([i](const auto& t) { return t.is_pop(i) && *i < 256; });
} }
bool Optimizer::is_pop_pop(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_pop_pop(i, j) && *i < 256 && *j < 256; }, 3);
}
bool Optimizer::is_push_rot(int* i) { bool Optimizer::is_push_rot(int* i) {
return is_pred([i](const auto& t) { return t.is_push_rot(i) && *i < 16; }, 3); return is_pred([i](const auto& t) { return t.is_push_rot(i) && *i < 16; }, 3);
} }
@ -543,12 +554,13 @@ bool Optimizer::find_at_least(int pb) {
p_ = q_ = 0; p_ = q_ = 0;
pb_ = pb; pb_ = pb;
// show_stack_transforms(); // show_stack_transforms();
int i = -100, j = -100, k = -100, c = 0; int i, j, k, l, c;
return (is_push_const(&i, &c) && rewrite_push_const(i, c)) || (is_nop() && rewrite_nop()) || return (is_push_const(&i, &c) && rewrite_push_const(i, c)) || (is_nop() && rewrite_nop()) ||
(!(mode_ & 1) && is_const_rot(&c) && rewrite_const_rot(c)) || (!(mode_ & 1) && is_const_rot(&c) && rewrite_const_rot(c)) ||
(is_const_push_xchgs() && rewrite_const_push_xchgs()) || (is_const_pop(&c, &i) && rewrite_const_pop(c, i)) || (is_const_push_xchgs() && rewrite_const_push_xchgs()) || (is_const_pop(&c, &i) && rewrite_const_pop(c, i)) ||
(is_xchg(&i, &j) && rewrite(AsmOp::Xchg(i, j))) || (is_push(&i) && rewrite(AsmOp::Push(i))) || (is_xchg(&i, &j) && rewrite(AsmOp::Xchg(i, j))) || (is_push(&i) && rewrite(AsmOp::Push(i))) ||
(is_pop(&i) && rewrite(AsmOp::Pop(i))) || (is_pop(&i) && rewrite(AsmOp::Pop(i))) || (is_pop_pop(&i, &j) && rewrite(AsmOp::Pop(i), AsmOp::Pop(j))) ||
(is_xchg_xchg(&i, &j, &k, &l) && rewrite(AsmOp::Xchg(i, j), AsmOp::Xchg(k, l))) ||
(!(mode_ & 1) && (!(mode_ & 1) &&
((is_rot() && rewrite(AsmOp::Custom("ROT", 3, 3))) || (is_rotrev() && rewrite(AsmOp::Custom("-ROT", 3, 3))) || ((is_rot() && rewrite(AsmOp::Custom("ROT", 3, 3))) || (is_rotrev() && rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
(is_2dup() && rewrite(AsmOp::Custom("2DUP", 2, 4))) || (is_2dup() && rewrite(AsmOp::Custom("2DUP", 2, 4))) ||
@ -629,10 +641,9 @@ void optimize_code(AsmOpList& ops) {
for (auto it = ops.list_.rbegin(); it < ops.list_.rend(); ++it) { for (auto it = ops.list_.rbegin(); it < ops.list_.rend(); ++it) {
op_list = AsmOpCons::cons(std::make_unique<AsmOp>(std::move(*it)), std::move(op_list)); op_list = AsmOpCons::cons(std::make_unique<AsmOp>(std::move(*it)), std::move(op_list));
} }
op_list = optimize_code(std::move(op_list), 1); for (int mode : {1, 1, 1, 1, 0, 0, 0, 0}) {
op_list = optimize_code(std::move(op_list), 1); op_list = optimize_code(std::move(op_list), mode);
op_list = optimize_code(std::move(op_list), 0); }
op_list = optimize_code(std::move(op_list), 0);
ops.list_.clear(); ops.list_.clear();
while (op_list) { while (op_list) {
ops.list_.push_back(std::move(*(op_list->car))); ops.list_.push_back(std::move(*(op_list->car)));

View file

@ -401,6 +401,57 @@ bool StackTransform::is_xchg(int *i, int *j) const {
return true; return true;
} }
bool StackTransform::is_xchg_xchg(int i, int j, int k, int l) const {
if (is_valid() && !d && n <= 4 && (i | j | k | l) >= 0) {
StackTransform t;
return t.apply_xchg(i, j) && t.apply_xchg(k, l) && t <= *this;
} else {
return false;
}
}
bool StackTransform::is_xchg_xchg(int *i, int *j, int *k, int *l) const {
if (!is_valid() || d || n > 4 || !dp || !is_permutation()) {
return false;
}
if (!n) {
*i = *j = *k = *l = 0;
return true;
}
if (n <= 2) {
*k = *l = 0;
return is_xchg(i, j);
}
if (n == 3) {
// rotation: a -> b -> c -> a
int a = A[0].first;
int b = A[0].second;
int s = (b == A[2].first ? 2 : 1);
int c = A[s].second;
if (b != A[s].first || c != A[3 - s].first || a != A[3 - s].second) {
return false;
}
// implement as XCHG s(a),s(c) ; XCHG s(a),s(b)
*i = *k = a;
*j = c;
*l = b;
return is_xchg_xchg(*i, *j, *k, *l);
}
*i = A[0].first;
*j = A[0].second;
if (get(*j) != *i) {
return false;
}
for (int s = 1; s < 4; s++) {
if (A[s].first != *j) {
*k = A[s].first;
*l = A[s].second;
return get(*l) == *k && is_xchg_xchg(*i, *j, *k, *l);
}
}
return false;
}
bool StackTransform::is_push(int i) const { bool StackTransform::is_push(int i) const {
return is_valid() && d == -1 && n == 1 && A[0].first == -1 && A[0].second == i; return is_valid() && d == -1 && n == 1 && A[0].first == -1 && A[0].second == i;
} }
@ -418,6 +469,7 @@ bool StackTransform::is_push(int *i) const {
// 0 2 3 4 .. = pop1 // 0 2 3 4 .. = pop1
// 1 0 3 4 .. = pop2 // 1 0 3 4 .. = pop2
// 1 2 0 4 .. = pop3 // 1 2 0 4 .. = pop3
// POP s(i) : 1 2 ... i-1 0 i+1 ... ; d=1, n=1, {(i,0)}
bool StackTransform::is_pop(int i) const { bool StackTransform::is_pop(int i) const {
if (!is_valid() || d != 1 || n > 1 || i < 0) { if (!is_valid() || d != 1 || n > 1 || i < 0) {
return false; return false;
@ -443,6 +495,38 @@ bool StackTransform::is_pop(int *i) const {
return false; return false;
} }
// POP s(i) ; POP s(j) : 2 ... i-1 0 i+1 ... j 1 j+2 ... ; d=2, n=2, {(i,0),(j+1,1)} if i <> j+1
bool StackTransform::is_pop_pop(int i, int j) const {
if (is_valid() && d == 2 && n <= 2 && i >= 0 && j >= 0) {
StackTransform t;
return t.apply_pop(i) && t.apply_pop(j) && t <= *this;
} else {
return false;
}
}
bool StackTransform::is_pop_pop(int *i, int *j) const {
if (!is_valid() || d != 2 || n > 2) {
return false;
}
if (!n) {
*i = *j = 0; // 2DROP
} else if (n == 2) {
*i = A[0].first - A[0].second;
*j = A[1].first - A[1].second;
if (A[0].second > A[1].second) {
std::swap(*i, *j);
}
} else if (!A[0].second) {
*i = A[0].first;
*j = 0;
} else {
*i = 0;
*j = A[0].first - 1;
}
return is_pop_pop(*i, *j);
}
const StackTransform StackTransform::rot{2, 0, 1, 3}; const StackTransform StackTransform::rot{2, 0, 1, 3};
const StackTransform StackTransform::rot_rev{1, 2, 0, 3}; const StackTransform StackTransform::rot_rev{1, 2, 0, 3};
@ -519,10 +603,9 @@ bool StackTransform::is_xchg2(int *i, int *j) const {
if (*i < 0 || *j < 0) { if (*i < 0 || *j < 0) {
return false; return false;
} }
if (n != 3) { if (n == 2 && !*i) {
return is_xchg2(*i, *j); *j = *i; // XCHG s0,s1 = XCHG2 s0,s0
} } else if (n == 3 && *i) {
if (*i) {
// XCHG2 s(i),s(i) = XCHG s1,s(i) ; XCHG s0,s(i) : 0->1, 1->i // XCHG2 s(i),s(i) = XCHG s1,s(i) ; XCHG s0,s(i) : 0->1, 1->i
*j = *i; *j = *i;
} // XCHG2 s0,s(i) = XCHG s0,s1 ; XCHG s0,s(i) : 0->i, 1->0 } // XCHG2 s0,s(i) = XCHG s0,s1 ; XCHG s0,s(i) : 0->i, 1->0

View file

@ -0,0 +1,115 @@
#!/usr/bin/fift -s
"TonUtil.fif" include
"GetOpt.fif" include
{ show-options-help 1 halt } : usage
"dns-msg-body.boc" =: savefile
begin-options
" <auto-dns-addr> [-o<savefile-boc>] (add|update|prolong) <subdomain> <expire-in-sec> ... " +cr +tab
+"Creates the internal message body containing a request to automatic DNS smart contract <auto-dns-addr> created by new-auto-dns.fif, "
+"to be sent later with a suitable payment from a wallet to <auto-dns-addr>, and saves it into <savefile-boc> ('" savefile $+ +"' by default). "
+"The operation to be performed is one of" +cr +tab
+"add <subdomain> <expire-in-sec> { owner <smc-addr> | cat <cat-id> (smc <smc-addr> | next <next-resolver-smc-addr> | adnl <adnl-addr> | text <string>) }" +cr +tab
+"update <subdomain> <expire-in-sec> { owner <smc-addr> | cat <cat-id> (smc <smc-addr> | next <next-resolver-smc-addr> | adnl <adnl-addr> | text <string>) }" +cr +tab
+"prolong <subdomain> <expire-in-sec>"
disable-digit-options generic-help-setopt
"o" "--output" { =: savefile } short-long-option-arg
"Sets output file for generated initialization message ('" savefile $+ +"' by default)" option-help
"h" "--help" { usage } short-long-option
"Shows a help message" option-help
parse-options
$# 4 < ' usage if
4 :$1..n
$1 true parse-load-address =: bounce 2=: dest-addr
$2 dup =: main-op-name atom =: main-op
$3 dup =: subdomain $len 127 > abort"subdomain name too long"
$4 parse-int dup 30 1<< < { now + } if =: expire-at
{ $* @ dup null? { second $@ ! } { drop } cond } : @skip
{ $* @ null? } : @end?
{ $* @ uncons $* ! } : @next
{ @next drop } 4 times
main-op dup `add eq? over `update eq? or swap `prolong eq? or
{ "unknown main operation '" main-op-name $+ +"'; one of 'add', 'update' or 'prolong' expected" abort } ifnot
main-op `prolong eq? not =: need-params
$# 4 > need-params <> abort"extra parameters, or no parameters for chosen main operation"
variable Values dictnew Values !
// ( i c -- )
{ over 0= abort"category cannot be zero"
<b swap ref, swap Values @ 16 b>idict!+ not abort"duplicate category id"
Values !
} : register-value
{ @end? abort"category number expected" @next (number) 1 <> abort"category must be integer"
dup 16 fits not abort"category does not fit into 16 bit integer"
dup 0= abort"category must be non-zero"
} : parse-cat-num
{ @end? abort"smart contract address expected"
@next false parse-load-address drop
} : cl-parse-smc-addr
{ @end? abort"adnl address expected"
@next parse-adnl-addr
} : cl-parse-adnl-addr
{ <b x{9fd3} s, -rot Addr, 0 8 u, b> } : serialize-smc-addr
{ <b x{ba93} s, -rot Addr, b> } : serialize-next-resolver
{ <b x{ad01} s, swap 256 u, 0 8 u, b> } : serialize-adnl-addr
{ <b x{1eda01} s, over $len 8 u, swap $, b> } : serialize-text
{ @end? abort"subdomain record value expected" @next
dup "smc" $= { drop cl-parse-smc-addr serialize-smc-addr } {
dup "next" $= { drop cl-parse-smc-addr serialize-next-resolver } {
dup "adnl" $= { drop cl-parse-adnl-addr serialize-adnl-addr } {
dup "text" $= { drop @next serialize-text } {
"unknown record type "' swap $+ +"'" abort
} cond } cond } cond } cond
} : parse-value
{ @next dup "owner" $= { drop -2 cl-parse-smc-addr serialize-smc-addr } {
dup "cat" $= { drop parse-cat-num parse-value } {
"unknown action '" swap $+ +"'" abort
} cond } cond
register-value
} : parse-action
{ { @end? not } { parse-action } while } : parse-actions
parse-actions
// ( S -- S1 .. Sn n )
{ 1 swap { dup "." $pos dup 0>= } { $| 1 $| nip rot 1+ swap } while drop swap
} : split-by-dots
// ( S -- s )
{ dup $len dup 0= abort"subdomain cannot be empty" 126 > abort"subdomain too long"
dup 0 chr $pos 1+ abort"subdomain contains null characters"
split-by-dots <b { // ... S b
swap dup $len 0= abort"empty subdomain component" $, 0 8 u,
} rot times b> <s
} : subdomain>s
main-op ( _( `add 0x72656764 ) _( `update 0x75706464 ) _( `prolong 0x70726f6c ) )
assq-val not abort"unknown main operation"
=: op-id
."Automatic DNS smart contract address = " dest-addr 2dup .addr cr 6 .Addr cr
."Action: " main-op .l subdomain type space expire-at . cr
."Operation code: 0x" op-id 8 0X. cr
."Value: "
Values @ dup null? { drop ."(none)" } { <s csr. } cond cr
<b op-id 32 u, expire-at 32 u, Values @ dict, b> =: actions-builder
// create an internal message
now 32 << actions-builder hashu 32 1<<1- and + =: query_id
<b op-id 32 i, query_id 64 u,
subdomain subdomain>s tuck sbits 8 / 7 i, swap s,
main-op `prolong eq? { Values @ ref, } ifnot
expire-at 32 u, b>
dup ."Internal message body is: " <s csr. cr
2 boc+>B dup Bx. cr
."Query_id is " query_id dup . ."= 0x" X. cr
savefile tuck B>file
."(Saved to file " type .")" cr

View file

@ -107,22 +107,24 @@ global var query_info;
;; no iterating and deleting all to not put too much gas gc ;; no iterating and deleting all to not put too much gas gc
;; burden on any random specific user request ;; burden on any random specific user request
;; over time it will do the garbage collection required ;; over time it will do the garbage collection required
(int mkey, cell domain, int found?) = gc.udict_get_min_ref?(32 + 128); (int mkey, _, int found?) = gc.udict_get_min?(256);
while (found? & max_steps) { ;; no short circuit optimization, two nested ifs while (found? & max_steps) { ;; no short circuit optimization, two nested ifs
nhk = (mkey >> 128); nhk = (mkey >> (256 - 32));
if (nhk < n) { if (nhk < n) {
slice sdomain = domain.begin_parse(); int key = mkey % (1 << (256 - 32));
(_, slice val, _, found?) = dd.pfxdict_get?(1023, sdomain); (slice val, found?) = dd.udict_get?(256 - 32, key);
if (found?) { if (found?) {
int exp = val.preload_uint(32); int exp = val.preload_uint(32);
if (exp <= n) { if (exp <= n) {
dd~pfxdict_delete?(1023, sdomain); dd~udict_delete?(256 - 32, key);
} }
} }
gc~udict_delete?(32 + 128, mkey); gc~udict_delete?(256, mkey);
(mkey, domain, found?) = gc.udict_get_min_ref?(32 + 128); (mkey, _, found?) = gc.udict_get_min?(256);
nhk = (found? ? mkey >> 32 : 0xffffffff); nhk = (found? ? mkey >> (256 - 32) : 0xffffffff);
max_steps -= 1; max_steps -= 1;
} else {
found? = false;
} }
} }
store_data(ctl, dd, gc, prices, nhk, n); store_data(ctl, dd, gc, prices, nhk, n);
@ -134,16 +136,15 @@ int calcprice_internal(slice domain, cell data, ppc, ppb) inline_ref { ;; only f
return ppc * (refs + 2) + ppb * bits; return ppc * (refs + 2) + ppb * bits;
} }
int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref { int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int strict) inline_ref {
if (cat_table.null?()) { ;; domain not found: return notf | 2^31 if (strict & cat_table.null?()) { ;; domain not found: return notf | 2^31
return 0xee6f7466; return 0xee6f7466;
} }
cell cown = cat_table.idict_get_ref(16, -2); if (owner_info.null?()) { ;; no owner on this domain: no-2 (in strict mode), ok else
if (cown.null?()) { ;; no owner on this domain: no-2 return strict & 0xee6f2d32;
return 0xee6f2d32;
} }
var ERR_BAD2 = 0xe2616432; var ERR_BAD2 = 0xe2616432;
slice sown = cown.begin_parse(); slice sown = owner_info.begin_parse();
if (sown.slice_bits() < 16 + 3 + 8 + 256) { ;; bad owner record: bad2 if (sown.slice_bits() < 16 + 3 + 8 + 256) { ;; bad owner record: bad2
return ERR_BAD2; return ERR_BAD2;
} }
@ -272,38 +273,38 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
return send_error(0xee6f5c30); return send_error(0xee6f5c30);
} }
int zeros = 0;
slice cdomain = domain;
repeat (cdomain.slice_bits() ^>> 3) {
int c = cdomain~load_uint(8);
zeros -= (c == 0);
}
;; if (zeros != 1) { ;; too much zero chars (overflow): ov\0
;; return send_error(0xef765c30); }
domain = begin_cell().store_uint(zeros, 7).store_slice(domain).end_cell().begin_parse();
(slice pfx, slice val, slice tail, int found?) = domdata.pfxdict_get?(1023, domain);
int n = now(); int n = now();
cell cat_table = null(); cell cat_table = cell owner_info = null();
int exp = 0; int key = int exp = int zeros = 0;
slice tail = domain;
repeat (tail.slice_bits() ^>> 3) {
cat_table = null();
int z = (tail~load_uint(8) == 0);
zeros -= z;
if (z) {
key = (string_hash(domain.skip_last_bits(tail.slice_bits())) >> 32);
var (val, found?) = domdata.udict_get?(256 - 32, key);
if (found?) { if (found?) {
exp = val~load_uint(32); exp = val~load_uint(32);
if (n > exp) { ;; expired domains behave as not registered if (exp >= n) { ;; entry not expired
found? = false; cell cat_table = val~load_ref();
} else { val.end_parse();
cat_table = val.preload_ref(); var (cown, ok) = cat_table.idict_get_ref?(16, -2);
if (ok) {
owner_info = cown;
} }
} }
}
}
}
if (zeros > 4) { ;; too much zero chars (overflow): ov\0
return send_error(0xef765c30);
}
;; ########################################################################## ;; ##########################################################################
int err = 0; int err = check_owner(cat_table, owner_info, src_wc, src_addr, qt != 1);
if (qt != 1) { ;; not a "register", check that domain exists and is controlled by correct smc
err = check_owner(cat_table, src_wc, src_addr);
}
if (err) { if (err) {
return send_error(err); return send_error(err);
} }
@ -317,8 +318,14 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
data = in_msg~load_ref(); data = in_msg~load_ref();
;; basic integrity check of (client-provided) dictionary ;; basic integrity check of (client-provided) dictionary
ifnot (data.dict_empty?()) { ;; 1000 gas! ifnot (data.dict_empty?()) { ;; 1000 gas!
(_, _, int minok) = idict_get_min?(data, 16); var (oinfo, ok) = data.idict_get_ref?(16, -2);
(_, _, int maxok) = idict_get_max?(data, 16); if (ok) {
var cs = oinfo.begin_parse();
throw_unless(31, cs.slice_bits() >= 16 + 3 + 8 + 256);
throw_unless(31, cs.preload_uint(19) == 0x9fd3 * 8 + 4);
}
(_, _, int minok) = data.idict_get_min?(16);
(_, _, int maxok) = data.idict_get_max?(16);
throw_unless(31, minok & maxok); throw_unless(31, minok & maxok);
} }
} else { } else {
@ -345,17 +352,12 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
if (exp > n + stdper) { ;; does not expire soon, cannot prolong if (exp > n + stdper) { ;; does not expire soon, cannot prolong
return send_error(0xf365726f); return send_error(0xf365726f);
} }
slice value = begin_cell().store_uint(exp + stdper, 32).store_ref(data).end_cell().begin_parse(); domdata~udict_set_builder(256 - 32, key, begin_cell().store_uint(exp + stdper, 32).store_ref(data));
ifnot (domdata~pfxdict_set?(1023, domain, value)) { ;; Set ERR | 2^31 int gckeyO = (exp << (256 - 32)) + key;
return send_error(0xf3657272); int gckeyN = gckeyO + (stdper << (256 - 32));
} gc~udict_delete?(256, gckeyO); ;; delete old gc entry, add new
gc~udict_set_builder(256, gckeyN, begin_cell());
int sh_low = domain.slice_hash() & ((1 << 128) - 1);
int gckeyO = (exp << 128) + sh_low;
int gckeyN = gckeyO + (stdper << 128);
gc~udict_delete?(32 + 128, gckeyO); ;; delete old gc entry, add new
gc~udict_set_ref(32 + 128, gckeyN, begin_cell().store_slice(domain).end_cell());
housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1); housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1);
return send_ok(price); return send_ok(price);
@ -363,29 +365,22 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
;; ########################################################################## ;; ##########################################################################
if (qt == 1) { ;; 0x72656764 -> regd | register domain if (qt == 1) { ;; 0x72656764 -> regd | register domain
if (found?) { ;; domain already exists: return alre | 2^31 ifnot (cat_table.null?()) { ;; domain already exists: return alre | 2^31
return send_error(0xe16c7265); return send_error(0xe16c7265);
} }
int expires_at = n + stdper; int expires_at = n + stdper;
slice value = begin_cell().store_uint(expires_at, 32).store_ref(data).end_cell().begin_parse(); domdata~udict_set_builder(256 - 32, key, begin_cell().store_uint(expires_at, 32).store_ref(data));
ifnot (domdata~pfxdict_set?(1023, domain, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272); int gckey = (expires_at << (256 - 32)) | key;
} gc~udict_set_builder(256, gckey, begin_cell());
int gckey = (expires_at << 128) | (domain.slice_hash() & ((1 << 128) - 1));
gc~udict_set_ref(32 + 128, gckey, begin_cell().store_slice(domain).end_cell());
;; using ref requires additional cell, but using value (DICTUSET) may
;; cause problems with very long domains or complex dictionaries
housekeeping(ctl, domdata, gc, prices, min(nhk, expires_at), lhk, 1); housekeeping(ctl, domdata, gc, prices, min(nhk, expires_at), lhk, 1);
return send_ok(price); return send_ok(price);
} }
;; ########################################################################## ;; ##########################################################################
if (qt == 4) { ;; 0x75706464 -> updd | update domain (data) if (qt == 4) { ;; 0x75706464 -> updd | update domain (data)
slice value = begin_cell().store_uint(exp, 32).store_ref(data).end_cell().begin_parse(); domdata~udict_set_builder(256 - 32, key, begin_cell().store_uint(exp, 32).store_ref(data));
ifnot (domdata~pfxdict_set?(1023, domain, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1); housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1);
return send_ok(price); return send_ok(price);
} }
@ -412,11 +407,11 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
;;===========================================================================;; ;;===========================================================================;;
(int, cell, int, slice) dnsdictlookup(slice domain, int nowtime) inline_ref { (int, cell, int, slice) dnsdictlookup(slice domain, int nowtime) inline_ref {
int bits = domain.slice_bits(); (int bits, int refs) = domain.slice_bits_refs();
throw_if(30, refs | (bits & 7)); ;; malformed input (~ 8n-bit)
ifnot (bits) { ifnot (bits) {
return (0, null(), 0, null()); ;; zero-length input return (0, null(), 0, null()); ;; zero-length input
} }
throw_if(30, bits & 7); ;; malformed input (~ 8n-bit)
int domain_last_byte = domain.slice_last(8).preload_uint(8); int domain_last_byte = domain.slice_last(8).preload_uint(8);
if (domain_last_byte) { if (domain_last_byte) {
@ -427,39 +422,31 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
if (bits == 8) { if (bits == 8) {
return (0, null(), 0, null()); ;; zero-length input, but with zero byte return (0, null(), 0, null()); ;; zero-length input, but with zero byte
} }
(_, cell root) = get_data().begin_parse().load_dict(); var ds = get_data().begin_parse();
(_, cell root) = (ds~load_ref(), ds~load_dict());
slice sd_tail = domain;
int zeros = 0;
repeat (bits >> 3) {
int c = sd_tail~load_uint(8);
zeros -= (c == 0);
}
;; can't move these declarations lower, will cause errors!
slice tail = slice pfx = sd_tail;
slice val = null(); slice val = null();
int exp = 0; int tail_bits = -1;
slice tail = domain;
do { repeat (bits >> 3) {
slice pfxname = begin_cell().store_uint(zeros, 7) if (tail~load_uint(8) == 0) {
.store_slice(domain).end_cell().begin_parse(); var key = (string_hash(domain.skip_last_bits(tail.slice_bits())) >> 32);
(pfx, val, tail, int succ) = root.pfxdict_get?(1023, pfxname); var (v, found?) = root.udict_get?(256 - 32, key);
if (succ) { if (found?) {
int exp = val~load_uint(32); if (v.preload_uint(32) >= nowtime) { ;; entry not expired
if (nowtime > exp) { ;; entry expired, skip val = v;
succ = false; tail_bits = tail.slice_bits();
}
} }
} }
zeros = succ ^ (zeros - 1); ;; break on success
} until (zeros <= 0);
ifnot (zeros) {
return (0, null(), 0, null()); ;; failed to find entry in prefix dictionary
} }
zeros = - zeros; if (val.null?()) {
return (exp, val.preload_ref(), tail.slice_empty?(), pfx); return (0, null(), 0, null()); ;; failed to find entry in subdomain dictionary
}
return (val~load_uint(32), val~load_ref(), tail_bits == 0, domain.skip_last_bits(tail_bits));
} }
;;8m dns-record-value ;;8m dns-record-value
@ -472,12 +459,12 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
category = -1; category = -1;
} }
int pfx_bits = pfx.slice_bits() - 7; int pfx_bits = pfx.slice_bits();
;; pfx.slice_bits() will contain 8m, where m is number of bytes in subdomain ;; pfx.slice_bits() will contain 8m, where m is number of bytes in subdomain
;; COUNTING the zero byte (if structurally correct: no multiple-ZB keys) ;; COUNTING the zero byte (if structurally correct: no multiple-ZB keys)
;; which corresponds to 8m, m=one plus the number of bytes in the subdomain found) ;; which corresponds to 8m, m=one plus the number of bytes in the subdomain found)
if (category == 0) { ifnot (category) {
return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0 return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0
} else { } else {
cell cat_found = cat_table.idict_get_ref(16, category); cell cat_found = cat_table.idict_get_ref(16, category);

View file

@ -94,8 +94,8 @@ file-base +"-dns" +contractid +".addr" load-address
// ( b V -- b' ) // ( b V -- b' )
{ dup first { dup first
dup `smc eq? { drop untriple 2swap drop x{9fd3} s, -rot Addr, 0 8 u, } { dup `smc eq? { drop untriple 2swap drop x{9fd3} s, -rot Addr, 0 8 u, } {
dup `next eq? { drop untriple 2swap drop x{ba93} s, -rot Addr, 0 8 u, } { dup `next eq? { drop untriple 2swap drop x{ba93} s, -rot Addr, } {
dup `adnl eq? { drop second swap x{ad01} s, swap 256 u, } { dup `adnl eq? { drop second swap x{ad01} s, swap 256 u, 0 8 u, } {
dup `text eq? { drop second swap x{1eda01} s, over $len 8 u, swap $, } { dup `text eq? { drop second swap x{1eda01} s, over $len 8 u, swap $, } {
abort"unknown value type" abort"unknown value type"
} cond } cond } cond } cond } cond } cond } cond } cond

View file

@ -6,6 +6,11 @@ forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS"; forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR"; forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR"; tuple cdr(tuple list) asm "CDR";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR"; forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR"; forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE"; forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
@ -22,6 +27,7 @@ forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND"; forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD"; forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL"; forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW"; int now() asm "NOW";
slice my_address() asm "MYADDR"; slice my_address() asm "MYADDR";
@ -115,7 +121,8 @@ cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value inde
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; (cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF"; cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
cell udict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETOPTREF"; (cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF"; (cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF"; (cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL"; (cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";

View file

@ -146,6 +146,23 @@ td::Result<DnsInterface::EntryData> DnsInterface::EntryData::from_cellslice(vm::
return td::Status::Error("Unknown entry data"); return td::Status::Error("Unknown entry data");
} }
SmartContract::Args DnsInterface::resolve_args_raw(td::Slice encoded_name, td::int16 category) {
SmartContract::Args res;
res.set_method_id("dnsresolve");
res.set_stack(
{vm::load_cell_slice_ref(vm::CellBuilder().store_bytes(encoded_name).finalize()), td::make_refint(category)});
return res;
}
td::Result<SmartContract::Args> DnsInterface::resolve_args(td::Slice name, td::int32 category_big) {
TRY_RESULT(category, td::narrow_cast_safe<td::int16>(category_big));
if (name.size() > get_default_max_name_size()) {
return td::Status::Error("Name is too long");
}
auto encoded_name = encode_name(name);
return resolve_args_raw(encoded_name, category);
}
td::Result<std::vector<DnsInterface::Entry>> DnsInterface::resolve(td::Slice name, td::int32 category) const { td::Result<std::vector<DnsInterface::Entry>> DnsInterface::resolve(td::Slice name, td::int32 category) const {
TRY_RESULT(raw_entries, resolve_raw(name, category)); TRY_RESULT(raw_entries, resolve_raw(name, category));
std::vector<Entry> entries; std::vector<Entry> entries;
@ -375,7 +392,7 @@ td::Ref<vm::Cell> ManualDns::create_init_data_fast(const td::Ed25519::PublicKey&
} }
size_t ManualDns::get_max_name_size() const { size_t ManualDns::get_max_name_size() const {
return 128; return get_default_max_name_size();
} }
td::Result<std::vector<ManualDns::RawEntry>> ManualDns::resolve_raw(td::Slice name, td::int32 category_big) const { td::Result<std::vector<ManualDns::RawEntry>> ManualDns::resolve_raw(td::Slice name, td::int32 category_big) const {
@ -388,9 +405,7 @@ td::Result<std::vector<ManualDns::RawEntry>> ManualDns::resolve_raw_or_throw(td:
return td::Status::Error("Name is too long"); return td::Status::Error("Name is too long");
} }
auto encoded_name = encode_name(name); auto encoded_name = encode_name(name);
auto res = run_get_method( auto res = run_get_method(resolve_args_raw(encoded_name, category));
"dnsresolve",
{vm::load_cell_slice_ref(vm::CellBuilder().store_bytes(encoded_name).finalize()), td::make_refint(category)});
if (!res.success) { if (!res.success) {
return td::Status::Error("get method failed"); return td::Status::Error("get method failed");
} }
@ -471,7 +486,7 @@ td::Result<td::Ref<vm::Cell>> ManualDns::create_update_query(td::Ed25519::Privat
return sign(pk, std::move(prepared)); return sign(pk, std::move(prepared));
} }
std::string ManualDns::encode_name(td::Slice name) { std::string DnsInterface::encode_name(td::Slice name) {
std::string res; std::string res;
while (!name.empty()) { while (!name.empty()) {
auto pos = name.rfind('.'); auto pos = name.rfind('.');
@ -487,7 +502,7 @@ std::string ManualDns::encode_name(td::Slice name) {
return res; return res;
} }
std::string ManualDns::decode_name(td::Slice name) { std::string DnsInterface::decode_name(td::Slice name) {
std::string res; std::string res;
if (!name.empty() && name.back() == 0) { if (!name.empty() && name.back() == 0) {
name.remove_suffix(1); name.remove_suffix(1);

View file

@ -165,6 +165,15 @@ class DnsInterface {
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const = 0; td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const = 0;
td::Result<std::vector<Entry>> resolve(td::Slice name, td::int32 category) const; td::Result<std::vector<Entry>> resolve(td::Slice name, td::int32 category) const;
static std::string encode_name(td::Slice name);
static std::string decode_name(td::Slice name);
static size_t get_default_max_name_size() {
return 128;
}
static SmartContract::Args resolve_args_raw(td::Slice encoded_name, td::int16 category);
static td::Result<SmartContract::Args> resolve_args(td::Slice name, td::int32 category);
}; };
class ManualDns : public ton::SmartContract, public DnsInterface { class ManualDns : public ton::SmartContract, public DnsInterface {
@ -223,9 +232,6 @@ class ManualDns : public ton::SmartContract, public DnsInterface {
td::Ed25519::PrivateKey& pk, td::Span<Action> actions, td::Ed25519::PrivateKey& pk, td::Span<Action> actions,
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const override; td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const override;
static std::string encode_name(td::Slice name);
static std::string decode_name(td::Slice name);
template <class ActionT> template <class ActionT>
struct CombinedActions { struct CombinedActions {
std::string name; std::string name;

View file

@ -1,3 +1,21 @@
/*
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 2019-2020 Telegram Systems LLP
*/
#include "MultisigWallet.h" #include "MultisigWallet.h"
#include "SmartContractCode.h" #include "SmartContractCode.h"

View file

@ -1,3 +1,21 @@
/*
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 2019-2020 Telegram Systems LLP
*/
#pragma once #pragma once
#include "vm/cells.h" #include "vm/cells.h"

View file

@ -24,19 +24,21 @@
#include "block/block-auto.h" #include "block/block-auto.h"
#include "vm/cellslice.h" #include "vm/cellslice.h"
#include "vm/cp0.h" #include "vm/cp0.h"
#include "vm/memo.h"
#include "vm/vm.h" #include "vm/vm.h"
#include "td/utils/crypto.h" #include "td/utils/crypto.h"
namespace ton { namespace ton {
namespace { namespace {
td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) { td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) {
td::Ref<vm::Stack> stack_ref{true}; td::Ref<vm::Stack> stack_ref{true};
td::RefInt256 acc_addr{true}; td::RefInt256 acc_addr{true};
//CHECK(acc_addr.write().import_bits(account.addr.cbits(), 256)); //CHECK(acc_addr.write().import_bits(account.addr.cbits(), 256));
vm::Stack& stack = stack_ref.write(); vm::Stack& stack = stack_ref.write();
stack.push_int(td::RefInt256{true, 10000000000}); stack.push_int(td::make_refint(10000000000));
stack.push_int(td::RefInt256{true, 10000000000}); stack.push_int(td::make_refint(10000000000));
stack.push_cell(vm::CellBuilder().finalize()); stack.push_cell(vm::CellBuilder().finalize());
stack.push_cellslice(std::move(body)); stack.push_cellslice(std::move(body));
return stack_ref; return stack_ref;
@ -90,6 +92,11 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
} }
SmartContract::Answer res; SmartContract::Answer res;
if (GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(DEBUG)) {
std::ostringstream os;
stack->dump(os, 2);
LOG(DEBUG) << "VM stack:\n" << os.str();
}
vm::VmState vm{state.code, std::move(stack), gas, 1, state.data, log}; vm::VmState vm{state.code, std::move(stack), gas, 1, state.data, log};
vm.set_c7(std::move(c7)); vm.set_c7(std::move(c7));
vm.set_chksig_always_succeed(ignore_chksig); vm.set_chksig_always_succeed(ignore_chksig);
@ -124,6 +131,21 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
} }
} // namespace } // namespace
td::Result<td::BufferSlice> SmartContract::Args::get_serialized_stack() {
if (!stack) {
return td::Status::Error("Args has no stack");
}
vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls
vm::VmStateInterface::Guard guard(&fstate);
// serialize parameters
vm::CellBuilder cb;
td::Ref<vm::Cell> cell;
if (!(stack.value()->serialize(cb) && cb.finalize_to(cell))) {
return td::Status::Error("Cannot serialize stack in args");
}
return vm::std_boc_serialize(std::move(cell));
}
td::Ref<vm::CellSlice> SmartContract::empty_slice() { td::Ref<vm::CellSlice> SmartContract::empty_slice() {
return vm::load_cell_slice_ref(vm::CellBuilder().finalize()); return vm::load_cell_slice_ref(vm::CellBuilder().finalize());
} }

View file

@ -90,6 +90,14 @@ class SmartContract : public td::CntObject {
this->ignore_chksig = ignore_chksig; this->ignore_chksig = ignore_chksig;
return std::move(*this); return std::move(*this);
} }
td::Result<td::int32> get_method_id() const {
if (!method_id) {
return td::Status::Error("Args has no method id");
}
return method_id.value();
}
td::Result<td::BufferSlice> get_serialized_stack();
}; };
Answer run_method(Args args = {}); Answer run_method(Args args = {});

View file

@ -25,12 +25,12 @@
namespace ton { namespace ton {
namespace { namespace {
constexpr static int WALLET_REVISION = 2; // WALLET_REVISION = 2;
constexpr static int WALLET2_REVISION = 2; // WALLET2_REVISION = 2;
constexpr static int WALLET3_REVISION = 2; // WALLET3_REVISION = 2;
constexpr static int HIGHLOAD_WALLET_REVISION = 2; // HIGHLOAD_WALLET_REVISION = 2;
constexpr static int HIGHLOAD_WALLET2_REVISION = 2; // HIGHLOAD_WALLET2_REVISION = 2;
constexpr static int DNS_REVISION = 1; // DNS_REVISION = 1;
const auto& get_map() { const auto& get_map() {
static auto map = [] { static auto map = [] {
std::map<std::string, td::Ref<vm::Cell>, std::less<>> map; std::map<std::string, td::Ref<vm::Cell>, std::less<>> map;

View file

@ -31,8 +31,11 @@ class WalletInterface {
struct Gift { struct Gift {
block::StdAddress destination; block::StdAddress destination;
td::int64 gramms; td::int64 gramms;
bool is_encrypted{false}; bool is_encrypted{false};
std::string message; std::string message;
td::Ref<vm::Cell> body;
}; };
virtual ~WalletInterface() { virtual ~WalletInterface() {
@ -50,6 +53,13 @@ class WalletInterface {
return make_a_gift_message(private_key, valid_until, {}); return make_a_gift_message(private_key, valid_until, {});
} }
static void store_gift_message(vm::CellBuilder &cb, const Gift &gift) { 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);
return;
}
if (gift.is_encrypted) { if (gift.is_encrypted) {
cb.store_long(1, 32); cb.store_long(1, 32);
} else { } else {

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "tlbc-gen-cpp.h" #include "tlbc-gen-cpp.h"
#include "td/utils/bits.h" #include "td/utils/bits.h"
@ -94,6 +94,7 @@ void init_forbidden_cpp_idents() {
l.insert("get_size"); l.insert("get_size");
l.insert("pack"); l.insert("pack");
l.insert("unpack"); l.insert("unpack");
l.insert("ops");
l.insert("cs"); l.insert("cs");
l.insert("cb"); l.insert("cb");
l.insert("cell_ref"); l.insert("cell_ref");
@ -2014,7 +2015,7 @@ void CppTypeCode::generate_skip_field(const Constructor& constr, const Field& fi
output_cpp_expr(ss, expr, 100, true); output_cpp_expr(ss, expr, 100, true);
ss << '.'; ss << '.';
} }
ss << (validating ? "validate_skip(cs, weak" : "skip(cs"); ss << (validating ? "validate_skip(ops, cs, weak" : "skip(cs");
output_negative_type_arguments(ss, expr); output_negative_type_arguments(ss, expr);
ss << ")"; ss << ")";
actions += Action{std::move(ss)}; actions += Action{std::move(ss)};
@ -2054,7 +2055,7 @@ void CppTypeCode::generate_skip_field(const Constructor& constr, const Field& fi
output_cpp_expr(ss, expr, 100); output_cpp_expr(ss, expr, 100);
ss << '.'; ss << '.';
} }
ss << (validating ? "validate_skip(cs, weak)" : "skip(cs)") << tail; ss << (validating ? "validate_skip(ops, cs, weak)" : "skip(cs)") << tail;
actions += Action{std::move(ss)}; actions += Action{std::move(ss)};
return; return;
} }
@ -2074,7 +2075,7 @@ void CppTypeCode::generate_skip_field(const Constructor& constr, const Field& fi
output_cpp_expr(ss, expr, 100); output_cpp_expr(ss, expr, 100);
ss << '.'; ss << '.';
} }
ss << "validate_skip_ref(cs, weak)" << tail; ss << "validate_skip_ref(ops, cs, weak)" << tail;
actions += Action{ss.str()}; actions += Action{ss.str()};
} }
@ -2101,8 +2102,8 @@ void CppTypeCode::generate_skip_cons_method(std::ostream& os, std::string nl, in
void CppTypeCode::generate_skip_method(std::ostream& os, int options) { void CppTypeCode::generate_skip_method(std::ostream& os, int options) {
bool validate = options & 1; bool validate = options & 1;
bool ret_ext = options & 2; bool ret_ext = options & 2;
os << "\nbool " << cpp_type_class_name << "::" << (validate ? "validate_" : "") << "skip(vm::CellSlice& cs" os << "\nbool " << cpp_type_class_name
<< (validate ? ", bool weak" : ""); << "::" << (validate ? "validate_skip(int* ops, vm::CellSlice& cs, bool weak" : "skip(vm::CellSlice& cs");
if (ret_ext) { if (ret_ext) {
os << skip_extra_args; os << skip_extra_args;
} }
@ -2470,7 +2471,7 @@ void CppTypeCode::generate_unpack_field(const CppTypeCode::ConsField& fi, const
output_cpp_expr(ss, expr, 100, true); output_cpp_expr(ss, expr, 100, true);
ss << '.'; ss << '.';
} }
ss << (validating ? "validate_fetch_to(cs, weak, " : "fetch_to(cs, ") << field_vars.at(i); ss << (validating ? "validate_fetch_to(ops, cs, weak, " : "fetch_to(cs, ") << field_vars.at(i);
output_negative_type_arguments(ss, expr); output_negative_type_arguments(ss, expr);
ss << ")"; ss << ")";
actions += Action{std::move(ss)}; actions += Action{std::move(ss)};
@ -2514,8 +2515,8 @@ void CppTypeCode::generate_unpack_field(const CppTypeCode::ConsField& fi, const
output_cpp_expr(ss, expr, 100); output_cpp_expr(ss, expr, 100);
ss << '.'; ss << '.';
} }
ss << (validating ? "validate_" : "") << "fetch_" << (cvt == ct_enum ? "enum_" : "") << "to(cs, " ss << (validating ? "validate_" : "") << "fetch_" << (cvt == ct_enum ? "enum_" : "")
<< (validating ? "weak, " : "") << field_vars.at(i) << ")" << tail; << (validating ? "to(ops, cs, weak, " : "to(cs, ") << field_vars.at(i) << ")" << tail;
field_var_set[i] = true; field_var_set[i] = true;
actions += Action{std::move(ss)}; actions += Action{std::move(ss)};
return; return;
@ -2540,7 +2541,7 @@ void CppTypeCode::generate_unpack_field(const CppTypeCode::ConsField& fi, const
output_cpp_expr(ss, expr, 100); output_cpp_expr(ss, expr, 100);
ss << '.'; ss << '.';
} }
ss << "validate_ref(" << field_vars.at(i) << "))" << tail; ss << "validate_ref(ops, " << field_vars.at(i) << "))" << tail;
actions += Action{ss.str()}; actions += Action{ss.str()};
} }
@ -2559,7 +2560,11 @@ void CppTypeCode::generate_unpack_method(std::ostream& os, CppTypeCode::ConsReco
<< "\n auto cs = load_cell_slice(std::move(cell_ref));" << "\n auto cs = load_cell_slice(std::move(cell_ref));"
<< "\n return " << (options & 1 ? "validate_" : "") << "unpack"; << "\n return " << (options & 1 ? "validate_" : "") << "unpack";
if (!(options & 8)) { if (!(options & 8)) {
os << "(cs, data"; os << "(";
if (options & 1) {
os << "ops, ";
}
os << "cs, data";
} else { } else {
os << "_" << cons_enum_name.at(rec.cons_idx) << "(cs"; os << "_" << cons_enum_name.at(rec.cons_idx) << "(cs";
for (const auto& f : rec.cpp_fields) { for (const auto& f : rec.cpp_fields) {
@ -2773,7 +2778,7 @@ void CppTypeCode::generate_pack_field(const CppTypeCode::ConsField& fi, const Co
output_cpp_expr(ss, expr, 100); output_cpp_expr(ss, expr, 100);
ss << '.'; ss << '.';
} }
ss << "validate_ref(" << field_vars.at(i) << "))" << tail; ss << "validate_ref(ops, " << field_vars.at(i) << "))" << tail;
actions += Action{ss.str()}; actions += Action{ss.str()};
} }
@ -3093,7 +3098,7 @@ void CppTypeCode::generate_header(std::ostream& os, int options) {
if (ret_params) { if (ret_params) {
os << " bool skip(vm::CellSlice& cs" << skip_extra_args << ") const;\n"; os << " bool skip(vm::CellSlice& cs" << skip_extra_args << ") const;\n";
} }
os << " bool validate_skip(vm::CellSlice& cs, bool weak = false) const override"; os << " bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override";
if (!inline_validate_skip) { if (!inline_validate_skip) {
os << ";\n"; os << ";\n";
} else if (sz) { } else if (sz) {
@ -3102,7 +3107,7 @@ void CppTypeCode::generate_header(std::ostream& os, int options) {
os << " {\n return true;\n }\n"; os << " {\n return true;\n }\n";
} }
if (ret_params) { if (ret_params) {
os << " bool validate_skip(vm::CellSlice& cs, bool weak" << skip_extra_args << ") const;\n"; os << " bool validate_skip(int *ops, vm::CellSlice& cs, bool weak" << skip_extra_args << ") const;\n";
os << " bool fetch_to(vm::CellSlice& cs, Ref<vm::CellSlice>& res" << skip_extra_args << ") const;\n"; os << " bool fetch_to(vm::CellSlice& cs, Ref<vm::CellSlice>& res" << skip_extra_args << ") const;\n";
} }
if (type.is_simple_enum) { if (type.is_simple_enum) {

View file

@ -114,38 +114,43 @@ bool TupleT::skip(vm::CellSlice& cs) const {
return !i; return !i;
} }
bool TupleT::validate_skip(vm::CellSlice& cs, bool weak) const { bool TupleT::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int i = n; int i = n;
for (; i > 0; --i) { for (; i > 0; --i) {
if (!X.validate_skip(cs, weak)) { if (!X.validate_skip(ops, cs, weak)) {
break; break;
} }
} }
return !i; return !i;
} }
bool TLB::validate_ref_internal(Ref<vm::Cell> cell_ref, bool weak) const { bool TLB::validate_ref_internal(int* ops, Ref<vm::Cell> cell_ref, bool weak) const {
if (ops && --*ops < 0) {
return false;
}
bool is_special; bool is_special;
auto cs = load_cell_slice_special(std::move(cell_ref), is_special); auto cs = load_cell_slice_special(std::move(cell_ref), is_special);
return always_special() ? is_special : (is_special ? weak : (validate_skip(cs) && cs.empty_ext())); return always_special() ? is_special : (is_special ? weak : (validate_skip(ops, cs) && cs.empty_ext()));
} }
bool TLB::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const { bool TLB::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
pp.open("raw@"); pp.open("raw@");
pp << *this << ' '; pp << *this << ' ';
vm::CellSlice cs_copy{cs}; vm::CellSlice cs_copy{cs};
if (!validate_skip(cs) || !cs_copy.cut_tail(cs)) { int size_limit = pp.limit;
if (!validate_skip(&size_limit, cs) || !cs_copy.cut_tail(cs)) {
return pp.fail("invalid value"); return pp.fail("invalid value");
} }
pp.raw_nl(); pp.raw_nl();
return cs_copy.print_rec(pp.os, &pp.limit, pp.indent) && pp.mkindent() && pp.close(); return (cs_copy.print_rec(pp.os, &pp.limit, pp.indent) && pp.mkindent() && pp.close()) ||
pp.fail("raw value too long");
} }
bool TLB::print_special(PrettyPrinter& pp, vm::CellSlice& cs) const { bool TLB::print_special(PrettyPrinter& pp, vm::CellSlice& cs) const {
pp.open("raw@"); pp.open("raw@");
pp << *this << ' '; pp << *this << ' ';
pp.raw_nl(); pp.raw_nl();
return cs.print_rec(pp.os, &pp.limit, pp.indent) && pp.mkindent() && pp.close(); return (cs.print_rec(pp.os, &pp.limit, pp.indent) && pp.mkindent() && pp.close()) || pp.fail("raw value too long");
} }
bool TLB::print_ref(PrettyPrinter& pp, Ref<vm::Cell> cell_ref) const { bool TLB::print_ref(PrettyPrinter& pp, Ref<vm::Cell> cell_ref) const {
@ -217,7 +222,7 @@ PrettyPrinter::~PrettyPrinter() {
} }
bool PrettyPrinter::fail(std::string msg) { bool PrettyPrinter::fail(std::string msg) {
os << "<FATAL: " << msg << ">"; os << "<FATAL: " << msg << ">" << std::endl;
failed = true; failed = true;
return false; return false;
} }

View file

@ -30,6 +30,7 @@ struct PrettyPrinter;
class TLB { class TLB {
public: public:
enum { default_validate_max_cells = 1024 };
virtual ~TLB() = default; virtual ~TLB() = default;
virtual int get_size(const vm::CellSlice& cs) const { virtual int get_size(const vm::CellSlice& cs) const {
return -1; return -1;
@ -37,14 +38,26 @@ class TLB {
virtual bool skip(vm::CellSlice& cs) const { virtual bool skip(vm::CellSlice& cs) const {
return cs.skip_ext(get_size(cs)); return cs.skip_ext(get_size(cs));
} }
virtual bool validate(const vm::CellSlice& cs, bool weak = false) const { virtual bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const {
return cs.have_ext(get_size(cs)); return cs.have_ext(get_size(cs));
} }
virtual bool validate_exact(const vm::CellSlice& cs, bool weak = false) const { virtual bool validate_exact(int* ops, const vm::CellSlice& cs, bool weak = false) const {
return (int)cs.size_ext() == get_size(cs); return (int)cs.size_ext() == get_size(cs);
} }
bool validate_upto(int ops, const vm::CellSlice& cs, bool weak = false) const {
return validate(&ops, cs, weak);
}
bool validate_exact_upto(int ops, const vm::CellSlice& cs, bool weak = false) const {
return validate_exact(&ops, cs, weak);
}
bool validate_csr(int* ops, Ref<vm::CellSlice> cs_ref, bool weak = false) const {
return cs_ref.not_null() && validate_skip_exact(ops, cs_ref.write(), weak);
}
bool validate_csr(int ops, Ref<vm::CellSlice> cs_ref, bool weak = false) const {
return validate_csr(&ops, std::move(cs_ref), weak);
}
bool validate_csr(Ref<vm::CellSlice> cs_ref, bool weak = false) const { bool validate_csr(Ref<vm::CellSlice> cs_ref, bool weak = false) const {
return cs_ref.not_null() && validate_skip_exact(cs_ref.write(), weak); return validate_csr(default_validate_max_cells, std::move(cs_ref), weak);
} }
Ref<vm::CellSlice> fetch(vm::CellSlice& cs) const { Ref<vm::CellSlice> fetch(vm::CellSlice& cs) const {
return cs.fetch_subslice_ext(get_size(cs)); return cs.fetch_subslice_ext(get_size(cs));
@ -52,67 +65,71 @@ class TLB {
Ref<vm::CellSlice> prefetch(const vm::CellSlice& cs) const { Ref<vm::CellSlice> prefetch(const vm::CellSlice& cs) const {
return cs.prefetch_subslice_ext(get_size(cs)); return cs.prefetch_subslice_ext(get_size(cs));
} }
virtual Ref<vm::CellSlice> validate_fetch(vm::CellSlice& cs, bool weak = false) const { virtual Ref<vm::CellSlice> validate_fetch(int* ops, vm::CellSlice& cs, bool weak = false) const {
return validate(cs, weak) ? cs.fetch_subslice_ext(get_size(cs)) : Ref<vm::CellSlice>{}; return validate(ops, cs, weak) ? cs.fetch_subslice_ext(get_size(cs)) : Ref<vm::CellSlice>{};
} }
virtual Ref<vm::CellSlice> validate_prefetch(const vm::CellSlice& cs, bool weak = false) const { virtual Ref<vm::CellSlice> validate_prefetch(int* ops, const vm::CellSlice& cs, bool weak = false) const {
return validate(cs, weak) ? cs.prefetch_subslice_ext(get_size(cs)) : Ref<vm::CellSlice>{}; return validate(ops, cs, weak) ? cs.prefetch_subslice_ext(get_size(cs)) : Ref<vm::CellSlice>{};
} }
bool fetch_to(vm::CellSlice& cs, Ref<vm::CellSlice>& res) const { bool fetch_to(vm::CellSlice& cs, Ref<vm::CellSlice>& res) const {
return (res = fetch(cs)).not_null(); return (res = fetch(cs)).not_null();
} }
bool validate_fetch_to(vm::CellSlice& cs, Ref<vm::CellSlice>& res, bool weak = false) const { bool validate_fetch_to(int* ops, vm::CellSlice& cs, Ref<vm::CellSlice>& res, bool weak = false) const {
return (res = validate_fetch(cs, weak)).not_null(); return (res = validate_fetch(ops, cs, weak)).not_null();
} }
bool store_from(vm::CellBuilder& cb, Ref<vm::CellSlice> field) const { bool store_from(vm::CellBuilder& cb, Ref<vm::CellSlice> field) const {
return field.not_null() && get_size(*field) == (int)field->size_ext() && cb.append_cellslice_bool(std::move(field)); return field.not_null() && get_size(*field) == (int)field->size_ext() && cb.append_cellslice_bool(std::move(field));
} }
bool validate_store_from(vm::CellBuilder& cb, Ref<vm::CellSlice> field, bool weak = false) const { bool validate_store_from(int* ops, vm::CellBuilder& cb, Ref<vm::CellSlice> field, bool weak = false) const {
if (field.is_null()) { if (field.is_null()) {
return false; return false;
} }
vm::CellSlice cs{*field}; vm::CellSlice cs{*field};
return validate_skip(cs, weak) && cs.empty_ext() && cb.append_cellslice_bool(std::move(field)); return validate_skip(ops, cs, weak) && cs.empty_ext() && cb.append_cellslice_bool(std::move(field));
} }
virtual bool extract(vm::CellSlice& cs) const { virtual bool extract(vm::CellSlice& cs) const {
return cs.only_ext(get_size(cs)); return cs.only_ext(get_size(cs));
} }
virtual bool validate_extract(vm::CellSlice& cs, bool weak = false) const { virtual bool validate_extract(int* ops, vm::CellSlice& cs, bool weak = false) const {
return validate(cs, weak) && extract(cs); return validate(ops, cs, weak) && extract(cs);
} }
int get_size_by_skip(const vm::CellSlice& cs) const { int get_size_by_skip(const vm::CellSlice& cs) const {
vm::CellSlice copy{cs}; vm::CellSlice copy{cs};
return skip(copy) ? copy.subtract_base_ext(cs) : -1; return skip(copy) ? copy.subtract_base_ext(cs) : -1;
} }
virtual bool validate_skip(vm::CellSlice& cs, bool weak = false) const { virtual bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const {
return validate(cs, weak) && skip(cs); return validate(ops, cs, weak) && skip(cs);
} }
bool validate_skip_exact(vm::CellSlice& cs, bool weak = false) const { bool validate_skip_upto(int ops, vm::CellSlice& cs, bool weak = false) const {
return validate_skip(cs, weak) && cs.empty_ext(); return validate_skip(&ops, cs, weak);
} }
bool validate_by_skip(const vm::CellSlice& cs, bool weak = false) const { bool validate_skip_exact(int* ops, vm::CellSlice& cs, bool weak = false) const {
return validate_skip(ops, cs, weak) && cs.empty_ext();
}
bool validate_by_skip(int* ops, const vm::CellSlice& cs, bool weak = false) const {
vm::CellSlice copy{cs}; vm::CellSlice copy{cs};
return validate_skip(copy, weak); return validate_skip(ops, copy, weak);
} }
bool validate_by_skip_exact(const vm::CellSlice& cs, bool weak = false) const { bool validate_by_skip_exact(int* ops, const vm::CellSlice& cs, bool weak = false) const {
vm::CellSlice copy{cs}; vm::CellSlice copy{cs};
return validate_skip_exact(copy, weak); return validate_skip_exact(ops, copy, weak);
} }
bool extract_by_skip(vm::CellSlice& cs) const { bool extract_by_skip(vm::CellSlice& cs) const {
vm::CellSlice copy{cs}; vm::CellSlice copy{cs};
return skip(copy) && cs.cut_tail(copy); return skip(copy) && cs.cut_tail(copy);
} }
bool validate_extract_by_skip(vm::CellSlice& cs, bool weak = false) const { bool validate_extract_by_skip(int* ops, vm::CellSlice& cs, bool weak = false) const {
vm::CellSlice copy{cs}; vm::CellSlice copy{cs};
return validate_skip(copy, weak) && cs.cut_tail(copy); return validate_skip(ops, copy, weak) && cs.cut_tail(copy);
} }
Ref<vm::CellSlice> validate_fetch_by_skip(vm::CellSlice& cs, bool weak = false) const { Ref<vm::CellSlice> validate_fetch_by_skip(int* ops, vm::CellSlice& cs, bool weak = false) const {
Ref<vm::CellSlice> copy{true, cs}; Ref<vm::CellSlice> copy{true, cs};
return validate_skip(cs, weak) && copy.unique_write().cut_tail(cs) ? copy : Ref<vm::CellSlice>{}; return validate_skip(ops, cs, weak) && copy.unique_write().cut_tail(cs) ? copy : Ref<vm::CellSlice>{};
} }
Ref<vm::CellSlice> validate_prefetch_by_skip(const vm::CellSlice& cs, bool weak = false) const { Ref<vm::CellSlice> validate_prefetch_by_skip(int* ops, const vm::CellSlice& cs, bool weak = false) const {
vm::CellSlice copy{cs}; vm::CellSlice copy{cs};
return validate_skip(copy, false) ? cs.prefetch_subslice_ext(copy.subtract_base_ext(cs)) : Ref<vm::CellSlice>{}; return validate_skip(ops, copy, false) ? cs.prefetch_subslice_ext(copy.subtract_base_ext(cs))
: Ref<vm::CellSlice>{};
} }
virtual bool skip_copy(vm::CellBuilder& cb, vm::CellSlice& cs) const { virtual bool skip_copy(vm::CellBuilder& cb, vm::CellSlice& cs) const {
return cb.append_cellslice_bool(fetch(cs)); return cb.append_cellslice_bool(fetch(cs));
@ -156,14 +173,29 @@ class TLB {
bool as_integer_to(Ref<vm::CellSlice> cs_ref, td::RefInt256& res) const { bool as_integer_to(Ref<vm::CellSlice> cs_ref, td::RefInt256& res) const {
return (res = as_integer(std::move(cs_ref))).not_null(); return (res = as_integer(std::move(cs_ref))).not_null();
} }
bool validate_ref(int* ops, Ref<vm::Cell> cell_ref, bool weak = false) const {
return cell_ref.not_null() && validate_ref_internal(ops, std::move(cell_ref), weak);
}
bool validate_ref(int ops, Ref<vm::Cell> cell_ref, bool weak = false) const {
return validate_ref(&ops, std::move(cell_ref), weak);
}
bool validate_ref(Ref<vm::Cell> cell_ref, bool weak = false) const { bool validate_ref(Ref<vm::Cell> cell_ref, bool weak = false) const {
return cell_ref.not_null() && validate_ref_internal(std::move(cell_ref), weak); return validate_ref(default_validate_max_cells, std::move(cell_ref), weak);
}
bool force_validate_ref(int* ops, Ref<vm::Cell> cell_ref) const {
return cell_ref.not_null() && validate_ref_internal(ops, std::move(cell_ref), false);
}
bool force_validate_ref(int ops, Ref<vm::Cell> cell_ref) const {
return force_validate_ref(&ops, std::move(cell_ref));
} }
bool force_validate_ref(Ref<vm::Cell> cell_ref) const { bool force_validate_ref(Ref<vm::Cell> cell_ref) const {
return cell_ref.not_null() && validate_ref_internal(std::move(cell_ref), false); return force_validate_ref(default_validate_max_cells, std::move(cell_ref));
} }
bool validate_skip_ref(vm::CellSlice& cs, bool weak = false) const { bool validate_skip_ref(int* ops, vm::CellSlice& cs, bool weak = false) const {
return validate_ref(cs.fetch_ref(), weak); return validate_ref(ops, cs.fetch_ref(), weak);
}
bool validate_skip_ref(int ops, vm::CellSlice& cs, bool weak = false) const {
return validate_skip_ref(&ops, cs, weak);
} }
virtual bool null_value(vm::CellBuilder& cb) const { virtual bool null_value(vm::CellBuilder& cb) const {
return false; return false;
@ -214,6 +246,9 @@ class TLB {
return print(os, *cs_ref, indent, rec_limit); return print(os, *cs_ref, indent, rec_limit);
} }
bool print_ref(std::ostream& os, Ref<vm::Cell> cell_ref, int indent = 0, int rec_limit = 0) const; bool print_ref(std::ostream& os, Ref<vm::Cell> cell_ref, int indent = 0, int rec_limit = 0) const;
bool print_ref(int rec_limit, std::ostream& os, Ref<vm::Cell> cell_ref, int indent = 0) const {
return print_ref(os, std::move(cell_ref), indent, rec_limit);
}
std::string as_string_skip(vm::CellSlice& cs, int indent = 0) const; std::string as_string_skip(vm::CellSlice& cs, int indent = 0) const;
std::string as_string(const vm::CellSlice& cs, int indent = 0) const; std::string as_string(const vm::CellSlice& cs, int indent = 0) const;
std::string as_string(Ref<vm::CellSlice> cs_ref, int indent = 0) const { std::string as_string(Ref<vm::CellSlice> cs_ref, int indent = 0) const {
@ -225,7 +260,7 @@ class TLB {
} }
protected: protected:
bool validate_ref_internal(Ref<vm::Cell> cell_ref, bool weak = false) const; bool validate_ref_internal(int* ops, Ref<vm::Cell> cell_ref, bool weak = false) const;
}; };
static inline std::ostream& operator<<(std::ostream& os, const TLB& type) { static inline std::ostream& operator<<(std::ostream& os, const TLB& type) {
@ -234,29 +269,29 @@ static inline std::ostream& operator<<(std::ostream& os, const TLB& type) {
struct TLB_Complex : TLB { struct TLB_Complex : TLB {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return validate_skip(cs); return validate_skip(nullptr, cs);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override = 0; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override = 0;
int get_size(const vm::CellSlice& cs) const override { int get_size(const vm::CellSlice& cs) const override {
return get_size_by_skip(cs); return get_size_by_skip(cs);
} }
bool validate(const vm::CellSlice& cs, bool weak = false) const override { bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return validate_by_skip(cs, weak); return validate_by_skip(ops, cs, weak);
} }
bool validate_exact(const vm::CellSlice& cs, bool weak = false) const override { bool validate_exact(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return validate_by_skip_exact(cs, weak); return validate_by_skip_exact(ops, cs, weak);
} }
bool extract(vm::CellSlice& cs) const override { bool extract(vm::CellSlice& cs) const override {
return extract_by_skip(cs); return extract_by_skip(cs);
} }
bool validate_extract(vm::CellSlice& cs, bool weak = false) const override { bool validate_extract(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return validate_extract_by_skip(cs, weak); return validate_extract_by_skip(ops, cs, weak);
} }
Ref<vm::CellSlice> validate_fetch(vm::CellSlice& cs, bool weak = false) const override { Ref<vm::CellSlice> validate_fetch(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return validate_fetch_by_skip(cs, weak); return validate_fetch_by_skip(ops, cs, weak);
} }
Ref<vm::CellSlice> validate_prefetch(const vm::CellSlice& cs, bool weak = false) const override { Ref<vm::CellSlice> validate_prefetch(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return validate_prefetch_by_skip(cs, weak); return validate_prefetch_by_skip(ops, cs, weak);
} }
td::RefInt256 as_integer(const vm::CellSlice& cs) const override { td::RefInt256 as_integer(const vm::CellSlice& cs) const override {
vm::CellSlice copy{cs}; vm::CellSlice copy{cs};
@ -615,23 +650,23 @@ struct FwdT final : TLB {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return X.skip(cs); return X.skip(cs);
} }
bool validate(const vm::CellSlice& cs, bool weak = false) const override { bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return X.validate(cs, weak); return X.validate(ops, cs, weak);
} }
Ref<vm::CellSlice> validate_fetch(vm::CellSlice& cs, bool weak = false) const override { Ref<vm::CellSlice> validate_fetch(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return X.validate_fetch(cs, weak); return X.validate_fetch(ops, cs, weak);
} }
Ref<vm::CellSlice> validate_prefetch(const vm::CellSlice& cs, bool weak = false) const override { Ref<vm::CellSlice> validate_prefetch(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return X.validate_prefetch(cs, weak); return X.validate_prefetch(ops, cs, weak);
} }
bool extract(vm::CellSlice& cs) const override { bool extract(vm::CellSlice& cs) const override {
return X.extract(cs); return X.extract(cs);
} }
bool validate_extract(vm::CellSlice& cs, bool weak = false) const override { bool validate_extract(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return X.validate_extract(cs, weak); return X.validate_extract(ops, cs, weak);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return X.validate_skip(cs, weak); return X.validate_skip(ops, cs, weak);
} }
bool skip_copy(vm::CellBuilder& cb, vm::CellSlice& cs) const override { bool skip_copy(vm::CellBuilder& cb, vm::CellSlice& cs) const override {
return X.skip_copy(cb, cs); return X.skip_copy(cb, cs);
@ -726,10 +761,10 @@ struct NatLess final : TLB {
int get_size(const vm::CellSlice& cs) const override { int get_size(const vm::CellSlice& cs) const override {
return n >= 0 ? w : -1; return n >= 0 ? w : -1;
} }
bool validate(const vm::CellSlice& cs, bool weak = false) const override { bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return n >= 0 && (unsigned)cs.prefetch_ulong(w) <= (unsigned)n; return n >= 0 && (unsigned)cs.prefetch_ulong(w) <= (unsigned)n;
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return n >= 0 && (unsigned)cs.fetch_ulong(w) <= (unsigned)n; return n >= 0 && (unsigned)cs.fetch_ulong(w) <= (unsigned)n;
} }
unsigned long long as_uint(const vm::CellSlice& cs) const override { unsigned long long as_uint(const vm::CellSlice& cs) const override {
@ -749,10 +784,10 @@ struct NatLeq final : TLB {
int get_size(const vm::CellSlice& cs) const override { int get_size(const vm::CellSlice& cs) const override {
return n >= 0 ? w : -1; return n >= 0 ? w : -1;
} }
bool validate(const vm::CellSlice& cs, bool weak = false) const override { bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return n >= 0 && (unsigned)cs.prefetch_ulong(w) <= (unsigned)n; return n >= 0 && (unsigned)cs.prefetch_ulong(w) <= (unsigned)n;
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return n >= 0 && (unsigned)cs.fetch_ulong(w) <= (unsigned)n; return n >= 0 && (unsigned)cs.fetch_ulong(w) <= (unsigned)n;
} }
unsigned long long as_uint(const vm::CellSlice& cs) const override { unsigned long long as_uint(const vm::CellSlice& cs) const override {
@ -771,7 +806,7 @@ struct TupleT final : TLB_Complex {
TupleT(int _n, const TLB& _X) : n(_n), X(_X) { TupleT(int _n, const TLB& _X) : n(_n), X(_X) {
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return 0; return 0;
} }
@ -786,8 +821,8 @@ struct CondT final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return !n || X.skip(cs); return !n || X.skip(cs);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return !n || (n > 0 && X.validate_skip(cs, weak)); return !n || (n > 0 && X.validate_skip(ops, cs, weak));
} }
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return 0; return 0;
@ -808,8 +843,8 @@ struct Cond final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return !n || field_type.skip(cs); return !n || field_type.skip(cs);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return !n || (n > 0 && field_type.validate_skip(cs, weak)); return !n || (n > 0 && field_type.validate_skip(ops, cs, weak));
} }
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return 0; return 0;
@ -905,7 +940,7 @@ struct Maybe : TLB_Complex {
Maybe(Args... args) : field_type(args...) { Maybe(Args... args) : field_type(args...) {
} }
bool skip(vm::CellSlice& cs) const override; bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {
return cs.have(1) ? (int)cs.prefetch_ulong(1) : -1; return cs.have(1) ? (int)cs.prefetch_ulong(1) : -1;
} }
@ -928,10 +963,10 @@ bool Maybe<T>::skip(vm::CellSlice& cs) const {
} }
template <class T> template <class T>
bool Maybe<T>::validate_skip(vm::CellSlice& cs, bool weak) const { bool Maybe<T>::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
int t = get_tag(cs); int t = get_tag(cs);
if (t > 0) { if (t > 0) {
return cs.advance(1) && field_type.validate_skip(cs, weak); return cs.advance(1) && field_type.validate_skip(ops, cs, weak);
} else if (!t) { } else if (!t) {
return cs.advance(1); return cs.advance(1);
} else { } else {
@ -979,11 +1014,11 @@ struct RefTo final : TLB {
int get_size(const vm::CellSlice& cs) const override { int get_size(const vm::CellSlice& cs) const override {
return 0x10000; return 0x10000;
} }
bool validate(const vm::CellSlice& cs, bool weak = false) const override { bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return cs.size_refs() ? ref_type.validate_ref(cs.prefetch_ref(), weak) : false; return cs.size_refs() ? ref_type.validate_ref(ops, cs.prefetch_ref(), weak) : false;
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return ref_type.validate_skip_ref(cs, weak); return ref_type.validate_skip_ref(ops, cs, weak);
} }
std::ostream& print_type(std::ostream& os) const override { std::ostream& print_type(std::ostream& os) const override {
return os << '^' << ref_type; return os << '^' << ref_type;
@ -1000,11 +1035,11 @@ struct RefT final : TLB {
int get_size(const vm::CellSlice& cs) const override { int get_size(const vm::CellSlice& cs) const override {
return 0x10000; return 0x10000;
} }
bool validate(const vm::CellSlice& cs, bool weak = false) const override { bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return X.validate_ref(cs.prefetch_ref(), weak); return X.validate_ref(ops, cs.prefetch_ref(), weak);
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return X.validate_skip_ref(cs, weak); return X.validate_skip_ref(ops, cs, weak);
} }
std::ostream& print_type(std::ostream& os) const override { std::ostream& print_type(std::ostream& os) const override {
return os << '^' << X; return os << '^' << X;
@ -1021,8 +1056,9 @@ struct Either final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override { bool skip(vm::CellSlice& cs) const override {
return cs.have(1) ? (cs.fetch_ulong(1) ? right_type.skip(cs) : left_type.skip(cs)) : false; return cs.have(1) ? (cs.fetch_ulong(1) ? right_type.skip(cs) : left_type.skip(cs)) : false;
} }
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override { bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return cs.have(1) ? (cs.fetch_ulong(1) ? right_type.validate_skip(cs, weak) : left_type.validate_skip(cs, weak)) return cs.have(1)
? (cs.fetch_ulong(1) ? right_type.validate_skip(ops, cs, weak) : left_type.validate_skip(ops, cs, weak))
: false; : false;
} }
int get_tag(const vm::CellSlice& cs) const override { int get_tag(const vm::CellSlice& cs) const override {

View file

@ -389,7 +389,7 @@ int exec_muldivmod(VmState* st, unsigned args, int quiet) {
auto x = stack.pop_int(); auto x = stack.pop_int();
typename td::BigInt256::DoubleInt tmp{0}; typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y); tmp.add_mul(*x, *y);
auto q = td::RefInt256{true}; auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode); tmp.mod_div(*z, q.unique_write(), round_mode);
switch ((args >> 2) & 3) { switch ((args >> 2) & 3) {
case 1: case 1:
@ -401,7 +401,7 @@ int exec_muldivmod(VmState* st, unsigned args, int quiet) {
stack.push_int_quiet(std::move(q), quiet); stack.push_int_quiet(std::move(q), quiet);
// fallthrough // fallthrough
case 2: case 2:
stack.push_int_quiet(td::RefInt256{true, tmp}, quiet); stack.push_int_quiet(td::make_refint(tmp), quiet);
break; break;
} }
return 0; return 0;
@ -450,17 +450,17 @@ int exec_mulshrmod(VmState* st, unsigned args, int mode) {
switch ((args >> 2) & 3) { switch ((args >> 2) & 3) {
case 1: case 1:
tmp.rshift(z, round_mode).normalize(); tmp.rshift(z, round_mode).normalize();
stack.push_int_quiet(td::RefInt256{true, tmp}, mode & 1); stack.push_int_quiet(td::make_refint(tmp), mode & 1);
break; break;
case 3: { case 3: {
typename td::BigInt256::DoubleInt tmp2{tmp}; typename td::BigInt256::DoubleInt tmp2{tmp};
tmp2.rshift(z, round_mode).normalize(); tmp2.rshift(z, round_mode).normalize();
stack.push_int_quiet(td::RefInt256{true, tmp2}, mode & 1); stack.push_int_quiet(td::make_refint(tmp2), mode & 1);
} }
// fallthrough // fallthrough
case 2: case 2:
tmp.mod_pow2(z, round_mode).normalize(); tmp.mod_pow2(z, round_mode).normalize();
stack.push_int_quiet(td::RefInt256{true, tmp}, mode & 1); stack.push_int_quiet(td::make_refint(tmp), mode & 1);
break; break;
} }
return 0; return 0;
@ -524,24 +524,24 @@ int exec_shldivmod(VmState* st, unsigned args, int mode) {
tmp <<= y; tmp <<= y;
switch ((args >> 2) & 3) { switch ((args >> 2) & 3) {
case 1: { case 1: {
auto q = td::RefInt256{true}; auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode); tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize(); q.unique_write().normalize();
stack.push_int_quiet(std::move(q), mode & 1); stack.push_int_quiet(std::move(q), mode & 1);
break; break;
} }
case 3: { case 3: {
auto q = td::RefInt256{true}; auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode); tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize(); q.unique_write().normalize();
stack.push_int_quiet(std::move(q), mode & 1); stack.push_int_quiet(std::move(q), mode & 1);
stack.push_int_quiet(td::RefInt256{true, tmp}, mode & 1); stack.push_int_quiet(td::make_refint(tmp), mode & 1);
break; break;
} }
case 2: { case 2: {
typename td::BigInt256::DoubleInt tmp2; typename td::BigInt256::DoubleInt tmp2;
tmp.mod_div(*z, tmp2, round_mode); tmp.mod_div(*z, tmp2, round_mode);
stack.push_int_quiet(td::RefInt256{true, tmp}, mode & 1); stack.push_int_quiet(td::make_refint(tmp), mode & 1);
break; break;
} }
} }
@ -740,7 +740,7 @@ int exec_bitsize(VmState* st, bool sgnd, bool quiet) {
} else if (!quiet) { } else if (!quiet) {
throw VmError{Excno::range_chk, "CHKSIZE for negative integer"}; throw VmError{Excno::range_chk, "CHKSIZE for negative integer"};
} else { } else {
stack.push_int_quiet(td::RefInt256{true}, quiet); stack.push_int_quiet(td::make_refint(), quiet);
} }
return 0; return 0;
} }

View file

@ -594,8 +594,7 @@ td::RefInt256 CellSlice::fetch_int256(unsigned bits, bool sgnd) {
if (!have(bits)) { if (!have(bits)) {
return {}; return {};
} else if (bits < td::BigInt256::word_shift) { } else if (bits < td::BigInt256::word_shift) {
long long val = sgnd ? fetch_long(bits) : fetch_ulong(bits); return td::make_refint(sgnd ? fetch_long(bits) : fetch_ulong(bits));
return td::RefInt256{true, val};
} else { } else {
td::RefInt256 res{true}; td::RefInt256 res{true};
res.unique_write().import_bits(data_bits(), bits, sgnd); res.unique_write().import_bits(data_bits(), bits, sgnd);
@ -608,8 +607,7 @@ td::RefInt256 CellSlice::prefetch_int256(unsigned bits, bool sgnd) const {
if (!have(bits)) { if (!have(bits)) {
return {}; return {};
} else if (bits < td::BigInt256::word_shift) { } else if (bits < td::BigInt256::word_shift) {
long long val = sgnd ? prefetch_long(bits) : prefetch_ulong(bits); return td::make_refint(sgnd ? prefetch_long(bits) : prefetch_ulong(bits));
return td::RefInt256{true, val};
} else { } else {
td::RefInt256 res{true}; td::RefInt256 res{true};
res.unique_write().import_bits(data_bits(), bits, sgnd); res.unique_write().import_bits(data_bits(), bits, sgnd);
@ -619,15 +617,15 @@ td::RefInt256 CellSlice::prefetch_int256(unsigned bits, bool sgnd) const {
td::RefInt256 CellSlice::prefetch_int256_zeroext(unsigned bits, bool sgnd) const { td::RefInt256 CellSlice::prefetch_int256_zeroext(unsigned bits, bool sgnd) const {
if (bits > 256u + sgnd) { if (bits > 256u + sgnd) {
return td::RefInt256{false}; return td::make_refint();
} else { } else {
unsigned ld_bits = std::min(bits, size()); unsigned ld_bits = std::min(bits, size());
if (bits < td::BigInt256::word_shift) { if (bits < td::BigInt256::word_shift) {
long long val = sgnd ? prefetch_long(ld_bits) : prefetch_ulong(ld_bits); long long val = sgnd ? prefetch_long(ld_bits) : prefetch_ulong(ld_bits);
val <<= bits - ld_bits; val <<= bits - ld_bits;
return td::RefInt256{true, val}; return td::make_refint(val);
} else { } else {
td::RefInt256 res{true}; auto res = td::make_refint();
res.unique_write().import_bits(data_bits(), ld_bits, sgnd); res.unique_write().import_bits(data_bits(), ld_bits, sgnd);
res <<= bits - ld_bits; res <<= bits - ld_bits;
return res; return res;

View file

@ -556,7 +556,7 @@ void Stack::push_int_quiet(td::RefInt256 val, bool quiet) {
if (!quiet) { if (!quiet) {
throw VmError{Excno::int_ov}; throw VmError{Excno::int_ov};
} else if (val->is_valid()) { } else if (val->is_valid()) {
push(td::RefInt256{true}); push(td::make_refint());
return; return;
} }
} }
@ -592,7 +592,7 @@ void Stack::push_builder(Ref<CellBuilder> cb) {
} }
void Stack::push_smallint(long long val) { void Stack::push_smallint(long long val) {
push(td::RefInt256{true, val}); push(td::make_refint(val));
} }
void Stack::push_bool(bool val) { void Stack::push_bool(bool val) {
@ -763,7 +763,7 @@ bool StackEntry::deserialize(CellSlice& cs, int mode) {
t = (int)cs.prefetch_ulong(16) & 0x1ff; t = (int)cs.prefetch_ulong(16) & 0x1ff;
if (t == 0xff) { if (t == 0xff) {
// vm_stk_nan#02ff = VmStackValue; // vm_stk_nan#02ff = VmStackValue;
return cs.advance(16) && set_int(td::RefInt256{true}); return cs.advance(16) && set_int(td::make_refint());
} else { } else {
// vm_stk_int#0201_ value:int257 = VmStackValue; // vm_stk_int#0201_ value:int257 = VmStackValue;
td::RefInt256 val; td::RefInt256 val;

View file

@ -147,6 +147,15 @@ class StackEntry {
bool is_atom() const { bool is_atom() const {
return tp == t_atom; return tp == t_atom;
} }
bool is_int() const {
return tp == t_int;
}
bool is_cell() const {
return tp == t_cell;
}
bool is_null() const {
return tp == t_null;
}
bool is(int wanted) const { bool is(int wanted) const {
return tp == wanted; return tp == wanted;
} }
@ -433,6 +442,12 @@ class Stack : public td::CntObject {
} }
return *this; return *this;
} }
std::vector<StackEntry> extract_contents() const & {
return stack;
}
std::vector<StackEntry> extract_contents() && {
return std::move(stack);
}
template <typename... Args> template <typename... Args>
const Stack& check_underflow(Args... args) const { const Stack& check_underflow(Args... args) const {
if (!at_least(args...)) { if (!at_least(args...)) {

View file

@ -260,7 +260,7 @@ int exec_rand_int(VmState* st) {
typename td::BigInt256::DoubleInt tmp{0}; typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y); tmp.add_mul(*x, *y);
tmp.rshift(256, -1).normalize(); tmp.rshift(256, -1).normalize();
stack.push_int(td::RefInt256{true, tmp}); stack.push_int(td::make_refint(tmp));
return 0; return 0;
} }
@ -608,14 +608,14 @@ bool parse_message_addr(CellSlice& cs, std::vector<StackEntry>& res) {
res.clear(); res.clear();
switch ((unsigned)cs.fetch_ulong(2)) { switch ((unsigned)cs.fetch_ulong(2)) {
case 0: // addr_none$00 = MsgAddressExt; case 0: // addr_none$00 = MsgAddressExt;
res.emplace_back(td::RefInt256{true, 0}); // -> (0) res.emplace_back(td::zero_refint()); // -> (0)
return true; return true;
case 1: { // addr_extern$01 case 1: { // addr_extern$01
unsigned len; unsigned len;
Ref<CellSlice> addr; Ref<CellSlice> addr;
if (cs.fetch_uint_to(9, len) // len:(## 9) if (cs.fetch_uint_to(9, len) // len:(## 9)
&& cs.fetch_subslice_to(len, addr)) { // external_address:(bits len) = MsgAddressExt; && cs.fetch_subslice_to(len, addr)) { // external_address:(bits len) = MsgAddressExt;
res.emplace_back(td::RefInt256{true, 1}); res.emplace_back(td::make_refint(1));
res.emplace_back(std::move(addr)); res.emplace_back(std::move(addr));
return true; return true;
} }
@ -628,9 +628,9 @@ bool parse_message_addr(CellSlice& cs, std::vector<StackEntry>& res) {
if (parse_maybe_anycast(cs, v) // anycast:(Maybe Anycast) if (parse_maybe_anycast(cs, v) // anycast:(Maybe Anycast)
&& cs.fetch_int_to(8, workchain) // workchain_id:int8 && cs.fetch_int_to(8, workchain) // workchain_id:int8
&& cs.fetch_subslice_to(256, addr)) { // address:bits256 = MsgAddressInt; && cs.fetch_subslice_to(256, addr)) { // address:bits256 = MsgAddressInt;
res.emplace_back(td::RefInt256{true, 2}); res.emplace_back(td::make_refint(2));
res.emplace_back(std::move(v)); res.emplace_back(std::move(v));
res.emplace_back(td::RefInt256{true, workchain}); res.emplace_back(td::make_refint(workchain));
res.emplace_back(std::move(addr)); res.emplace_back(std::move(addr));
return true; return true;
} }
@ -644,9 +644,9 @@ bool parse_message_addr(CellSlice& cs, std::vector<StackEntry>& res) {
&& cs.fetch_uint_to(9, len) // addr_len:(## 9) && cs.fetch_uint_to(9, len) // addr_len:(## 9)
&& cs.fetch_int_to(32, workchain) // workchain_id:int32 && cs.fetch_int_to(32, workchain) // workchain_id:int32
&& cs.fetch_subslice_to(len, addr)) { // address:(bits addr_len) = MsgAddressInt; && cs.fetch_subslice_to(len, addr)) { // address:(bits addr_len) = MsgAddressInt;
res.emplace_back(td::RefInt256{true, 3}); res.emplace_back(td::make_refint(3));
res.emplace_back(std::move(v)); res.emplace_back(std::move(v));
res.emplace_back(td::RefInt256{true, workchain}); res.emplace_back(td::make_refint(workchain));
res.emplace_back(std::move(addr)); res.emplace_back(std::move(addr));
return true; return true;
} }

View file

@ -1,3 +1,21 @@
/*
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 2019-2020 Telegram Systems LLP
*/
#include "utils.h" #include "utils.h"
namespace vm { namespace vm {
@ -88,7 +106,7 @@ td::Result<vm::StackEntry> convert_stack_entry(td::Slice str) {
return vm::StackEntry{ return vm::StackEntry{
Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}}; Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}};
} }
auto num = td::RefInt256{true}; auto num = td::make_refint();
auto& x = num.unique_write(); auto& x = num.unique_write();
if (l >= 3 && str[0] == '0' && str[1] == 'x') { if (l >= 3 && str[0] == '0' && str[1] == 'x') {
if (x.parse_hex(str.data() + 2, l - 2) != l - 2) { if (x.parse_hex(str.data() + 2, l - 2) != l - 2) {

View file

@ -1,3 +1,21 @@
/*
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 2019-2020 Telegram Systems LLP
*/
#pragma once #pragma once
#include "stack.hpp" #include "stack.hpp"

View file

@ -385,11 +385,13 @@ int VmState::step() {
if (code->size()) { if (code->size()) {
return dispatch->dispatch(this, code.write()); return dispatch->dispatch(this, code.write());
} else if (code->size_refs()) { } else if (code->size_refs()) {
VM_LOG(this) << "execute implicit JMPREF\n"; VM_LOG(this) << "execute implicit JMPREF";
gas.consume_chk(implicit_jmpref_gas_price);
Ref<Continuation> cont = Ref<OrdCont>{true, load_cell_slice_ref(code->prefetch_ref()), get_cp()}; Ref<Continuation> cont = Ref<OrdCont>{true, load_cell_slice_ref(code->prefetch_ref()), get_cp()};
return jump(std::move(cont)); return jump(std::move(cont));
} else { } else {
VM_LOG(this) << "execute implicit RET\n"; VM_LOG(this) << "execute implicit RET";
gas.consume_chk(implicit_ret_gas_price);
return ret(); return ret();
} }
} }
@ -402,6 +404,7 @@ int VmState::run() {
Guard guard(this); Guard guard(this);
do { do {
// LOG(INFO) << "[BS] data cells: " << DataCell::get_total_data_cells(); // LOG(INFO) << "[BS] data cells: " << DataCell::get_total_data_cells();
try {
try { try {
try { try {
res = step(); res = step();
@ -424,6 +427,7 @@ int VmState::run() {
// LOG(INFO) << "[EXX] data cells: " << DataCell::get_total_data_cells(); // LOG(INFO) << "[EXX] data cells: " << DataCell::get_total_data_cells();
return ~vme2.get_errno(); return ~vme2.get_errno();
} }
}
} catch (VmNoGas vmoog) { } catch (VmNoGas vmoog) {
++steps; ++steps;
VM_LOG(this) << "unhandled out-of-gas exception: gas consumed=" << gas.gas_consumed() VM_LOG(this) << "unhandled out-of-gas exception: gas consumed=" << gas.gas_consumed()

View file

@ -104,6 +104,8 @@ class VmState final : public VmStateInterface {
cell_create_gas_price = 500, cell_create_gas_price = 500,
exception_gas_price = 50, exception_gas_price = 50,
tuple_entry_gas_price = 1, tuple_entry_gas_price = 1,
implicit_jmpref_gas_price = 10,
implicit_ret_gas_price = 5,
max_data_depth = 512 max_data_depth = 512
}; };
VmState(); VmState();

View file

@ -49,8 +49,8 @@ class TonTest {
"workchain": -1, "workchain": -1,
"shard": -9223372036854775808, "shard": -9223372036854775808,
"seqno": 0, "seqno": 0,
"root_hash": "VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=", "root_hash": "F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk=",
"file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=" "file_hash": "XplPz01CXAps5qeSWUtxcyBfdAo5zVb1N979KLSKD24="
} }
} }
}""" }"""
@ -58,29 +58,27 @@ class TonTest {
fun createTestWallet() { fun createTestWallet() {
val client = ClientKotlin() val client = ClientKotlin()
val dir = getContext().getExternalFilesDir(null).toString() + "/"; val dir = getContext().getExternalFilesDir(null).toString() + "/";
val words = getContext().getString(R.string.wallet_mnemonic_words).split(" ").toTypedArray();
runBlocking { runBlocking {
client.send(TonApi.Init(TonApi.Options(TonApi.Config(config, "", false, false), dir))) val info = client.send(TonApi.Init(TonApi.Options(TonApi.Config(config, "", false, false), TonApi.KeyStoreTypeDirectory(dir)))) as TonApi.OptionsInfo;
val key = client.send(TonApi.CreateNewKey("local password".toByteArray(), "mnemonic password".toByteArray(), "".toByteArray())) as TonApi.Key val key = client.send(TonApi.CreateNewKey("local password".toByteArray(), "mnemonic password".toByteArray(), "".toByteArray())) as TonApi.Key
val walletAddress = client.send(TonApi.TestWalletGetAccountAddress(TonApi.TestWalletInitialAccountState(key.publicKey))) as TonApi.AccountAddress; val inputKey = TonApi.InputKeyRegular(key, "local password".toByteArray())
val testGiverState = client.send(TonApi.TestGiverGetAccountState()) as TonApi.TestGiverAccountState val walletAddress = client.send(TonApi.GetAccountAddress(TonApi.WalletV3InitialAccountState(key.publicKey, info.configInfo.defaultWalletId), 1)) as TonApi.AccountAddress
client.send(TonApi.TestGiverSendGrams(walletAddress, testGiverState.seqno, 6660000000, "".toByteArray())) as TonApi.Ok val giverKey = client.send(TonApi.ImportKey("local password".toByteArray(), "".toByteArray(), TonApi.ExportedKey(words))) as TonApi.Key
val giverInputKey = TonApi.InputKeyRegular(giverKey, "local password".toByteArray())
val giverAddress = client.send(TonApi.GetAccountAddress(TonApi.WalletV3InitialAccountState(giverKey.publicKey, info.configInfo.defaultWalletId), 1)) as TonApi.AccountAddress;
while ((client.send(TonApi.GenericGetAccountState(walletAddress)) as TonApi.GenericAccountStateUninited).accountState.balance <= 0L) { val queryInfo = client.send(TonApi.CreateQuery(giverInputKey, giverAddress, 60, TonApi.ActionMsg(arrayOf(TonApi.MsgMessage(walletAddress, 6660000000, TonApi.MsgDataText("Helo") )), true))) as TonApi.QueryInfo;
client.send(TonApi.QuerySend(queryInfo.id)) as TonApi.Ok;
while ((client.send(TonApi.GetAccountState(walletAddress)) as TonApi.FullAccountState).balance <= 0L) {
delay(1000L) delay(1000L)
} }
val inputKey = TonApi.InputKey(key, "local password".toByteArray()); val queryInfo2 = client.send(TonApi.CreateQuery(inputKey, walletAddress, 60, TonApi.ActionMsg(arrayOf(), true))) as TonApi.QueryInfo;
client.send(TonApi.TestWalletInit(inputKey)) as TonApi.Ok client.send(TonApi.QuerySend(queryInfo2.id)) as TonApi.Ok;
while ((client.send(TonApi.GetAccountState(walletAddress)) as TonApi.FullAccountState).accountState !is TonApi.WalletV3AccountState) {
while (client.send(TonApi.GenericGetAccountState(walletAddress)) !is TonApi.GenericAccountStateTestWallet) {
delay(1000L)
}
val state = client.send(TonApi.GenericGetAccountState(walletAddress)) as TonApi.GenericAccountStateTestWallet
val balance = state.accountState.balance
client.send(TonApi.GenericSendGrams(inputKey, walletAddress, walletAddress, 10, 0, true, "hello".toByteArray())) as TonApi.Ok
while ((client.send(TonApi.GenericGetAccountState(walletAddress)) as TonApi.GenericAccountStateTestWallet).accountState.balance == balance) {
delay(1000L) delay(1000L)
} }
} }

View file

@ -53,7 +53,6 @@ public class TonTestJava {
return result[0]; return result[0];
} }
} }
String config = "{\n"+ String config = "{\n"+
" \"liteservers\": [\n"+ " \"liteservers\": [\n"+
" {\n"+ " {\n"+
@ -71,8 +70,8 @@ public class TonTestJava {
" \"workchain\": -1,\n"+ " \"workchain\": -1,\n"+
" \"shard\": -9223372036854775808,\n"+ " \"shard\": -9223372036854775808,\n"+
" \"seqno\": 0,\n"+ " \"seqno\": 0,\n"+
" \"root_hash\": \"VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=\",\n" + " \"root_hash\": \"F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk=\",\n"+
" \"file_hash\": \"eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=\"\n" + " \"file_hash\": \"XplPz01CXAps5qeSWUtxcyBfdAo5zVb1N979KLSKD24=\"\n"+
" }\n"+ " }\n"+
" }\n"+ " }\n"+
"}"; "}";
@ -84,21 +83,27 @@ public class TonTestJava {
@Test @Test
public void createTestWallet() { public void createTestWallet() {
appendLog("start..."); appendLog("start...");
String dir = getContext().getExternalFilesDir(null) + "/";
String[] words = getContext().getString(R.string.wallet_mnemonic_words).split(" ");
JavaClient client = new JavaClient(); JavaClient client = new JavaClient();
Object result = client.send(new TonApi.Init(new TonApi.Options(new TonApi.Config(config, "", false, false), getContext().getExternalFilesDir(null) + "/"))); Object result = client.send(new TonApi.Init(new TonApi.Options(new TonApi.Config(config, "", false, false), new TonApi.KeyStoreTypeDirectory((dir)))));
if (!(result instanceof TonApi.Ok)) { if (!(result instanceof TonApi.OptionsInfo)) {
appendLog("failed to set config"); appendLog("failed to set config");
return; return;
} }
appendLog("config set ok"); appendLog("config set ok");
TonApi.OptionsInfo info = (TonApi.OptionsInfo)result;
TonApi.Key key = (TonApi.Key) client.send(new TonApi.CreateNewKey("local password".getBytes(), "mnemonic password".getBytes(), "".getBytes())); TonApi.Key key = (TonApi.Key) client.send(new TonApi.CreateNewKey("local password".getBytes(), "mnemonic password".getBytes(), "".getBytes()));
appendLog("got private key"); TonApi.InputKey inputKey = new TonApi.InputKeyRegular(key, "local password".getBytes());
TonApi.AccountAddress walletAddress = (TonApi.AccountAddress) client.send(new TonApi.TestWalletGetAccountAddress(new TonApi.TestWalletInitialAccountState(key.publicKey))); TonApi.AccountAddress walletAddress = (TonApi.AccountAddress)client.send(new TonApi.GetAccountAddress(new TonApi.WalletV3InitialAccountState(key.publicKey, info.configInfo.defaultWalletId), 1));
appendLog("got account address");
TonApi.Key giverKey = (TonApi.Key)client.send(new TonApi.ImportKey("local password".getBytes(), "".getBytes(), new TonApi.ExportedKey(words))) ;
TonApi.InputKey giverInputKey = new TonApi.InputKeyRegular(giverKey, "local password".getBytes());
TonApi.AccountAddress giverAddress = (TonApi.AccountAddress)client.send(new TonApi.GetAccountAddress(new TonApi.WalletV3InitialAccountState(giverKey.publicKey, info.configInfo.defaultWalletId), 1));
appendLog("sending grams..."); appendLog("sending grams...");
TonApi.TestGiverAccountState testGiverState = (TonApi.TestGiverAccountState) client.send(new TonApi.TestGiverGetAccountState()); TonApi.QueryInfo queryInfo = (TonApi.QueryInfo)client.send(new TonApi.CreateQuery(giverInputKey, giverAddress, 60, new TonApi.ActionMsg(new TonApi.MsgMessage[]{new TonApi.MsgMessage(walletAddress, 6660000000L, new TonApi.MsgDataText("Helo") )}, true)));
result = client.send(new TonApi.TestGiverSendGrams(walletAddress, testGiverState.seqno, 6660000000L, "".getBytes())); result = client.send(new TonApi.QuerySend(queryInfo.id));
if (!(result instanceof TonApi.Ok)) { if (!(result instanceof TonApi.Ok)) {
appendLog("failed to send grams"); appendLog("failed to send grams");
return; return;
@ -106,63 +111,17 @@ public class TonTestJava {
appendLog("grams sent, getting balance"); appendLog("grams sent, getting balance");
while (true) { while (true) {
TonApi.GenericAccountStateUninited accountStateUninited = (TonApi.GenericAccountStateUninited) client.send(new TonApi.GenericGetAccountState(walletAddress)); TonApi.FullAccountState state = (TonApi.FullAccountState) client.send(new TonApi.GetAccountState(walletAddress));
if (accountStateUninited == null || accountStateUninited.accountState.balance <= 0L) { if (state.balance <= 0L) {
try { try {
Thread.sleep(1000); Thread.sleep(1000);
} catch (Throwable e) { } catch (Throwable e) {
appendLog(e.toString()); appendLog(e.toString());
} }
} else { } else {
appendLog(String.format("balance = %d", accountStateUninited.accountState.balance)); appendLog(String.format("balance = %d", state.balance));
break; break;
} }
} }
TonApi.InputKey inputKey = new TonApi.InputKey(key, "local password".getBytes());
result = client.send(new TonApi.TestWalletInit(inputKey));
if (!(result instanceof TonApi.Ok)) {
return;
}
appendLog("init test wallet ok, getting state...");
while (true) {
TonApi.GenericAccountState accountState = (TonApi.GenericAccountState) client.send(new TonApi.GenericGetAccountState(walletAddress));
if (!(accountState instanceof TonApi.GenericAccountStateTestWallet)) {
try {
Thread.sleep(1000);
} catch (Throwable e) {
appendLog(e.toString());
}
} else {
appendLog("got account state");
break;
}
}
appendLog("sending grams...");
TonApi.GenericAccountStateTestWallet state = (TonApi.GenericAccountStateTestWallet) client.send(new TonApi.GenericGetAccountState(walletAddress));
long balance = state.accountState.balance;
result = client.send(new TonApi.GenericSendGrams(inputKey, walletAddress, walletAddress, 10, 0, true, "hello".getBytes()));
if (!(result instanceof TonApi.Ok)) {
return;
}
appendLog(String.format("grams sent, current balance %d, receving...", balance));
while (true) {
TonApi.GenericAccountStateTestWallet wallet = (TonApi.GenericAccountStateTestWallet) client.send(new TonApi.GenericGetAccountState(walletAddress));
if (wallet == null || wallet.accountState.balance == balance) {
try {
Thread.sleep(1000);
} catch (Throwable e) {
appendLog(e.toString());
}
} else {
appendLog(String.format("grams received, balance = %d", balance));
break;
}
}
appendLog("OK");
} }
} }

View file

@ -1,3 +1,5 @@
<resources> <resources>
<string name="app_name">tonlib</string> <string name="app_name">tonlib</string>
<!-- add your wallet mnemonic word. By default it is wallet.v3 with revision=1 -->
<!-- <string name="wallet_mnemonic_words">...</string> -->
</resources> </resources>

View file

@ -1,3 +1,21 @@
/*
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 "lite-client-common.h" #include "lite-client-common.h"
#include "auto/tl/lite_api.hpp" #include "auto/tl/lite_api.hpp"

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "crypto/block/block.h" #include "crypto/block/block.h"

View file

@ -61,6 +61,7 @@
#include "ton/ton-shard.h" #include "ton/ton-shard.h"
#include "openssl/rand.hpp" #include "openssl/rand.hpp"
#include "crypto/vm/utils.h" #include "crypto/vm/utils.h"
#include "crypto/common/util.h"
#if TD_DARWIN || TD_LINUX #if TD_DARWIN || TD_LINUX
#include <unistd.h> #include <unistd.h>
@ -142,6 +143,8 @@ void TestNode::got_result() {
parse_line(std::move(data)); parse_line(std::move(data));
} }
if (ex_mode_ && !running_queries_ && ex_queries_.size() == 0) { if (ex_mode_ && !running_queries_ && ex_queries_.size() == 0) {
std::cout.flush();
std::cerr.flush();
std::_Exit(0); std::_Exit(0);
} }
} }
@ -179,6 +182,14 @@ bool TestNode::envelope_send_query(td::BufferSlice query, td::Promise<td::Buffer
return true; return true;
} }
td::Promise<td::Unit> TestNode::trivial_promise() {
return td::PromiseCreator::lambda([Self = actor_id(this)](td::Result<td::Unit> res) {
if (res.is_error()) {
LOG(ERROR) << "error: " << res.move_as_error();
}
});
}
bool TestNode::register_blkid(const ton::BlockIdExt& blkid) { bool TestNode::register_blkid(const ton::BlockIdExt& blkid) {
for (const auto& id : known_blk_ids_) { for (const auto& id : known_blk_ids_) {
if (id == blkid) { if (id == blkid) {
@ -570,8 +581,13 @@ bool TestNode::seekeoln() {
return eoln(); return eoln();
} }
bool TestNode::parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr) { bool TestNode::parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool allow_none) {
return block::parse_std_account_addr(get_word(), wc, addr) || set_error("cannot parse account address"); auto word = get_word();
if (allow_none && (word == "none" || word == "root")) {
wc = ton::workchainInvalid;
return true;
}
return block::parse_std_account_addr(word, wc, addr) || set_error("cannot parse account address");
} }
bool TestNode::convert_uint64(td::Slice word, td::uint64& val) { bool TestNode::convert_uint64(td::Slice word, td::uint64& val) {
@ -634,6 +650,14 @@ bool TestNode::parse_uint32(td::uint32& val) {
return convert_uint32(get_word(), val) || set_error("cannot parse 32-bit unsigned integer"); return convert_uint32(get_word(), val) || set_error("cannot parse 32-bit unsigned integer");
} }
bool TestNode::parse_int32(td::int32& val) {
return convert_int32(get_word(), val) || set_error("cannot parse 32-bit integer");
}
bool TestNode::parse_int16(int& val) {
return (convert_int32(get_word(), val) && val == (td::int16)val) || set_error("cannot parse 16-bit integer");
}
bool TestNode::set_error(td::Status error) { bool TestNode::set_error(td::Status error) {
if (error.is_ok()) { if (error.is_ok()) {
return true; return true;
@ -801,8 +825,12 @@ bool TestNode::show_help(std::string command) {
"saveaccount[code|data] <filename> <addr> [<block-id-ext>]\tSaves into specified file the most recent state " "saveaccount[code|data] <filename> <addr> [<block-id-ext>]\tSaves into specified file the most recent state "
"(StateInit) or just the code or data of specified account; <addr> is in " "(StateInit) or just the code or data of specified account; <addr> is in "
"[<workchain>:]<hex-or-base64-addr> format\n" "[<workchain>:]<hex-or-base64-addr> format\n"
"runmethod[x] <addr> [<block-id-ext>] <method-id> <params>...\tRuns GET method <method-id> of account <addr> " "runmethod[full] <addr> [<block-id-ext>] <method-id> <params>...\tRuns GET method <method-id> of account "
"<addr> "
"with specified parameters\n" "with specified parameters\n"
"dnsresolve [<block-id-ext>] <domain> [<category>]\tResolves a domain starting from root dns smart contract\n"
"dnsresolvestep <addr> [<block-id-ext>] <domain> [<category>]\tResolves a subdomain using dns smart contract "
"<addr>\n"
"allshards [<block-id-ext>]\tShows shard configuration from the most recent masterchain " "allshards [<block-id-ext>]\tShows shard configuration from the most recent masterchain "
"state or from masterchain state corresponding to <block-id-ext>\n" "state or from masterchain state corresponding to <block-id-ext>\n"
"getconfig [<param>...]\tShows specified or all configuration parameters from the latest masterchain state\n" "getconfig [<param>...]\tShows specified or all configuration parameters from the latest masterchain state\n"
@ -872,21 +900,30 @@ bool TestNode::do_parse_line() {
(seekeoln() (seekeoln()
? get_account_state(workchain, addr, mc_last_id_, filename, mode) ? get_account_state(workchain, addr, mc_last_id_, filename, mode)
: parse_block_id_ext(blkid) && seekeoln() && get_account_state(workchain, addr, blkid, filename, mode)); : parse_block_id_ext(blkid) && seekeoln() && get_account_state(workchain, addr, blkid, filename, mode));
} else if (word == "runmethod" || word == "runmethodx") { } else if (word == "runmethod" || word == "runmethodx" || word == "runmethodfull") {
std::string method; std::string method;
return parse_account_addr(workchain, addr) && get_word_to(method) && return parse_account_addr(workchain, addr) && get_word_to(method) &&
(parse_block_id_ext(method, blkid) ? get_word_to(method) : (blkid = mc_last_id_).is_valid()) && (parse_block_id_ext(method, blkid) ? get_word_to(method) : (blkid = mc_last_id_).is_valid()) &&
parse_run_method(workchain, addr, blkid, method, word.size() > 9); parse_run_method(workchain, addr, blkid, method, word.size() <= 10);
} else if (word == "dnsresolve" || word == "dnsresolvestep") {
workchain = ton::workchainInvalid;
bool step = (word.size() > 10);
std::string domain;
int cat = 0;
return (!step || parse_account_addr(workchain, addr)) && get_word_to(domain) &&
(parse_block_id_ext(domain, blkid) ? get_word_to(domain) : (blkid = mc_last_id_).is_valid()) &&
(seekeoln() || parse_int16(cat)) && seekeoln() &&
dns_resolve_start(workchain, addr, blkid, domain, cat, step);
} else if (word == "allshards") { } else if (word == "allshards") {
return eoln() ? get_all_shards() : (parse_block_id_ext(blkid) && seekeoln() && get_all_shards(false, blkid)); return eoln() ? get_all_shards() : (parse_block_id_ext(blkid) && seekeoln() && get_all_shards(false, blkid));
} else if (word == "saveconfig") { } else if (word == "saveconfig") {
blkid = mc_last_id_; blkid = mc_last_id_;
std::string filename; std::string filename;
return get_word_to(filename) && (seekeoln() || parse_block_id_ext(blkid)) && seekeoln() && return get_word_to(filename) && (seekeoln() || parse_block_id_ext(blkid)) && seekeoln() &&
get_config_params(blkid, -1, filename); get_config_params(blkid, trivial_promise(), -1, filename);
} else if (word == "getconfig" || word == "getconfigfrom") { } else if (word == "getconfig" || word == "getconfigfrom") {
blkid = mc_last_id_; blkid = mc_last_id_;
return (word == "getconfig" || parse_block_id_ext(blkid)) && get_config_params(blkid, 0); return (word == "getconfig" || parse_block_id_ext(blkid)) && get_config_params(blkid, trivial_promise(), 0);
} else if (word == "getblock") { } else if (word == "getblock") {
return parse_block_id_ext(blkid) && seekeoln() && get_block(blkid, false); return parse_block_id_ext(blkid) && seekeoln() && get_block(blkid, false);
} else if (word == "dumpblock") { } else if (word == "dumpblock") {
@ -1031,7 +1068,17 @@ bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress a
return set_error(R.move_as_error().to_string()); return set_error(R.move_as_error().to_string());
} }
parse_ptr_ = parse_end_; parse_ptr_ = parse_end_;
auto params = R.move_as_ok(); auto P = td::PromiseCreator::lambda([](td::Result<std::vector<vm::StackEntry>> R) {
if (R.is_error()) {
LOG(ERROR) << R.move_as_error();
}
});
return start_run_method(workchain, addr, ref_blkid, method_name, R.move_as_ok(), ext_mode ? 0x1f : 0, std::move(P));
}
bool TestNode::start_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid,
std::string method_name, std::vector<vm::StackEntry> params, int mode,
td::Promise<std::vector<vm::StackEntry>> promise) {
if (!ref_blkid.is_valid()) { if (!ref_blkid.is_valid()) {
return set_error("must obtain last block information before making other queries"); return set_error("must obtain last block information before making other queries");
} }
@ -1039,29 +1086,31 @@ bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress a
return set_error("server connection not ready"); return set_error("server connection not ready");
} }
auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(workchain, addr); auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(workchain, addr);
int mode = (ext_mode ? 0x1f : 0);
if (!mode) { if (!mode) {
auto b = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getAccountState>( auto b = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getAccountState>(
ton::create_tl_lite_block_id(ref_blkid), std::move(a)), ton::create_tl_lite_block_id(ref_blkid), std::move(a)),
true); true);
LOG(INFO) << "requesting account state for " << workchain << ":" << addr.to_hex() << " with respect to " LOG(INFO) << "requesting account state for " << workchain << ":" << addr.to_hex() << " with respect to "
<< ref_blkid.to_str() << " to run method " << method_name << " with " << params.size() << " parameters"; << ref_blkid.to_str() << " to run method " << method_name << " with " << params.size() << " parameters";
return envelope_send_query( return envelope_send_query(std::move(b), [
std::move(b), [ Self = actor_id(this), workchain, addr, ref_blkid, method_name, Self = actor_id(this), workchain, addr, ref_blkid, method_name, params = std::move(params),
params = std::move(params) ](td::Result<td::BufferSlice> R) mutable { promise = std::move(promise)
](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) { if (R.is_error()) {
promise.set_error(R.move_as_error());
return; return;
} }
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_accountState>(R.move_as_ok(), true); auto F = ton::fetch_tl_object<ton::lite_api::liteServer_accountState>(R.move_as_ok(), true);
if (F.is_error()) { if (F.is_error()) {
LOG(ERROR) << "cannot parse answer to liteServer.getAccountState"; LOG(ERROR) << "cannot parse answer to liteServer.getAccountState";
promise.set_error(td::Status::Error("cannot parse answer to liteServer.getAccountState"));
} else { } else {
auto f = F.move_as_ok(); auto f = F.move_as_ok();
td::actor::send_closure_later(Self, &TestNode::run_smc_method, 0, ref_blkid, ton::create_block_id(f->id_), td::actor::send_closure_later(Self, &TestNode::run_smc_method, 0, ref_blkid, ton::create_block_id(f->id_),
ton::create_block_id(f->shardblk_), std::move(f->shard_proof_), ton::create_block_id(f->shardblk_), std::move(f->shard_proof_),
std::move(f->proof_), std::move(f->state_), workchain, addr, method_name, std::move(f->proof_), std::move(f->state_), workchain, addr, method_name,
std::move(params), td::BufferSlice(), td::BufferSlice(), td::BufferSlice(), std::move(params), td::BufferSlice(), td::BufferSlice(), td::BufferSlice(),
-0x10000); -0x10000, std::move(promise));
} }
}); });
} else { } else {
@ -1087,26 +1136,260 @@ bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress a
<< " with respect to " << ref_blkid.to_str() << " to run method " << method_name << " with " << " with respect to " << ref_blkid.to_str() << " to run method " << method_name << " with "
<< params.size() << " parameters"; << params.size() << " parameters";
return envelope_send_query(std::move(b), [ return envelope_send_query(std::move(b), [
Self = actor_id(this), workchain, addr, ref_blkid, method_name, mode, params = std::move(params) Self = actor_id(this), workchain, addr, ref_blkid, method_name, mode, params = std::move(params),
promise = std::move(promise)
](td::Result<td::BufferSlice> R) mutable { ](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) { if (R.is_error()) {
promise.set_error(R.move_as_error());
return; return;
} }
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_runMethodResult>(R.move_as_ok(), true); auto F = ton::fetch_tl_object<ton::lite_api::liteServer_runMethodResult>(R.move_as_ok(), true);
if (F.is_error()) { if (F.is_error()) {
LOG(ERROR) << "cannot parse answer to liteServer.runSmcMethod"; LOG(ERROR) << "cannot parse answer to liteServer.runSmcMethod";
promise.set_error(td::Status::Error("cannot parse answer to liteServer.runSmcMethod"));
} else { } else {
auto f = F.move_as_ok(); auto f = F.move_as_ok();
td::actor::send_closure_later(Self, &TestNode::run_smc_method, mode, ref_blkid, ton::create_block_id(f->id_), td::actor::send_closure_later(Self, &TestNode::run_smc_method, mode, ref_blkid, ton::create_block_id(f->id_),
ton::create_block_id(f->shardblk_), std::move(f->shard_proof_), ton::create_block_id(f->shardblk_), std::move(f->shard_proof_),
std::move(f->proof_), std::move(f->state_proof_), workchain, addr, method_name, std::move(f->proof_), std::move(f->state_proof_), workchain, addr, method_name,
std::move(params), std::move(f->init_c7_), std::move(f->lib_extras_), std::move(params), std::move(f->init_c7_), std::move(f->lib_extras_),
std::move(f->result_), f->exit_code_); std::move(f->result_), f->exit_code_, std::move(promise));
} }
}); });
} }
} }
bool TestNode::dns_resolve_start(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid,
std::string domain, int cat, int mode) {
if (domain.size() > 1023) {
return set_error("domain name too long");
}
if (domain.size() >= 2 && domain[0] == '"' && domain.back() == '"') {
domain.erase(0, 1);
domain.pop_back();
}
std::vector<std::string> components;
std::size_t i, p = 0;
for (i = 0; i < domain.size(); i++) {
if (!domain[i] || (unsigned char)domain[i] >= 0xfe || (unsigned char)domain[i] <= ' ') {
return set_error("invalid characters in a domain name");
}
if (domain[i] == '.') {
if (i == p) {
return set_error("domain name cannot have an empty component");
}
components.emplace_back(domain, p, i - p);
p = i + 1;
}
}
if (i > p) {
components.emplace_back(domain, p, i - p);
}
std::string qdomain, qdomain0;
while (!components.empty()) {
qdomain += components.back();
qdomain += '\0';
components.pop_back();
}
if (!(ready_ && !client_.empty())) {
return set_error("server connection not ready");
}
if (workchain == ton::workchainInvalid) {
if (dns_root_queried_) {
workchain = ton::masterchainId;
addr = dns_root_;
} else {
auto P = td::PromiseCreator::lambda([this, blkid, domain, cat, mode](td::Result<td::Unit> R) {
if (R.is_error()) {
LOG(ERROR) << "cannot obtain root dns address from configuration: " << R.move_as_error();
} else if (dns_root_queried_) {
dns_resolve_start(ton::masterchainId, dns_root_, blkid, domain, cat, mode);
} else {
LOG(ERROR) << "cannot obtain root dns address from configuration parameter #4";
}
});
return get_config_params(mc_last_id_, std::move(P), 0x5000, "", {4});
}
}
return dns_resolve_send(workchain, addr, blkid, domain, qdomain, cat, mode);
}
bool TestNode::dns_resolve_send(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid,
std::string domain, std::string qdomain, int cat, int mode) {
LOG(INFO) << "dns_resolve for '" << domain << "' category=" << cat << " mode=" << mode
<< " starting from smart contract " << workchain << ":" << addr.to_hex() << " with respect to block "
<< blkid.to_str();
std::string qdomain0;
if (qdomain.size() <= 127) {
qdomain0 = qdomain;
} else {
qdomain0 = std::string{qdomain, 0, 127};
qdomain[125] = '\xff';
qdomain[126] = '\x0';
}
vm::CellBuilder cb;
Ref<vm::Cell> cell;
if (!(cb.store_bytes_bool(td::Slice(qdomain0)) && cb.finalize_to(cell))) {
return set_error("cannot store domain name into slice");
}
std::vector<vm::StackEntry> params;
params.emplace_back(vm::load_cell_slice_ref(std::move(cell)));
params.emplace_back(td::make_refint(cat));
auto P = td::PromiseCreator::lambda([this, workchain, addr, blkid, domain, qdomain, cat,
mode](td::Result<std::vector<vm::StackEntry>> R) {
if (R.is_error()) {
LOG(ERROR) << R.move_as_error();
return;
}
auto S = R.move_as_ok();
if (S.size() < 2 || !S[S.size() - 2].is_int() || !(S.back().is_cell() || S.back().is_null())) {
LOG(ERROR) << "dnsresolve did not return a value of type (int,cell)";
return;
}
auto cell = S.back().as_cell();
S.pop_back();
auto x = S.back().as_int();
S.clear();
if (!x->signed_fits_bits(32)) {
LOG(ERROR) << "invalid integer result of dnsresolve (" << x << ")";
return;
}
return dns_resolve_finish(workchain, addr, blkid, domain, qdomain, cat, mode, (int)x->to_long(), std::move(cell));
});
return start_run_method(workchain, addr, blkid, "dnsresolve", std::move(params), 0x1f, std::move(P));
}
bool TestNode::show_dns_record(std::ostream& os, int cat, Ref<vm::Cell> value, bool raw_dump) {
if (raw_dump) {
bool ok = show_dns_record(os, cat, value, false);
if (!ok) {
os << "cannot parse dns record; raw value: ";
vm::load_cell_slice(value).print_rec(print_limit_, os);
}
return ok;
}
if (value.is_null()) {
os << "(null)";
return true;
}
// block::gen::t_DNSRecord.print_ref(print_limit_, os, value);
if (!block::gen::t_DNSRecord.validate_ref(value)) {
return false;
}
block::gen::t_DNSRecord.print_ref(print_limit_, os, value);
auto cs = vm::load_cell_slice(value);
auto tag = block::gen::t_DNSRecord.get_tag(cs);
ton::WorkchainId wc;
ton::StdSmcAddress addr;
switch (tag) {
case block::gen::DNSRecord::dns_adnl_address: {
block::gen::DNSRecord::Record_dns_adnl_address rec;
if (tlb::unpack_exact(cs, rec)) {
os << "\n\tadnl address " << rec.adnl_addr.to_hex() << " = " << td::adnl_id_encode(rec.adnl_addr, true);
}
break;
}
case block::gen::DNSRecord::dns_smc_address: {
block::gen::DNSRecord::Record_dns_smc_address rec;
if (tlb::unpack_exact(cs, rec) && block::tlb::t_MsgAddressInt.extract_std_address(rec.smc_addr, wc, addr)) {
os << "\tsmart contract " << wc << ":" << addr.to_hex() << " = " << block::StdAddress{wc, addr}.rserialize(true);
}
break;
}
case block::gen::DNSRecord::dns_next_resolver: {
block::gen::DNSRecord::Record_dns_next_resolver rec;
if (tlb::unpack_exact(cs, rec) && block::tlb::t_MsgAddressInt.extract_std_address(rec.resolver, wc, addr)) {
os << "\tnext resolver " << wc << ":" << addr.to_hex() << " = " << block::StdAddress{wc, addr}.rserialize(true);
}
break;
}
}
return true;
}
void TestNode::dns_resolve_finish(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid,
std::string domain, std::string qdomain, int cat, int mode, int used_bits,
Ref<vm::Cell> value) {
if (used_bits <= 0) {
td::TerminalIO::out() << "domain '" << domain << "' not found" << std::endl;
return;
}
if ((used_bits & 7) || (unsigned)used_bits > 8 * std::min<std::size_t>(qdomain.size(), 126)) {
LOG(ERROR) << "too many bits used (" << used_bits << " out of " << qdomain.size() * 8 << ")";
return;
}
int pos = (used_bits >> 3);
if (qdomain[pos - 1]) {
LOG(ERROR) << "domain split not at a component boundary";
return;
}
bool end = ((std::size_t)pos == qdomain.size());
if (!end) {
LOG(INFO) << "partial information obtained";
if (value.is_null()) {
td::TerminalIO::out() << "domain '" << domain << "' not found: no next resolver" << std::endl;
return;
}
Ref<vm::CellSlice> nx_address;
ton::WorkchainId nx_wc;
ton::StdSmcAddress nx_addr;
if (!(block::gen::t_DNSRecord.cell_unpack_dns_next_resolver(value, nx_address) &&
block::tlb::t_MsgAddressInt.extract_std_address(std::move(nx_address), nx_wc, nx_addr))) {
LOG(ERROR) << "cannot parse next resolver info for " << domain.substr(qdomain.size() - pos);
std::ostringstream out;
vm::load_cell_slice(value).print_rec(print_limit_, out);
td::TerminalIO::err() << out.str() << std::endl;
return;
}
LOG(INFO) << "next resolver is " << nx_wc << ":" << nx_addr.to_hex();
if ((mode & 1)) {
return; // no recursive resolving
}
if (!(dns_resolve_send(nx_wc, nx_addr, blkid, domain, qdomain.substr(pos), cat, mode))) {
LOG(ERROR) << "cannot send next dns query";
return;
}
LOG(INFO) << "recursive dns query to '" << domain.substr(qdomain.size() - pos) << "' sent";
return;
}
auto out = td::TerminalIO::out();
out << "Result for domain '" << domain << "' category " << cat << (cat ? "" : " (all categories)") << std::endl;
try {
if (value.not_null()) {
std::ostringstream os0;
vm::load_cell_slice(value).print_rec(print_limit_, os0);
out << "raw data: " << os0.str() << std::endl;
}
if (!cat) {
vm::Dictionary dict{value, 16};
if (!dict.check_for_each([this, &out](Ref<vm::CellSlice> cs, td::ConstBitPtr key, int n) {
CHECK(n == 16);
int x = (int)key.get_int(16);
if (cs.is_null() || cs->size_ext() != 0x10000) {
out << "category #" << x << " : value is not a reference" << std::endl;
return false;
}
std::ostringstream os;
(void)show_dns_record(os, x, cs->prefetch_ref(), true);
out << "category #" << x << " : " << os.str() << std::endl;
return true;
})) {
out << "invalid dns record dictionary" << std::endl;
}
} else {
std::ostringstream os;
(void)show_dns_record(os, cat, value, true);
out << "category #" << cat << " : " << os.str() << std::endl;
}
} catch (vm::VmError& err) {
LOG(ERROR) << "vm error while traversing dns resolve result: " << err.get_msg();
} catch (vm::VmVirtError& err) {
LOG(ERROR) << "vm virtualization error while traversing dns resolve result: " << err.get_msg();
}
}
bool TestNode::get_one_transaction(ton::BlockIdExt blkid, ton::WorkchainId workchain, ton::StdSmcAddress addr, bool TestNode::get_one_transaction(ton::BlockIdExt blkid, ton::WorkchainId workchain, ton::StdSmcAddress addr,
ton::LogicalTime lt, bool dump) { ton::LogicalTime lt, bool dump) {
if (!blkid.is_valid_full()) { if (!blkid.is_valid_full()) {
@ -1192,8 +1475,8 @@ void TestNode::got_account_state(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, t
if (info.root.not_null()) { if (info.root.not_null()) {
out << "account state is "; out << "account state is ";
std::ostringstream outp; std::ostringstream outp;
block::gen::t_Account.print_ref(outp, info.root); block::gen::t_Account.print_ref(print_limit_, outp, info.root);
vm::load_cell_slice(info.root).print_rec(outp); vm::load_cell_slice(info.root).print_rec(print_limit_, outp);
out << outp.str(); out << outp.str();
out << "last transaction lt = " << info.last_trans_lt << " hash = " << info.last_trans_hash.to_hex() << std::endl; out << "last transaction lt = " << info.last_trans_lt << " hash = " << info.last_trans_hash.to_hex() << std::endl;
block::gen::Account::Record_account acc; block::gen::Account::Record_account acc;
@ -1269,7 +1552,8 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state, td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state,
ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method, ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method,
std::vector<vm::StackEntry> params, td::BufferSlice remote_c7, std::vector<vm::StackEntry> params, td::BufferSlice remote_c7,
td::BufferSlice remote_libs, td::BufferSlice remote_result, int remote_exit_code) { td::BufferSlice remote_libs, td::BufferSlice remote_result, int remote_exit_code,
td::Promise<std::vector<vm::StackEntry>> promise) {
LOG(INFO) << "got (partial) account state with mode=" << mode << " for " << workchain << ":" << addr.to_hex() LOG(INFO) << "got (partial) account state with mode=" << mode << " for " << workchain << ":" << addr.to_hex()
<< " with respect to blocks " << blk.to_str() << " with respect to blocks " << blk.to_str()
<< (shard_blk == blk ? "" : std::string{" and "} + shard_blk.to_str()); << (shard_blk == blk ? "" : std::string{" and "} + shard_blk.to_str());
@ -1287,6 +1571,7 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
auto r_info = account_state.validate(ref_blk, block::StdAddress(workchain, addr)); auto r_info = account_state.validate(ref_blk, block::StdAddress(workchain, addr));
if (r_info.is_error()) { if (r_info.is_error()) {
LOG(ERROR) << r_info.error().message(); LOG(ERROR) << r_info.error().message();
promise.set_error(r_info.move_as_error());
return; return;
} }
auto out = td::TerminalIO::out(); auto out = td::TerminalIO::out();
@ -1294,12 +1579,14 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
if (info.root.is_null()) { if (info.root.is_null()) {
LOG(ERROR) << "account state of " << workchain << ":" << addr.to_hex() << " is empty (cannot run method `" LOG(ERROR) << "account state of " << workchain << ":" << addr.to_hex() << " is empty (cannot run method `"
<< method << "`)"; << method << "`)";
promise.set_error(td::Status::Error(PSLICE() << "account state of " << workchain << ":" << addr.to_hex()
<< " is empty (cannot run method `" << method << "`)"));
return; return;
} }
if (false) { if (false) {
// DEBUG (dump state) // DEBUG (dump state)
std::ostringstream os; std::ostringstream os;
vm::CellSlice{vm::NoVm(), info.true_root}.print_rec(os); vm::CellSlice{vm::NoVm(), info.true_root}.print_rec(print_limit_, os);
out << "dump of account state (proof): " << os.str() << std::endl; out << "dump of account state (proof): " << os.str() << std::endl;
} }
// set deserialization limits // set deserialization limits
@ -1313,9 +1600,9 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
bool ok = val.deserialize(r_c7); bool ok = val.deserialize(r_c7);
val.dump(os); val.dump(os);
// os << std::endl; // os << std::endl;
// block::gen::t_VmStackValue.print_ref(os, r_c7); // block::gen::t_VmStackValue.print_ref(print_limit_, os, r_c7);
// os << std::endl; // os << std::endl;
// vm::CellSlice{vm::NoVmOrd(), r_c7}.print_rec(os); // vm::CellSlice{vm::NoVmOrd(), r_c7}.print_rec(print_limit_, os);
out << "remote_c7 (deserialized=" << ok << "): " << os.str() << std::endl; out << "remote_c7 (deserialized=" << ok << "): " << os.str() << std::endl;
} }
block::gen::Account::Record_account acc; block::gen::Account::Record_account acc;
@ -1324,6 +1611,7 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) && if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) &&
balance.validate_unpack(store.balance))) { balance.validate_unpack(store.balance))) {
LOG(ERROR) << "error unpacking account state"; LOG(ERROR) << "error unpacking account state";
promise.set_error(td::Status::Error("error unpacking account state"));
return; return;
} }
int tag = block::gen::t_AccountState.get_tag(*store.state); int tag = block::gen::t_AccountState.get_tag(*store.state);
@ -1369,6 +1657,7 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
if (exit_code != 0) { if (exit_code != 0) {
LOG(ERROR) << "VM terminated with error code " << exit_code; LOG(ERROR) << "VM terminated with error code " << exit_code;
out << "result: error " << exit_code << std::endl; out << "result: error " << exit_code << std::endl;
promise.set_error(td::Status::Error(PSLICE() << "VM terminated with non-zero exit code " << exit_code));
return; return;
} }
stack = vm.get_stack_ref(); stack = vm.get_stack_ref();
@ -1399,6 +1688,8 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
out << os.str(); out << os.str();
} }
} }
out.flush();
promise.set_result(stack->extract_contents());
} catch (vm::VmVirtError& err) { } catch (vm::VmVirtError& err) {
out << "virtualization error while parsing runSmcMethod result: " << err.get_msg(); out << "virtualization error while parsing runSmcMethod result: " << err.get_msg();
} catch (vm::VmError& err) { } catch (vm::VmError& err) {
@ -1485,8 +1776,8 @@ void TestNode::got_one_transaction(ton::BlockIdExt req_blkid, ton::BlockIdExt bl
} else { } else {
out << "transaction is "; out << "transaction is ";
std::ostringstream outp; std::ostringstream outp;
block::gen::t_Transaction.print_ref(outp, root); block::gen::t_Transaction.print_ref(print_limit_, outp, root, 0);
vm::load_cell_slice(root).print_rec(outp); vm::load_cell_slice(root).print_rec(print_limit_, outp);
out << outp.str(); out << outp.str();
} }
} }
@ -1611,8 +1902,8 @@ void TestNode::got_last_transactions(std::vector<ton::BlockIdExt> blkids, td::Bu
out << "transaction #" << c << " from block " << blkid.to_str() << (dump ? " is " : "\n"); out << "transaction #" << c << " from block " << blkid.to_str() << (dump ? " is " : "\n");
if (dump) { if (dump) {
std::ostringstream outp; std::ostringstream outp;
block::gen::t_Transaction.print_ref(outp, info.transaction); block::gen::t_Transaction.print_ref(print_limit_, outp, info.transaction);
vm::load_cell_slice(info.transaction).print_rec(outp); vm::load_cell_slice(info.transaction).print_rec(print_limit_, outp);
out << outp.str(); out << outp.str();
} }
block::gen::Transaction::Record trans; block::gen::Transaction::Record trans;
@ -1740,8 +2031,8 @@ void TestNode::got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::Bu
auto out = td::TerminalIO::out(); auto out = td::TerminalIO::out();
out << "shard configuration is "; out << "shard configuration is ";
std::ostringstream outp; std::ostringstream outp;
block::gen::t_ShardHashes.print_ref(outp, root); block::gen::t_ShardHashes.print_ref(print_limit_, outp, root);
vm::load_cell_slice(root).print_rec(outp); vm::load_cell_slice(root).print_rec(print_limit_, outp);
out << outp.str(); out << outp.str();
block::ShardConfig sh_conf; block::ShardConfig sh_conf;
if (!sh_conf.unpack(vm::load_cell_slice_ref(root))) { if (!sh_conf.unpack(vm::load_cell_slice_ref(root))) {
@ -1764,26 +2055,35 @@ void TestNode::got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::Bu
show_new_blkids(); show_new_blkids();
} }
bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string filename) { bool TestNode::get_config_params(ton::BlockIdExt blkid, td::Promise<td::Unit> do_after, int mode, std::string filename,
std::vector<int> params; std::vector<int> params) {
if (mode >= 0 && !seekeoln()) { if (mode < 0) {
mode = 0x8000;
}
if (!(mode & 0x9000) && !seekeoln()) {
mode |= 0x1000; mode |= 0x1000;
while (!seekeoln()) { while (!seekeoln()) {
int x; int x;
if (!convert_int32(get_word(), x)) { if (!convert_int32(get_word(), x)) {
do_after.set_error(td::Status::Error("integer configuration parameter id expected"));
return set_error("integer configuration parameter id expected"); return set_error("integer configuration parameter id expected");
} }
params.push_back(x); params.push_back(x);
} }
} }
if (!(ready_ && !client_.empty())) { if (!(ready_ && !client_.empty())) {
do_after.set_error(td::Status::Error("integer configuration parameter id expected"));
return set_error("server connection not ready"); return set_error("server connection not ready");
} }
if (!blkid.is_masterchain_ext()) { if (!blkid.is_masterchain_ext()) {
do_after.set_error(td::Status::Error("integer configuration parameter id expected"));
return set_error("only masterchain blocks contain configuration"); return set_error("only masterchain blocks contain configuration");
} }
if (blkid == mc_last_id_) {
mode |= 0x2000;
}
auto params_copy = params; auto params_copy = params;
auto b = (mode & 0x3000) == 0x1000 auto b = (mode & 0x1000)
? ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getConfigParams>( ? ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getConfigParams>(
0, ton::create_tl_lite_block_id(blkid), std::move(params_copy)), 0, ton::create_tl_lite_block_id(blkid), std::move(params_copy)),
true) true)
@ -1792,9 +2092,11 @@ bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string fi
true); true);
LOG(INFO) << "requesting " << params.size() << " configuration parameters with respect to masterchain block " LOG(INFO) << "requesting " << params.size() << " configuration parameters with respect to masterchain block "
<< blkid.to_str(); << blkid.to_str();
return envelope_send_query(std::move(b), [ Self = actor_id(this), mode, filename, blkid, return envelope_send_query(std::move(b), [
params = std::move(params) ](td::Result<td::BufferSlice> R) mutable { Self = actor_id(this), mode, filename, blkid, params = std::move(params), do_after = std::move(do_after)
](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) { if (R.is_error()) {
do_after.set_error(R.move_as_error());
return; return;
} }
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_configInfo>(R.move_as_ok(), true); auto F = ton::fetch_tl_object<ton::lite_api::liteServer_configInfo>(R.move_as_ok(), true);
@ -1804,13 +2106,14 @@ bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string fi
auto f = F.move_as_ok(); auto f = F.move_as_ok();
td::actor::send_closure_later(Self, &TestNode::got_config_params, blkid, ton::create_block_id(f->id_), td::actor::send_closure_later(Self, &TestNode::got_config_params, blkid, ton::create_block_id(f->id_),
std::move(f->state_proof_), std::move(f->config_proof_), mode, filename, std::move(f->state_proof_), std::move(f->config_proof_), mode, filename,
std::move(params)); std::move(params), std::move(do_after));
} }
}); });
} }
void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, td::BufferSlice state_proof, void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, td::BufferSlice state_proof,
td::BufferSlice cfg_proof, int mode, std::string filename, std::vector<int> params) { td::BufferSlice cfg_proof, int mode, std::string filename, std::vector<int> params,
td::Promise<td::Unit> do_after) {
LOG(INFO) << "got configuration parameters"; LOG(INFO) << "got configuration parameters";
if (!blkid.is_masterchain_ext()) { if (!blkid.is_masterchain_ext()) {
LOG(ERROR) << "reference block " << blkid.to_str() << " for the configuration is not a valid masterchain block"; LOG(ERROR) << "reference block " << blkid.to_str() << " for the configuration is not a valid masterchain block";
@ -1833,7 +2136,7 @@ void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blki
return; return;
} }
auto config = res.move_as_ok(); auto config = res.move_as_ok();
if (mode < 0) { if (mode & 0x8000) {
auto F = vm::std_boc_serialize(config->get_root_cell(), 2); auto F = vm::std_boc_serialize(config->get_root_cell(), 2);
if (F.is_error()) { if (F.is_error()) {
LOG(ERROR) << "cannot serialize configuration: " << F.move_as_error().to_string(); LOG(ERROR) << "cannot serialize configuration: " << F.move_as_error().to_string();
@ -1859,26 +2162,32 @@ void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blki
} else { } else {
std::ostringstream os; std::ostringstream os;
if (i >= 0) { if (i >= 0) {
block::gen::ConfigParam{i}.print_ref(os, value); block::gen::ConfigParam{i}.print_ref(print_limit_, os, value);
os << std::endl; os << std::endl;
} }
vm::load_cell_slice(value).print_rec(os); vm::load_cell_slice(value).print_rec(print_limit_, os);
out << os.str() << std::endl; out << os.str() << std::endl;
if (i == 4 && (mode & 0x2000)) {
register_config_param4(value);
}
} }
} }
} else { } else {
config->foreach_config_param([&out](int i, Ref<vm::Cell> value) { config->foreach_config_param([this, &out, mode](int i, Ref<vm::Cell> value) {
out << "ConfigParam(" << i << ") = "; out << "ConfigParam(" << i << ") = ";
if (value.is_null()) { if (value.is_null()) {
out << "(null)\n"; out << "(null)\n";
} else { } else {
std::ostringstream os; std::ostringstream os;
if (i >= 0) { if (i >= 0) {
block::gen::ConfigParam{i}.print_ref(os, value); block::gen::ConfigParam{i}.print_ref(print_limit_, os, value);
os << std::endl; os << std::endl;
} }
vm::load_cell_slice(value).print_rec(os); vm::load_cell_slice(value).print_rec(print_limit_, os);
out << os.str() << std::endl; out << os.str() << std::endl;
if (i == 4 && (mode & 0x2000)) {
register_config_param4(value);
}
} }
return true; return true;
}); });
@ -1888,6 +2197,25 @@ void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blki
} catch (vm::VmVirtError& err) { } catch (vm::VmVirtError& err) {
LOG(ERROR) << "virtualization error while traversing configuration: " << err.get_msg(); LOG(ERROR) << "virtualization error while traversing configuration: " << err.get_msg();
} }
do_after.set_result(td::Unit());
}
bool TestNode::register_config_param4(Ref<vm::Cell> value) {
if (value.is_null()) {
return false;
}
vm::CellSlice cs{vm::NoVmOrd(), std::move(value)};
ton::StdSmcAddress addr;
if (cs.size_ext() == 256 && cs.fetch_bits_to(addr)) {
dns_root_queried_ = true;
if (dns_root_ != addr) {
dns_root_ = addr;
LOG(INFO) << "dns root set to -1:" << addr.to_hex();
}
return true;
} else {
return false;
}
} }
bool TestNode::get_block(ton::BlockIdExt blkid, bool dump) { bool TestNode::get_block(ton::BlockIdExt blkid, bool dump) {
@ -1981,8 +2309,8 @@ void TestNode::got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump)
auto out = td::TerminalIO::out(); auto out = td::TerminalIO::out();
out << "block contents is "; out << "block contents is ";
std::ostringstream outp; std::ostringstream outp;
block::gen::t_Block.print_ref(outp, root); block::gen::t_Block.print_ref(print_limit_, outp, root);
vm::load_cell_slice(root).print_rec(outp); vm::load_cell_slice(root).print_rec(print_limit_, outp);
out << outp.str(); out << outp.str();
show_block_header(blkid, std::move(root), 0xffff); show_block_header(blkid, std::move(root), 0xffff);
} else { } else {
@ -2037,8 +2365,8 @@ void TestNode::got_state(ton::BlockIdExt blkid, ton::RootHash root_hash, ton::Fi
auto out = td::TerminalIO::out(); auto out = td::TerminalIO::out();
out << "shard state contents is "; out << "shard state contents is ";
std::ostringstream outp; std::ostringstream outp;
block::gen::t_ShardState.print_ref(outp, root); block::gen::t_ShardState.print_ref(print_limit_, outp, root);
vm::load_cell_slice(root).print_rec(outp); vm::load_cell_slice(root).print_rec(print_limit_, outp);
out << outp.str(); out << outp.str();
show_state_header(blkid, std::move(root), 0xffff); show_state_header(blkid, std::move(root), 0xffff);
} else { } else {
@ -2173,7 +2501,7 @@ void TestNode::got_block_header(ton::BlockIdExt blkid, td::BufferSlice data, int
auto root = res.move_as_ok(); auto root = res.move_as_ok();
std::ostringstream outp; std::ostringstream outp;
vm::CellSlice cs{vm::NoVm(), root}; vm::CellSlice cs{vm::NoVm(), root};
cs.print_rec(outp); cs.print_rec(print_limit_, outp);
td::TerminalIO::out() << outp.str(); td::TerminalIO::out() << outp.str();
try { try {
auto virt_root = vm::MerkleProof::virtualize(root, 1); auto virt_root = vm::MerkleProof::virtualize(root, 1);
@ -2405,6 +2733,11 @@ int main(int argc, char* argv[]) {
td::actor::send_closure(x, &TestNode::set_db_root, fname.str()); td::actor::send_closure(x, &TestNode::set_db_root, fname.str());
return td::Status::OK(); return td::Status::OK();
}); });
p.add_option('L', "print-limit", "sets maximum count of recursively printed objects", [&](td::Slice arg) {
auto plimit = td::to_integer<int>(arg);
td::actor::send_closure(x, &TestNode::set_print_limit, plimit);
return plimit >= 0 ? td::Status::OK() : td::Status::Error("printing limit must be non-negative");
});
p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) { p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) {
verbosity = td::to_integer<int>(arg); verbosity = td::to_integer<int>(arg);
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity); SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity);

View file

@ -50,6 +50,7 @@ class TestNode : public td::actor::Actor {
bool readline_enabled_ = true; bool readline_enabled_ = true;
bool server_ok_ = false; bool server_ok_ = false;
td::int32 liteserver_idx_ = -1; td::int32 liteserver_idx_ = -1;
int print_limit_ = 1024;
bool ready_ = false; bool ready_ = false;
bool inited_ = false; bool inited_ = false;
@ -66,6 +67,9 @@ class TestNode : public td::actor::Actor {
ton::BlockIdExt last_block_id_, last_state_id_; ton::BlockIdExt last_block_id_, last_state_id_;
td::BufferSlice last_block_data_, last_state_data_; td::BufferSlice last_block_data_, last_state_data_;
ton::StdSmcAddress dns_root_;
bool dns_root_queried_{false};
std::string line_; std::string line_;
const char *parse_ptr_, *parse_end_; const char *parse_ptr_, *parse_end_;
td::Status error_; td::Status error_;
@ -118,16 +122,31 @@ class TestNode : public td::actor::Actor {
ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string filename, int mode); ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string filename, int mode);
bool parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid, bool parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid,
std::string method_name, bool ext_mode); std::string method_name, bool ext_mode);
bool start_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid,
std::string method_name, std::vector<vm::StackEntry> params, int mode,
td::Promise<std::vector<vm::StackEntry>> promise);
void run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton::BlockIdExt shard_blk, void run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton::BlockIdExt shard_blk,
td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state, td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state,
ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method, ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method,
std::vector<vm::StackEntry> params, td::BufferSlice remote_c7, td::BufferSlice remote_libs, std::vector<vm::StackEntry> params, td::BufferSlice remote_c7, td::BufferSlice remote_libs,
td::BufferSlice remote_result, int remote_exit_code); td::BufferSlice remote_result, int remote_exit_code,
td::Promise<std::vector<vm::StackEntry>> promise);
bool register_config_param4(Ref<vm::Cell> value);
bool dns_resolve_start(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid, std::string domain,
int cat, int mode);
bool dns_resolve_send(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid, std::string domain,
std::string qdomain, int cat, int mode);
void dns_resolve_finish(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid,
std::string domain, std::string qdomain, int cat, int mode, int used_bits,
Ref<vm::Cell> value);
bool show_dns_record(std::ostream& os, int cat, Ref<vm::Cell> value, bool raw_dump);
bool get_all_shards(bool use_last = true, ton::BlockIdExt blkid = {}); bool get_all_shards(bool use_last = true, ton::BlockIdExt blkid = {});
void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data); void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data);
bool get_config_params(ton::BlockIdExt blkid, int mode = 0, std::string filename = ""); bool get_config_params(ton::BlockIdExt blkid, td::Promise<td::Unit> do_after, int mode = 0, std::string filename = "",
std::vector<int> params = {});
void got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, td::BufferSlice state_proof, void got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, td::BufferSlice state_proof,
td::BufferSlice cfg_proof, int mode, std::string filename, std::vector<int> params); td::BufferSlice cfg_proof, int mode, std::string filename, std::vector<int> params,
td::Promise<td::Unit> do_after);
bool get_block(ton::BlockIdExt blk, bool dump = false); bool get_block(ton::BlockIdExt blk, bool dump = false);
void got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump); void got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump);
bool get_state(ton::BlockIdExt blk, bool dump = false); bool get_state(ton::BlockIdExt blk, bool dump = false);
@ -173,7 +192,7 @@ class TestNode : public td::actor::Actor {
bool set_error(td::Status error); bool set_error(td::Status error);
bool set_error(std::string err_msg); bool set_error(std::string err_msg);
void show_context() const; void show_context() const;
bool parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr); bool parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool allow_none = false);
static int parse_hex_digit(int c); static int parse_hex_digit(int c);
static bool parse_hash(const char* str, ton::Bits256& hash); static bool parse_hash(const char* str, ton::Bits256& hash);
static bool parse_hash(td::Slice str, ton::Bits256& hash); static bool parse_hash(td::Slice str, ton::Bits256& hash);
@ -186,6 +205,8 @@ class TestNode : public td::actor::Actor {
bool parse_hash(ton::Bits256& hash); bool parse_hash(ton::Bits256& hash);
bool parse_lt(ton::LogicalTime& lt); bool parse_lt(ton::LogicalTime& lt);
bool parse_uint32(td::uint32& val); bool parse_uint32(td::uint32& val);
bool parse_int32(td::int32& val);
bool parse_int16(int& val);
bool parse_shard_id(ton::ShardIdFull& shard); bool parse_shard_id(ton::ShardIdFull& shard);
bool parse_block_id_ext(ton::BlockIdExt& blkid, bool allow_incomplete = false); bool parse_block_id_ext(ton::BlockIdExt& blkid, bool allow_incomplete = false);
bool parse_block_id_ext(std::string blk_id_string, ton::BlockIdExt& blkid, bool allow_incomplete = false) const; bool parse_block_id_ext(std::string blk_id_string, ton::BlockIdExt& blkid, bool allow_incomplete = false) const;
@ -195,6 +216,7 @@ class TestNode : public td::actor::Actor {
bool register_blkid(const ton::BlockIdExt& blkid); bool register_blkid(const ton::BlockIdExt& blkid);
bool show_new_blkids(bool all = false); bool show_new_blkids(bool all = false);
bool complete_blkid(ton::BlockId partial_blkid, ton::BlockIdExt& complete_blkid) const; bool complete_blkid(ton::BlockId partial_blkid, ton::BlockIdExt& complete_blkid) const;
td::Promise<td::Unit> trivial_promise();
public: public:
void conn_ready() { void conn_ready() {
@ -237,6 +259,11 @@ class TestNode : public td::actor::Actor {
fail_timeout_ = ts; fail_timeout_ = ts;
alarm_timestamp().relax(fail_timeout_); alarm_timestamp().relax(fail_timeout_);
} }
void set_print_limit(int plimit) {
if (plimit >= 0) {
print_limit_ = plimit;
}
}
void add_cmd(td::BufferSlice data) { void add_cmd(td::BufferSlice data) {
ex_mode_ = true; ex_mode_ = true;
ex_queries_.push_back(std::move(data)); ex_queries_.push_back(std::move(data));

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here. from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "third_party/FAAArrayQueue.h" #include "third_party/FAAArrayQueue.h"
#include "third_party/HazardPointers.h" #include "third_party/HazardPointers.h"
@ -56,7 +56,10 @@ extern "C" {
#include "td/utils/Random.h" #include "td/utils/Random.h"
#include "td/utils/Slice.h" #include "td/utils/Slice.h"
#include "td/utils/Status.h" #include "td/utils/Status.h"
#include "td/utils/StealingQueue.h"
#include "td/utils/ThreadSafeCounter.h"
#include "td/utils/UInt.h" #include "td/utils/UInt.h"
#include "td/utils/VectorQueue.h"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
@ -796,10 +799,13 @@ class MpmcQueueBenchmark : public td::Benchmark {
std::vector<td::thread> m_threads(m_); std::vector<td::thread> m_threads(m_);
auto impl = std::make_unique<Impl>(n_ + m_ + 1); auto impl = std::make_unique<Impl>(n_ + m_ + 1);
size_t thread_id = 0; size_t thread_id = 0;
td::ThreadSafeCounter counter;
CHECK(counter.sum() == 0);
for (auto &thread : m_threads) { for (auto &thread : m_threads) {
thread = td::thread([&, thread_id] { thread = td::thread([&, thread_id] {
while (true) { while (true) {
size_t value = impl->pop(thread_id); size_t value = impl->pop(thread_id);
counter.add(-static_cast<td::int64>(value));
if (!value) { if (!value) {
break; break;
} }
@ -811,6 +817,7 @@ class MpmcQueueBenchmark : public td::Benchmark {
thread = td::thread([&, thread_id] { thread = td::thread([&, thread_id] {
for (int i = 0; i < n / n_; i++) { for (int i = 0; i < n / n_; i++) {
impl->push(static_cast<size_t>(i + 1), thread_id); impl->push(static_cast<size_t>(i + 1), thread_id);
counter.add(i + 1);
} }
}); });
thread_id++; thread_id++;
@ -818,7 +825,10 @@ class MpmcQueueBenchmark : public td::Benchmark {
for (auto &thread : n_threads) { for (auto &thread : n_threads) {
thread.join(); thread.join();
} }
for (int i = 0; i < m_; i++) { while (counter.sum() != 0) {
td::this_thread::yield();
}
for (int i = 0; i < m_ + n_ + 1; i++) {
impl->push(0, thread_id); impl->push(0, thread_id);
} }
for (auto &thread : m_threads) { for (auto &thread : m_threads) {
@ -832,6 +842,62 @@ class MpmcQueueBenchmark : public td::Benchmark {
int m_; int m_;
}; };
template <class Impl>
class MpmcQueueBenchmark2 : public td::Benchmark {
public:
MpmcQueueBenchmark2(int n, int k) : n_(n), k_(k) {
}
std::string get_description() const override {
return PSTRING() << "MpmcQueueBenchmark2 " << n_ << " " << k_ << " " << Impl::get_description();
}
void run(int n) override {
std::vector<td::thread> n_threads(n_);
auto impl = std::make_unique<Impl>(n_ + 1);
size_t thread_id = 0;
std::atomic<int> left{k_};
for (int i = 0; i < k_; i++) {
impl->push(n, thread_id);
}
for (auto &thread : n_threads) {
thread = td::thread([&, thread_id] {
while (true) {
size_t value = impl->pop(thread_id);
if (value > 1) {
impl->push(value - 1, thread_id);
}
if (value == 1) {
left--;
}
if (!value) {
break;
}
}
});
thread_id++;
}
while (left.load() != 0) {
td::this_thread::yield();
}
for (int i = 0; i < n_ + 1; i++) {
impl->push(0, thread_id);
}
for (auto &thread : n_threads) {
thread.join();
}
impl.reset();
}
private:
int n_;
int k_;
};
class Cheat { class Cheat {
public: public:
explicit Cheat(size_t thread_n) : impl_(static_cast<int>(thread_n)) { explicit Cheat(size_t thread_n) : impl_(static_cast<int>(thread_n)) {
@ -959,26 +1025,95 @@ std::string CfQueueT<FAAArrayQueue<Cell>, Cell>::get_description() {
template <class Value> template <class Value>
class MoodyQueue { class MoodyQueue {
public: public:
explicit MoodyQueue(size_t) { explicit MoodyQueue(size_t n) {
for (size_t i = 0; i < n; i++) {
p.push_back(moodycamel::ProducerToken(q));
c.push_back(moodycamel::ConsumerToken(q));
}
} }
static std::string get_description() { static std::string get_description() {
return "moodycamel queue"; return "moodycamel queue";
} }
void push(Value v, size_t) { void push(Value v, size_t tid) {
q.enqueue(v); q.enqueue(p[tid], v);
} }
Value pop(size_t) { Value pop(size_t tid) {
Value res; Value res;
while (!q.try_dequeue(res)) { while (!q.try_dequeue(c[tid], res)) {
} }
//q.wait_dequeue(c[tid], res);
return res; return res;
} }
bool try_pop(Value &value, size_t) { bool try_pop(Value &value, size_t tid) {
return q.try_dequeue(value); return q.try_dequeue(c[tid], value);
} }
private: private:
moodycamel::ConcurrentQueue<Value> q; moodycamel::ConcurrentQueue<Value> q;
std::vector<moodycamel::ProducerToken> p;
std::vector<moodycamel::ConsumerToken> c;
};
struct Sem {
public:
void post() {
if (++cnt_ == 0) {
{
std::unique_lock<std::mutex> lk(mutex_);
}
cnd_.notify_one();
}
}
void wait(int cnt = 1) {
auto was = cnt_.fetch_sub(cnt);
if (was >= cnt) {
return;
}
std::unique_lock<std::mutex> lk(mutex_);
cnd_.wait(lk, [&] { return cnt_ >= 0; });
}
private:
std::mutex mutex_;
std::condition_variable cnd_;
std::atomic<int> cnt_{0};
};
template <class Value>
class MagicQueue {
public:
explicit MagicQueue(size_t n) : n_(n), qs_(n_ - 1), pos_{0} {
}
static std::string get_description() {
return "magic queue";
}
void push(Value v, size_t tid) {
if (v == 0) {
return;
}
if (tid + 1 == n_) {
qs_[pos_].push(v);
pos_ = (pos_ + 1) % (n_ - 1);
} else {
qs_[tid].push(v);
}
}
Value pop(size_t tid) {
CHECK(tid + 1 != n_);
if (qs_[tid].empty()) {
return 0;
}
return qs_[tid].pop();
}
bool try_pop(Value &value, size_t) {
UNREACHABLE();
}
private:
size_t n_;
std::vector<td::VectorQueue<Value>> qs_;
size_t pos_;
}; };
#if TG_LCR_QUEUE #if TG_LCR_QUEUE
@ -1110,65 +1245,155 @@ class WaitQueue {
static std::string get_description() { static std::string get_description() {
return "Wait + " + Q::get_description(); return "Wait + " + Q::get_description();
} }
explicit WaitQueue(size_t threads_n) : impl(threads_n) {
explicit WaitQueue(size_t threads_n) : impl(threads_n), slots(threads_n) {
for (size_t i = 0; i < threads_n; i++) {
waiter.init_slot(slots[i].slot, static_cast<int>(i));
} }
}
T pop(size_t thread_id) { T pop(size_t thread_id) {
T res; T res;
int yields = 0; while (true) {
while (!impl.try_pop(res, thread_id)) { if (slots[thread_id].local_queue.try_pop(res)) {
yields = waiter.wait(yields, static_cast<uint32>(thread_id)); break;
} }
waiter.stop_wait(yields, static_cast<uint32>(thread_id)); if (impl.try_pop(res, thread_id)) {
break;
}
waiter.wait(slots[thread_id].slot);
}
waiter.stop_wait(slots[thread_id].slot);
//LOG(ERROR) << "GOT";
return res; return res;
} }
void push_local(T value, size_t thread_id) {
auto o_value = slots[thread_id].local_queue.push(value);
if (o_value) {
push(o_value.unwrap, thread_id);
}
}
void push(T value, size_t thread_id) { void push(T value, size_t thread_id) {
impl.push(std::move(value), static_cast<uint32>(thread_id)); impl.push(value, static_cast<uint32>(thread_id));
waiter.notify(); waiter.notify();
} }
private: private:
W waiter; W waiter;
Q impl; Q impl;
struct Slot {
typename W::Slot slot;
td::actor::core::LocalQueue<T> local_queue;
};
std::vector<Slot> slots;
};
template <class Q, class W, class T>
class StealingWaitQueue {
public:
static std::string get_description() {
return "StealWait + " + Q::get_description();
}
explicit StealingWaitQueue(size_t threads_n) : impl(threads_n), slots(threads_n) {
for (size_t i = 0; i < threads_n; i++) {
waiter.init_slot(slots[i].slot, static_cast<int>(i));
}
}
T pop(size_t thread_id) {
T res;
while (true) {
if (slots[thread_id].stealing_queue.local_pop(res)) {
break;
}
if (slots[thread_id].local_queue.try_pop(res)) {
break;
}
if (impl.try_pop(res, thread_id)) {
break;
}
bool ok = false;
for (size_t i = 1; i < slots.size(); i++) {
auto pos = (i + thread_id) % slots.size();
if (slots[thread_id].stealing_queue.steal(res, slots[pos].stealing_queue)) {
ok = true;
break;
}
}
if (ok) {
break;
}
waiter.wait(slots[thread_id].slot);
}
waiter.stop_wait(slots[thread_id].slot);
//LOG(ERROR) << "GOT";
return res;
}
void push_local(T value, size_t thread_id) {
auto o_value = slots[thread_id].local_queue.push(value);
if (o_value) {
push(o_value.unwrap, thread_id);
}
}
void push(T value, size_t thread_id) {
slots[thread_id].stealing_queue.local_push(value,
[&](auto value) { impl.push(value, static_cast<uint32>(thread_id)); });
waiter.notify();
}
private:
W waiter;
Q impl;
struct Slot {
typename W::Slot slot;
td::actor::core::LocalQueue<T> local_queue;
td::StealingQueue<T> stealing_queue;
};
std::vector<Slot> slots;
}; };
void run_queue_bench(int n, int m) { void run_queue_bench(int n, int m) {
bench(MpmcQueueBenchmark<WaitQueue<td::MpmcQueue<size_t>, td::MpmcWaiter, size_t>>(n, m), 2); bench(MpmcQueueBenchmark<MoodyQueue<size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<td::MpmcQueue<size_t>>(n, m), 2); bench(MpmcQueueBenchmark<StealingWaitQueue<td::MpmcQueue<size_t>, td::MpmcEagerWaiter, size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<td::MpmcQueueOld<size_t>>(n, m), 2); bench(MpmcQueueBenchmark<StealingWaitQueue<td::MpmcQueue<size_t>, td::MpmcSleepyWaiter, size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<Cheat>(n, m), 2); bench(MpmcQueueBenchmark<WaitQueue<td::MpmcQueue<size_t>, td::MpmcEagerWaiter, size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<CfQueue<FAAArrayQueue<size_t>>>(n, m), 2); bench(MpmcQueueBenchmark<WaitQueue<td::MpmcQueue<size_t>, td::MpmcSleepyWaiter, size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<CfQueue<LazyIndexArrayQueue<size_t>>>(n, m), 2); //bench(MpmcQueueBenchmark<td::MpmcQueue<size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<StupidQueue<size_t>>(n, m), 2); //bench(MpmcQueueBenchmark<td::MpmcQueueOld<size_t>>(n, m), 2);
//bench(MpmcQueueBenchmark<Cheat>(n, m), 2);
//bench(MpmcQueueBenchmark<CfQueue<FAAArrayQueue<size_t>>>(n, m), 2);
//bench(MpmcQueueBenchmark<CfQueue<LazyIndexArrayQueue<size_t>>>(n, m), 2);
//bench(MpmcQueueBenchmark<StupidQueue<size_t>>(n, m), 2);
//bench(MpmcQueueBenchmark<MpQueue>(n, m), 2); //bench(MpmcQueueBenchmark<MpQueue>(n, m), 2);
#if TG_LCR_QUEUE #if TG_LCR_QUEUE
bench(MpmcQueueBenchmark<CfQueue<LCRQueue<size_t>>>(n, m), 2); bench(MpmcQueueBenchmark<CfQueue<LCRQueue<size_t>>>(n, m), 2);
#endif #endif
} }
void run_queue_bench2(int n, int k) {
bench(MpmcQueueBenchmark2<StealingWaitQueue<td::MpmcQueue<size_t>, td::MpmcSleepyWaiter, size_t>>(n, k), 2);
bench(MpmcQueueBenchmark2<StealingWaitQueue<td::MpmcQueue<size_t>, td::MpmcEagerWaiter, size_t>>(n, k), 2);
bench(MpmcQueueBenchmark2<MagicQueue<size_t>>(n, k), 2);
bench(MpmcQueueBenchmark2<MoodyQueue<size_t>>(n, k), 2);
bench(MpmcQueueBenchmark2<WaitQueue<td::MpmcQueue<size_t>, td::MpmcEagerWaiter, size_t>>(n, k), 2);
bench(MpmcQueueBenchmark2<WaitQueue<td::MpmcQueue<size_t>, td::MpmcSleepyWaiter, size_t>>(n, k), 2);
//bench(MpmcQueueBenchmark2<td::MpmcQueue<size_t>>(n, k), 2);
//bench(MpmcQueueBenchmark2<td::MpmcQueueOld<size_t>>(n, k), 2);
//bench(MpmcQueueBenchmark2<Cheat>(n, k), 2);
//bench(MpmcQueueBenchmark2<CfQueue<FAAArrayQueue<size_t>>>(n, k), 2);
//bench(MpmcQueueBenchmark2<CfQueue<LazyIndexArrayQueue<size_t>>>(n, k), 2);
//bench(MpmcQueueBenchmark2<StupidQueue<size_t>>(n, k), 2);
struct Sem { //bench(MpmcQueueBenchmark<MpQueue>(n, m), 2);
public: #if TG_LCR_QUEUE
void post() { bench(MpmcQueueBenchmark2<CfQueue<LCRQueue<size_t>>>(n, k), 2);
if (++cnt_ == 0) { #endif
{
std::unique_lock<std::mutex> lk(mutex_);
} }
cnd_.notify_one();
}
}
void wait(int cnt = 1) {
auto was = cnt_.fetch_sub(cnt);
if (was >= cnt) {
return;
}
std::unique_lock<std::mutex> lk(mutex_);
cnd_.wait(lk, [&] { return cnt_ >= 0; });
}
private:
std::mutex mutex_;
std::condition_variable cnd_;
std::atomic<int> cnt_{0};
};
class ChainedSpawn : public td::Benchmark { class ChainedSpawn : public td::Benchmark {
public: public:
@ -1415,17 +1640,18 @@ class YieldMany : public td::Benchmark {
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc > 1) { if (argc > 1) {
if (argv[1][0] == 'a') { if (argv[1][0] == 'a') {
bench_n(MpmcQueueBenchmark<td::MpmcQueue<size_t>>(1, 1), 1 << 26); bench_n(MpmcQueueBenchmark2<WaitQueue<td::MpmcQueue<size_t>, td::MpmcEagerWaiter, size_t>>(50, 1), 1 << 26);
//bench_n(MpmcQueueBenchmark<td::MpmcQueue<size_t>>(1, 1), 1 << 26);
//bench_n(MpmcQueueBenchmark<MpQueue>(1, 40), 1 << 20); //bench_n(MpmcQueueBenchmark<MpQueue>(1, 40), 1 << 20);
//bench_n(MpmcQueueBenchmark<CfQueue<LCRQueue<size_t>>>(1, 40), 1 << 20); //bench_n(MpmcQueueBenchmark<CfQueue<LCRQueue<size_t>>>(1, 40), 1 << 20);
} else { } else {
bench_n(MpmcQueueBenchmark<td::MpmcQueueOld<size_t>>(1, 1), 1 << 26); bench_n(MpmcQueueBenchmark2<WaitQueue<td::MpmcQueue<size_t>, td::MpmcSleepyWaiter, size_t>>(50, 1), 1 << 26);
//bench_n(MpmcQueueBenchmark<td::MpmcQueueOld<size_t>>(1, 1), 1 << 26);
//bench_n(MpmcQueueBenchmark<CfQueue<LCRQueue<size_t>>>(1, 40), 1 << 20); //bench_n(MpmcQueueBenchmark<CfQueue<LCRQueue<size_t>>>(1, 40), 1 << 20);
//bench_n(MpmcQueueBenchmark<CfQueue<FAAArrayQueue<size_t>>>(1, 1), 1 << 26); //bench_n(MpmcQueueBenchmark<CfQueue<FAAArrayQueue<size_t>>>(1, 1), 1 << 26);
} }
return 0; return 0;
} }
bench(YieldMany(false)); bench(YieldMany(false));
bench(YieldMany(true)); bench(YieldMany(true));
bench(SpawnMany(false)); bench(SpawnMany(false));
@ -1436,6 +1662,22 @@ int main(int argc, char **argv) {
bench(ChainedSpawnInplace(true)); bench(ChainedSpawnInplace(true));
bench(ChainedSpawn(false)); bench(ChainedSpawn(false));
bench(ChainedSpawn(true)); bench(ChainedSpawn(true));
run_queue_bench(10, 10);
run_queue_bench(10, 1);
run_queue_bench(1, 10);
run_queue_bench(1, 1);
run_queue_bench(2, 10);
run_queue_bench(2, 2);
run_queue_bench(10, 1);
run_queue_bench2(50, 1);
run_queue_bench2(50, 2);
run_queue_bench2(1, 100);
run_queue_bench2(1, 1000);
run_queue_bench2(10, 2);
run_queue_bench2(10, 1000);
return 0; return 0;
bench(ActorDummyQuery()); bench(ActorDummyQuery());
@ -1466,16 +1708,5 @@ int main(int argc, char **argv) {
bench(CalcHashSha256Benchmark<BlockSha256MpmcQueue<BoundedMpmcQueue<std::function<void()>>>>()); bench(CalcHashSha256Benchmark<BlockSha256MpmcQueue<BoundedMpmcQueue<std::function<void()>>>>());
bench(CalcHashSha256Benchmark<BlockSha256MpmcQueue<td::MpmcQueue<std::function<void()>>>>()); bench(CalcHashSha256Benchmark<BlockSha256MpmcQueue<td::MpmcQueue<std::function<void()>>>>());
run_queue_bench(1, 10);
run_queue_bench(1, 1);
run_queue_bench(2, 10);
run_queue_bench(2, 2);
run_queue_bench(10, 10);
run_queue_bench(2, 2);
run_queue_bench(1, 10);
run_queue_bench(10, 1);
run_queue_bench(10, 10);
return 0; return 0;
} }

View file

@ -269,17 +269,17 @@ static inline thread_id_t thread_id() {
namespace moodycamel { namespace moodycamel {
namespace details { namespace details {
#if defined(__GNUC__) #if defined(__GNUC__)
inline bool likely(bool x) { static inline bool(likely)(bool x) {
return __builtin_expect((x), true); return __builtin_expect((x), true);
} }
inline bool unlikely(bool x) { static inline bool(unlikely)(bool x) {
return __builtin_expect((x), false); return __builtin_expect((x), false);
} }
#else #else
inline bool likely(bool x) { static inline bool(likely)(bool x) {
return x; return x;
} }
inline bool unlikely(bool x) { static inline bool(unlikely)(bool x) {
return x; return x;
} }
#endif #endif
@ -300,8 +300,8 @@ struct const_numeric_max {
: static_cast<T>(-1); : static_cast<T>(-1);
}; };
#if defined(__GNUC__) && !defined(__clang__) #if defined(__GLIBCXX__)
typedef ::max_align_t std_max_align_t; // GCC forgot to add it to std:: for a while typedef ::max_align_t std_max_align_t; // libstdc++ forgot to add it to std:: for a while
#else #else
typedef std::max_align_t std_max_align_t; // Others (e.g. MSVC) insist it can *only* be accessed via std:: typedef std::max_align_t std_max_align_t; // Others (e.g. MSVC) insist it can *only* be accessed via std::
#endif #endif
@ -1140,7 +1140,7 @@ class ConcurrentQueue {
// If there was at least one non-empty queue but it appears empty at the time // If there was at least one non-empty queue but it appears empty at the time
// we try to dequeue from it, we need to make sure every queue's been tried // we try to dequeue from it, we need to make sure every queue's been tried
if (nonEmptyCount > 0) { if (nonEmptyCount > 0) {
if (details::likely(best->dequeue(item))) { if ((details::likely)(best->dequeue(item))) {
return true; return true;
} }
for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
@ -1335,7 +1335,10 @@ class ConcurrentQueue {
private: private:
friend struct ProducerToken; friend struct ProducerToken;
friend struct ConsumerToken; friend struct ConsumerToken;
struct ExplicitProducer;
friend struct ExplicitProducer; friend struct ExplicitProducer;
struct ImplicitProducer;
friend struct ImplicitProducer;
friend class ConcurrentQueueTests; friend class ConcurrentQueueTests;
enum AllocationMode { CanAlloc, CannotAlloc }; enum AllocationMode { CanAlloc, CannotAlloc };
@ -1380,7 +1383,7 @@ class ConcurrentQueue {
} }
auto prodCount = producerCount.load(std::memory_order_relaxed); auto prodCount = producerCount.load(std::memory_order_relaxed);
auto globalOffset = globalExplicitConsumerOffset.load(std::memory_order_relaxed); auto globalOffset = globalExplicitConsumerOffset.load(std::memory_order_relaxed);
if (details::unlikely(token.desiredProducer == nullptr)) { if ((details::unlikely)(token.desiredProducer == nullptr)) {
// Aha, first time we're dequeueing anything. // Aha, first time we're dequeueing anything.
// Figure out our local position // Figure out our local position
// Note: offset is from start, not end, but we're traversing from end -- subtract from count first // Note: offset is from start, not end, but we're traversing from end -- subtract from count first
@ -1442,7 +1445,7 @@ class ConcurrentQueue {
FreeList& operator=(FreeList const&) MOODYCAMEL_DELETE_FUNCTION; FreeList& operator=(FreeList const&) MOODYCAMEL_DELETE_FUNCTION;
inline void add(N* node) { inline void add(N* node) {
#if MCDBGQ_NOLOCKFREE_FREELIST #ifdef MCDBGQ_NOLOCKFREE_FREELIST
debug::DebugLock lock(mutex); debug::DebugLock lock(mutex);
#endif #endif
// We know that the should-be-on-freelist bit is 0 at this point, so it's safe to // We know that the should-be-on-freelist bit is 0 at this point, so it's safe to
@ -1455,7 +1458,7 @@ class ConcurrentQueue {
} }
inline N* try_get() { inline N* try_get() {
#if MCDBGQ_NOLOCKFREE_FREELIST #ifdef MCDBGQ_NOLOCKFREE_FREELIST
debug::DebugLock lock(mutex); debug::DebugLock lock(mutex);
#endif #endif
auto head = freeListHead.load(std::memory_order_acquire); auto head = freeListHead.load(std::memory_order_acquire);
@ -1529,7 +1532,7 @@ class ConcurrentQueue {
static const std::uint32_t REFS_MASK = 0x7FFFFFFF; static const std::uint32_t REFS_MASK = 0x7FFFFFFF;
static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000; static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000;
#if MCDBGQ_NOLOCKFREE_FREELIST #ifdef MCDBGQ_NOLOCKFREE_FREELIST
debug::DebugMutex mutex; debug::DebugMutex mutex;
#endif #endif
}; };
@ -1548,7 +1551,7 @@ class ConcurrentQueue {
, freeListNext(nullptr) , freeListNext(nullptr)
, shouldBeOnFreeList(false) , shouldBeOnFreeList(false)
, dynamicallyAllocated(true) { , dynamicallyAllocated(true) {
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
owner = nullptr; owner = nullptr;
#endif #endif
} }
@ -1679,14 +1682,14 @@ class ConcurrentQueue {
std::atomic<bool> shouldBeOnFreeList; std::atomic<bool> shouldBeOnFreeList;
bool dynamicallyAllocated; // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool' bool dynamicallyAllocated; // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool'
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
void* owner; void* owner;
#endif #endif
}; };
static_assert(std::alignment_of<Block>::value >= std::alignment_of<details::max_align_t>::value, static_assert(std::alignment_of<Block>::value >= std::alignment_of<details::max_align_t>::value,
"Internal error: Blocks must be at least as aligned as the type they are wrapping"); "Internal error: Blocks must be at least as aligned as the type they are wrapping");
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
public: public:
struct MemStats; struct MemStats;
@ -1756,7 +1759,7 @@ class ConcurrentQueue {
ConcurrentQueue* parent; ConcurrentQueue* parent;
protected: protected:
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
friend struct MemStats; friend struct MemStats;
#endif #endif
}; };
@ -1902,7 +1905,7 @@ class ConcurrentQueue {
if (newBlock == nullptr) { if (newBlock == nullptr) {
return false; return false;
} }
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
newBlock->owner = this; newBlock->owner = this;
#endif #endif
newBlock->ConcurrentQueue::Block::template reset_empty<explicit_context>(); newBlock->ConcurrentQueue::Block::template reset_empty<explicit_context>();
@ -1916,7 +1919,7 @@ class ConcurrentQueue {
++pr_blockIndexSlotsUsed; ++pr_blockIndexSlotsUsed;
} }
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward<U>(element)))) { if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
// The constructor may throw. We want the element not to appear in the queue in // The constructor may throw. We want the element not to appear in the queue in
// that case (without corrupting the queue): // that case (without corrupting the queue):
MOODYCAMEL_TRY { MOODYCAMEL_TRY {
@ -1941,7 +1944,7 @@ class ConcurrentQueue {
blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, std::memory_order_release); blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, std::memory_order_release);
pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1);
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward<U>(element)))) { if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
this->tailIndex.store(newTailIndex, std::memory_order_release); this->tailIndex.store(newTailIndex, std::memory_order_release);
return true; return true;
} }
@ -1985,13 +1988,14 @@ class ConcurrentQueue {
// incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now
// have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon
// incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount. // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount.
assert(overcommit <= myDequeueCount); // However, we can't assert this since both dequeueOptimisticCount and dequeueOvercommit may (independently)
// overflow; in such a case, though, the logic still holds since the difference between the two is maintained.
// Note that we reload tail here in case it changed; it will be the same value as before or greater, since // Note that we reload tail here in case it changed; it will be the same value as before or greater, since
// this load is sequenced after (happens after) the earlier load above. This is supported by read-read // this load is sequenced after (happens after) the earlier load above. This is supported by read-read
// coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order // coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order
tail = this->tailIndex.load(std::memory_order_acquire); tail = this->tailIndex.load(std::memory_order_acquire);
if (details::likely(details::circular_less_than<index_t>(myDequeueCount - overcommit, tail))) { if ((details::likely)(details::circular_less_than<index_t>(myDequeueCount - overcommit, tail))) {
// Guaranteed to be at least one element to dequeue! // Guaranteed to be at least one element to dequeue!
// Get the index. Note that since there's guaranteed to be at least one element, this // Get the index. Note that since there's guaranteed to be at least one element, this
@ -2033,10 +2037,10 @@ class ConcurrentQueue {
} }
} guard = {block, index}; } guard = {block, index};
element = std::move(el); element = std::move(el); // NOLINT
} else { } else {
element = std::move(el); element = std::move(el); // NOLINT
el.~T(); el.~T(); // NOLINT
block->ConcurrentQueue::Block::template set_empty<explicit_context>(index); block->ConcurrentQueue::Block::template set_empty<explicit_context>(index);
} }
@ -2119,7 +2123,7 @@ class ConcurrentQueue {
return false; return false;
} }
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
newBlock->owner = this; newBlock->owner = this;
#endif #endif
newBlock->ConcurrentQueue::Block::template set_all_empty<explicit_context>(); newBlock->ConcurrentQueue::Block::template set_all_empty<explicit_context>();
@ -2151,7 +2155,8 @@ class ConcurrentQueue {
block = block->next; block = block->next;
} }
if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst),
new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))) {
blockIndex.load(std::memory_order_relaxed) blockIndex.load(std::memory_order_relaxed)
->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); ->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release);
} }
@ -2172,7 +2177,8 @@ class ConcurrentQueue {
if (details::circular_less_than<index_t>(newTailIndex, stopIndex)) { if (details::circular_less_than<index_t>(newTailIndex, stopIndex)) {
stopIndex = newTailIndex; stopIndex = newTailIndex;
} }
if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst),
new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))) {
while (currentTailIndex != stopIndex) { while (currentTailIndex != stopIndex) {
new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++);
} }
@ -2186,9 +2192,10 @@ class ConcurrentQueue {
// may only define a (noexcept) move constructor, and so calls to the // may only define a (noexcept) move constructor, and so calls to the
// cctor will not compile, even if they are in an if branch that will never // cctor will not compile, even if they are in an if branch that will never
// be executed // be executed
new ((*this->tailBlock)[currentTailIndex]) T( new ((*this->tailBlock)[currentTailIndex])
details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR( T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(
T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); T, decltype(*itemFirst),
new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
++currentTailIndex; ++currentTailIndex;
++itemFirst; ++itemFirst;
} }
@ -2236,7 +2243,7 @@ class ConcurrentQueue {
this->tailBlock = this->tailBlock->next; this->tailBlock = this->tailBlock->next;
} }
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst))) && if (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new ((T*)nullptr) T(details::deref_noexcept(itemFirst))) &&
firstAllocatedBlock != nullptr) { firstAllocatedBlock != nullptr) {
blockIndex.load(std::memory_order_relaxed) blockIndex.load(std::memory_order_relaxed)
->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); ->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release);
@ -2257,7 +2264,7 @@ class ConcurrentQueue {
std::atomic_thread_fence(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_acquire);
auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed); auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed);
assert(overcommit <= myDequeueCount); ;
tail = this->tailIndex.load(std::memory_order_acquire); tail = this->tailIndex.load(std::memory_order_acquire);
auto actualCount = static_cast<size_t>(tail - (myDequeueCount - overcommit)); auto actualCount = static_cast<size_t>(tail - (myDequeueCount - overcommit));
@ -2418,7 +2425,7 @@ class ConcurrentQueue {
private: private:
#endif #endif
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
friend struct MemStats; friend struct MemStats;
#endif #endif
}; };
@ -2500,7 +2507,7 @@ class ConcurrentQueue {
(MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) {
return false; return false;
} }
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex); debug::DebugLock lock(mutex);
#endif #endif
// Find out where we'll be inserting this block in the block index // Find out where we'll be inserting this block in the block index
@ -2516,12 +2523,12 @@ class ConcurrentQueue {
idxEntry->value.store(nullptr, std::memory_order_relaxed); idxEntry->value.store(nullptr, std::memory_order_relaxed);
return false; return false;
} }
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
newBlock->owner = this; newBlock->owner = this;
#endif #endif
newBlock->ConcurrentQueue::Block::template reset_empty<implicit_context>(); newBlock->ConcurrentQueue::Block::template reset_empty<implicit_context>();
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward<U>(element)))) { if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
// May throw, try to insert now before we publish the fact that we have this new block // May throw, try to insert now before we publish the fact that we have this new block
MOODYCAMEL_TRY { MOODYCAMEL_TRY {
new ((*newBlock)[currentTailIndex]) T(std::forward<U>(element)); new ((*newBlock)[currentTailIndex]) T(std::forward<U>(element));
@ -2539,7 +2546,7 @@ class ConcurrentQueue {
this->tailBlock = newBlock; this->tailBlock = newBlock;
if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward<U>(element)))) { if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward<U>(element)))) {
this->tailIndex.store(newTailIndex, std::memory_order_release); this->tailIndex.store(newTailIndex, std::memory_order_release);
return true; return true;
} }
@ -2562,9 +2569,8 @@ class ConcurrentQueue {
std::atomic_thread_fence(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_acquire);
index_t myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); index_t myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed);
assert(overcommit <= myDequeueCount);
tail = this->tailIndex.load(std::memory_order_acquire); tail = this->tailIndex.load(std::memory_order_acquire);
if (details::likely(details::circular_less_than<index_t>(myDequeueCount - overcommit, tail))) { if ((details::likely)(details::circular_less_than<index_t>(myDequeueCount - overcommit, tail))) {
index_t index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); index_t index = this->headIndex.fetch_add(1, std::memory_order_acq_rel);
// Determine which block the element is in // Determine which block the element is in
@ -2575,7 +2581,7 @@ class ConcurrentQueue {
auto& el = *((*block)[index]); auto& el = *((*block)[index]);
if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) { if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
// Note: Acquiring the mutex with every dequeue instead of only when a block // Note: Acquiring the mutex with every dequeue instead of only when a block
// is released is very sub-optimal, but it is, after all, purely debug code. // is released is very sub-optimal, but it is, after all, purely debug code.
debug::DebugLock lock(producer->mutex); debug::DebugLock lock(producer->mutex);
@ -2595,14 +2601,14 @@ class ConcurrentQueue {
} }
} guard = {block, index, entry, this->parent}; } guard = {block, index, entry, this->parent};
element = std::move(el); element = std::move(el); // NOLINT
} else { } else {
element = std::move(el); element = std::move(el); // NOLINT
el.~T(); el.~T(); // NOLINT
if (block->ConcurrentQueue::Block::template set_empty<implicit_context>(index)) { if (block->ConcurrentQueue::Block::template set_empty<implicit_context>(index)) {
{ {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex); debug::DebugLock lock(mutex);
#endif #endif
// Add the block back into the global free pool (and remove from block index) // Add the block back into the global free pool (and remove from block index)
@ -2642,7 +2648,7 @@ class ConcurrentQueue {
((startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1)); ((startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1));
index_t currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1); index_t currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1);
if (blockBaseDiff > 0) { if (blockBaseDiff > 0) {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex); debug::DebugLock lock(mutex);
#endif #endif
do { do {
@ -2679,7 +2685,7 @@ class ConcurrentQueue {
return false; return false;
} }
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
newBlock->owner = this; newBlock->owner = this;
#endif #endif
newBlock->ConcurrentQueue::Block::template reset_empty<implicit_context>(); newBlock->ConcurrentQueue::Block::template reset_empty<implicit_context>();
@ -2714,16 +2720,18 @@ class ConcurrentQueue {
if (details::circular_less_than<index_t>(newTailIndex, stopIndex)) { if (details::circular_less_than<index_t>(newTailIndex, stopIndex)) {
stopIndex = newTailIndex; stopIndex = newTailIndex;
} }
if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst),
new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))) {
while (currentTailIndex != stopIndex) { while (currentTailIndex != stopIndex) {
new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++);
} }
} else { } else {
MOODYCAMEL_TRY { MOODYCAMEL_TRY {
while (currentTailIndex != stopIndex) { while (currentTailIndex != stopIndex) {
new ((*this->tailBlock)[currentTailIndex]) T( new ((*this->tailBlock)[currentTailIndex])
details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR( T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(
T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); T, decltype(*itemFirst),
new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
++currentTailIndex; ++currentTailIndex;
++itemFirst; ++itemFirst;
} }
@ -2788,7 +2796,6 @@ class ConcurrentQueue {
std::atomic_thread_fence(std::memory_order_acquire); std::atomic_thread_fence(std::memory_order_acquire);
auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed); auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed);
assert(overcommit <= myDequeueCount);
tail = this->tailIndex.load(std::memory_order_acquire); tail = this->tailIndex.load(std::memory_order_acquire);
auto actualCount = static_cast<size_t>(tail - (myDequeueCount - overcommit)); auto actualCount = static_cast<size_t>(tail - (myDequeueCount - overcommit));
@ -2843,7 +2850,7 @@ class ConcurrentQueue {
if (block->ConcurrentQueue::Block::template set_many_empty<implicit_context>( if (block->ConcurrentQueue::Block::template set_many_empty<implicit_context>(
blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) { blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex); debug::DebugLock lock(mutex);
#endif #endif
entry->value.store(nullptr, std::memory_order_relaxed); entry->value.store(nullptr, std::memory_order_relaxed);
@ -2865,7 +2872,7 @@ class ConcurrentQueue {
if (block->ConcurrentQueue::Block::template set_many_empty<implicit_context>( if (block->ConcurrentQueue::Block::template set_many_empty<implicit_context>(
blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) { blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) {
{ {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex); debug::DebugLock lock(mutex);
#endif #endif
// Note that the set_many_empty above did a release, meaning that anybody who acquires the block // Note that the set_many_empty above did a release, meaning that anybody who acquires the block
@ -2945,7 +2952,7 @@ class ConcurrentQueue {
} }
inline size_t get_block_index_index_for_index(index_t index, BlockIndexHeader*& localBlockIndex) const { inline size_t get_block_index_index_for_index(index_t index, BlockIndexHeader*& localBlockIndex) const {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex); debug::DebugLock lock(mutex);
#endif #endif
index &= ~static_cast<index_t>(BLOCK_SIZE - 1); index &= ~static_cast<index_t>(BLOCK_SIZE - 1);
@ -3026,10 +3033,10 @@ class ConcurrentQueue {
private: private:
#endif #endif
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
mutable debug::DebugMutex mutex; mutable debug::DebugMutex mutex;
#endif #endif
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
friend struct MemStats; friend struct MemStats;
#endif #endif
}; };
@ -3065,7 +3072,7 @@ class ConcurrentQueue {
} }
inline void add_block_to_free_list(Block* block) { inline void add_block_to_free_list(Block* block) {
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
block->owner = nullptr; block->owner = nullptr;
#endif #endif
freeList.add(block); freeList.add(block);
@ -3103,7 +3110,7 @@ class ConcurrentQueue {
return nullptr; return nullptr;
} }
#if MCDBGQ_TRACKMEM #ifdef MCDBGQ_TRACKMEM
public: public:
struct MemStats { struct MemStats {
size_t allocatedBlocks; size_t allocatedBlocks;
@ -3221,7 +3228,7 @@ class ConcurrentQueue {
} }
ProducerBase* recycle_or_create_producer(bool isExplicit, bool& recycled) { ProducerBase* recycle_or_create_producer(bool isExplicit, bool& recycled) {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
debug::DebugLock lock(implicitProdMutex); debug::DebugLock lock(implicitProdMutex);
#endif #endif
// Try to re-use one first // Try to re-use one first
@ -3386,7 +3393,7 @@ class ConcurrentQueue {
// Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
debug::DebugLock lock(implicitProdMutex); debug::DebugLock lock(implicitProdMutex);
#endif #endif
@ -3443,6 +3450,7 @@ class ConcurrentQueue {
// Insert! // Insert!
auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed); auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed);
while (true) { while (true) {
// NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
if (newCount >= (mainHash->capacity >> 1) && if (newCount >= (mainHash->capacity >> 1) &&
!implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) { !implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) {
// We've acquired the resize lock, try to allocate a bigger hash table. // We've acquired the resize lock, try to allocate a bigger hash table.
@ -3539,7 +3547,7 @@ class ConcurrentQueue {
details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener); details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener);
// Remove from hash // Remove from hash
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
debug::DebugLock lock(implicitProdMutex); debug::DebugLock lock(implicitProdMutex);
#endif #endif
auto hash = implicitProducerHash.load(std::memory_order_acquire); auto hash = implicitProducerHash.load(std::memory_order_acquire);
@ -3650,7 +3658,7 @@ class ConcurrentQueue {
std::atomic<std::uint32_t> nextExplicitConsumerId; std::atomic<std::uint32_t> nextExplicitConsumerId;
std::atomic<std::uint32_t> globalExplicitConsumerOffset; std::atomic<std::uint32_t> globalExplicitConsumerOffset;
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH #ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
debug::DebugMutex implicitProdMutex; debug::DebugMutex implicitProdMutex;
#endif #endif

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
@ -41,6 +41,7 @@ class ActorInfo : private HeapNode, private ListNode {
} }
~ActorInfo() { ~ActorInfo() {
VLOG(actor) << "Destroy actor [" << name_ << "]"; VLOG(actor) << "Destroy actor [" << name_ << "]";
CHECK(!actor_);
} }
bool is_alive() const { bool is_alive() const {

View file

@ -14,13 +14,15 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "td/actor/core/CpuWorker.h" #include "td/actor/core/CpuWorker.h"
#include "td/actor/core/ActorExecutor.h" #include "td/actor/core/ActorExecutor.h"
#include "td/actor/core/SchedulerContext.h" #include "td/actor/core/SchedulerContext.h"
#include "td/actor/core/Scheduler.h" // FIXME: afer LocalQueue is in a separate file
namespace td { namespace td {
namespace actor { namespace actor {
namespace core { namespace core {
@ -28,20 +30,64 @@ void CpuWorker::run() {
auto thread_id = get_thread_id(); auto thread_id = get_thread_id();
auto &dispatcher = *SchedulerContext::get(); auto &dispatcher = *SchedulerContext::get();
int yields = 0; MpmcWaiter::Slot slot;
waiter_.init_slot(slot, thread_id);
while (true) { while (true) {
SchedulerMessage message; SchedulerMessage message;
if (queue_.try_pop(message, thread_id)) { if (try_pop(message, thread_id)) {
waiter_.stop_wait(slot);
if (!message) { if (!message) {
return; return;
} }
ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue()); ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue());
yields = waiter_.stop_wait(yields, thread_id);
} else { } else {
yields = waiter_.wait(yields, thread_id); waiter_.wait(slot);
} }
} }
} }
bool CpuWorker::try_pop_local(SchedulerMessage &message) {
SchedulerMessage::Raw *raw_message;
if (local_queues_[id_].try_pop(raw_message)) {
message = SchedulerMessage(SchedulerMessage::acquire_t{}, raw_message);
return true;
}
return false;
}
bool CpuWorker::try_pop_global(SchedulerMessage &message, size_t thread_id) {
SchedulerMessage::Raw *raw_message;
if (queue_.try_pop(raw_message, thread_id)) {
message = SchedulerMessage(SchedulerMessage::acquire_t{}, raw_message);
return true;
}
return false;
}
bool CpuWorker::try_pop(SchedulerMessage &message, size_t thread_id) {
if (++cnt_ == 51) {
cnt_ = 0;
if (try_pop_global(message, thread_id) || try_pop_local(message)) {
return true;
}
} else {
if (try_pop_local(message) || try_pop_global(message, thread_id)) {
return true;
}
}
for (size_t i = 1; i < local_queues_.size(); i++) {
size_t pos = (i + id_) % local_queues_.size();
SchedulerMessage::Raw *raw_message;
if (local_queues_[id_].steal(raw_message, local_queues_[pos])) {
message = SchedulerMessage(SchedulerMessage::acquire_t{}, raw_message);
return true;
}
}
return false;
}
} // namespace core } // namespace core
} // namespace actor } // namespace actor
} // namespace td } // namespace td

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
@ -22,19 +22,32 @@
#include "td/utils/MpmcQueue.h" #include "td/utils/MpmcQueue.h"
#include "td/utils/MpmcWaiter.h" #include "td/utils/MpmcWaiter.h"
#include "td/utils/Span.h"
namespace td { namespace td {
namespace actor { namespace actor {
namespace core { namespace core {
template <class T>
struct LocalQueue;
class CpuWorker { class CpuWorker {
public: public:
CpuWorker(MpmcQueue<SchedulerMessage> &queue, MpmcWaiter &waiter) : queue_(queue), waiter_(waiter) { CpuWorker(MpmcQueue<SchedulerMessage::Raw *> &queue, MpmcWaiter &waiter, size_t id,
MutableSpan<LocalQueue<SchedulerMessage::Raw *>> local_queues)
: queue_(queue), waiter_(waiter), id_(id), local_queues_(local_queues) {
} }
void run(); void run();
private: private:
MpmcQueue<SchedulerMessage> &queue_; MpmcQueue<SchedulerMessage::Raw *> &queue_;
MpmcWaiter &waiter_; MpmcWaiter &waiter_;
size_t id_;
MutableSpan<LocalQueue<SchedulerMessage::Raw *>> local_queues_;
size_t cnt_{0};
bool try_pop(SchedulerMessage &message, size_t thread_id);
bool try_pop_local(SchedulerMessage &message);
bool try_pop_global(SchedulerMessage &message, size_t thread_id);
}; };
} // namespace core } // namespace core
} // namespace actor } // namespace actor

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "td/actor/core/Scheduler.h" #include "td/actor/core/Scheduler.h"
@ -24,6 +24,7 @@
namespace td { namespace td {
namespace actor { namespace actor {
namespace core { namespace core {
Scheduler::Scheduler(std::shared_ptr<SchedulerGroupInfo> scheduler_group_info, SchedulerId id, size_t cpu_threads_count) Scheduler::Scheduler(std::shared_ptr<SchedulerGroupInfo> scheduler_group_info, SchedulerId id, size_t cpu_threads_count)
: scheduler_group_info_(std::move(scheduler_group_info)), cpu_threads_(cpu_threads_count) { : scheduler_group_info_(std::move(scheduler_group_info)), cpu_threads_(cpu_threads_count) {
scheduler_group_info_->active_scheduler_count++; scheduler_group_info_->active_scheduler_count++;
@ -31,17 +32,21 @@ Scheduler::Scheduler(std::shared_ptr<SchedulerGroupInfo> scheduler_group_info, S
info_->id = id; info_->id = id;
if (cpu_threads_count != 0) { if (cpu_threads_count != 0) {
info_->cpu_threads_count = cpu_threads_count; info_->cpu_threads_count = cpu_threads_count;
info_->cpu_queue = std::make_unique<MpmcQueue<SchedulerMessage>>(1024, max_thread_count()); info_->cpu_queue = std::make_unique<MpmcQueue<SchedulerMessage::Raw *>>(1024, max_thread_count());
info_->cpu_queue_waiter = std::make_unique<MpmcWaiter>(); info_->cpu_queue_waiter = std::make_unique<MpmcWaiter>();
info_->cpu_local_queue = std::vector<LocalQueue<SchedulerMessage::Raw *>>(cpu_threads_count);
} }
info_->io_queue = std::make_unique<MpscPollableQueue<SchedulerMessage>>(); info_->io_queue = std::make_unique<MpscPollableQueue<SchedulerMessage>>();
info_->io_queue->init(); info_->io_queue->init();
info_->cpu_workers.resize(cpu_threads_count); info_->cpu_workers.resize(cpu_threads_count);
td::uint8 cpu_worker_id = 0;
for (auto &worker : info_->cpu_workers) { for (auto &worker : info_->cpu_workers) {
worker = std::make_unique<WorkerInfo>(WorkerInfo::Type::Cpu, true); worker = std::make_unique<WorkerInfo>(WorkerInfo::Type::Cpu, true, CpuWorkerId{cpu_worker_id});
cpu_worker_id++;
} }
info_->io_worker = std::make_unique<WorkerInfo>(WorkerInfo::Type::Io, !info_->cpu_workers.empty()); info_->io_worker = std::make_unique<WorkerInfo>(WorkerInfo::Type::Io, !info_->cpu_workers.empty(), CpuWorkerId{});
poll_.init(); poll_.init();
io_worker_ = std::make_unique<IoWorker>(*info_->io_queue); io_worker_ = std::make_unique<IoWorker>(*info_->io_queue);
@ -62,8 +67,9 @@ Scheduler::~Scheduler() {
void Scheduler::start() { void Scheduler::start() {
for (size_t i = 0; i < cpu_threads_.size(); i++) { for (size_t i = 0; i < cpu_threads_.size(); i++) {
cpu_threads_[i] = td::thread([this, i] { cpu_threads_[i] = td::thread([this, i] {
this->run_in_context_impl(*this->info_->cpu_workers[i], this->run_in_context_impl(*this->info_->cpu_workers[i], [this, i] {
[this] { CpuWorker(*info_->cpu_queue, *info_->cpu_queue_waiter).run(); }); CpuWorker(*info_->cpu_queue, *info_->cpu_queue_waiter, i, info_->cpu_local_queue).run();
});
}); });
cpu_threads_[i].set_name(PSLICE() << "#" << info_->id.value() << ":cpu#" << i); cpu_threads_[i].set_name(PSLICE() << "#" << info_->id.value() << ":cpu#" << i);
} }
@ -121,9 +127,14 @@ void Scheduler::do_stop() {
scheduler_group_info_->active_scheduler_count_condition_variable.notify_all(); scheduler_group_info_->active_scheduler_count_condition_variable.notify_all();
} }
Scheduler::ContextImpl::ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, Scheduler::ContextImpl::ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id,
SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap) SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap)
: creator_(creator), scheduler_id_(scheduler_id), scheduler_group_(scheduler_group), poll_(poll), heap_(heap) { : creator_(creator)
, scheduler_id_(scheduler_id)
, cpu_worker_id_(cpu_worker_id)
, scheduler_group_(scheduler_group)
, poll_(poll)
, heap_(heap) {
} }
SchedulerId Scheduler::ContextImpl::get_scheduler_id() const { SchedulerId Scheduler::ContextImpl::get_scheduler_id() const {
@ -138,7 +149,18 @@ void Scheduler::ContextImpl::add_to_queue(ActorInfoPtr actor_info_ptr, Scheduler
if (need_poll || !info.cpu_queue) { if (need_poll || !info.cpu_queue) {
info.io_queue->writer_put(std::move(actor_info_ptr)); info.io_queue->writer_put(std::move(actor_info_ptr));
} else { } else {
info.cpu_queue->push(std::move(actor_info_ptr), get_thread_id()); if (scheduler_id == get_scheduler_id() && cpu_worker_id_.is_valid()) {
// may push local
CHECK(actor_info_ptr);
auto raw = actor_info_ptr.release();
auto should_notify = info.cpu_local_queue[cpu_worker_id_.value()].push(
raw, [&](auto value) { info.cpu_queue->push(value, get_thread_id()); });
if (should_notify) {
info.cpu_queue_waiter->notify();
}
return;
}
info.cpu_queue->push(actor_info_ptr.release(), get_thread_id());
info.cpu_queue_waiter->notify(); info.cpu_queue_waiter->notify();
} }
} }
@ -254,13 +276,26 @@ void Scheduler::close_scheduler_group(SchedulerGroupInfo &group_info) {
} }
// Drain cpu queue // Drain cpu queue
for (auto &q : scheduler_info.cpu_local_queue) {
auto &cpu_queue = q;
while (true) {
SchedulerMessage::Raw *raw_message;
if (!cpu_queue.try_pop(raw_message)) {
break;
}
SchedulerMessage(SchedulerMessage::acquire_t{}, raw_message);
// message's destructor is called
queues_are_empty = false;
}
}
if (scheduler_info.cpu_queue) { if (scheduler_info.cpu_queue) {
auto &cpu_queue = *scheduler_info.cpu_queue; auto &cpu_queue = *scheduler_info.cpu_queue;
while (true) { while (true) {
SchedulerMessage message; SchedulerMessage::Raw *raw_message;
if (!cpu_queue.try_pop(message, get_thread_id())) { if (!cpu_queue.try_pop(raw_message, get_thread_id())) {
break; break;
} }
SchedulerMessage(SchedulerMessage::acquire_t{}, raw_message);
// message's destructor is called // message's destructor is called
queues_are_empty = false; queues_are_empty = false;
} }

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
@ -38,9 +38,11 @@
#include "td/utils/List.h" #include "td/utils/List.h"
#include "td/utils/logging.h" #include "td/utils/logging.h"
#include "td/utils/MpmcQueue.h" #include "td/utils/MpmcQueue.h"
#include "td/utils/StealingQueue.h"
#include "td/utils/MpmcWaiter.h" #include "td/utils/MpmcWaiter.h"
#include "td/utils/MpscLinkQueue.h" #include "td/utils/MpscLinkQueue.h"
#include "td/utils/MpscPollableQueue.h" #include "td/utils/MpscPollableQueue.h"
#include "td/utils/optional.h"
#include "td/utils/port/Poll.h" #include "td/utils/port/Poll.h"
#include "td/utils/port/detail/Iocp.h" #include "td/utils/port/detail/Iocp.h"
#include "td/utils/port/thread.h" #include "td/utils/port/thread.h"
@ -66,16 +68,52 @@ class IoWorker;
struct WorkerInfo { struct WorkerInfo {
enum class Type { Io, Cpu } type{Type::Io}; enum class Type { Io, Cpu } type{Type::Io};
WorkerInfo() = default; WorkerInfo() = default;
explicit WorkerInfo(Type type, bool allow_shared) : type(type), actor_info_creator(allow_shared) { explicit WorkerInfo(Type type, bool allow_shared, CpuWorkerId cpu_worker_id)
: type(type), actor_info_creator(allow_shared), cpu_worker_id(cpu_worker_id) {
} }
ActorInfoCreator actor_info_creator; ActorInfoCreator actor_info_creator;
CpuWorkerId cpu_worker_id;
};
template <class T>
struct LocalQueue {
public:
template <class F>
bool push(T value, F &&overflow_f) {
auto res = std::move(next_);
next_ = std::move(value);
if (res) {
queue_.local_push(res.unwrap(), overflow_f);
return true;
}
return false;
}
bool try_pop(T &message) {
if (!next_) {
return queue_.local_pop(message);
}
message = next_.unwrap();
return true;
}
bool steal(T &message, LocalQueue<T> &other) {
return queue_.steal(message, other.queue_);
}
private:
td::optional<T> next_;
StealingQueue<T> queue_;
char pad[TD_CONCURRENCY_PAD - sizeof(optional<T>)];
}; };
struct SchedulerInfo { struct SchedulerInfo {
SchedulerId id; SchedulerId id;
// will be read by all workers is any thread // will be read by all workers is any thread
std::unique_ptr<MpmcQueue<SchedulerMessage>> cpu_queue; std::unique_ptr<MpmcQueue<SchedulerMessage::Raw *>> cpu_queue;
std::unique_ptr<MpmcWaiter> cpu_queue_waiter; std::unique_ptr<MpmcWaiter> cpu_queue_waiter;
std::vector<LocalQueue<SchedulerMessage::Raw *>> cpu_local_queue;
//std::vector<td::StealingQueue<SchedulerMessage>> cpu_stealing_queue;
// only scheduler itself may read from io_queue_ // only scheduler itself may read from io_queue_
std::unique_ptr<MpscPollableQueue<SchedulerMessage>> io_queue; std::unique_ptr<MpscPollableQueue<SchedulerMessage>> io_queue;
size_t cpu_threads_count{0}; size_t cpu_threads_count{0};
@ -156,11 +194,12 @@ class Scheduler {
class ContextImpl : public SchedulerContext { class ContextImpl : public SchedulerContext {
public: public:
ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, SchedulerGroupInfo *scheduler_group, Poll *poll, ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id,
KHeap<double> *heap); SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap);
SchedulerId get_scheduler_id() const override; SchedulerId get_scheduler_id() const override;
void add_to_queue(ActorInfoPtr actor_info_ptr, SchedulerId scheduler_id, bool need_poll) override; void add_to_queue(ActorInfoPtr actor_info_ptr, SchedulerId scheduler_id, bool need_poll) override;
ActorInfoCreator &get_actor_info_creator() override; ActorInfoCreator &get_actor_info_creator() override;
bool has_poll() override; bool has_poll() override;
@ -181,6 +220,7 @@ class Scheduler {
ActorInfoCreator *creator_; ActorInfoCreator *creator_;
SchedulerId scheduler_id_; SchedulerId scheduler_id_;
CpuWorkerId cpu_worker_id_;
SchedulerGroupInfo *scheduler_group_; SchedulerGroupInfo *scheduler_group_;
Poll *poll_; Poll *poll_;
@ -193,8 +233,8 @@ class Scheduler {
td::detail::Iocp::Guard iocp_guard(&scheduler_group_info_->iocp); td::detail::Iocp::Guard iocp_guard(&scheduler_group_info_->iocp);
#endif #endif
bool is_io_worker = worker_info.type == WorkerInfo::Type::Io; bool is_io_worker = worker_info.type == WorkerInfo::Type::Io;
ContextImpl context(&worker_info.actor_info_creator, info_->id, scheduler_group_info_.get(), ContextImpl context(&worker_info.actor_info_creator, info_->id, worker_info.cpu_worker_id,
is_io_worker ? &poll_ : nullptr, is_io_worker ? &heap_ : nullptr); scheduler_group_info_.get(), is_io_worker ? &poll_ : nullptr, is_io_worker ? &heap_ : nullptr);
SchedulerContext::Guard guard(&context); SchedulerContext::Guard guard(&context);
f(); f();
} }

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
@ -26,8 +26,7 @@ namespace actor {
namespace core { namespace core {
class SchedulerId { class SchedulerId {
public: public:
SchedulerId() : id_(-1) { SchedulerId() = default;
}
explicit SchedulerId(uint8 id) : id_(id) { explicit SchedulerId(uint8 id) : id_(id) {
} }
bool is_valid() const { bool is_valid() const {
@ -42,7 +41,27 @@ class SchedulerId {
} }
private: private:
int32 id_{0}; int32 id_{-1};
};
class CpuWorkerId {
public:
CpuWorkerId() = default;
explicit CpuWorkerId(uint8 id) : id_(id) {
}
bool is_valid() const {
return id_ >= 0;
}
uint8 value() const {
CHECK(is_valid());
return static_cast<uint8>(id_);
}
bool operator==(CpuWorkerId other) const {
return id_ == other.id_;
}
private:
int32 id_{-1};
}; };
} // namespace core } // namespace core
} // namespace actor } // namespace actor

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "td/actor/core/ActorLocker.h" #include "td/actor/core/ActorLocker.h"
#include "td/actor/actor.h" #include "td/actor/actor.h"
@ -410,7 +410,7 @@ class Master : public Actor {
private: private:
uint32 l = 0; uint32 l = 0;
uint32 r = 100000; uint32 r = 1000;
core::ActorInfoPtr worker; core::ActorInfoPtr worker;
void start_up() override { void start_up() override {
worker = detail::create_actor<Worker>(ActorOptions().with_name("Master")); worker = detail::create_actor<Worker>(ActorOptions().with_name("Master"));
@ -444,8 +444,8 @@ TEST(Actor2, scheduler_simple) {
core::Scheduler scheduler{group_info, SchedulerId{0}, 2}; core::Scheduler scheduler{group_info, SchedulerId{0}, 2};
scheduler.start(); scheduler.start();
scheduler.run_in_context([] { scheduler.run_in_context([] {
global_cnt = 10; global_cnt = 1000;
for (int i = 0; i < 10; i++) { for (int i = 0; i < global_cnt; i++) {
detail::create_actor<Master>(ActorOptions().with_name("Master")); detail::create_actor<Master>(ActorOptions().with_name("Master"));
} }
}); });
@ -734,7 +734,7 @@ TEST(Actor2, actor_ping_pong) {
return; return;
} }
auto dest = td::Random::fast(0, (int)next_.size() - 1); auto dest = td::Random::fast(0, (int)next_.size() - 1);
if (td::Random::fast(0, 1) == 0 && 0) { if (td::Random::fast(0, 1) == 0) {
send_closure(next_[dest], &PingPong::query, left - 1, std::move(data)); send_closure(next_[dest], &PingPong::query, left - 1, std::move(data));
} else { } else {
send_closure_later(next_[dest], &PingPong::query, left - 1, std::move(data)); send_closure_later(next_[dest], &PingPong::query, left - 1, std::move(data));
@ -755,7 +755,7 @@ TEST(Actor2, actor_ping_pong) {
std::shared_ptr<td::Destructor> watcher_; std::shared_ptr<td::Destructor> watcher_;
}; };
int N = td::Random::fast(2, 10); int N = td::Random::fast(2, 100);
//N = 2; //N = 2;
std::vector<ActorOwn<PingPong>> actors; std::vector<ActorOwn<PingPong>> actors;
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {

View file

@ -230,6 +230,7 @@ set(TDUTILS_SOURCE
td/utils/SpinLock.h td/utils/SpinLock.h
td/utils/StackAllocator.h td/utils/StackAllocator.h
td/utils/Status.h td/utils/Status.h
td/utils/StealingQueue.h
td/utils/Storer.h td/utils/Storer.h
td/utils/StorerBase.h td/utils/StorerBase.h
td/utils/StringBuilder.h td/utils/StringBuilder.h
@ -281,6 +282,7 @@ set(TDUTILS_TEST_SOURCE
${CMAKE_CURRENT_SOURCE_DIR}/test/pq.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/pq.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/SharedObjectPool.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/SharedObjectPool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/SharedSlice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/SharedSlice.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/StealingQueue.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/variant.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/variant.cpp
PARENT_SCOPE PARENT_SCOPE
) )

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
@ -117,24 +117,28 @@ template <class T>
class OneValue<T *> { class OneValue<T *> {
public: public:
bool set_value(T *value) { bool set_value(T *value) {
T *was = nullptr; T *was = Empty();
return state_.compare_exchange_strong(was, value, std::memory_order_acq_rel); return state_.compare_exchange_strong(was, value, std::memory_order_acq_rel);
} }
bool get_value(T *&value) { bool get_value(T *&value) {
value = state_.exchange(Taken(), std::memory_order_acq_rel); value = state_.exchange(Taken(), std::memory_order_acq_rel);
return value != nullptr; return value != Empty();
} }
void reset() { void reset() {
state_ = nullptr; state_ = Empty();
} }
OneValue() { OneValue() {
} }
private: private:
std::atomic<T *> state_{nullptr}; std::atomic<T *> state_{Empty()};
T *Taken() { static T *Empty() {
static T xxx; static int64 xxx;
return &xxx; return reinterpret_cast<T *>(&xxx);
}
static T *Taken() {
static int64 xxx;
return reinterpret_cast<T *>(&xxx);
} }
}; };

View file

@ -14,63 +14,86 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
#include "td/utils/common.h" #include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread.h" #include "td/utils/port/thread.h"
#include <atomic> #include <atomic>
#include <algorithm>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
namespace td { namespace td {
class MpmcWaiter { class MpmcEagerWaiter {
public: public:
int wait(int yields, uint32 worker_id) { struct Slot {
if (yields < RoundsTillSleepy) { private:
friend class MpmcEagerWaiter;
int yields;
uint32 worker_id;
};
void init_slot(Slot &slot, uint32 worker_id) {
slot.yields = 0;
slot.worker_id = worker_id;
}
void wait(Slot &slot) {
if (slot.yields < RoundsTillSleepy) {
td::this_thread::yield(); td::this_thread::yield();
return yields + 1; slot.yields++;
} else if (yields == RoundsTillSleepy) { return;
} else if (slot.yields == RoundsTillSleepy) {
auto state = state_.load(std::memory_order_relaxed); auto state = state_.load(std::memory_order_relaxed);
if (!State::has_worker(state)) { if (!State::has_worker(state)) {
auto new_state = State::with_worker(state, worker_id); auto new_state = State::with_worker(state, slot.worker_id);
if (state_.compare_exchange_strong(state, new_state, std::memory_order_acq_rel)) { if (state_.compare_exchange_strong(state, new_state, std::memory_order_acq_rel)) {
td::this_thread::yield(); td::this_thread::yield();
return yields + 1; slot.yields++;
return;
} }
if (state == State::awake()) { if (state == State::awake()) {
return 0; slot.yields = 0;
return;
} }
} }
td::this_thread::yield(); td::this_thread::yield();
return 0; slot.yields = 0;
} else if (yields < RoundsTillAsleep) { return;
} else if (slot.yields < RoundsTillAsleep) {
auto state = state_.load(std::memory_order_acquire); auto state = state_.load(std::memory_order_acquire);
if (State::still_sleepy(state, worker_id)) { if (State::still_sleepy(state, slot.worker_id)) {
td::this_thread::yield(); td::this_thread::yield();
return yields + 1; slot.yields++;
return;
} }
return 0; slot.yields = 0;
return;
} else { } else {
auto state = state_.load(std::memory_order_acquire); auto state = state_.load(std::memory_order_acquire);
if (State::still_sleepy(state, worker_id)) { if (State::still_sleepy(state, slot.worker_id)) {
std::unique_lock<std::mutex> lock(mutex_); std::unique_lock<std::mutex> lock(mutex_);
if (state_.compare_exchange_strong(state, State::asleep(), std::memory_order_acq_rel)) { if (state_.compare_exchange_strong(state, State::asleep(), std::memory_order_acq_rel)) {
condition_variable_.wait(lock); condition_variable_.wait(lock);
} }
} }
return 0; slot.yields = 0;
return;
} }
} }
int stop_wait(int yields, uint32 worker_id) { void stop_wait(Slot &slot) {
if (yields > RoundsTillSleepy) { if (slot.yields > RoundsTillSleepy) {
notify_cold(); notify_cold();
} }
return 0; slot.yields = 0;
return;
}
void close() {
} }
void notify() { void notify() {
@ -102,8 +125,8 @@ class MpmcWaiter {
return (state >> 1) == (worker + 1); return (state >> 1) == (worker + 1);
} }
}; };
//enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 }; enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 };
enum { RoundsTillSleepy = 1, RoundsTillAsleep = 2 }; // enum { RoundsTillSleepy = 1, RoundsTillAsleep = 2 };
std::atomic<uint32> state_{State::awake()}; std::atomic<uint32> state_{State::awake()};
std::mutex mutex_; std::mutex mutex_;
std::condition_variable condition_variable_; std::condition_variable condition_variable_;
@ -117,4 +140,208 @@ class MpmcWaiter {
} }
}; };
class MpmcSleepyWaiter {
public:
struct Slot {
private:
friend class MpmcSleepyWaiter;
enum State { Search, Work, Sleep } state_{Work};
void park() {
std::unique_lock<std::mutex> guard(mutex_);
condition_variable_.wait(guard, [&] { return unpark_flag_; });
unpark_flag_ = false;
}
bool cancel_park() {
auto res = unpark_flag_;
unpark_flag_ = false;
return res;
}
void unpark() {
//TODO: try unlock guard before notify_all
std::unique_lock<std::mutex> guard(mutex_);
unpark_flag_ = true;
condition_variable_.notify_all();
}
std::mutex mutex_;
std::condition_variable condition_variable_;
bool unpark_flag_{false}; // TODO: move out of lock
int yield_cnt{0};
int32 worker_id{0};
char padding[128];
};
// There are a lot of workers
// Each has a slot
//
// States of a worker:
// - searching for work | Search
// - processing work | Work
// - sleeping | Sleep
//
// When somebody adds a work it calls notify
//
// notify
// if there are workers in search phase do nothing.
// if all workers are awake do nothing
// otherwise wake some random worker
//
// Initially all workers are in Search mode.
//
// When worker found nothing it may try to call wait.
// This may put it in a Sleep for some time.
// After wait return worker will be in Search state again.
//
// Suppose worker found a work and ready to process it.
// Than it may call stop_wait. This will cause transition from
// Search to Work state.
//
// Main invariant:
// After notify is called there should be at least on worker in Search or Work state.
// If possible - in Search state
//
void init_slot(Slot &slot, int32 worker_id) {
slot.state_ = Slot::State::Work;
slot.unpark_flag_ = false;
slot.worker_id = worker_id;
VLOG(waiter) << "Init slot " << worker_id;
}
int VERBOSITY_NAME(waiter) = VERBOSITY_NAME(DEBUG) + 10;
void wait(Slot &slot) {
if (slot.state_ == Slot::State::Work) {
VLOG(waiter) << "Work -> Search";
state_++;
slot.state_ = Slot::State::Search;
slot.yield_cnt = 0;
return;
}
if (slot.state_ == Slot::Search) {
if (slot.yield_cnt++ < 10 && false) {
td::this_thread::yield();
return;
}
slot.state_ = Slot::State::Sleep;
std::unique_lock<std::mutex> guard(sleepers_mutex_);
auto state_view = StateView(state_.fetch_add((1 << PARKING_SHIFT) - 1));
CHECK(state_view.searching_count != 0);
bool should_search = state_view.searching_count == 1;
if (closed_) {
return;
}
sleepers_.push_back(&slot);
LOG_CHECK(slot.unpark_flag_ == false) << slot.worker_id;
VLOG(waiter) << "add to sleepers " << slot.worker_id;
//guard.unlock();
if (should_search) {
VLOG(waiter) << "Search -> Search once then Sleep ";
return;
}
VLOG(waiter) << "Search -> Sleep " << state_view.searching_count << " " << state_view.parked_count;
}
CHECK(slot.state_ == Slot::State::Sleep);
VLOG(waiter) << "Park " << slot.worker_id;
slot.park();
VLOG(waiter) << "Resume " << slot.worker_id;
slot.state_ = Slot::State::Search;
slot.yield_cnt = 0;
}
void stop_wait(Slot &slot) {
if (slot.state_ == Slot::State::Work) {
return;
}
if (slot.state_ == Slot::State::Sleep) {
VLOG(waiter) << "Search once then Sleep -> Work/Search " << slot.worker_id;
slot.state_ = Slot::State::Work;
std::unique_lock<std::mutex> guard(sleepers_mutex_);
auto it = std::find(sleepers_.begin(), sleepers_.end(), &slot);
if (it != sleepers_.end()) {
sleepers_.erase(it);
VLOG(waiter) << "remove from sleepers " << slot.worker_id;
state_.fetch_sub((1 << PARKING_SHIFT) - 1);
guard.unlock();
} else {
guard.unlock();
VLOG(waiter) << "not in sleepers" << slot.worker_id;
CHECK(slot.cancel_park());
}
}
VLOG(waiter) << "Search once then Sleep -> Work " << slot.worker_id;
slot.state_ = Slot::State::Search;
auto state_view = StateView(state_.fetch_sub(1));
CHECK(state_view.searching_count != 0);
CHECK(state_view.searching_count < 1000);
bool should_notify = state_view.searching_count == 1;
if (should_notify) {
VLOG(waiter) << "Notify others";
notify();
}
VLOG(waiter) << "Search -> Work ";
slot.state_ = Slot::State::Work;
}
void notify() {
auto view = StateView(state_.load());
//LOG(ERROR) << view.parked_count;
if (view.searching_count > 0 || view.parked_count == 0) {
VLOG(waiter) << "Ingore notify: " << view.searching_count << " " << view.parked_count;
return;
}
VLOG(waiter) << "Notify: " << view.searching_count << " " << view.parked_count;
std::unique_lock<std::mutex> guard(sleepers_mutex_);
view = StateView(state_.load());
if (view.searching_count > 0) {
VLOG(waiter) << "Skip notify: got searching";
return;
}
CHECK(view.parked_count == static_cast<int>(sleepers_.size()));
if (sleepers_.empty()) {
VLOG(waiter) << "Skip notify: no sleepers";
return;
}
auto sleeper = sleepers_.back();
sleepers_.pop_back();
state_.fetch_sub((1 << PARKING_SHIFT) - 1);
VLOG(waiter) << "Unpark " << sleeper->worker_id;
sleeper->unpark();
}
void close() {
StateView state(state_.load());
LOG_CHECK(state.parked_count == 0) << state.parked_count;
LOG_CHECK(state.searching_count == 0) << state.searching_count;
}
private:
static constexpr td::int32 PARKING_SHIFT = 16;
struct StateView {
td::int32 parked_count;
td::int32 searching_count;
explicit StateView(int32 x) {
parked_count = x >> PARKING_SHIFT;
searching_count = x & ((1 << PARKING_SHIFT) - 1);
}
};
std::atomic<td::int32> state_{0};
std::mutex sleepers_mutex_;
std::vector<Slot *> sleepers_;
bool closed_ = false;
};
using MpmcWaiter = MpmcSleepyWaiter;
} // namespace td } // namespace td

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
@ -49,9 +49,7 @@ class AtomicRefCnt {
}; };
template <class DataT, class DeleterT> template <class DataT, class DeleterT>
class SharedPtrRaw class SharedPtrRaw : public DeleterT, private MpscLinkQueueImpl::Node {
: public DeleterT
, private MpscLinkQueueImpl::Node {
public: public:
explicit SharedPtrRaw(DeleterT deleter) : DeleterT(std::move(deleter)), ref_cnt_{0}, option_magic_(Magic) { explicit SharedPtrRaw(DeleterT deleter) : DeleterT(std::move(deleter)), ref_cnt_{0}, option_magic_(Magic) {
} }
@ -100,6 +98,7 @@ template <class T, class DeleterT = std::default_delete<T>>
class SharedPtr { class SharedPtr {
public: public:
using Raw = detail::SharedPtrRaw<T, DeleterT>; using Raw = detail::SharedPtrRaw<T, DeleterT>;
struct acquire_t {};
SharedPtr() = default; SharedPtr() = default;
~SharedPtr() { ~SharedPtr() {
if (!raw_) { if (!raw_) {
@ -112,6 +111,8 @@ class SharedPtr {
raw_->inc(); raw_->inc();
} }
} }
SharedPtr(acquire_t, Raw *raw) : raw_(raw) {
}
SharedPtr(const SharedPtr &other) : SharedPtr(other.raw_) { SharedPtr(const SharedPtr &other) : SharedPtr(other.raw_) {
} }
SharedPtr &operator=(const SharedPtr &other) { SharedPtr &operator=(const SharedPtr &other) {

View file

@ -0,0 +1,124 @@
/*
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 2019-2020 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Status.h"
#include "td/utils/Span.h"
namespace td {
template <class T, size_t N = 256 /*must be a power of two*/>
class StealingQueue {
public:
// tries to put a value
// returns if succeeded
// only owner is alowed to to do this
template <class F>
void local_push(T value, F&& overflow_f) {
while (true) {
auto tail = tail_.load(std::memory_order_relaxed);
auto head = head_.load(); //TODO: memory order
if (static_cast<size_t>(tail - head) < N) {
buf_[tail & MASK].store(value, std::memory_order_relaxed);
tail_.store(tail + 1, std::memory_order_release);
return;
}
// queue is full
// TODO: batch insert into global queue?
auto n = N / 2 + 1;
auto new_head = head + n;
if (!head_.compare_exchange_strong(head, new_head)) {
continue;
}
for (size_t i = 0; i < n; i++) {
overflow_f(buf_[(i + head) & MASK].load(std::memory_order_relaxed));
}
overflow_f(value);
return;
}
}
// tries to pop a value
// returns if succeeded
// only owner is alowed to to do this
bool local_pop(T& value) {
auto tail = tail_.load(std::memory_order_relaxed);
auto head = head_.load();
if (head == tail) {
return false;
}
value = buf_[head & MASK].load(std::memory_order_relaxed);
return head_.compare_exchange_strong(head, head + 1);
}
bool steal(T& value, StealingQueue<T, N>& other) {
while (true) {
auto tail = tail_.load(std::memory_order_relaxed);
auto head = head_.load(); //TODO: memory order
auto other_head = other.head_.load();
auto other_tail = other.tail_.load(std::memory_order_acquire);
if (other_tail < other_head) {
continue;
}
size_t n = other_tail - other_head;
if (n > N) {
continue;
}
n -= n / 2;
n = td::min(n, static_cast<size_t>(head + N - tail));
if (n == 0) {
return false;
}
for (size_t i = 0; i < n; i++) {
buf_[(i + tail) & MASK].store(other.buf_[(i + other_head) & MASK].load(std::memory_order_relaxed),
std::memory_order_relaxed);
}
if (!other.head_.compare_exchange_strong(other_head, other_head + n)) {
continue;
}
n--;
value = buf_[(tail + n) & MASK].load(std::memory_order_relaxed);
tail_.store(tail + n, std::memory_order_release);
return true;
}
}
StealingQueue() {
for (auto& x : buf_) {
x.store(T{}, std::memory_order_relaxed);
}
std::atomic_thread_fence(std::memory_order_seq_cst);
}
private:
std::atomic<td::int64> head_{0};
std::atomic<td::int64> tail_{0};
static constexpr size_t MASK{N - 1};
std::array<std::atomic<T>, N> buf_;
};
}; // namespace td

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "TsFileLog.h" #include "TsFileLog.h"
@ -24,8 +24,10 @@ namespace td {
namespace detail { namespace detail {
class TsFileLog : public LogInterface { class TsFileLog : public LogInterface {
public: public:
Status init(string path) { Status init(string path, td::int64 rotate_threshold, bool redirect_stderr) {
path_ = std::move(path); path_ = std::move(path);
rotate_threshold_ = rotate_threshold;
redirect_stderr_ = redirect_stderr;
for (int i = 0; i < (int)logs_.size(); i++) { for (int i = 0; i < (int)logs_.size(); i++) {
logs_[i].id = i; logs_[i].id = i;
} }
@ -54,6 +56,8 @@ class TsFileLog : public LogInterface {
int id; int id;
}; };
static constexpr int MAX_THREAD_ID = 128; static constexpr int MAX_THREAD_ID = 128;
td::int64 rotate_threshold_;
bool redirect_stderr_;
std::string path_; std::string path_;
std::array<Info, MAX_THREAD_ID> logs_; std::array<Info, MAX_THREAD_ID> logs_;
@ -70,7 +74,7 @@ class TsFileLog : public LogInterface {
} }
Status init_info(Info *info) { Status init_info(Info *info) {
TRY_STATUS(info->log.init(get_path(info), std::numeric_limits<int64>::max(), info->id == 0)); TRY_STATUS(info->log.init(get_path(info), std::numeric_limits<int64>::max(), info->id == 0 && redirect_stderr_));
info->is_inited = true; info->is_inited = true;
return Status::OK(); return Status::OK();
} }
@ -92,9 +96,9 @@ class TsFileLog : public LogInterface {
}; };
} // namespace detail } // namespace detail
Result<td::unique_ptr<LogInterface>> TsFileLog::create(string path) { Result<td::unique_ptr<LogInterface>> TsFileLog::create(string path, td::int64 rotate_threshold, bool redirect_stderr) {
auto res = td::make_unique<detail::TsFileLog>(); auto res = td::make_unique<detail::TsFileLog>();
TRY_STATUS(res->init(path)); TRY_STATUS(res->init(path, rotate_threshold, redirect_stderr));
return std::move(res); return std::move(res);
} }
} // namespace td } // namespace td

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
@ -22,7 +22,10 @@
namespace td { namespace td {
class TsFileLog { class TsFileLog {
static constexpr int64 DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
public: public:
static Result<td::unique_ptr<LogInterface>> create(string path); static Result<td::unique_ptr<LogInterface>> create(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD,
bool redirect_stderr = true);
}; };
} // namespace td } // namespace td

View file

@ -1,3 +1,21 @@
/*
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 2019-2020 Telegram Systems LLP
*/
#include "rlimit.h" #include "rlimit.h"
#if TD_LINUX || TD_ANDROID #if TD_LINUX || TD_ANDROID
#include <unistd.h> #include <unistd.h>

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "td/utils/port/config.h" #include "td/utils/port/config.h"

View file

@ -1,3 +1,21 @@
/*
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 "user.h" #include "user.h"
#if TD_LINUX #if TD_LINUX
#include <unistd.h> #include <unistd.h>

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "td/utils/port/config.h" #include "td/utils/port/config.h"

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "td/utils/MpmcWaiter.h" #include "td/utils/MpmcWaiter.h"
#include "td/utils/port/sleep.h" #include "td/utils/port/sleep.h"
@ -25,21 +25,22 @@
#include <atomic> #include <atomic>
#if !TD_THREAD_UNSUPPORTED #if !TD_THREAD_UNSUPPORTED
TEST(MpmcWaiter, stress_one_one) { template <class W>
void test_waiter_stress_one_one() {
td::Stage run; td::Stage run;
td::Stage check; td::Stage check;
std::vector<td::thread> threads; std::vector<td::thread> threads;
std::atomic<size_t> value{0}; std::atomic<size_t> value{0};
size_t write_cnt = 10; size_t write_cnt = 10;
td::unique_ptr<td::MpmcWaiter> waiter; td::unique_ptr<W> waiter;
size_t threads_n = 2; size_t threads_n = 2;
for (size_t i = 0; i < threads_n; i++) { for (size_t i = 0; i < threads_n; i++) {
threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] { threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
for (td::uint64 round = 1; round < 100000; round++) { for (td::uint64 round = 1; round < 100000; round++) {
if (id == 0) { if (id == 0) {
value = 0; value = 0;
waiter = td::make_unique<td::MpmcWaiter>(); waiter = td::make_unique<W>();
write_cnt = td::Random::fast(1, 10); write_cnt = td::Random::fast(1, 10);
} }
run.wait(round * threads_n); run.wait(round * threads_n);
@ -49,17 +50,19 @@ TEST(MpmcWaiter, stress_one_one) {
waiter->notify(); waiter->notify();
} }
} else { } else {
int yields = 0; typename W::Slot slot;
waiter->init_slot(slot, id);
for (size_t i = 1; i <= write_cnt; i++) { for (size_t i = 1; i <= write_cnt; i++) {
while (true) { while (true) {
auto x = value.load(std::memory_order_relaxed); auto x = value.load(std::memory_order_relaxed);
if (x >= i) { if (x >= i) {
break; break;
} }
yields = waiter->wait(yields, id); waiter->wait(slot);
} }
yields = waiter->stop_wait(yields, id); waiter->stop_wait(slot);
} }
waiter->stop_wait(slot);
} }
check.wait(round * threads_n); check.wait(round * threads_n);
} }
@ -69,7 +72,15 @@ TEST(MpmcWaiter, stress_one_one) {
thread.join(); thread.join();
} }
} }
TEST(MpmcWaiter, stress) { TEST(MpmcEagerWaiter, stress_one_one) {
test_waiter_stress_one_one<td::MpmcEagerWaiter>();
}
TEST(MpmcSleepyWaiter, stress_one_one) {
test_waiter_stress_one_one<td::MpmcSleepyWaiter>();
}
template <class W>
void test_waiter_stress() {
td::Stage run; td::Stage run;
td::Stage check; td::Stage check;
@ -81,7 +92,7 @@ TEST(MpmcWaiter, stress) {
size_t end_pos; size_t end_pos;
size_t write_cnt; size_t write_cnt;
size_t threads_n = 20; size_t threads_n = 20;
td::unique_ptr<td::MpmcWaiter> waiter; td::unique_ptr<W> waiter;
for (size_t i = 0; i < threads_n; i++) { for (size_t i = 0; i < threads_n; i++) {
threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] { threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
for (td::uint64 round = 1; round < 1000; round++) { for (td::uint64 round = 1; round < 1000; round++) {
@ -92,7 +103,7 @@ TEST(MpmcWaiter, stress) {
end_pos = write_n * write_cnt; end_pos = write_n * write_cnt;
write_pos = 0; write_pos = 0;
read_pos = 0; read_pos = 0;
waiter = td::make_unique<td::MpmcWaiter>(); waiter = td::make_unique<W>();
} }
run.wait(round * threads_n); run.wait(round * threads_n);
if (id <= write_n) { if (id <= write_n) {
@ -104,21 +115,26 @@ TEST(MpmcWaiter, stress) {
waiter->notify(); waiter->notify();
} }
} else if (id > 10 && id - 10 <= read_n) { } else if (id > 10 && id - 10 <= read_n) {
int yields = 0; typename W::Slot slot;
waiter->init_slot(slot, id);
while (true) { while (true) {
auto x = read_pos.load(std::memory_order_relaxed); auto x = read_pos.load(std::memory_order_relaxed);
if (x == end_pos) { if (x == end_pos) {
waiter->stop_wait(slot);
break; break;
} }
if (x == write_pos.load(std::memory_order_relaxed)) { if (x == write_pos.load(std::memory_order_relaxed)) {
yields = waiter->wait(yields, id); waiter->wait(slot);
continue; continue;
} }
yields = waiter->stop_wait(yields, id); waiter->stop_wait(slot);
read_pos.compare_exchange_strong(x, x + 1, std::memory_order_relaxed); read_pos.compare_exchange_strong(x, x + 1, std::memory_order_relaxed);
} }
} }
check.wait(round * threads_n); check.wait(round * threads_n);
if (id == 0) {
waiter->close();
}
} }
})); }));
} }
@ -126,4 +142,10 @@ TEST(MpmcWaiter, stress) {
thread.join(); thread.join();
} }
} }
TEST(MpmcEagerWaiter, stress_multi) {
test_waiter_stress<td::MpmcEagerWaiter>();
}
TEST(MpmcSleepyWaiter, stress_multi) {
test_waiter_stress<td::MpmcSleepyWaiter>();
}
#endif // !TD_THREAD_UNSUPPORTED #endif // !TD_THREAD_UNSUPPORTED

View file

@ -0,0 +1,153 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU 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 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2019-2020 Telegram Systems LLP
*/
#include "td/utils/tests.h"
#include "td/utils/benchmark.h"
#include "td/utils/StealingQueue.h"
#include "td/utils/MpmcQueue.h"
namespace td {
TEST(StealingQueue, very_simple) {
StealingQueue<int, 8> q;
q.local_push(1, [](auto x) { UNREACHABLE(); });
int x;
CHECK(q.local_pop(x));
ASSERT_EQ(1, x);
}
TEST(AtomicRead, simple) {
td::Stage run;
td::Stage check;
size_t threads_n = 10;
std::vector<td::thread> threads;
int x{0};
std::atomic<int> version{0};
int64 res = 0;
for (size_t i = 0; i < threads_n; i++) {
threads.push_back(td::thread([&, id = static_cast<uint32>(i)] {
for (uint64 round = 1; round < 10000; round++) {
if (id == 0) {
}
run.wait(round * threads_n);
if (id == 0) {
version++;
x++;
version++;
} else {
int y = 0;
auto v1 = version.load();
y = x;
auto v2 = version.load();
if (v1 == v2 && v1 % 2 == 0) {
res += y;
}
}
check.wait(round * threads_n);
}
}));
}
td::do_not_optimize_away(res);
for (auto &thread : threads) {
thread.join();
}
}
TEST(StealingQueue, simple) {
uint64 sum;
std::atomic<uint64> got_sum;
td::Stage run;
td::Stage check;
size_t threads_n = 10;
std::vector<td::thread> threads;
std::vector<StealingQueue<int, 8>> lq(threads_n);
MpmcQueue<int> gq(threads_n);
constexpr uint64 XN = 20;
uint64 x_sum[XN];
x_sum[0] = 0;
x_sum[1] = 1;
for (uint64 i = 2; i < XN; i++) {
x_sum[i] = i + x_sum[i - 1] + x_sum[i - 2];
}
td::Random::Xorshift128plus rnd(123);
for (size_t i = 0; i < threads_n; i++) {
threads.push_back(td::thread([&, id = static_cast<uint32>(i)] {
for (uint64 round = 1; round < 10000; round++) {
if (id == 0) {
sum = 0;
int n = rnd() % 5;
for (int j = 0; j < n; j++) {
int x = rand() % XN;
sum += x_sum[x];
gq.push(x, id);
}
got_sum = 0;
}
run.wait(round * threads_n);
while (got_sum.load() != sum) {
auto x = [&] {
int res;
if (lq[id].local_pop(res)) {
return res;
}
if (gq.try_pop(res, id)) {
return res;
}
if (lq[id].steal(res, lq[rand() % threads_n])) {
//LOG(ERROR) << "STEAL";
return res;
}
return 0;
}();
if (x == 0) {
continue;
}
//LOG(ERROR) << x << " " << got_sum.load() << " " << sum;
got_sum.fetch_add(x, std::memory_order_relaxed);
lq[id].local_push(x - 1, [&](auto y) {
//LOG(ERROR) << "OVERFLOW";
gq.push(y, id);
});
if (x > 1) {
lq[id].local_push(x - 2, [&](auto y) { gq.push(y, id); });
}
}
check.wait(round * threads_n);
}
}));
}
for (auto &thread : threads) {
thread.join();
}
}
} // namespace td

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "td/utils/tests.h" #include "td/utils/tests.h"
#include "td/utils/FileLog.h" #include "td/utils/FileLog.h"
@ -80,7 +80,8 @@ void bench_log(std::string name, int threads_n, F &&f) {
}; };
TEST(Log, TsLogger) { TEST(Log, TsLogger) {
bench_log("NewTsFileLog", 4, [] { return td::TsFileLog::create("tmplog").move_as_ok(); }); bench_log("NewTsFileLog", 4,
[] { return td::TsFileLog::create("tmplog", std::numeric_limits<td::int64>::max(), false).move_as_ok(); });
bench_log("TsFileLog", 8, [] { bench_log("TsFileLog", 8, [] {
class FileLog : public td::LogInterface { class FileLog : public td::LogInterface {
public: public:

View file

@ -250,7 +250,7 @@ void TerminalIOImpl::line_cb(std::string cmd) {
cmd_queue_.push(td::BufferSlice{std::move(cmd)}); cmd_queue_.push(td::BufferSlice{std::move(cmd)});
} }
void TerminalIO::output_stdout(td::Slice slice) { void TerminalIO::output_stdout(td::Slice slice, double max_wait) {
auto &fd = td::Stdout(); auto &fd = td::Stdout();
if (fd.empty()) { if (fd.empty()) {
return; return;
@ -264,7 +264,7 @@ void TerminalIO::output_stdout(td::Slice slice) {
} }
// Resource temporary unavailable // Resource temporary unavailable
if (end_time == 0) { if (end_time == 0) {
end_time = Time::now() + 0.01; end_time = Time::now() + max_wait;
} else if (Time::now() > end_time) { } else if (Time::now() > end_time) {
break; break;
} }
@ -281,10 +281,10 @@ void TerminalIO::output(std::string line) {
void TerminalIO::output(td::Slice line) { void TerminalIO::output(td::Slice line) {
auto instance = TerminalIOImpl::instance(); auto instance = TerminalIOImpl::instance();
if (!instance) { if (!instance) {
output_stdout(line); output_stdout(line, 10.0);
} else { } else {
instance->deactivate_readline(); instance->deactivate_readline();
output_stdout(line); output_stdout(line, 10.0);
instance->reactivate_readline(); instance->reactivate_readline();
} }
} }
@ -298,23 +298,33 @@ void TerminalIO::output_stderr(td::Slice line) {
if (!instance) { if (!instance) {
td::TsCerr() << line; td::TsCerr() << line;
} else { } else {
instance->deactivate_readline();
if (instance->readline_used()) { if (instance->readline_used()) {
output_stdout(line); instance->deactivate_readline();
output_stdout(line, 0.01);
instance->reactivate_readline();
} else { } else {
td::TsCerr() << line; td::TsCerr() << line;
} }
} }
} }
TerminalIOOutputter::~TerminalIOOutputter() { void TerminalIOOutputter::flush() {
if (buffer_) { if (buffer_) {
CHECK(sb_); CHECK(sb_);
if (!sb_->as_cslice().empty()) {
if (is_err_) { if (is_err_) {
TerminalIO::output_stderr(sb_->as_cslice()); TerminalIO::output_stderr(sb_->as_cslice());
} else { } else {
TerminalIO::output(sb_->as_cslice()); TerminalIO::output(sb_->as_cslice());
} }
}
sb_->clear();
}
}
TerminalIOOutputter::~TerminalIOOutputter() {
if (buffer_) {
flush();
delete[] buffer_; delete[] buffer_;
} }
} }

View file

@ -32,7 +32,7 @@ class TerminalIOOutputter {
TerminalIOOutputter(bool is_err) TerminalIOOutputter(bool is_err)
: buffer_(new char[BUFFER_SIZE]) : buffer_(new char[BUFFER_SIZE])
, is_err_(is_err) , is_err_(is_err)
, sb_(std::make_unique<StringBuilder>(td::MutableSlice{buffer_, BUFFER_SIZE})) { , sb_(std::make_unique<StringBuilder>(td::MutableSlice{buffer_, BUFFER_SIZE}, true)) {
} }
TerminalIOOutputter(TerminalIOOutputter &&X) = default; TerminalIOOutputter(TerminalIOOutputter &&X) = default;
@ -56,6 +56,7 @@ class TerminalIOOutputter {
bool is_error() const { bool is_error() const {
return sb_->is_error(); return sb_->is_error();
} }
void flush();
~TerminalIOOutputter(); ~TerminalIOOutputter();
private: private:
@ -80,7 +81,7 @@ class TerminalIO : public actor::Actor {
static void output(td::Slice slice); static void output(td::Slice slice);
static void output_stderr(std::string line); static void output_stderr(std::string line);
static void output_stderr(td::Slice slice); static void output_stderr(td::Slice slice);
static void output_stdout(td::Slice line); static void output_stdout(td::Slice line, double max_wait);
static TerminalIOOutputter out() { static TerminalIOOutputter out() {
return TerminalIOOutputter{false}; return TerminalIOOutputter{false};
} }

View file

@ -51,7 +51,7 @@ ton.blockId workchain:int32 shard:int64 seqno:int32 = internal.BlockId;
ton.blockIdExt workchain:int32 shard:int64 seqno:int32 root_hash:bytes file_hash:bytes = ton.BlockIdExt; ton.blockIdExt workchain:int32 shard:int64 seqno:int32 root_hash:bytes file_hash:bytes = ton.BlockIdExt;
raw.fullAccountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId block_id:ton.blockIdExt frozen_hash:bytes sync_utime:int53 = raw.FullAccountState; raw.fullAccountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId block_id:ton.blockIdExt frozen_hash:bytes sync_utime:int53 = raw.FullAccountState;
raw.message source:string destination:string value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes message:bytes is_message_encrypted:Bool = raw.Message; raw.message source:string destination:string value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes msg_data:msg.Data = raw.Message;
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction; raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions; raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
@ -83,8 +83,13 @@ syncStateInProgress from_seqno:int32 to_seqno:int32 current_seqno:int32 = SyncSt
// MSG // MSG
// //
msg.dataText text:string = msg.Data; msg.dataRaw body:bytes = msg.Data;
msg.dataEncryptedText text:string = msg.Data; msg.dataText text:bytes = msg.Data;
msg.dataDecryptedText text:bytes = msg.Data;
msg.dataEncryptedText text:bytes = msg.Data;
msg.dataArray elements:vector<msg.Data> = msg.DataArray;
msg.message destination:accountAddress amount:int64 data:msg.Data = msg.Message; msg.message destination:accountAddress amount:int64 data:msg.Data = msg.Message;
// //
@ -111,7 +116,7 @@ dns.resolved entries:vector<dns.entry> = dns.Resolved;
// //
actionNoop = Action; actionNoop = Action;
actionMsg messages:vector<msg.Message> allow_send_to_uninited:Bool = Action; actionMsg messages:vector<msg.message> allow_send_to_uninited:Bool = Action;
actionDns actions:vector<dns.Action> = Action; actionDns actions:vector<dns.Action> = Action;
//actionMultisig actions:vector<multisig.order> = Action; //actionMultisig actions:vector<multisig.order> = Action;
@ -205,11 +210,14 @@ sync = ton.BlockIdExt;
// revision = 0 -- use default revision // revision = 0 -- use default revision
// revision = x (x > 0) -- use revision x // revision = x (x > 0) -- use revision x
// revision = -1 -- use experimental (newest) revision. Only for debug purpose
getAccountAddress initial_account_state:InitialAccountState revision:int32 = AccountAddress; getAccountAddress initial_account_state:InitialAccountState revision:int32 = AccountAddress;
guessAccountRevision initial_account_state:InitialAccountState = AccountRevisionList; guessAccountRevision initial_account_state:InitialAccountState = AccountRevisionList;
getAccountState account_address:accountAddress = FullAccountState; getAccountState account_address:accountAddress = FullAccountState;
createQuery private_key:InputKey address:accountAddress timeout:int32 action:Action = query.Info; createQuery private_key:InputKey address:accountAddress timeout:int32 action:Action = query.Info;
msg.decrypt input_key:InputKey data:msg.dataArray = msg.DataArray;
query.send id:int53 = Ok; query.send id:int53 = Ok;
query.forget id:int53 = Ok; query.forget id:int53 = Ok;
query.estimateFees id:int53 ignore_chksig:Bool = query.Fees; query.estimateFees id:int53 ignore_chksig:Bool = query.Fees;

Binary file not shown.

View file

@ -35,6 +35,7 @@
#include "td/utils/benchmark.h" #include "td/utils/benchmark.h"
#include "td/utils/filesystem.h" #include "td/utils/filesystem.h"
#include "td/utils/optional.h" #include "td/utils/optional.h"
#include "td/utils/overloaded.h"
#include "td/utils/port/path.h" #include "td/utils/port/path.h"
#include "td/utils/PathView.h" #include "td/utils/PathView.h"
#include "td/utils/tests.h" #include "td/utils/tests.h"
@ -70,7 +71,7 @@ TEST(Tonlib, Text) {
auto cs = vm::load_cell_slice(cb.finalize()); auto cs = vm::load_cell_slice(cb.finalize());
auto cs2 = cs; auto cs2 = cs;
cs.print_rec(std::cerr); cs.print_rec(std::cerr);
CHECK(block::gen::t_Text.validate_exact(cs2)); CHECK(block::gen::t_Text.validate_exact_upto(1024, cs2));
auto got_str = vm::CellText::load(cs).move_as_ok(); auto got_str = vm::CellText::load(cs).move_as_ok();
ASSERT_EQ(str, got_str); ASSERT_EQ(str, got_str);
} }
@ -506,12 +507,32 @@ TEST(Tonlib, KeysApi) {
sync_send(client, make_object<tonlib_api::deleteKey>( sync_send(client, make_object<tonlib_api::deleteKey>(
make_object<tonlib_api::key>(new_imported_key->public_key_, new_imported_key->secret_.copy()))) make_object<tonlib_api::key>(new_imported_key->public_key_, new_imported_key->secret_.copy())))
.move_as_ok(); .move_as_ok();
td::Ed25519::PrivateKey pkey(exported_raw_key->data_.copy());
auto raw_imported_key = sync_send(client, make_object<tonlib_api::importUnencryptedKey>(new_local_password.copy(), auto raw_imported_key = sync_send(client, make_object<tonlib_api::importUnencryptedKey>(new_local_password.copy(),
std::move(exported_raw_key))) std::move(exported_raw_key)))
.move_as_ok(); .move_as_ok();
CHECK(raw_imported_key->public_key_ == key->public_key_); CHECK(raw_imported_key->public_key_ == key->public_key_);
CHECK(raw_imported_key->secret_ != key->secret_); CHECK(raw_imported_key->secret_ != key->secret_);
auto other_public_key = td::Ed25519::generate_private_key().move_as_ok().get_public_key().move_as_ok();
std::string text = "hello world";
std::vector<tonlib_api::object_ptr<tonlib_api::msg_Data>> elements;
elements.push_back(make_object<tonlib_api::msg_dataEncryptedText>(
SimpleEncryptionV2::encrypt_data(text, other_public_key, pkey).move_as_ok().as_slice().str()));
auto decrypted =
sync_send(client, make_object<tonlib_api::msg_decrypt>(
make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(key->public_key_, raw_imported_key->secret_.copy()),
new_local_password.copy()),
make_object<tonlib_api::msg_dataArray>(std::move(elements))))
.move_as_ok();
downcast_call(*decrypted->elements_[0],
td::overloaded([](auto &) { UNREACHABLE(); },
[&](tonlib_api::msg_dataDecryptedText &decrypted) { CHECK(decrypted.text_ == text); }));
} }
TEST(Tonlib, ConfigCache) { TEST(Tonlib, ConfigCache) {

View file

@ -234,7 +234,7 @@ td::Result<QueryId> create_send_grams_query(Client& client, const Wallet& source
std::vector<tonlib_api::object_ptr<tonlib_api::msg_message>> msgs; std::vector<tonlib_api::object_ptr<tonlib_api::msg_message>> msgs;
tonlib_api::object_ptr<tonlib_api::msg_Data> data; tonlib_api::object_ptr<tonlib_api::msg_Data> data;
if (encrypted) { if (encrypted) {
data = tonlib_api::make_object<tonlib_api::msg_dataEncryptedText>(std::move(message)); data = tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(std::move(message));
} else { } else {
data = tonlib_api::make_object<tonlib_api::msg_dataText>(std::move(message)); data = tonlib_api::make_object<tonlib_api::msg_dataText>(std::move(message));
} }
@ -310,6 +310,13 @@ td::Result<tonlib_api::object_ptr<tonlib_api::raw_transactions>> get_transaction
return std::move(got_transactions); return std::move(got_transactions);
} }
std::string read_text(tonlib_api::msg_Data& data) {
std::string text;
downcast_call(data, td::overloaded([](auto& other) {}, [&](tonlib_api::msg_dataText& data) { text = data.text_; },
[&](tonlib_api::msg_dataDecryptedText& data) { text = data.text_; }));
return text;
}
td::Status transfer_grams(Client& client, const Wallet& wallet, std::string address, td::int64 amount, td::Status transfer_grams(Client& client, const Wallet& wallet, std::string address, td::int64 amount,
bool fast = false) { bool fast = false) {
auto src_state = get_account_state(client, wallet.address); auto src_state = get_account_state(client, wallet.address);
@ -320,7 +327,7 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
bool encrypt = true; bool encrypt = true;
auto r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast); auto r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast);
if (r_query_id.is_error()) { if (r_query_id.is_error()) {
LOG(INFO) << "Send query WITHOUT message encryption"; LOG(INFO) << "Send query WITHOUT message encryption " << r_query_id.error();
encrypt = false; encrypt = false;
r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast); r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast);
} else { } else {
@ -363,7 +370,7 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
const auto& txn = tr->transactions_[0]; const auto& txn = tr->transactions_[0];
CHECK(txn->in_msg_->body_hash_ == query_info.body_hash); CHECK(txn->in_msg_->body_hash_ == query_info.body_hash);
ASSERT_EQ(1u, txn->out_msgs_.size()); ASSERT_EQ(1u, txn->out_msgs_.size());
ASSERT_EQ(message, txn->out_msgs_[0]->message_); ASSERT_EQ(message, read_text(*txn->out_msgs_[0]->msg_data_));
lt = txn->out_msgs_[0]->created_lt_; lt = txn->out_msgs_[0]->created_lt_;
auto fee_difference = fees.first.sum() - txn->fee_; auto fee_difference = fees.first.sum() - txn->fee_;
first_fee = txn->fee_; first_fee = txn->fee_;
@ -389,7 +396,7 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
} }
ASSERT_EQ(new_src_state.address, txn->in_msg_->source_); ASSERT_EQ(new_src_state.address, txn->in_msg_->source_);
if (!encrypt) { if (!encrypt) {
ASSERT_EQ(message, txn->in_msg_->message_); ASSERT_EQ(message, read_text(*txn->in_msg_->msg_data_));
} }
auto fee_difference = fees.second.sum() - txn->fee_; auto fee_difference = fees.second.sum() - txn->fee_;
auto desc = PSTRING() << fee_difference << " storage:[" << fees.second.storage_fee << " vs " << txn->storage_fee_ auto desc = PSTRING() << fee_difference << " storage:[" << fees.second.storage_fee << " vs " << txn->storage_fee_
@ -550,19 +557,11 @@ void test_multisig(Client& client, const Wallet& giver_wallet) {
void dns_resolve(Client& client, const Wallet& dns, std::string name) { void dns_resolve(Client& client, const Wallet& dns, std::string name) {
using namespace ton::tonlib_api; using namespace ton::tonlib_api;
auto address = dns.get_address(); auto address = dns.get_address();
while (true) {
auto resolved = auto resolved =
sync_send(client, make_object<::ton::tonlib_api::dns_resolve>(std::move(address), name, 1, 0)).move_as_ok(); sync_send(client, make_object<::ton::tonlib_api::dns_resolve>(std::move(address), name, 1, 4)).move_as_ok();
CHECK(resolved->entries_.size() == 1); CHECK(resolved->entries_.size() == 1);
LOG(INFO) << to_string(resolved); LOG(INFO) << to_string(resolved);
if (resolved->entries_[0]->category_ == -1) {
auto entry = ton::move_tl_object_as<dns_entryDataNextResolver>(resolved->entries_[0]->entry_);
address = std::move(entry->resolver_);
continue;
}
LOG(INFO) << "OK"; LOG(INFO) << "OK";
break;
}
} }
void test_dns(Client& client, const Wallet& giver_wallet) { void test_dns(Client& client, const Wallet& giver_wallet) {
@ -580,10 +579,10 @@ void test_dns(Client& client, const Wallet& giver_wallet) {
make_object<dns_entry>("A", -1, make_object<dns_entryDataNextResolver>(A_B.get_address())))); make_object<dns_entry>("A", -1, make_object<dns_entryDataNextResolver>(A_B.get_address()))));
auto init_A = create_update_dns_query(client, A, std::move(actions)).move_as_ok(); auto init_A = create_update_dns_query(client, A, std::move(actions)).move_as_ok();
actions.push_back(make_object<dns_actionSet>( actions.push_back(make_object<dns_actionSet>(
make_object<dns_entry>("B.A", -1, make_object<dns_entryDataNextResolver>(A_B_C.get_address())))); make_object<dns_entry>("B", -1, make_object<dns_entryDataNextResolver>(A_B_C.get_address()))));
auto init_A_B = create_update_dns_query(client, A_B, std::move(actions)).move_as_ok(); auto init_A_B = create_update_dns_query(client, A_B, std::move(actions)).move_as_ok();
actions.push_back( actions.push_back(
make_object<dns_actionSet>(make_object<dns_entry>("C.B.A", 1, make_object<dns_entryDataText>("Hello dns")))); make_object<dns_actionSet>(make_object<dns_entry>("C", 1, make_object<dns_entryDataText>("Hello dns"))));
auto init_A_B_C = create_update_dns_query(client, A_B_C, std::move(actions)).move_as_ok(); auto init_A_B_C = create_update_dns_query(client, A_B_C, std::move(actions)).move_as_ok();
LOG(INFO) << "Send dns init queries"; LOG(INFO) << "Send dns init queries";

View file

@ -1,3 +1,21 @@
/*
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 "KeyValue.h" #include "KeyValue.h"
#include "td/utils/filesystem.h" #include "td/utils/filesystem.h"

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "td/utils/SharedSlice.h" #include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h" #include "td/utils/Slice.h"

View file

@ -63,6 +63,24 @@ struct GetAccountState {
td::optional<ton::BlockIdExt> block_id; td::optional<ton::BlockIdExt> block_id;
using ReturnType = td::unique_ptr<AccountState>; using ReturnType = td::unique_ptr<AccountState>;
}; };
struct RemoteRunSmcMethod {
block::StdAddress address;
td::optional<ton::BlockIdExt> block_id;
ton::SmartContract::Args args;
bool need_result{false};
using ReturnType = RemoteRunSmcMethodReturnType;
};
struct RemoteRunSmcMethodReturnType {
ton::SmartContract::State smc_state;
ton::BlockIdExt block_id;
// result
// c7
// libs
};
struct GetPrivateKey { struct GetPrivateKey {
KeyStorage::InputKey input_key; KeyStorage::InputKey input_key;
using ReturnType = KeyStorage::PrivateKey; using ReturnType = KeyStorage::PrivateKey;
@ -120,6 +138,16 @@ static block::AccountState create_account_state(ton::tl_object_ptr<ton::lite_api
res.state = std::move(from->state_); res.state = std::move(from->state_);
return res; return res;
} }
static block::AccountState create_account_state(ton::tl_object_ptr<ton::lite_api::liteServer_runMethodResult>& from) {
block::AccountState res;
res.blk = ton::create_block_id(from->id_);
res.shard_blk = ton::create_block_id(from->shardblk_);
res.shard_proof = std::move(from->shard_proof_);
res.proof = std::move(from->proof_);
res.state = std::move(from->state_proof_);
res.is_virtualized = from->mode_ > 0;
return res;
}
struct RawAccountState { struct RawAccountState {
td::int64 balance = -1; td::int64 balance = -1;
@ -373,7 +401,6 @@ class AccountState {
if (o_revision) { if (o_revision) {
wallet_type_ = WalletType::WalletV3; wallet_type_ = WalletType::WalletV3;
wallet_revision_ = o_revision.value(); wallet_revision_ = o_revision.value();
LOG(ERROR) << "!!!" << wallet_revision_;
set_new_state({ton::WalletV3::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id_)}); set_new_state({ton::WalletV3::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id_)});
return wallet_type_; return wallet_type_;
} }
@ -381,7 +408,6 @@ class AccountState {
if (o_revision) { if (o_revision) {
wallet_type_ = WalletType::HighloadWalletV2; wallet_type_ = WalletType::HighloadWalletV2;
wallet_revision_ = o_revision.value(); wallet_revision_ = o_revision.value();
LOG(ERROR) << "!!!" << wallet_revision_;
set_new_state( set_new_state(
{ton::HighloadWalletV2::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id_)}); {ton::HighloadWalletV2::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id_)});
return wallet_type_; return wallet_type_;
@ -390,7 +416,6 @@ class AccountState {
if (o_revision) { if (o_revision) {
wallet_type_ = WalletType::ManualDns; wallet_type_ = WalletType::ManualDns;
wallet_revision_ = o_revision.value(); wallet_revision_ = o_revision.value();
LOG(ERROR) << "!!!" << wallet_revision_;
auto dns = ton::ManualDns::create(key, wallet_id_, wallet_revision_); auto dns = ton::ManualDns::create(key, wallet_id_, wallet_revision_);
set_new_state(dns->get_state()); set_new_state(dns->get_state());
return wallet_type_; return wallet_type_;
@ -841,6 +866,119 @@ class GetTransactionHistory : public td::actor::Actor {
} }
}; };
class RemoteRunSmcMethod : public td::actor::Actor {
public:
RemoteRunSmcMethod(ExtClientRef ext_client_ref, int_api::RemoteRunSmcMethod query, td::actor::ActorShared<> parent,
td::Promise<int_api::RemoteRunSmcMethod::ReturnType>&& promise)
: query_(std::move(query)), promise_(std::move(promise)), parent_(std::move(parent)) {
client_.set_client(ext_client_ref);
}
private:
int_api::RemoteRunSmcMethod query_;
td::Promise<int_api::RemoteRunSmcMethod::ReturnType> promise_;
td::actor::ActorShared<> parent_;
ExtClient client_;
void with_run_method_result(
td::Result<ton::tl_object_ptr<ton::lite_api::liteServer_runMethodResult>> r_run_method_result) {
check(do_with_run_method_result(std::move(r_run_method_result)));
}
td::Status do_with_run_method_result(
td::Result<ton::tl_object_ptr<ton::lite_api::liteServer_runMethodResult>> r_run_method_result) {
TRY_RESULT(run_method_result, std::move(r_run_method_result));
TRY_RESULT_PREFIX(state, TRY_VM(do_with_run_method_result(std::move(run_method_result))),
TonlibError::ValidateAccountState());
promise_.set_value(std::move(state));
stop();
return td::Status::OK();
}
td::Result<int_api::RemoteRunSmcMethod::ReturnType> do_with_run_method_result(
ton::tl_object_ptr<ton::lite_api::liteServer_runMethodResult> run_method_result) {
auto account_state = create_account_state(run_method_result);
TRY_RESULT(info, account_state.validate(query_.block_id.value(), query_.address));
auto serialized_state = account_state.state.clone();
int_api::RemoteRunSmcMethod::ReturnType res;
res.block_id = query_.block_id.value();
auto cell = info.root;
if (cell.is_null()) {
return res;
}
block::gen::Account::Record_account account;
if (!tlb::unpack_cell(cell, account)) {
return td::Status::Error("Failed to unpack Account");
}
block::gen::AccountStorage::Record storage;
if (!tlb::csr_unpack(account.storage, storage)) {
return td::Status::Error("Failed to unpack AccountStorage");
}
auto state_tag = block::gen::t_AccountState.get_tag(*storage.state);
if (state_tag < 0) {
return td::Status::Error("Failed to parse AccountState tag");
}
if (state_tag != block::gen::AccountState::account_active) {
return td::Status::Error("Account is not active");
}
block::gen::AccountState::Record_account_active state;
if (!tlb::csr_unpack(storage.state, state)) {
return td::Status::Error("Failed to parse AccountState");
}
block::gen::StateInit::Record state_init;
if (!tlb::csr_unpack(state.x, state_init)) {
return td::Status::Error("Failed to parse StateInit");
}
state_init.code->prefetch_maybe_ref(res.smc_state.code);
state_init.data->prefetch_maybe_ref(res.smc_state.data);
return res;
}
void with_last_block(td::Result<LastBlockState> r_last_block) {
check(do_with_last_block(std::move(r_last_block)));
}
td::Status with_block_id() {
TRY_RESULT(method_id, query_.args.get_method_id());
TRY_RESULT(serialized_stack, query_.args.get_serialized_stack());
client_.send_query(
//liteServer.runSmcMethod mode:# id:tonNode.blockIdExt account:liteServer.accountId method_id:long params:bytes = liteServer.RunMethodResult;
ton::lite_api::liteServer_runSmcMethod(
0x1f, ton::create_tl_lite_block_id(query_.block_id.value()),
ton::create_tl_object<ton::lite_api::liteServer_accountId>(query_.address.workchain, query_.address.addr),
method_id, std::move(serialized_stack)),
[self = this](auto r_state) { self->with_run_method_result(std::move(r_state)); },
query_.block_id.value().id.seqno);
return td::Status::OK();
}
td::Status do_with_last_block(td::Result<LastBlockState> r_last_block) {
TRY_RESULT(last_block, std::move(r_last_block));
query_.block_id = std::move(last_block.last_block_id);
with_block_id();
return td::Status::OK();
}
void start_up() override {
if (query_.block_id) {
check(with_block_id());
} else {
client_.with_last_block(
[self = this](td::Result<LastBlockState> r_last_block) { self->with_last_block(std::move(r_last_block)); });
}
}
void check(td::Status status) {
if (status.is_error()) {
promise_.set_error(std::move(status));
stop();
}
}
void hangup() override {
check(TonlibError::Cancelled());
}
};
class GetRawAccountState : public td::actor::Actor { class GetRawAccountState : public td::actor::Actor {
public: public:
GetRawAccountState(ExtClientRef ext_client_ref, block::StdAddress address, td::optional<ton::BlockIdExt> block_id, GetRawAccountState(ExtClientRef ext_client_ref, block::StdAddress address, td::optional<ton::BlockIdExt> block_id,
@ -882,8 +1020,8 @@ class GetRawAccountState : public td::actor::Actor {
res.block_id = block_id_.value(); res.block_id = block_id_.value();
res.info = std::move(info); res.info = std::move(info);
auto cell = res.info.root; auto cell = res.info.root;
std::ostringstream outp; //std::ostringstream outp;
block::gen::t_Account.print_ref(outp, cell); //block::gen::t_Account.print_ref(outp, cell);
//LOG(INFO) << outp.str(); //LOG(INFO) << outp.str();
if (cell.is_null()) { if (cell.is_null()) {
return res; return res;
@ -1693,6 +1831,50 @@ struct ToRawTransactions {
return td::Status::Error("Failed to unpack Message"); return td::Status::Error("Failed to unpack Message");
} }
td::Ref<vm::CellSlice> body;
if (message.body->prefetch_long(1) == 0) {
body = std::move(message.body);
body.write().advance(1);
} else {
body = vm::load_cell_slice_ref(message.body->prefetch_ref());
}
auto body_cell = vm::CellBuilder().append_cellslice(*body).finalize();
auto body_hash = body_cell->get_hash().as_slice().str();
tonlib_api::object_ptr<tonlib_api::msg_Data> data;
if (body->size() >= 32 && static_cast<td::uint32>(body->prefetch_long(32)) <= 1) {
auto type = body.write().fetch_long(32);
td::Status status;
auto r_body_message = vm::CellString::load(body.write());
LOG_IF(WARNING, r_body_message.is_error()) << "Failed to parse a message: " << r_body_message.error();
if (r_body_message.is_ok()) {
if (type == 0) {
LOG(ERROR) << "OK " << r_body_message.ok();
data = tonlib_api::make_object<tonlib_api::msg_dataText>(r_body_message.move_as_ok());
} else {
LOG(ERROR) << "TRY DECRYPT";
auto encrypted_message = r_body_message.move_as_ok();
auto r_decrypted_message = [&]() -> td::Result<std::string> {
if (!private_key_) {
return TonlibError::EmptyField("private_key");
}
TRY_RESULT(decrypted, SimpleEncryptionV2::decrypt_data(encrypted_message, private_key_.value()));
return decrypted.as_slice().str();
}();
if (r_decrypted_message.is_ok()) {
data = tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(r_decrypted_message.move_as_ok());
} else {
data = tonlib_api::make_object<tonlib_api::msg_dataEncryptedText>(encrypted_message);
}
}
}
}
if (!data) {
data = tonlib_api::make_object<tonlib_api::msg_dataRaw>(to_bytes(std::move(body_cell)));
}
auto tag = block::gen::CommonMsgInfo().get_tag(*message.info); auto tag = block::gen::CommonMsgInfo().get_tag(*message.info);
if (tag < 0) { if (tag < 0) {
return td::Status::Error("Failed to read CommonMsgInfo tag"); return td::Status::Error("Failed to read CommonMsgInfo tag");
@ -1710,41 +1892,10 @@ struct ToRawTransactions {
TRY_RESULT(fwd_fee, to_balance(msg_info.fwd_fee)); TRY_RESULT(fwd_fee, to_balance(msg_info.fwd_fee));
TRY_RESULT(ihr_fee, to_balance(msg_info.ihr_fee)); TRY_RESULT(ihr_fee, to_balance(msg_info.ihr_fee));
auto created_lt = static_cast<td::int64>(msg_info.created_lt); auto created_lt = static_cast<td::int64>(msg_info.created_lt);
td::Ref<vm::CellSlice> body;
if (message.body->prefetch_long(1) == 0) {
body = std::move(message.body);
body.write().advance(1);
} else {
body = vm::load_cell_slice_ref(message.body->prefetch_ref());
}
auto body_hash = vm::CellBuilder().append_cellslice(*body).finalize()->get_hash().as_slice().str();
std::string body_message;
bool is_encrypted = false;
if (body->size() >= 32 && body->prefetch_long(32) <= 1) {
auto type = body.write().fetch_long(32);
auto r_body_message = vm::CellString::load(body.write());
if (type == 1) {
LOG(ERROR) << "TRY DECRYPT";
r_body_message = [&]() -> td::Result<std::string> {
TRY_RESULT(message, std::move(r_body_message));
if (!private_key_) {
return TonlibError::EmptyField("private_key");
}
TRY_RESULT(decrypted, SimpleEncryptionV2::decrypt_data(message, private_key_.value()));
is_encrypted = true;
return decrypted.as_slice().str();
}();
}
if (r_body_message.is_ok()) {
body_message = r_body_message.move_as_ok();
} else {
LOG(WARNING) << "Failed to parse a message: " << r_body_message.error();
}
}
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), std::move(dest), balance, fwd_fee, return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), std::move(dest), balance, fwd_fee,
ihr_fee, created_lt, std::move(body_hash), ihr_fee, created_lt, std::move(body_hash),
std::move(body_message), is_encrypted); std::move(data));
} }
case block::gen::CommonMsgInfo::ext_in_msg_info: { case block::gen::CommonMsgInfo::ext_in_msg_info: {
block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info; block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info;
@ -1752,16 +1903,8 @@ struct ToRawTransactions {
return td::Status::Error("Failed to unpack CommonMsgInfo::ext_in_msg_info"); return td::Status::Error("Failed to unpack CommonMsgInfo::ext_in_msg_info");
} }
TRY_RESULT(dest, to_std_address(msg_info.dest)); TRY_RESULT(dest, to_std_address(msg_info.dest));
td::Ref<vm::CellSlice> body;
if (message.body->prefetch_long(1) == 0) {
body = std::move(message.body);
body.write().advance(1);
} else {
body = vm::load_cell_slice_ref(message.body->prefetch_ref());
}
auto body_hash = vm::CellBuilder().append_cellslice(*body).finalize()->get_hash().as_slice().str();
return tonlib_api::make_object<tonlib_api::raw_message>("", std::move(dest), 0, 0, 0, 0, std::move(body_hash), return tonlib_api::make_object<tonlib_api::raw_message>("", std::move(dest), 0, 0, 0, 0, std::move(body_hash),
"", false); std::move(data));
} }
case block::gen::CommonMsgInfo::ext_out_msg_info: { case block::gen::CommonMsgInfo::ext_out_msg_info: {
block::gen::CommonMsgInfo::Record_ext_out_msg_info msg_info; block::gen::CommonMsgInfo::Record_ext_out_msg_info msg_info;
@ -1769,7 +1912,8 @@ struct ToRawTransactions {
return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info"); return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info");
} }
TRY_RESULT(src, to_std_address(msg_info.src)); TRY_RESULT(src, to_std_address(msg_info.src));
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), "", 0, 0, 0, 0, "", "", false); return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), "", 0, 0, 0, 0, std::move(body_hash),
std::move(data));
} }
} }
@ -2027,8 +2171,12 @@ class GenericCreateSendGrams : public TonlibQueryActor {
struct Action { struct Action {
block::StdAddress destination; block::StdAddress destination;
td::int64 amount; td::int64 amount;
bool is_encrypted{false};
bool should_encrypt; bool should_encrypt;
std::string message; std::string message;
td::Ref<vm::Cell> body;
}; };
bool allow_send_to_uninited_{false}; bool allow_send_to_uninited_{false};
std::vector<Action> actions_; std::vector<Action> actions_;
@ -2064,27 +2212,36 @@ class GenericCreateSendGrams : public TonlibQueryActor {
res.amount = message.amount_; res.amount = message.amount_;
auto status = auto status =
downcast_call2<td::Status>(*message.data_, td::overloaded( downcast_call2<td::Status>(*message.data_, td::overloaded(
[&](tonlib_api::msg_dataText& text) { [&](tonlib_api::msg_dataRaw& text) {
// Use this limit as a preventive check TRY_RESULT(body, vm::std_boc_deserialize(text.body_));
if (text.text_.size() > ton::Wallet::max_message_size) { res.body = std::move(body);
return TonlibError::MessageTooLong();
}
res.message = text.text_;
res.should_encrypt = false;
return td::Status::OK(); return td::Status::OK();
}, },
[&](tonlib_api::msg_dataEncryptedText& text) { [&](tonlib_api::msg_dataText& text) {
// Use this limit as a preventive check res.message = text.text_;
if (text.text_.size() > ton::Wallet::max_message_size) { res.should_encrypt = false;
return TonlibError::MessageTooLong(); res.is_encrypted = false;
} return td::Status::OK();
},
[&](tonlib_api::msg_dataDecryptedText& text) {
res.message = text.text_; res.message = text.text_;
if (!has_private_key_) { if (!has_private_key_) {
return TonlibError::EmptyField("input_key"); return TonlibError::EmptyField("input_key");
} }
res.should_encrypt = true; res.should_encrypt = true;
res.is_encrypted = true;
return td::Status::OK();
},
[&](tonlib_api::msg_dataEncryptedText& text) {
res.message = text.text_;
res.should_encrypt = false;
res.is_encrypted = true;
return td::Status::OK(); return td::Status::OK();
})); }));
// Use this limit as a preventive check
if (res.message.size() > ton::Wallet::max_message_size) {
return TonlibError::MessageTooLong();
}
TRY_STATUS(std::move(status)); TRY_STATUS(std::move(status));
return res; return res;
} }
@ -2301,7 +2458,9 @@ class GenericCreateSendGrams : public TonlibQueryActor {
if (action.amount == source_->get_balance()) { if (action.amount == source_->get_balance()) {
gift.gramms = -1; gift.gramms = -1;
} }
if (action.should_encrypt) { if (action.body.not_null()) {
gift.body = action.body;
} else if (action.should_encrypt) {
LOG(ERROR) << "TRY ENCRYPT"; LOG(ERROR) << "TRY ENCRYPT";
if (!private_key_) { if (!private_key_) {
return TonlibError::EmptyField("private_key"); return TonlibError::EmptyField("private_key");
@ -2311,7 +2470,8 @@ class GenericCreateSendGrams : public TonlibQueryActor {
} }
auto wallet = destination->get_wallet(); auto wallet = destination->get_wallet();
TRY_RESULT_PREFIX(public_key, wallet->get_public_key(), TRY_RESULT_PREFIX(public_key, wallet->get_public_key(),
TonlibError::AccountActionUnsupported("Get public key (in destination)")); TonlibError::AccountActionUnsupported(PSLICE() << "Get public key (in destination) : "
<< destination->get_wallet_type()));
TRY_RESULT_PREFIX(encrypted_message, TRY_RESULT_PREFIX(encrypted_message,
SimpleEncryptionV2::encrypt_data(action.message, public_key, private_key_.value()), SimpleEncryptionV2::encrypt_data(action.message, public_key, private_key_.value()),
TonlibError::Internal()); TonlibError::Internal());
@ -2319,7 +2479,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
gift.is_encrypted = true; gift.is_encrypted = true;
} else { } else {
gift.message = action.message; gift.message = action.message;
gift.is_encrypted = false; gift.is_encrypted = action.is_encrypted;
} }
i++; i++;
gifts.push_back(gift); gifts.push_back(gift);
@ -2349,7 +2509,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
if (source_->get_wallet_type() == AccountState::Giver) { if (source_->get_wallet_type() == AccountState::Giver) {
valid_until = 0; valid_until = 0;
private_key_ = td::Ed25519::PrivateKey(td::SecureString(std::string('\0', 32))); private_key_ = td::Ed25519::PrivateKey(td::SecureString(std::string(32, '\0')));
} }
return with_wallet(*source_->get_wallet()); return with_wallet(*source_->get_wallet());
@ -2387,6 +2547,40 @@ td::Status TonlibClient::do_request(tonlib_api::createQuery& request,
return td::Status::OK(); return td::Status::OK();
} }
td::Status TonlibClient::do_request(tonlib_api::msg_decrypt& request,
td::Promise<object_ptr<tonlib_api::msg_dataArray>>&& promise) {
if (!request.input_key_) {
return TonlibError::EmptyField("input_key");
}
if (!request.data_) {
return TonlibError::EmptyField("data");
}
TRY_RESULT(input_key, from_tonlib(*request.input_key_));
make_request(
int_api::GetPrivateKey{std::move(input_key)},
promise.wrap([elements = std::move(request.data_)](auto key) mutable {
auto private_key = td::Ed25519::PrivateKey(std::move(key.private_key));
elements->elements_ = td::transform(std::move(elements->elements_), [&private_key](auto msg) {
if (!msg) {
return std::move(msg);
}
using ReturnType = tonlib_api::object_ptr<tonlib_api::msg_Data>;
return downcast_call2<ReturnType>(
*msg, td::overloaded([&msg](auto&) { return std::move(msg); },
[&msg, &private_key](tonlib_api::msg_dataEncryptedText& encrypted) -> ReturnType {
auto r_decrypted = SimpleEncryptionV2::decrypt_data(encrypted.text_, private_key);
if (r_decrypted.is_error()) {
return std::move(msg);
}
return tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(
r_decrypted.move_as_ok().as_slice().str());
}));
});
return std::move(elements);
}));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::raw_createQuery& request, td::Status TonlibClient::do_request(const tonlib_api::raw_createQuery& request,
td::Promise<object_ptr<tonlib_api::query_info>>&& promise) { td::Promise<object_ptr<tonlib_api::query_info>>&& promise) {
if (!request.destination_) { if (!request.destination_) {
@ -2698,19 +2892,32 @@ td::Result<tonlib_api::object_ptr<tonlib_api::dns_EntryData>> to_tonlib_api(
} }
void TonlibClient::finish_dns_resolve(std::string name, td::int32 category, td::int32 ttl, void TonlibClient::finish_dns_resolve(std::string name, td::int32 category, td::int32 ttl,
td::optional<ton::BlockIdExt> block_id, td::unique_ptr<AccountState> smc, td::optional<ton::BlockIdExt> block_id, DnsFinishData dns_finish_data,
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise) { td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise) {
if (smc->get_wallet_type() != AccountState::WalletType::ManualDns) { block_id = dns_finish_data.block_id;
return promise.set_error(TonlibError::AccountTypeUnexpected("ManualDns")); // TODO: check if the smartcontract supports Dns interface
} // TODO: should we use some DnsInterface instead of ManualDns?
block_id = smc->get_block_id(); auto dns = ton::ManualDns::create(dns_finish_data.smc_state);
auto dns = ton::ManualDns::create(smc->get_smc_state());
TRY_RESULT_PROMISE(promise, entries, dns->resolve(name, category)); TRY_RESULT_PROMISE(promise, entries, dns->resolve(name, category));
if (entries.size() == 1 && entries[0].category == -1 && entries[0].name != name && ttl > 0 && if (entries.size() == 1 && entries[0].category == -1 && entries[0].name != name && ttl > 0 &&
entries[0].data.type == ton::ManualDns::EntryData::Type::NextResolver) { entries[0].data.type == ton::ManualDns::EntryData::Type::NextResolver) {
td::Slice got_name = entries[0].name;
if (got_name.size() >= name.size()) {
TRY_STATUS_PROMISE(promise, TonlibError::Internal("domain is too long"));
}
auto dot_position = name.size() - got_name.size() - 1;
auto suffix = name.substr(dot_position + 1);
auto prefix = name.substr(0, dot_position);
if (name[dot_position] != '.') {
TRY_STATUS_PROMISE(promise, td::Status::Error("next resolver error: domain split not at a component boundary "));
}
if (suffix != got_name) {
TRY_STATUS_PROMISE(promise, TonlibError::Internal("domain is not a suffix of the query"));
}
auto address = entries[0].data.data.get<ton::ManualDns::EntryDataNextResolver>().resolver; auto address = entries[0].data.data.get<ton::ManualDns::EntryDataNextResolver>().resolver;
return do_dns_request(name, category, ttl - 1, std::move(block_id), address, std::move(promise)); return do_dns_request(prefix, category, ttl - 1, std::move(block_id), address, std::move(promise));
} }
std::vector<tonlib_api::object_ptr<tonlib_api::dns_entry>> api_entries; std::vector<tonlib_api::object_ptr<tonlib_api::dns_entry>> api_entries;
@ -2726,9 +2933,29 @@ void TonlibClient::do_dns_request(std::string name, td::int32 category, td::int3
td::optional<ton::BlockIdExt> block_id, block::StdAddress address, td::optional<ton::BlockIdExt> block_id, block::StdAddress address,
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise) { td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise) {
auto block_id_copy = block_id.copy(); auto block_id_copy = block_id.copy();
td::Promise<DnsFinishData> new_promise =
promise.send_closure(actor_id(this), &TonlibClient::finish_dns_resolve, name, category, ttl, std::move(block_id));
if (0) {
make_request(int_api::GetAccountState{address, std::move(block_id_copy)}, make_request(int_api::GetAccountState{address, std::move(block_id_copy)},
promise.send_closure(actor_id(this), &TonlibClient::finish_dns_resolve, name, category, ttl, new_promise.wrap([](auto&& account_state) {
std::move(block_id))); return DnsFinishData{account_state->get_block_id(), account_state->get_smc_state()};
}));
return;
}
TRY_RESULT_PROMISE(promise, args, ton::DnsInterface::resolve_args(name, category));
int_api::RemoteRunSmcMethod query;
query.address = std::move(address);
query.args = std::move(args);
query.block_id = std::move(block_id_copy);
query.need_result = false;
make_request(std::move(query), new_promise.wrap([](auto&& run_method) {
return DnsFinishData{run_method.block_id, run_method.smc_state};
}));
;
} }
td::Status TonlibClient::do_request(const tonlib_api::dns_resolve& request, td::Status TonlibClient::do_request(const tonlib_api::dns_resolve& request,
@ -3015,6 +3242,14 @@ td::Status TonlibClient::do_request(int_api::GetAccountState request,
return td::Status::OK(); return td::Status::OK();
} }
td::Status TonlibClient::do_request(int_api::RemoteRunSmcMethod request,
td::Promise<int_api::RemoteRunSmcMethod::ReturnType>&& promise) {
auto actor_id = actor_id_++;
actors_[actor_id] = td::actor::create_actor<RemoteRunSmcMethod>(
"RemoteRunSmcMethod", client_.get_client(), std::move(request), actor_shared(this, actor_id), std::move(promise));
return td::Status::OK();
}
td::Status TonlibClient::do_request(int_api::GetPrivateKey request, td::Promise<KeyStorage::PrivateKey>&& promise) { td::Status TonlibClient::do_request(int_api::GetPrivateKey request, td::Promise<KeyStorage::PrivateKey>&& promise) {
TRY_RESULT(pk, key_storage_.load_private_key(std::move(request.input_key))); TRY_RESULT(pk, key_storage_.load_private_key(std::move(request.input_key)));
promise.set_value(std::move(pk)); promise.set_value(std::move(pk));

View file

@ -42,6 +42,9 @@ struct GetAccountState;
struct GetPrivateKey; struct GetPrivateKey;
struct GetDnsResolver; struct GetDnsResolver;
struct SendMessage; struct SendMessage;
struct RemoteRunSmcMethod;
struct RemoteRunSmcMethodReturnType;
inline std::string to_string(const int_api::SendMessage&) { inline std::string to_string(const int_api::SendMessage&) {
return "Send message"; return "Send message";
} }
@ -278,6 +281,8 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(tonlib_api::createQuery& request, td::Promise<object_ptr<tonlib_api::query_info>>&& promise); td::Status do_request(tonlib_api::createQuery& request, td::Promise<object_ptr<tonlib_api::query_info>>&& promise);
td::Status do_request(tonlib_api::msg_decrypt& request, td::Promise<object_ptr<tonlib_api::msg_dataArray>>&& promise);
td::int64 next_smc_id_{0}; td::int64 next_smc_id_{0};
std::map<td::int64, td::unique_ptr<AccountState>> smcs_; std::map<td::int64, td::unique_ptr<AccountState>> smcs_;
@ -299,13 +304,18 @@ class TonlibClient : public td::actor::Actor {
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise); td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
void do_dns_request(std::string name, td::int32 category, td::int32 ttl, td::optional<ton::BlockIdExt> block_id, void do_dns_request(std::string name, td::int32 category, td::int32 ttl, td::optional<ton::BlockIdExt> block_id,
block::StdAddress address, td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise); block::StdAddress address, td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
struct DnsFinishData {
ton::BlockIdExt block_id;
ton::SmartContract::State smc_state;
};
void finish_dns_resolve(std::string name, td::int32 category, td::int32 ttl, td::optional<ton::BlockIdExt> block_id, void finish_dns_resolve(std::string name, td::int32 category, td::int32 ttl, td::optional<ton::BlockIdExt> block_id,
td::unique_ptr<AccountState> smc, DnsFinishData dns_finish_data, td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
td::Status do_request(int_api::GetAccountState request, td::Promise<td::unique_ptr<AccountState>>&&); td::Status do_request(int_api::GetAccountState request, td::Promise<td::unique_ptr<AccountState>>&&);
td::Status do_request(int_api::GetPrivateKey request, td::Promise<KeyStorage::PrivateKey>&&); td::Status do_request(int_api::GetPrivateKey request, td::Promise<KeyStorage::PrivateKey>&&);
td::Status do_request(int_api::GetDnsResolver request, td::Promise<block::StdAddress>&&); td::Status do_request(int_api::GetDnsResolver request, td::Promise<block::StdAddress>&&);
td::Status do_request(int_api::RemoteRunSmcMethod request,
td::Promise<int_api::RemoteRunSmcMethodReturnType>&& promise);
td::Status do_request(int_api::SendMessage request, td::Promise<td::Unit>&& promise); td::Status do_request(int_api::SendMessage request, td::Promise<td::Unit>&& promise);
td::Status do_request(const tonlib_api::liteServer_getInfo& request, td::Status do_request(const tonlib_api::liteServer_getInfo& request,

View file

@ -345,10 +345,14 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out() td::TerminalIO::out()
<< "gethistory <key_id> - get history fo simple wallet with requested key (last 10 transactions)\n"; << "gethistory <key_id> - get history fo simple wallet with requested key (last 10 transactions)\n";
td::TerminalIO::out() << "init <key_id> - init simple wallet with requested key\n"; td::TerminalIO::out() << "init <key_id> - init simple wallet with requested key\n";
td::TerminalIO::out() << "transfer[f] <from_key_id> <to_key_id> <amount> - transfer <amount> of grams from " td::TerminalIO::out() << "transfer[f][F][e][k][c] <from_key_id> (<to_key_id> <value> <message>|<file_name>) - "
"<from_key_id> to <to_key_id>.\n" "make transfer from <from_key_id>\n"
<< "\t<from_key_id> could also be 'giver'\n" << "\t 'f' modifier - allow send to uninited account\n"
<< "\t<to_key_id> could also be 'giver' or smartcontract address\n"; << "\t 'F' modifier - read list of messages from <file_name> (in same format <to_key_id> "
"<value> <message>, one per line)\n"
<< "\t 'e' modifier - encrypt all messages\n"
<< "\t 'k' modifier - use fake key\n"
<< "\t 'c' modifier - just esmitate fees\n";
} else if (cmd == "genkey") { } else if (cmd == "genkey") {
generate_key(); generate_key();
} else if (cmd == "exit" || cmd == "quit") { } else if (cmd == "exit" || cmd == "quit") {
@ -445,15 +449,6 @@ class TonlibCli : public td::actor::Actor {
promise.set_value(td::Unit()); promise.set_value(td::Unit());
return; return;
} }
if (resolved->entries_[0]->name_ == name) {
td::TerminalIO::out() << "Done\n";
for (auto& entry : resolved->entries_) {
td::TerminalIO::out() << " " << entry->name_ << " " << entry->category_ << " "
<< tonlib::to_dns_entry_data(*entry->entry_).move_as_ok() << "\n";
}
promise.set_value(td::Unit());
return;
}
if (resolved->entries_[0]->entry_->get_id() == tonlib_api::dns_entryDataNextResolver::ID && ttl != 0) { if (resolved->entries_[0]->entry_->get_id() == tonlib_api::dns_entryDataNextResolver::ID && ttl != 0) {
td::TerminalIO::out() << "Redirect resolver\n"; td::TerminalIO::out() << "Redirect resolver\n";
auto entry = tonlib_api::move_object_as<tonlib_api::dns_entryDataNextResolver>(resolved->entries_[0]->entry_); auto entry = tonlib_api::move_object_as<tonlib_api::dns_entryDataNextResolver>(resolved->entries_[0]->entry_);
@ -461,7 +456,12 @@ class TonlibCli : public td::actor::Actor {
promise.send_closure(actor_id(this), &TonlibCli::do_dns_resolve, name, category, 0)); promise.send_closure(actor_id(this), &TonlibCli::do_dns_resolve, name, category, 0));
return; return;
} }
promise.set_error(td::Status::Error("Failed to resolve")); td::TerminalIO::out() << "Done\n";
for (auto& entry : resolved->entries_) {
td::TerminalIO::out() << " " << entry->name_ << " " << entry->category_ << " "
<< tonlib::to_dns_entry_data(*entry->entry_).move_as_ok() << "\n";
}
promise.set_value(td::Unit());
} }
void dns_resolve(td::ConstParser& parser, td::Promise<td::Unit> promise) { void dns_resolve(td::ConstParser& parser, td::Promise<td::Unit> promise) {
@ -531,7 +531,7 @@ class TonlibCli : public td::actor::Actor {
: nullptr; : nullptr;
send_query(tonlib_api::make_object<tonlib_api::createQuery>(std::move(key), std::move(address.address), 60, send_query(tonlib_api::make_object<tonlib_api::createQuery>(std::move(key), std::move(address.address), 60,
std::move(action)), std::move(action)),
promise.send_closure(actor_id(this), &TonlibCli::transfer2)); promise.send_closure(actor_id(this), &TonlibCli::transfer2, false));
} }
void remote_time(td::Promise<td::Unit> promise) { void remote_time(td::Promise<td::Unit> promise) {
@ -1389,13 +1389,21 @@ class TonlibCli : public td::actor::Actor {
} else { } else {
sb << " From " << t->in_msg_->source_; sb << " From " << t->in_msg_->source_;
} }
if (!t->in_msg_->message_.empty()) { auto print_msg_data = [](td::StringBuilder& sb,
tonlib_api::object_ptr<tonlib_api::msg_Data>& msg_data) {
if (!msg_data) {
return;
}
sb << " "; sb << " ";
if (t->in_msg_->is_message_encrypted_) { downcast_call(*msg_data,
sb << "e"; td::overloaded([&](tonlib_api::msg_dataRaw& raw) { sb << "<unknown message>"; },
} [&](tonlib_api::msg_dataText& raw) { sb << "{" << raw.text_ << "}"; },
sb << "msg{" << t->in_msg_->message_ << "}"; [&](tonlib_api::msg_dataEncryptedText& raw) { sb << "<encrypted>"; },
} [&](tonlib_api::msg_dataDecryptedText& raw) {
sb << "decrypted{" << raw.text_ << "}";
}));
};
print_msg_data(sb, t->in_msg_->msg_data_);
for (auto& ot : t->out_msgs_) { for (auto& ot : t->out_msgs_) {
sb << "\n\t"; sb << "\n\t";
if (ot->destination_.empty()) { if (ot->destination_.empty()) {
@ -1404,13 +1412,7 @@ class TonlibCli : public td::actor::Actor {
sb << " To " << ot->destination_; sb << " To " << ot->destination_;
} }
sb << " " << Grams{td::uint64(ot->value_)}; sb << " " << Grams{td::uint64(ot->value_)};
if (!ot->message_.empty()) { print_msg_data(sb, ot->msg_data_);
sb << " ";
if (ot->is_message_encrypted_) {
sb << "e";
}
sb << "msg{" << ot->message_ << "}";
}
} }
sb << "\n"; sb << "\n";
} }
@ -1423,6 +1425,8 @@ class TonlibCli : public td::actor::Actor {
bool from_file = false; bool from_file = false;
bool force = false; bool force = false;
bool use_encryption = false; bool use_encryption = false;
bool use_fake_key = false;
bool estimate_fees = false;
if (cmd != "init") { if (cmd != "init") {
td::ConstParser cmd_parser(cmd); td::ConstParser cmd_parser(cmd);
cmd_parser.advance(td::Slice("transfer").size()); cmd_parser.advance(td::Slice("transfer").size());
@ -1435,6 +1439,10 @@ class TonlibCli : public td::actor::Actor {
force = true; force = true;
} else if (c == 'e') { } else if (c == 'e') {
use_encryption = true; use_encryption = true;
} else if (c == 'k') {
use_fake_key = true;
} else if (c == 'c') {
estimate_fees = true;
} else { } else {
cmd_promise.set_error(td::Status::Error(PSLICE() << "Unknown suffix '" << c << "'")); cmd_promise.set_error(td::Status::Error(PSLICE() << "Unknown suffix '" << c << "'"));
return; return;
@ -1464,7 +1472,7 @@ class TonlibCli : public td::actor::Actor {
tonlib_api::object_ptr<tonlib_api::msg_Data> data; tonlib_api::object_ptr<tonlib_api::msg_Data> data;
if (use_encryption) { if (use_encryption) {
data = tonlib_api::make_object<tonlib_api::msg_dataEncryptedText>(message.str()); data = tonlib_api::make_object<tonlib_api::msg_dataDecryptedText>(message.str());
} else { } else {
data = tonlib_api::make_object<tonlib_api::msg_dataText>(message.str()); data = tonlib_api::make_object<tonlib_api::msg_dataText>(message.str());
} }
@ -1495,25 +1503,38 @@ class TonlibCli : public td::actor::Actor {
td::Slice password; // empty by default td::Slice password; // empty by default
using tonlib_api::make_object; using tonlib_api::make_object;
auto key = !from_address.secret.empty() tonlib_api::object_ptr<tonlib_api::InputKey> key =
!from_address.secret.empty()
? make_object<tonlib_api::inputKeyRegular>( ? make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(from_address.public_key, from_address.secret.copy()), make_object<tonlib_api::key>(from_address.public_key, from_address.secret.copy()),
td::SecureString(password)) td::SecureString(password))
: nullptr; : nullptr;
if (use_fake_key) {
key = make_object<tonlib_api::inputKeyFake>();
}
bool allow_send_to_uninited = force; bool allow_send_to_uninited = force;
send_query(make_object<tonlib_api::createQuery>( send_query(make_object<tonlib_api::createQuery>(
std::move(key), std::move(from_address.address), 60, std::move(key), std::move(from_address.address), 60,
make_object<tonlib_api::actionMsg>(std::move(messages), allow_send_to_uninited)), make_object<tonlib_api::actionMsg>(std::move(messages), allow_send_to_uninited)),
cmd_promise.send_closure(actor_id(this), &TonlibCli::transfer2)); cmd_promise.send_closure(actor_id(this), &TonlibCli::transfer2, estimate_fees));
} }
void transfer2(td::Result<tonlib_api::object_ptr<tonlib_api::query_info>> r_info, td::Promise<td::Unit> cmd_promise) { void transfer2(bool estimate_fees, td::Result<tonlib_api::object_ptr<tonlib_api::query_info>> r_info,
send_query(tonlib_api::make_object<tonlib_api::query_send>(r_info.ok()->id_), cmd_promise.wrap([](auto&& info) { td::Promise<td::Unit> cmd_promise) {
td::TerminalIO::out() << "Transfer sent!\n"; if (estimate_fees) {
send_query(tonlib_api::make_object<tonlib_api::query_estimateFees>(r_info.ok()->id_, true),
cmd_promise.wrap([](auto&& info) {
td::TerminalIO::out() << "Extimated fees: " << to_string(info);
return td::Unit(); return td::Unit();
})); }));
} else {
send_query(tonlib_api::make_object<tonlib_api::query_send>(r_info.ok()->id_), cmd_promise.wrap([](auto&& info) {
td::TerminalIO::out() << "Transfer sent: " << to_string(info);
return td::Unit();
}));
}
} }
void get_hints(td::Slice prefix) { void get_hints(td::Slice prefix) {

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "apply-block.hpp" #include "apply-block.hpp"
#include "adnl/utils.hpp" #include "adnl/utils.hpp"
@ -79,8 +79,8 @@ void ApplyBlock::got_block_handle(BlockHandle handle) {
} }
if (handle_->is_applied()) { if (handle_->is_applied()) {
auto P = auto P = td::PromiseCreator::lambda(
td::PromiseCreator::lambda([SelfId = actor_id(this), seqno = handle_->id().id.seqno](td::Result<BlockIdExt> R) { [ SelfId = actor_id(this), seqno = handle_->id().id.seqno ](td::Result<BlockIdExt> R) {
R.ensure(); R.ensure();
auto h = R.move_as_ok(); auto h = R.move_as_ok();
if (h.id.seqno < seqno) { if (h.id.seqno < seqno) {
@ -119,7 +119,8 @@ void ApplyBlock::got_block_handle(BlockHandle handle) {
td::actor::send_closure(manager_, &ValidatorManager::set_block_data, handle_, block_, std::move(P)); td::actor::send_closure(manager_, &ValidatorManager::set_block_data, handle_, block_, std::move(P));
} else { } else {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle = handle_](td::Result<td::Ref<BlockData>> R) { auto P =
td::PromiseCreator::lambda([ SelfId = actor_id(this), handle = handle_ ](td::Result<td::Ref<BlockData>> R) {
CHECK(handle->received()); CHECK(handle->received());
if (R.is_error()) { if (R.is_error()) {
td::actor::send_closure(SelfId, &ApplyBlock::abort_query, R.move_as_error()); td::actor::send_closure(SelfId, &ApplyBlock::abort_query, R.move_as_error());

View file

@ -1,3 +1,21 @@
/*
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 "archive-mover.hpp" #include "archive-mover.hpp"
#include "td/actor/MultiPromise.h" #include "td/actor/MultiPromise.h"
#include "validator/fabric.h" #include "validator/fabric.h"

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "td/actor/actor.h" #include "td/actor/actor.h"

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "validator/interfaces/db.h" #include "validator/interfaces/db.h"

View file

@ -1,3 +1,21 @@
/*
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 "fileref.hpp" #include "fileref.hpp"
#include "auto/tl/ton_api.hpp" #include "auto/tl/ton_api.hpp"
#include "td/utils/overloaded.h" #include "td/utils/overloaded.h"

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "td/actor/actor.h" #include "td/actor/actor.h"

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "validator/validator.h" #include "validator/validator.h"

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "collator-impl.h" #include "collator-impl.h"
#include "vm/boc.h" #include "vm/boc.h"
@ -589,9 +589,9 @@ void Collator::got_neighbor_out_queue(int i, td::Result<Ref<MessageQueue>> res)
return; return;
} }
descr.set_queue_root(qinfo.out_queue->prefetch_ref(0)); descr.set_queue_root(qinfo.out_queue->prefetch_ref(0));
// TODO: comment the next two lines in the future when the output queues become huge // comment the next two lines in the future when the output queues become huge
CHECK(block::gen::t_OutMsgQueueInfo.validate_ref(outq_descr->root_cell())); // CHECK(block::gen::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell()));
CHECK(block::tlb::t_OutMsgQueueInfo.validate_ref(outq_descr->root_cell())); // CHECK(block::tlb::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell()));
// unpack ProcessedUpto // unpack ProcessedUpto
LOG(DEBUG) << "unpacking ProcessedUpto of neighbor " << descr.blk_.to_str(); LOG(DEBUG) << "unpacking ProcessedUpto of neighbor " << descr.blk_.to_str();
if (verbosity >= 2) { if (verbosity >= 2) {
@ -1506,7 +1506,7 @@ bool Collator::fetch_config_params() {
// fetch block_grams_created // fetch block_grams_created
auto cell = config_->get_config_param(14); auto cell = config_->get_config_param(14);
if (cell.is_null()) { if (cell.is_null()) {
basechain_create_fee_ = masterchain_create_fee_ = td::RefInt256{true, 0}; basechain_create_fee_ = masterchain_create_fee_ = td::zero_refint();
} else { } else {
block::gen::BlockCreateFees::Record create_fees; block::gen::BlockCreateFees::Record create_fees;
if (!(tlb::unpack_cell(cell, create_fees) && if (!(tlb::unpack_cell(cell, create_fees) &&
@ -1781,7 +1781,7 @@ bool Collator::out_msg_queue_cleanup() {
block::gen::t_OutMsgQueue.print(std::cerr, *rt); block::gen::t_OutMsgQueue.print(std::cerr, *rt);
rt->print_rec(std::cerr); rt->print_rec(std::cerr);
} }
CHECK(block::gen::t_OutMsgQueue.validate(*rt)); // DEBUG, comment later if SLOW // CHECK(block::gen::t_OutMsgQueue.validate_upto(100000, *rt)); // DEBUG, comment later if SLOW
return register_out_msg_queue_op(true); return register_out_msg_queue_op(true);
} }
@ -1853,13 +1853,13 @@ bool Collator::combine_account_transactions() {
block::gen::t_AccountBlock.print_ref(std::cerr, cell); block::gen::t_AccountBlock.print_ref(std::cerr, cell);
csr->print_rec(std::cerr); csr->print_rec(std::cerr);
} }
if (!block::gen::t_AccountBlock.validate_ref(cell)) { if (!block::gen::t_AccountBlock.validate_ref(100000, cell)) {
block::gen::t_AccountBlock.print_ref(std::cerr, cell); block::gen::t_AccountBlock.print_ref(std::cerr, cell);
csr->print_rec(std::cerr); csr->print_rec(std::cerr);
return fatal_error(std::string{"new AccountBlock for "} + z.first.to_hex() + return fatal_error(std::string{"new AccountBlock for "} + z.first.to_hex() +
" failed to pass automatic validation tests"); " failed to pass automatic validation tests");
} }
if (!block::tlb::t_AccountBlock.validate_ref(cell)) { if (!block::tlb::t_AccountBlock.validate_ref(100000, cell)) {
block::gen::t_AccountBlock.print_ref(std::cerr, cell); block::gen::t_AccountBlock.print_ref(std::cerr, cell);
csr->print_rec(std::cerr); csr->print_rec(std::cerr);
return fatal_error(std::string{"new AccountBlock for "} + z.first.to_hex() + return fatal_error(std::string{"new AccountBlock for "} + z.first.to_hex() +
@ -1923,10 +1923,10 @@ bool Collator::combine_account_transactions() {
block::gen::t_ShardAccountBlocks.print_ref(std::cerr, shard_account_blocks_); block::gen::t_ShardAccountBlocks.print_ref(std::cerr, shard_account_blocks_);
vm::load_cell_slice(shard_account_blocks_).print_rec(std::cerr); vm::load_cell_slice(shard_account_blocks_).print_rec(std::cerr);
} }
if (!block::gen::t_ShardAccountBlocks.validate_ref(shard_account_blocks_)) { if (!block::gen::t_ShardAccountBlocks.validate_ref(100000, shard_account_blocks_)) {
return fatal_error("new ShardAccountBlocks failed to pass automatic validity tests"); return fatal_error("new ShardAccountBlocks failed to pass automatic validity tests");
} }
if (!block::tlb::t_ShardAccountBlocks.validate_ref(shard_account_blocks_)) { if (!block::tlb::t_ShardAccountBlocks.validate_ref(100000, shard_account_blocks_)) {
return fatal_error("new ShardAccountBlocks failed to pass handwritten validity tests"); return fatal_error("new ShardAccountBlocks failed to pass handwritten validity tests");
} }
auto shard_accounts = account_dict->get_root(); auto shard_accounts = account_dict->get_root();
@ -1937,10 +1937,10 @@ bool Collator::combine_account_transactions() {
} }
if (verify >= 2) { if (verify >= 2) {
LOG(INFO) << "verifying new ShardAccounts"; LOG(INFO) << "verifying new ShardAccounts";
if (!block::gen::t_ShardAccounts.validate(*shard_accounts)) { if (!block::gen::t_ShardAccounts.validate_upto(100000, *shard_accounts)) {
return fatal_error("new ShardAccounts failed to pass automatic validity tests"); return fatal_error("new ShardAccounts failed to pass automatic validity tests");
} }
if (!block::tlb::t_ShardAccounts.validate(*shard_accounts)) { if (!block::tlb::t_ShardAccounts.validate_upto(100000, *shard_accounts)) {
return fatal_error("new ShardAccounts failed to pass handwritten validity tests"); return fatal_error("new ShardAccounts failed to pass handwritten validity tests");
} }
} }
@ -3012,7 +3012,7 @@ bool Collator::create_mc_state_extra() {
std::cerr << "updated shard configuration to "; std::cerr << "updated shard configuration to ";
block::gen::t_ShardHashes.print(std::cerr, *state_extra.shard_hashes); block::gen::t_ShardHashes.print(std::cerr, *state_extra.shard_hashes);
} }
if (!block::gen::t_ShardHashes.validate(*state_extra.shard_hashes)) { if (!block::gen::t_ShardHashes.validate_upto(10000, *state_extra.shard_hashes)) {
return fatal_error("new ShardHashes is invalid"); return fatal_error("new ShardHashes is invalid");
} }
// 4. check extension flags // 4. check extension flags
@ -3110,7 +3110,7 @@ bool Collator::create_mc_state_extra() {
state_extra.r1.block_create_stats = cs; state_extra.r1.block_create_stats = cs;
if (verify >= 2) { if (verify >= 2) {
LOG(INFO) << "verifying new BlockCreateStats"; LOG(INFO) << "verifying new BlockCreateStats";
if (!block::gen::t_BlockCreateStats.validate_csr(cs)) { if (!block::gen::t_BlockCreateStats.validate_csr(100000, cs)) {
cs->print_rec(std::cerr); cs->print_rec(std::cerr);
block::gen::t_BlockCreateStats.print(std::cerr, *cs); block::gen::t_BlockCreateStats.print(std::cerr, *cs);
return fatal_error("BlockCreateStats in the new masterchain state failed to pass automated validity checks"); return fatal_error("BlockCreateStats in the new masterchain state failed to pass automated validity checks");
@ -3128,8 +3128,8 @@ bool Collator::create_mc_state_extra() {
} }
if (verify >= 2) { if (verify >= 2) {
LOG(INFO) << "verifying new McStateExtra"; LOG(INFO) << "verifying new McStateExtra";
CHECK(block::gen::t_McStateExtra.validate_ref(mc_state_extra_)); CHECK(block::gen::t_McStateExtra.validate_ref(1000000, mc_state_extra_));
CHECK(block::tlb::t_McStateExtra.validate_ref(mc_state_extra_)); CHECK(block::tlb::t_McStateExtra.validate_ref(1000000, mc_state_extra_));
} }
LOG(INFO) << "McStateExtra created"; LOG(INFO) << "McStateExtra created";
return true; return true;
@ -3467,8 +3467,8 @@ bool Collator::create_shard_state() {
} }
if (verify >= 2) { if (verify >= 2) {
LOG(INFO) << "verifying new ShardState"; LOG(INFO) << "verifying new ShardState";
CHECK(block::gen::t_ShardState.validate_ref(state_root)); CHECK(block::gen::t_ShardState.validate_ref(1000000, state_root));
CHECK(block::tlb::t_ShardState.validate_ref(state_root)); CHECK(block::tlb::t_ShardState.validate_ref(1000000, state_root));
} }
LOG(INFO) << "creating Merkle update for the ShardState"; LOG(INFO) << "creating Merkle update for the ShardState";
state_update = vm::MerkleUpdate::generate(prev_state_root_, state_root, state_usage_tree_.get()); state_update = vm::MerkleUpdate::generate(prev_state_root_, state_root, state_usage_tree_.get());
@ -3688,7 +3688,7 @@ bool Collator::create_block() {
} }
if (verify >= 1) { if (verify >= 1) {
LOG(INFO) << "verifying new Block"; LOG(INFO) << "verifying new Block";
if (!block::gen::t_Block.validate_ref(new_block)) { if (!block::gen::t_Block.validate_ref(1000000, new_block)) {
return fatal_error("new Block failed to pass automatic validity tests"); return fatal_error("new Block failed to pass automatic validity tests");
} }
} }
@ -3836,10 +3836,10 @@ td::Result<bool> Collator::register_external_message_cell(Ref<vm::Cell> ext_msg,
return td::Status::Error("external message has been rejected before"); return td::Status::Error("external message has been rejected before");
} }
} }
if (!block::gen::t_Message_Any.validate_ref(ext_msg)) { if (!block::gen::t_Message_Any.validate_ref(256, ext_msg)) {
return td::Status::Error("external message is not a (Message Any) according to automated checks"); return td::Status::Error("external message is not a (Message Any) according to automated checks");
} }
if (!block::tlb::t_Message.validate_ref(ext_msg)) { if (!block::tlb::t_Message.validate_ref(256, ext_msg)) {
return td::Status::Error("external message is not a (Message Any) according to hand-written checks"); return td::Status::Error("external message is not a (Message Any) according to hand-written checks");
} }
block::gen::CommonMsgInfo::Record_ext_in_msg_info info; block::gen::CommonMsgInfo::Record_ext_in_msg_info info;

View file

@ -56,10 +56,10 @@ td::Result<Ref<ExtMessageQ>> ExtMessageQ::create_ext_message(td::BufferSlice dat
return td::Status::Error("external message must begin with ext_in_msg_info$10"); return td::Status::Error("external message must begin with ext_in_msg_info$10");
} }
ton::Bits256 hash{ext_msg->get_hash().bits()}; ton::Bits256 hash{ext_msg->get_hash().bits()};
if (!block::gen::t_Message_Any.validate_ref(ext_msg)) { if (!block::gen::t_Message_Any.validate_ref(128, ext_msg)) {
return td::Status::Error("external message is not a (Message Any) according to automated checks"); return td::Status::Error("external message is not a (Message Any) according to automated checks");
} }
if (!block::tlb::t_Message.validate_ref(ext_msg)) { if (!block::tlb::t_Message.validate_ref(128, ext_msg)) {
return td::Status::Error("external message is not a (Message Any) according to hand-written checks"); return td::Status::Error("external message is not a (Message Any) according to hand-written checks");
} }
block::gen::CommonMsgInfo::Record_ext_in_msg_info info; block::gen::CommonMsgInfo::Record_ext_in_msg_info info;

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "validate-query.hpp" #include "validate-query.hpp"
#include "top-shard-descr.hpp" #include "top-shard-descr.hpp"
@ -499,7 +499,7 @@ bool ValidateQuery::extract_collated_data_from(Ref<vm::Cell> croot, int idx) {
} }
if (block::gen::t_TopBlockDescrSet.has_valid_tag(cs)) { if (block::gen::t_TopBlockDescrSet.has_valid_tag(cs)) {
LOG(DEBUG) << "collated datum # " << idx << " is a TopBlockDescrSet"; LOG(DEBUG) << "collated datum # " << idx << " is a TopBlockDescrSet";
if (!block::gen::t_TopBlockDescrSet.validate(cs)) { if (!block::gen::t_TopBlockDescrSet.validate_upto(10000, cs)) {
return reject_query("invalid TopBlockDescrSet"); return reject_query("invalid TopBlockDescrSet");
} }
if (top_shard_descr_dict_) { if (top_shard_descr_dict_) {
@ -776,7 +776,7 @@ bool ValidateQuery::fetch_config_params() {
// fetch block_grams_created // fetch block_grams_created
auto cell = config_->get_config_param(14); auto cell = config_->get_config_param(14);
if (cell.is_null()) { if (cell.is_null()) {
basechain_create_fee_ = masterchain_create_fee_ = td::RefInt256{true, 0}; basechain_create_fee_ = masterchain_create_fee_ = td::zero_refint();
} else { } else {
block::gen::BlockCreateFees::Record create_fees; block::gen::BlockCreateFees::Record create_fees;
if (!(tlb::unpack_cell(cell, create_fees) && if (!(tlb::unpack_cell(cell, create_fees) &&
@ -1229,8 +1229,8 @@ void ValidateQuery::got_neighbor_out_queue(int i, td::Result<Ref<MessageQueue>>
descr.set_queue_root(qinfo.out_queue->prefetch_ref(0)); descr.set_queue_root(qinfo.out_queue->prefetch_ref(0));
// TODO: comment the next two lines in the future when the output queues become huge // TODO: comment the next two lines in the future when the output queues become huge
// (do this carefully) // (do this carefully)
CHECK(block::gen::t_OutMsgQueueInfo.validate_ref(outq_descr->root_cell())); CHECK(block::gen::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell()));
CHECK(block::tlb::t_OutMsgQueueInfo.validate_ref(outq_descr->root_cell())); CHECK(block::tlb::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell()));
// unpack ProcessedUpto // unpack ProcessedUpto
LOG(DEBUG) << "unpacking ProcessedUpto of neighbor " << descr.blk_.to_str(); LOG(DEBUG) << "unpacking ProcessedUpto of neighbor " << descr.blk_.to_str();
if (verbosity >= 2) { if (verbosity >= 2) {
@ -2049,13 +2049,13 @@ bool ValidateQuery::unpack_block_data() {
auto outmsg_cs = vm::load_cell_slice_ref(std::move(extra.out_msg_descr)); auto outmsg_cs = vm::load_cell_slice_ref(std::move(extra.out_msg_descr));
// run some hand-written checks from block::tlb:: // run some hand-written checks from block::tlb::
// (automatic tests from block::gen:: have been already run for the entire block) // (automatic tests from block::gen:: have been already run for the entire block)
if (!block::tlb::t_InMsgDescr.validate(*inmsg_cs)) { if (!block::tlb::t_InMsgDescr.validate_upto(1000000, *inmsg_cs)) {
return reject_query("InMsgDescr of the new block failed to pass handwritten validity tests"); return reject_query("InMsgDescr of the new block failed to pass handwritten validity tests");
} }
if (!block::tlb::t_OutMsgDescr.validate(*outmsg_cs)) { if (!block::tlb::t_OutMsgDescr.validate_upto(1000000, *outmsg_cs)) {
return reject_query("OutMsgDescr of the new block failed to pass handwritten validity tests"); return reject_query("OutMsgDescr of the new block failed to pass handwritten validity tests");
} }
if (!block::tlb::t_ShardAccountBlocks.validate_ref(extra.account_blocks)) { if (!block::tlb::t_ShardAccountBlocks.validate_ref(1000000, extra.account_blocks)) {
return reject_query("ShardAccountBlocks of the new block failed to pass handwritten validity tests"); return reject_query("ShardAccountBlocks of the new block failed to pass handwritten validity tests");
} }
in_msg_dict_ = std::make_unique<vm::AugmentedDictionary>(std::move(inmsg_cs), 256, block::tlb::aug_InMsgDescr); in_msg_dict_ = std::make_unique<vm::AugmentedDictionary>(std::move(inmsg_cs), 256, block::tlb::aug_InMsgDescr);
@ -2274,11 +2274,11 @@ bool ValidateQuery::precheck_one_account_update(td::ConstBitPtr acc_id, Ref<vm::
"AccountBlock for this account"); "AccountBlock for this account");
} }
if (new_value.not_null()) { if (new_value.not_null()) {
if (!block::gen::t_ShardAccount.validate_csr(new_value)) { if (!block::gen::t_ShardAccount.validate_csr(10000, new_value)) {
return reject_query("new state of account "s + acc_id.to_hex(256) + return reject_query("new state of account "s + acc_id.to_hex(256) +
" failed to pass automated validity checks for ShardAccount"); " failed to pass automated validity checks for ShardAccount");
} }
if (!block::tlb::t_ShardAccount.validate_csr(new_value)) { if (!block::tlb::t_ShardAccount.validate_csr(10000, new_value)) {
return reject_query("new state of account "s + acc_id.to_hex(256) + return reject_query("new state of account "s + acc_id.to_hex(256) +
" failed to pass hand-written validity checks for ShardAccount"); " failed to pass hand-written validity checks for ShardAccount");
} }
@ -2428,10 +2428,10 @@ bool ValidateQuery::precheck_one_account_block(td::ConstBitPtr acc_id, Ref<vm::C
return reject_query("(HASH_UPDATE Account) from the AccountBlock of "s + acc_id.to_hex(256) + return reject_query("(HASH_UPDATE Account) from the AccountBlock of "s + acc_id.to_hex(256) +
" has incorrect new hash"); " has incorrect new hash");
} }
if (!block::gen::t_AccountBlock.validate(*acc_blk_root)) { if (!block::gen::t_AccountBlock.validate_upto(1000000, *acc_blk_root)) {
return reject_query("AccountBlock of "s + acc_id.to_hex(256) + " failed to pass automated validity checks"); return reject_query("AccountBlock of "s + acc_id.to_hex(256) + " failed to pass automated validity checks");
} }
if (!block::tlb::t_AccountBlock.validate(*acc_blk_root)) { if (!block::tlb::t_AccountBlock.validate_upto(1000000, *acc_blk_root)) {
return reject_query("AccountBlock of "s + acc_id.to_hex(256) + " failed to pass hand-written validity checks"); return reject_query("AccountBlock of "s + acc_id.to_hex(256) + " failed to pass hand-written validity checks");
} }
unsigned last_trans_lt_len = 1; unsigned last_trans_lt_len = 1;
@ -4794,10 +4794,10 @@ bool ValidateQuery::check_new_state() {
} }
bool ValidateQuery::check_config_update(Ref<vm::CellSlice> old_conf_params, Ref<vm::CellSlice> new_conf_params) { bool ValidateQuery::check_config_update(Ref<vm::CellSlice> old_conf_params, Ref<vm::CellSlice> new_conf_params) {
if (!block::gen::t_ConfigParams.validate_csr(new_conf_params)) { if (!block::gen::t_ConfigParams.validate_csr(10000, new_conf_params)) {
return reject_query("new configuration failed to pass automated validity checks"); return reject_query("new configuration failed to pass automated validity checks");
} }
if (!block::gen::t_ConfigParams.validate_csr(old_conf_params)) { if (!block::gen::t_ConfigParams.validate_csr(10000, old_conf_params)) {
return fatal_error("old configuration failed to pass automated validity checks"); return fatal_error("old configuration failed to pass automated validity checks");
} }
td::Bits256 old_cfg_addr, new_cfg_addr; td::Bits256 old_cfg_addr, new_cfg_addr;
@ -4908,7 +4908,8 @@ bool ValidateQuery::check_one_prev_dict_update(ton::BlockSeqno seqno, Ref<vm::Ce
} }
CHECK(new_val_extra.not_null()); CHECK(new_val_extra.not_null());
vm::CellSlice cs{*new_val_extra}; vm::CellSlice cs{*new_val_extra};
if (!(block::gen::t_KeyMaxLt.validate_skip(cs) && block::gen::t_KeyExtBlkRef.validate_skip(cs) && cs.empty_ext())) { if (!(block::gen::t_KeyMaxLt.validate_skip_upto(16, cs) && block::gen::t_KeyExtBlkRef.validate_skip_upto(16, cs) &&
cs.empty_ext())) {
return reject_query(PSTRING() << "entry with seqno " << seqno return reject_query(PSTRING() << "entry with seqno " << seqno
<< " in the new previous blocks dictionary failed to pass automated validity checks " << " in the new previous blocks dictionary failed to pass automated validity checks "
"form KeyMaxLt + KeyExtBlkRef"); "form KeyMaxLt + KeyExtBlkRef");
@ -5039,7 +5040,7 @@ bool ValidateQuery::check_mc_state_extra() {
<< " while the block header claims is_key_block=" << is_key_block_); << " while the block header claims is_key_block=" << is_key_block_);
} }
// last_key_block:(Maybe ExtBlkRef) // last_key_block:(Maybe ExtBlkRef)
if (!block::gen::t_Maybe_ExtBlkRef.validate_csr(new_extra.r1.last_key_block)) { if (!block::gen::t_Maybe_ExtBlkRef.validate_csr(16, new_extra.r1.last_key_block)) {
return reject_query( return reject_query(
"last_key_block:(Maybe ExtBlkRef) in the new masterchain state failed to pass automated validity checks"); "last_key_block:(Maybe ExtBlkRef) in the new masterchain state failed to pass automated validity checks");
} }
@ -5360,7 +5361,7 @@ bool ValidateQuery::try_validate() {
} }
} }
LOG(INFO) << "running automated validity checks for block candidate " << id_.to_str(); LOG(INFO) << "running automated validity checks for block candidate " << id_.to_str();
if (!block::gen::t_Block.validate_ref(block_root_)) { if (!block::gen::t_Block.validate_ref(1000000, block_root_)) {
return reject_query("block "s + id_.to_str() + " failed to pass automated validity checks"); return reject_query("block "s + id_.to_str() + " failed to pass automated validity checks");
} }
if (!fix_all_processed_upto()) { if (!fix_all_processed_upto()) {

View file

@ -1,3 +1,21 @@
/*
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 2019-2020 Telegram Systems LLP
*/
#include "import-db-slice.hpp" #include "import-db-slice.hpp"
#include "validator/db/fileref.hpp" #include "validator/db/fileref.hpp"
#include "td/utils/overloaded.h" #include "td/utils/overloaded.h"

View file

@ -1,3 +1,21 @@
/*
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 2019-2020 Telegram Systems LLP
*/
#pragma once #pragma once
#include "td/actor/actor.h" #include "td/actor/actor.h"

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "td/actor/actor.h" #include "td/actor/actor.h"

View file

@ -1,3 +1,21 @@
/*
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 "download-archive-slice.hpp" #include "download-archive-slice.hpp"
#include "td/utils/port/path.h" #include "td/utils/port/path.h"
#include "td/utils/overloaded.h" #include "td/utils/overloaded.h"

View file

@ -1,3 +1,21 @@
/*
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
*/
#pragma once #pragma once
#include "td/utils/int_types.h" #include "td/utils/int_types.h"