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-dht test-dht)
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-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
#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
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 "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;
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)) {
return false;
}
@ -265,14 +265,14 @@ Ref<vm::CellSlice> MsgAddressInt::pack_std_address(ton::WorkchainId workchain, c
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)) {
case addr_none:
case addr_ext:
return t_MsgAddressExt.validate_skip(cs, weak);
return t_MsgAddressExt.validate_skip(ops, cs, weak);
case addr_std:
case addr_var:
return t_MsgAddressInt.validate_skip(cs, weak);
return t_MsgAddressInt.validate_skip(ops, cs, weak);
}
return false;
}
@ -284,7 +284,7 @@ bool VarUInteger::skip(vm::CellSlice& cs) const {
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
bool Grams::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_16.validate_skip(cs, weak);
bool Grams::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_16.validate_skip(ops, cs, weak);
}
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);
}
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);
if (!n) {
// hmn_leaf
return value_type.validate_skip(cs, weak);
return value_type.validate_skip(ops, cs, weak);
} else {
// hmn_fork
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);
}
bool Hashmap::validate_skip(vm::CellSlice& cs, bool weak) const {
bool Hashmap::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
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 {
@ -491,9 +491,9 @@ int HashmapE::get_size(const vm::CellSlice& cs) const {
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);
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 {
@ -583,8 +583,8 @@ bool HashmapE::store_ref(vm::CellBuilder& cb, Ref<vm::Cell> arg) const {
const ExtraCurrencyCollection t_ExtraCurrencyCollection;
bool CurrencyCollection::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_Grams.validate_skip(cs, weak) && t_ExtraCurrencyCollection.validate_skip(cs, weak);
bool CurrencyCollection::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_Grams.validate_skip(ops, cs, weak) && t_ExtraCurrencyCollection.validate_skip(ops, cs, weak);
}
bool CurrencyCollection::skip(vm::CellSlice& cs) const {
@ -641,25 +641,25 @@ bool CurrencyCollection::pack(vm::CellBuilder& cb, const block::CurrencyCollecti
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);
switch (tag) {
case int_msg_info:
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(cs, weak) // dest
&& t_CurrencyCollection.validate_skip(cs, weak) // value
&& t_Grams.validate_skip(cs, weak) // ihr_fee
&& t_Grams.validate_skip(cs, weak) // fwd_fee
&& cs.advance(64 + 32); // created_lt:uint64 created_at:uint32
return cs.advance(4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
&& t_MsgAddressInt.validate_skip(ops, cs, weak) // src
&& t_MsgAddressInt.validate_skip(ops, cs, weak) // dest
&& t_CurrencyCollection.validate_skip(ops, cs, weak) // value
&& t_Grams.validate_skip(ops, cs, weak) // ihr_fee
&& t_Grams.validate_skip(ops, cs, weak) // fwd_fee
&& cs.advance(64 + 32); // created_lt:uint64 created_at:uint32
case ext_in_msg_info:
return cs.advance(2) && t_MsgAddressExt.validate_skip(cs, weak) // src
&& t_MsgAddressInt.validate_skip(cs, weak) // dest
&& t_Grams.validate_skip(cs, weak); // import_fee
return cs.advance(2) && t_MsgAddressExt.validate_skip(ops, cs, weak) // src
&& t_MsgAddressInt.validate_skip(ops, cs, weak) // dest
&& t_Grams.validate_skip(ops, cs, weak); // import_fee
case ext_out_msg_info:
return cs.advance(2) && t_MsgAddressInt.validate_skip(cs, weak) // src
&& t_MsgAddressExt.validate_skip(cs, weak) // dest
&& cs.advance(64 + 32); // created_lt:uint64 created_at:uint32
return cs.advance(2) && t_MsgAddressInt.validate_skip(ops, cs, weak) // src
&& t_MsgAddressExt.validate_skip(ops, cs, weak) // dest
&& cs.advance(64 + 32); // created_lt:uint64 created_at:uint32
}
return false;
}
@ -721,28 +721,29 @@ const CommonMsgInfo t_CommonMsgInfo;
const TickTock t_TickTock;
const RefAnything t_RefCell;
bool StateInit::validate_skip(vm::CellSlice& cs, bool weak) const {
return Maybe<UInt>{5}.validate_skip(cs, weak) // split_depth:(Maybe (## 5))
&& Maybe<TickTock>{}.validate_skip(cs, weak) // special:(Maybe TickTock)
&& Maybe<RefAnything>{}.validate_skip(cs, weak) // code:(Maybe ^Cell)
&& Maybe<RefAnything>{}.validate_skip(cs, weak) // data:(Maybe ^Cell)
&& Maybe<RefAnything>{}.validate_skip(cs, weak); // library:(Maybe ^Cell)
bool StateInit::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return Maybe<UInt>{5}.validate_skip(ops, cs, weak) // split_depth:(Maybe (## 5))
&& Maybe<TickTock>{}.validate_skip(ops, cs, weak) // special:(Maybe TickTock)
&& Maybe<RefAnything>{}.validate_skip(ops, cs, weak) // code:(Maybe ^Cell)
&& Maybe<RefAnything>{}.validate_skip(ops, cs, weak) // data:(Maybe ^Cell)
&& Maybe<RefAnything>{}.validate_skip(ops, cs, weak); // library:(Maybe ^Cell)
}
bool StateInit::get_ticktock(vm::CellSlice& cs, int& ticktock) const {
bool have_tt;
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;
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 Either<Anything, RefAnything> body_type;
return t_CommonMsgInfo.validate_skip(cs, weak) // info:CommonMsgInfo
&& init_type.validate_skip(cs, weak) // init:(Maybe (Either StateInit ^StateInit))
&& body_type.validate_skip(cs, weak); // body:(Either X ^X)
return t_CommonMsgInfo.validate_skip(ops, cs, weak) // info:CommonMsgInfo
&& init_type.validate_skip(ops, cs, weak) // init:(Maybe (Either StateInit ^StateInit))
&& body_type.validate_skip(ops, cs, weak); // body:(Either X ^X)
}
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 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)) {
case interm_addr_regular:
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;
bool MsgEnvelope::validate_skip(vm::CellSlice& cs, bool weak) const {
return cs.fetch_ulong(4) == 4 // msg_envelope#4
&& t_IntermediateAddress.validate_skip(cs, weak) // cur_addr:IntermediateAddress
&& t_IntermediateAddress.validate_skip(cs, weak) // next_addr:IntermediateAddress
&& t_Grams.validate_skip(cs, weak) // fwd_fee_remaining:Grams
&& t_Ref_Message.validate_skip(cs, weak); // msg:^Message
bool MsgEnvelope::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.fetch_ulong(4) == 4 // msg_envelope#4
&& t_IntermediateAddress.validate_skip(ops, cs, weak) // cur_addr:IntermediateAddress
&& t_IntermediateAddress.validate_skip(ops, cs, weak) // next_addr:IntermediateAddress
&& t_Grams.validate_skip(ops, cs, weak) // fwd_fee_remaining:Grams
&& t_Ref_Message.validate_skip(ops, cs, weak); // msg:^Message
}
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 RefTo<MsgEnvelope> t_Ref_MsgEnvelope;
bool StorageUsed::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_7.validate_skip(cs, weak) // cells:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(cs, weak) // bits:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(cs, weak); // public_cells:(VarUInteger 7)
bool StorageUsed::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_7.validate_skip(ops, cs, weak) // cells:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(ops, cs, weak) // bits:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(ops, cs, weak); // public_cells:(VarUInteger 7)
}
bool StorageUsed::skip(vm::CellSlice& cs) const {
@ -863,9 +864,9 @@ bool StorageUsed::skip(vm::CellSlice& cs) const {
const StorageUsed t_StorageUsed;
bool StorageUsedShort::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_7.validate_skip(cs, weak) // cells:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(cs, weak); // bits:(VarUInteger 7)
bool StorageUsedShort::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_7.validate_skip(ops, cs, weak) // cells:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(ops, cs, weak); // bits:(VarUInteger 7)
}
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)
}
bool StorageInfo::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_StorageUsed.validate_skip(cs, weak) // used:StorageUsed
&& cs.advance(32) // last_paid:uint32
&& t_Maybe_Grams.validate_skip(cs, weak); // due_payment:(Maybe Grams)
bool StorageInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_StorageUsed.validate_skip(ops, cs, weak) // used:StorageUsed
&& cs.advance(32) // last_paid:uint32
&& t_Maybe_Grams.validate_skip(ops, cs, weak); // due_payment:(Maybe Grams)
}
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)) {
case account_uninit:
return cs.advance(2);
case account_frozen:
return cs.advance(2 + 256);
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;
}
@ -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);
}
bool AccountStorage::validate_skip(vm::CellSlice& cs, bool weak) const {
return cs.advance(64) && t_CurrencyCollection.validate_skip(cs, weak) && t_AccountState.validate_skip(cs, weak);
bool AccountStorage::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.advance(64) && t_CurrencyCollection.validate_skip(ops, cs, weak) &&
t_AccountState.validate_skip(ops, cs, weak);
}
const AccountStorage t_AccountStorage;
@ -940,15 +942,15 @@ bool Account::skip(vm::CellSlice& cs) const {
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)) {
case account_none:
return allow_empty && cs.advance(1);
case account:
return cs.advance(1) // account$1
&& t_MsgAddressInt.validate_skip(cs, weak) // addr:MsgAddressInt
&& t_StorageInfo.validate_skip(cs, weak) // storage_stat:StorageInfo
&& t_AccountStorage.validate_skip(cs, weak); // storage:AccountStorage
return cs.advance(1) // account$1
&& t_MsgAddressInt.validate_skip(ops, cs, weak) // addr:MsgAddressInt
&& t_StorageInfo.validate_skip(ops, cs, weak) // storage_stat:StorageInfo
&& t_AccountStorage.validate_skip(ops, cs, weak); // storage:AccountStorage
}
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) {
return false;
}
if (!n) {
// ahmn_leaf
vm::CellSlice cs_extra{cs};
if (!aug.extra_type.validate_skip(cs, weak)) {
if (!aug.extra_type.validate_skip(ops, cs, weak)) {
return false;
}
cs_extra.cut_tail(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;
}
cs_value.cut_tail(cs);
@ -1055,13 +1057,14 @@ bool HashmapAugNode::validate_skip(vm::CellSlice& cs, bool weak) const {
return false;
}
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;
}
auto cs_left = load_cell_slice(cs.fetch_ref());
auto cs_right = load_cell_slice(cs.fetch_ref());
vm::CellSlice cs_extra{cs};
if (!aug.extra_type.validate_skip(cs, weak)) {
if (!aug.extra_type.validate_skip(ops, cs, weak)) {
return false;
}
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);
}
bool HashmapAug::validate_skip(vm::CellSlice& cs, bool weak) const {
bool HashmapAug::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
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 {
@ -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);
}
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;
switch (get_tag(cs)) {
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());
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;
auto cs_root = load_cell_slice_special(cs.fetch_ref(), special);
if (special) {
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);
}
break;
@ -1121,9 +1124,10 @@ bool DepthBalanceInfo::skip(vm::CellSlice& cs) const {
cs); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection = DepthBalanceInfo;
}
bool DepthBalanceInfo::validate_skip(vm::CellSlice& cs, bool weak) const {
return cs.fetch_ulong(5) <= 30 && t_CurrencyCollection.validate_skip(
cs, weak); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection
bool DepthBalanceInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.fetch_ulong(5) <= 30 &&
t_CurrencyCollection.validate_skip(ops, cs,
weak); // depth_balance$_ split_depth:(#<= 30) balance:CurrencyCollection
}
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
}
bool TrStoragePhase::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_Grams.validate_skip(cs, weak) // storage_fees_collected:Grams
&& t_Maybe_Grams.validate_skip(cs, weak) // storage_fees_due:Grams
&& t_AccStatusChange.validate_skip(cs, weak); // status_change:AccStatusChange
bool TrStoragePhase::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_Grams.validate_skip(ops, cs, weak) // storage_fees_collected:Grams
&& t_Maybe_Grams.validate_skip(ops, cs, weak) // storage_fees_due:Grams
&& t_AccStatusChange.validate_skip(ops, cs, weak); // status_change:AccStatusChange
}
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
}
bool TrCreditPhase::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_Maybe_Grams.validate_skip(cs, weak) // due_fees_collected:(Maybe Grams)
&& t_CurrencyCollection.validate_skip(cs, weak); // credit:CurrencyCollection
bool TrCreditPhase::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_Maybe_Grams.validate_skip(ops, cs, weak) // due_fees_collected:(Maybe Grams)
&& t_CurrencyCollection.validate_skip(ops, cs, weak); // credit:CurrencyCollection
}
const TrCreditPhase t_TrCreditPhase;
@ -1204,15 +1208,15 @@ bool TrComputeInternal1::skip(vm::CellSlice& cs) const {
// vm_final_state_hash:uint256
}
bool TrComputeInternal1::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_7.validate_skip(cs, weak) // gas_used:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(cs, weak) // gas_limit:(VarUInteger 7)
&& Maybe<VarUInteger>{3}.validate_skip(cs, weak) // gas_credit:(Maybe (VarUInteger 3))
&& cs.advance(8 + 32) // mode:int8 exit_code:int32
&& Maybe<Int>{32}.validate_skip(cs, weak) // exit_arg:(Maybe int32)
&& cs.advance(32 + 256 + 256); // vm_steps:uint32
// vm_init_state_hash:uint256
// vm_final_state_hash:uint256
bool TrComputeInternal1::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_VarUInteger_7.validate_skip(ops, cs, weak) // gas_used:(VarUInteger 7)
&& t_VarUInteger_7.validate_skip(ops, cs, weak) // gas_limit:(VarUInteger 7)
&& Maybe<VarUInteger>{3}.validate_skip(ops, cs, weak) // gas_credit:(Maybe (VarUInteger 3))
&& cs.advance(8 + 32) // mode:int8 exit_code:int32
&& Maybe<Int>{32}.validate_skip(ops, cs, weak) // exit_arg:(Maybe int32)
&& cs.advance(32 + 256 + 256); // vm_steps:uint32
// vm_init_state_hash:uint256
// vm_final_state_hash:uint256
}
const TrComputeInternal1 t_TrComputeInternal1;
@ -1231,14 +1235,14 @@ bool TrComputePhase::skip(vm::CellSlice& cs) const {
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)) {
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:
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_Ref_TrComputeInternal1.validate_skip(cs, weak); // ^[ gas_used:(..) .. ]
&& t_Grams.validate_skip(ops, cs, weak) // gas_fees:Grams
&& t_Ref_TrComputeInternal1.validate_skip(ops, cs, weak); // ^[ gas_used:(..) .. ]
}
return false;
}
@ -1258,17 +1262,17 @@ bool TrActionPhase::skip(vm::CellSlice& cs) const {
&& t_StorageUsedShort.skip(cs); // tot_msg_size:StorageUsedShort
}
bool TrActionPhase::validate_skip(vm::CellSlice& cs, bool weak) const {
return cs.advance(3) // success:Bool valid:Bool no_funds:Bool
&& t_AccStatusChange.validate_skip(cs, weak) // status_change:AccStatusChange
&& t_Maybe_Grams.validate_skip(cs, weak) // total_fwd_fees:(Maybe Grams)
&& t_Maybe_Grams.validate_skip(cs, weak) // total_action_fees:(Maybe Grams)
&& cs.advance(32) // result_code:int32
&& Maybe<Int>{32}.validate_skip(cs, weak) // result_arg:(Maybe int32)
&& cs.advance(16 * 4 + 256) // tot_actions:uint16 spec_actions:uint16
// skipped_actions:uint16 msgs_created:uint16
// action_list_hash:uint256
&& t_StorageUsedShort.validate_skip(cs, weak); // tot_msg_size:StorageUsed
bool TrActionPhase::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.advance(3) // success:Bool valid:Bool no_funds:Bool
&& t_AccStatusChange.validate_skip(ops, cs, weak) // status_change:AccStatusChange
&& t_Maybe_Grams.validate_skip(ops, cs, weak) // total_fwd_fees:(Maybe Grams)
&& t_Maybe_Grams.validate_skip(ops, cs, weak) // total_action_fees:(Maybe Grams)
&& cs.advance(32) // result_code:int32
&& Maybe<Int>{32}.validate_skip(ops, cs, weak) // result_arg:(Maybe int32)
&& cs.advance(16 * 4 + 256) // tot_actions:uint16 spec_actions:uint16
// skipped_actions:uint16 msgs_created:uint16
// action_list_hash:uint256
&& t_StorageUsedShort.validate_skip(ops, cs, weak); // tot_msg_size:StorageUsed
}
const TrActionPhase t_TrActionPhase;
@ -1290,19 +1294,19 @@ bool TrBouncePhase::skip(vm::CellSlice& cs) const {
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)) {
case tr_phase_bounce_negfunds:
return cs.advance(2); // tr_phase_bounce_negfunds$00
case tr_phase_bounce_nofunds:
return cs.advance(2) // tr_phase_bounce_nofunds$01
&& t_StorageUsedShort.validate_skip(cs, weak) // msg_size:StorageUsedShort
&& t_Grams.validate_skip(cs, weak); // req_fwd_fees:Grams
return cs.advance(2) // tr_phase_bounce_nofunds$01
&& t_StorageUsedShort.validate_skip(ops, cs, weak) // msg_size:StorageUsedShort
&& t_Grams.validate_skip(ops, cs, weak); // req_fwd_fees:Grams
case tr_phase_bounce_ok:
return cs.advance(1) // tr_phase_bounce_ok$1
&& t_StorageUsedShort.validate_skip(cs, weak) // msg_size:StorageUsedShort
&& t_Grams.validate_skip(cs, weak) // msg_fees:Grams
&& t_Grams.validate_skip(cs, weak); // fwd_fees:Grams
return cs.advance(1) // tr_phase_bounce_ok$1
&& t_StorageUsedShort.validate_skip(ops, cs, weak) // msg_size:StorageUsedShort
&& t_Grams.validate_skip(ops, cs, weak) // msg_fees:Grams
&& t_Grams.validate_skip(ops, cs, weak); // fwd_fees:Grams
}
return false;
}
@ -1322,7 +1326,7 @@ bool SplitMergeInfo::skip(vm::CellSlice& cs) const {
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)) {
return false;
}
@ -1392,52 +1396,52 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
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)) {
case trans_ord:
return cs.advance(4 + 1) // trans_ord$0000 credit_first:Bool
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase)
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(1) // aborted:Bool
&& Maybe<TrBouncePhase>{}.validate_skip(cs, weak) // bounce:(Maybe TrBouncePhase)
&& cs.advance(1); // destroyed:Bool
return cs.advance(4 + 1) // trans_ord$0000 credit_first:Bool
&& Maybe<TrStoragePhase>{}.validate_skip(ops, cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.validate_skip(ops, cs, weak) // credit_ph:(Maybe TrCreditPhase)
&& t_TrComputePhase.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(ops, cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(1) // aborted:Bool
&& Maybe<TrBouncePhase>{}.validate_skip(ops, cs, weak) // bounce:(Maybe TrBouncePhase)
&& cs.advance(1); // destroyed:Bool
case trans_storage:
return cs.advance(4) // trans_storage$0001
&& t_TrStoragePhase.validate_skip(cs, weak); // storage_ph:TrStoragePhase
return cs.advance(4) // trans_storage$0001
&& t_TrStoragePhase.validate_skip(ops, cs, weak); // storage_ph:TrStoragePhase
case trans_tick_tock:
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
&& t_TrStoragePhase.validate_skip(cs, weak) // storage_ph:TrStoragePhase
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
&& t_TrStoragePhase.validate_skip(ops, cs, weak) // storage_ph:TrStoragePhase
&& t_TrComputePhase.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(ops, cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
case trans_split_prepare:
return cs.advance(4) // trans_split_prepare$0100
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
return cs.advance(4) // trans_split_prepare$0100
&& t_SplitMergeInfo.validate_skip(ops, cs, weak) // split_info:SplitMergeInfo
&& Maybe<TrStoragePhase>{}.validate_skip(ops, cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& t_TrComputePhase.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(ops, cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
case trans_split_install:
return cs.advance(4) // trans_split_install$0101
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
&& t_Ref_Transaction.validate_skip(cs, weak) // prepare_transaction:^Transaction
&& cs.advance(1); // installed:Bool
return cs.advance(4) // trans_split_install$0101
&& t_SplitMergeInfo.validate_skip(ops, cs, weak) // split_info:SplitMergeInfo
&& t_Ref_Transaction.validate_skip(ops, cs, weak) // prepare_transaction:^Transaction
&& cs.advance(1); // installed:Bool
case trans_merge_prepare:
return cs.advance(4) // trans_merge_prepare$0110
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
&& t_TrStoragePhase.validate_skip(cs, weak) // storage_ph:TrStoragePhase
&& cs.advance(1); // aborted:Bool
return cs.advance(4) // trans_merge_prepare$0110
&& t_SplitMergeInfo.validate_skip(ops, cs, weak) // split_info:SplitMergeInfo
&& t_TrStoragePhase.validate_skip(ops, cs, weak) // storage_ph:TrStoragePhase
&& cs.advance(1); // aborted:Bool
case trans_merge_install:
return cs.advance(4) // trans_merge_install$0111
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
&& t_Ref_Transaction.validate_skip(cs, weak) // prepare_transaction:^Transaction
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase)
&& Maybe<TrComputePhase>{}.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
return cs.advance(4) // trans_merge_install$0111
&& t_SplitMergeInfo.validate_skip(ops, cs, weak) // split_info:SplitMergeInfo
&& t_Ref_Transaction.validate_skip(ops, cs, weak) // prepare_transaction:^Transaction
&& Maybe<TrStoragePhase>{}.validate_skip(ops, cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.validate_skip(ops, cs, weak) // credit_ph:(Maybe TrCreditPhase)
&& Maybe<TrComputePhase>{}.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(ops, cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
}
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)
}
bool Transaction_aux::validate_skip(vm::CellSlice& cs, bool weak) const {
return Maybe<RefTo<Message>>{}.validate_skip(cs, weak) // in_msg:(Maybe ^Message)
&& HashmapE{15, t_Ref_Message}.validate_skip(cs, weak); // out_msgs:(HashmapE 15 ^Message)
bool Transaction_aux::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return Maybe<RefTo<Message>>{}.validate_skip(ops, cs, weak) // in_msg:(Maybe ^Message)
&& HashmapE{15, t_Ref_Message}.validate_skip(ops, cs, weak); // out_msgs:(HashmapE 15 ^Message)
}
const Transaction_aux t_Transaction_aux;
@ -1520,18 +1524,18 @@ bool Transaction::skip(vm::CellSlice& cs) const {
&& 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
&&
cs.advance(
256 + 64 + 256 + 64 + 32 +
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(cs, weak) // end_status:AccountStatus
&& RefTo<Transaction_aux>{}.validate_skip(cs, weak) // ^[ in_msg:... out_msgs:... ]
&& t_CurrencyCollection.validate_skip(cs, weak) // total_fees:CurrencyCollection
&& t_Ref_HashUpdate.validate_skip(cs, weak) // state_update:^(HASH_UPDATE Account)
&& RefTo<TransactionDescr>{}.validate_skip(cs, weak); // description:^TransactionDescr
&& t_AccountStatus.validate_skip(ops, cs, weak) // orig_status:AccountStatus
&& t_AccountStatus.validate_skip(ops, cs, weak) // end_status:AccountStatus
&& RefTo<Transaction_aux>{}.validate_skip(ops, cs, weak) // ^[ in_msg:... out_msgs:... ]
&& t_CurrencyCollection.validate_skip(ops, cs, weak) // total_fees:CurrencyCollection
&& t_Ref_HashUpdate.validate_skip(ops, cs, weak) // state_update:^(HASH_UPDATE Account)
&& RefTo<TransactionDescr>{}.validate_skip(ops, cs, weak); // description:^TransactionDescr
}
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)
}
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
&& cs.advance(256) // account_addr:bits256
&&
t_AccountTransactions.validate_skip(cs, weak) // transactions:(HashmapAug 64 ^Transaction CurrencyCollection)
&& t_Ref_HashUpdate.validate_skip(cs, weak); // state_update:^(HASH_UPDATE Account)
&& t_AccountTransactions.validate_skip(ops, cs,
weak) // transactions:(HashmapAug 64 ^Transaction CurrencyCollection)
&& 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 {
@ -1620,8 +1624,8 @@ const Aug_ShardAccountBlocks aug_ShardAccountBlocks;
const HashmapAugE t_ShardAccountBlocks{256,
aug_ShardAccountBlocks}; // (HashmapAugE 256 AccountBlock CurrencyCollection)
bool ImportFees::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_Grams.validate_skip(cs, weak) && t_CurrencyCollection.validate_skip(cs, weak);
bool ImportFees::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_Grams.validate_skip(ops, cs, weak) && t_CurrencyCollection.validate_skip(ops, cs, weak);
}
bool ImportFees::skip(vm::CellSlice& cs) const {
@ -1676,44 +1680,44 @@ bool InMsg::skip(vm::CellSlice& cs) const {
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)) {
case msg_import_ext:
return cs.advance(3) // msg_import_ext$000
&& t_Ref_Message.validate_skip(cs, weak) // msg:^Message
&& t_Ref_Transaction.validate_skip(cs, weak); // transaction:^Transaction
return cs.advance(3) // msg_import_ext$000
&& t_Ref_Message.validate_skip(ops, cs, weak) // msg:^Message
&& t_Ref_Transaction.validate_skip(ops, cs, weak); // transaction:^Transaction
case msg_import_ihr:
return cs.advance(3) // msg_import_ihr$010
&& t_Ref_Message.validate_skip(cs, weak) // msg:^Message
&& t_Ref_Transaction.validate_skip(cs, weak) // transaction:^Transaction
&& t_Grams.validate_skip(cs, weak) // ihr_fee:Grams
&& t_RefCell.validate_skip(cs, weak); // proof_created:^Cell
return cs.advance(3) // msg_import_ihr$010
&& t_Ref_Message.validate_skip(ops, cs, weak) // msg:^Message
&& t_Ref_Transaction.validate_skip(ops, cs, weak) // transaction:^Transaction
&& t_Grams.validate_skip(ops, cs, weak) // ihr_fee:Grams
&& t_RefCell.validate_skip(ops, cs, weak); // proof_created:^Cell
case msg_import_imm:
return cs.advance(3) // msg_import_imm$011
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(cs, weak) // transaction:^Transaction
&& t_Grams.validate_skip(cs, weak); // fwd_fee:Grams
return cs.advance(3) // msg_import_imm$011
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(ops, cs, weak) // transaction:^Transaction
&& t_Grams.validate_skip(ops, cs, weak); // fwd_fee:Grams
case msg_import_fin:
return cs.advance(3) // msg_import_fin$100
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(cs, weak) // transaction:^Transaction
&& t_Grams.validate_skip(cs, weak); // fwd_fee:Grams
return cs.advance(3) // msg_import_fin$100
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(ops, cs, weak) // transaction:^Transaction
&& t_Grams.validate_skip(ops, cs, weak); // fwd_fee:Grams
case msg_import_tr:
return cs.advance(3) // msg_import_tr$101
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope
&& t_Grams.validate_skip(cs, weak); // transit_fee:Grams
return cs.advance(3) // msg_import_tr$101
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& t_Grams.validate_skip(ops, cs, weak); // transit_fee:Grams
case msg_discard_fin:
return cs.advance(3) // msg_discard_fin$110
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope
&& cs.advance(64) // transaction_id:uint64
&& t_Grams.validate_skip(cs, weak); // fwd_fee:Grams
return cs.advance(3) // msg_discard_fin$110
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& cs.advance(64) // transaction_id:uint64
&& t_Grams.validate_skip(ops, cs, weak); // fwd_fee:Grams
case msg_discard_tr:
return cs.advance(3) // msg_discard_tr$111
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // in_msg:^MsgEnvelope
&& cs.advance(64) // transaction_id:uint64
&& t_Grams.validate_skip(cs, weak) // fwd_fee:Grams
&& t_RefCell.validate_skip(cs, weak); // proof_delivered:^Cell
return cs.advance(3) // msg_discard_tr$111
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // in_msg:^MsgEnvelope
&& cs.advance(64) // transaction_id:uint64
&& t_Grams.validate_skip(ops, cs, weak) // fwd_fee:Grams
&& t_RefCell.validate_skip(ops, cs, weak); // proof_delivered:^Cell
}
return false;
}
@ -1851,37 +1855,37 @@ bool OutMsg::skip(vm::CellSlice& cs) const {
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)) {
case msg_export_ext:
return cs.advance(3) // msg_export_ext$000
&& t_Ref_Message.validate_skip(cs, weak) // msg:^Message
&& t_Ref_Transaction.validate_skip(cs, weak); // transaction:^Transaction
return cs.advance(3) // msg_export_ext$000
&& t_Ref_Message.validate_skip(ops, cs, weak) // msg:^Message
&& t_Ref_Transaction.validate_skip(ops, cs, weak); // transaction:^Transaction
case msg_export_imm:
return cs.advance(3) // msg_export_imm$010
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(cs, weak) // transaction:^Transaction
&& RefTo<InMsg>{}.validate_skip(cs, weak); // reimport:^InMsg
return cs.advance(3) // msg_export_imm$010
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(ops, cs, weak) // transaction:^Transaction
&& RefTo<InMsg>{}.validate_skip(ops, cs, weak); // reimport:^InMsg
case msg_export_new:
return cs.advance(3) // msg_export_new$001
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(cs, weak); // transaction:^Transaction
return cs.advance(3) // msg_export_new$001
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& t_Ref_Transaction.validate_skip(ops, cs, weak); // transaction:^Transaction
case msg_export_tr:
return cs.advance(3) // msg_export_tr$011
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope
&& RefTo<InMsg>{}.validate_skip(cs, weak); // imported:^InMsg
return cs.advance(3) // msg_export_tr$011
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& RefTo<InMsg>{}.validate_skip(ops, cs, weak); // imported:^InMsg
case msg_export_deq_imm:
return cs.advance(3) // msg_export_deq_imm$100
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope
&& RefTo<InMsg>{}.validate_skip(cs, weak); // reimport:^InMsg
return cs.advance(3) // msg_export_deq_imm$100
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& RefTo<InMsg>{}.validate_skip(ops, cs, weak); // reimport:^InMsg
case msg_export_deq:
return cs.advance(3) // msg_export_deq$110
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope
&& cs.advance(64); // import_block_lt:uint64
return cs.advance(3) // msg_export_deq$110
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& cs.advance(64); // import_block_lt:uint64
case msg_export_tr_req:
return cs.advance(3) // msg_export_tr_req$111
&& t_Ref_MsgEnvelope.validate_skip(cs, weak) // out_msg:^MsgEnvelope
&& RefTo<InMsg>{}.validate_skip(cs, weak); // imported:^InMsg
return cs.advance(3) // msg_export_tr_req$111
&& t_Ref_MsgEnvelope.validate_skip(ops, cs, weak) // out_msg:^MsgEnvelope
&& RefTo<InMsg>{}.validate_skip(ops, cs, weak); // imported:^InMsg
}
return false;
}
@ -1954,8 +1958,8 @@ const OutMsg t_OutMsg;
const Aug_OutMsgDescr aug_OutMsgDescr;
const OutMsgDescr t_OutMsgDescr;
bool EnqueuedMsg::validate_skip(vm::CellSlice& cs, bool weak) const {
return cs.advance(64) && t_Ref_MsgEnvelope.validate_skip(cs, weak);
bool EnqueuedMsg::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.advance(64) && t_Ref_MsgEnvelope.validate_skip(ops, cs, weak);
}
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);
}
bool OutMsgQueueInfo::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_OutMsgQueue.validate_skip(cs, weak) && t_ProcessedInfo.validate_skip(cs, weak) &&
t_IhrPendingInfo.validate_skip(cs, weak);
bool OutMsgQueueInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_OutMsgQueue.validate_skip(ops, cs, weak) && t_ProcessedInfo.validate_skip(ops, cs, weak) &&
t_IhrPendingInfo.validate_skip(ops, cs, weak);
}
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 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;
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) &&
@ -2112,8 +2116,8 @@ bool ShardIdent::pack(vm::CellBuilder& cb, ton::ShardIdFull data) const {
const ShardIdent t_ShardIdent;
bool BlockIdExt::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_ShardIdent.validate_skip(cs, weak) && cs.advance(32 + 256 * 2);
bool BlockIdExt::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_ShardIdent.validate_skip(ops, cs, weak) && cs.advance(32 + 256 * 2);
}
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)
}
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;
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
&& 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
&& t_Ref_OutMsgQueueInfo.validate_skip(cs, weak) // out_msg_queue_info:^OutMsgQueueInfo
&& cs.advance(1) // before_split:Bool
&& t_ShardAccounts.validate_skip_ref(cs, weak) // accounts:^ShardAccounts
&& t_Ref_OutMsgQueueInfo.validate_skip(ops, cs, weak) // out_msg_queue_info:^OutMsgQueueInfo
&& cs.advance(1) // before_split:Bool
&& t_ShardAccounts.validate_skip_ref(ops, cs, weak) // accounts:^ShardAccounts
&&
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) ]
&& 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;
@ -2173,12 +2177,12 @@ bool ShardState_aux::skip(vm::CellSlice& cs) const {
&& Maybe<BlkMasterInfo>{}.skip(cs); // master_ref:(Maybe BlkMasterInfo)
}
bool ShardState_aux::validate_skip(vm::CellSlice& cs, bool weak) const {
return cs.advance(128) // overload_history:uint64 underload_history:uint64
&& t_CurrencyCollection.validate_skip(cs, weak) // total_balance:CurrencyCollection
&& t_CurrencyCollection.validate_skip(cs, weak) // total_validator_fees:CurrencyCollection
&& HashmapE{256, t_LibDescr}.validate_skip(cs, weak) // libraries:(HashmapE 256 LibDescr)
&& Maybe<BlkMasterInfo>{}.validate_skip(cs, weak); // master_ref:(Maybe BlkMasterInfo)
bool ShardState_aux::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return cs.advance(128) // overload_history:uint64 underload_history:uint64
&& t_CurrencyCollection.validate_skip(ops, cs, weak) // total_balance:CurrencyCollection
&& t_CurrencyCollection.validate_skip(ops, cs, weak) // total_validator_fees:CurrencyCollection
&& HashmapE{256, t_LibDescr}.validate_skip(ops, cs, weak) // libraries:(HashmapE 256 LibDescr)
&& Maybe<BlkMasterInfo>{}.validate_skip(ops, cs, weak); // master_ref:(Maybe BlkMasterInfo)
}
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)
}
bool LibDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
return get_tag(cs) == shared_lib_descr && cs.advance(2) // shared_lib_descr$00
&& cs.fetch_ref().not_null() // lib:^Cell
&& Hashmap{256, t_True}.validate_skip(cs, weak); // publishers:(Hashmap 256 False)
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
&& cs.fetch_ref().not_null() // lib:^Cell
&& Hashmap{256, t_True}.validate_skip(ops, cs, weak); // publishers:(Hashmap 256 False)
}
const LibDescr t_LibDescr;
@ -2202,9 +2206,9 @@ bool BlkPrevInfo::skip(vm::CellSlice& cs) const {
&& (!merged || t_ExtBlkRef.skip(cs)); // prev_alt:merged?ExtBlkRef
}
bool BlkPrevInfo::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_ExtBlkRef.validate_skip(cs, weak) // prev_blk_info$_ {merged:#} prev:ExtBlkRef
&& (!merged || t_ExtBlkRef.validate_skip(cs, weak)); // prev_alt:merged?ExtBlkRef
bool BlkPrevInfo::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_ExtBlkRef.validate_skip(ops, cs, weak) // prev_blk_info$_ {merged:#} prev:ExtBlkRef
&& (!merged || t_ExtBlkRef.validate_skip(ops, cs, weak)); // prev_alt:merged?ExtBlkRef
}
const BlkPrevInfo t_BlkPrevInfo_0{0};
@ -2213,8 +2217,8 @@ bool McStateExtra::skip(vm::CellSlice& cs) const {
return block::gen::t_McStateExtra.skip(cs);
}
bool McStateExtra::validate_skip(vm::CellSlice& cs, bool weak) const {
return block::gen::t_McStateExtra.validate_skip(cs, weak); // ??
bool McStateExtra::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return block::gen::t_McStateExtra.validate_skip(ops, cs, weak); // ??
}
const McStateExtra t_McStateExtra;
@ -2241,8 +2245,8 @@ bool ShardFeeCreated::skip(vm::CellSlice& cs) const {
return t_CurrencyCollection.skip(cs) && t_CurrencyCollection.skip(cs);
}
bool ShardFeeCreated::validate_skip(vm::CellSlice& cs, bool weak) const {
return t_CurrencyCollection.validate_skip(cs, weak) && t_CurrencyCollection.validate_skip(cs, weak);
bool ShardFeeCreated::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {
return t_CurrencyCollection.validate_skip(ops, cs, weak) && t_CurrencyCollection.validate_skip(ops, cs, weak);
}
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
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
#include "common/refcnt.hpp"
@ -59,7 +59,7 @@ struct VarUInteger final : TLB_Complex {
ln = 32 - td::count_leading_zeroes32(n - 1);
}
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;
unsigned long long as_uint(const vm::CellSlice& cs) 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);
}
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;
unsigned long long as_uint(const vm::CellSlice& cs) 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);
}
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;
long long as_int(const vm::CellSlice& cs) 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);
}
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;
long long as_int(const vm::CellSlice& cs) 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 {
return validate_skip(cs, false, n);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return cs.advance(get_size(cs));
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return skip(cs);
}
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));
}
};
@ -149,7 +149,7 @@ struct HmLabel final : TLB_Complex {
int 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;
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) {
}
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 {
@ -173,7 +173,7 @@ struct HashmapNode final : TLB_Complex {
}
int get_size(const 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 {
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) {
}
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 {
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) {
}
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;
};
@ -232,7 +232,7 @@ struct HashmapAugNode final : TLB_Complex {
HashmapAugNode(int _n, const AugmentationCheckData& _aug) : aug(_aug), n(_n) {
}
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 {
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)) {
}
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;
int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(1);
@ -252,7 +252,7 @@ struct HashmapAugE 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;
bool null_value(vm::CellBuilder& cb) 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 {
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 {
return (int)cs.prefetch_ulong(2);
}
@ -303,7 +303,7 @@ extern const MsgAddressExt t_MsgAddressExt;
struct MsgAddress final : TLB_Complex {
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 {
return (int)cs.prefetch_ulong(2);
}
@ -318,8 +318,8 @@ struct ExtraCurrencyCollection final : TLB {
int get_size(const vm::CellSlice& cs) const override {
return dict_type.get_size(cs);
}
bool validate(const vm::CellSlice& cs, bool weak) const override {
return dict_type.validate(cs, weak);
bool validate(int* ops, const vm::CellSlice& cs, bool weak) const override {
return dict_type.validate(ops, cs, weak);
}
bool null_value(vm::CellBuilder& cb) const override {
return cb.store_zeroes_bool(1);
@ -348,7 +348,7 @@ extern const ExtraCurrencyCollection t_ExtraCurrencyCollection;
struct CurrencyCollection final : TLB_Complex {
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;
bool null_value(vm::CellBuilder& cb) const override {
return cb.store_bits_same_bool(1 + 4, false);
@ -371,7 +371,7 @@ extern const CurrencyCollection t_CurrencyCollection;
struct CommonMsgInfo final : TLB_Complex {
enum { int_msg_info = 0, ext_in_msg_info = 2, ext_out_msg_info = 3 };
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 v = (int)cs.prefetch_ulong(2);
return v == 1 ? int_msg_info : v;
@ -402,14 +402,14 @@ struct TickTock final : TLB {
extern const TickTock t_TickTock;
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;
};
extern const StateInit t_StateInit;
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 get_created_lt(vm::CellSlice& cs, unsigned long long& created_lt) 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 };
int get_size(const 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 {
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 {
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;
struct Record {
typedef MsgEnvelope type_class;
@ -463,28 +463,28 @@ extern const RefTo<MsgEnvelope> t_Ref_MsgEnvelope;
struct StorageUsed final : TLB_Complex {
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;
struct StorageUsedShort final : TLB_Complex {
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;
struct StorageInfo final : TLB_Complex {
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;
struct AccountState final : TLB_Complex {
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 t = (int)cs.prefetch_ulong(2);
return t == 3 ? account_active : t;
@ -496,7 +496,7 @@ extern const AccountState t_AccountState;
struct AccountStorage final : TLB_Complex {
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;
};
@ -508,7 +508,7 @@ struct Account final : TLB_Complex {
Account(bool _allow_empty = false) : allow_empty(_allow_empty) {
}
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;
bool skip_copy_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 {
return cs.advance_ext(0x140, 1);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return cs.advance(0x140) && t_Ref_Account.validate_skip(cs, weak);
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return cs.advance(0x140) && t_Ref_Account.validate_skip(ops, cs, weak);
}
static bool unpack(vm::CellSlice& cs, Record& info) {
return info.unpack(cs);
@ -569,7 +569,7 @@ extern const ShardAccount t_ShardAccount;
struct DepthBalanceInfo final : TLB_Complex {
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 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 {
return dict_type.skip(cs);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(cs, weak);
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(ops, cs, weak);
}
};
@ -615,7 +615,7 @@ extern const AccStatusChange t_AccStatusChange;
struct TrStoragePhase final : TLB_Complex {
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 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 {
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;
struct TrComputeInternal1 final : TLB_Complex {
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 {
@ -639,7 +639,7 @@ struct ComputeSkipReason final : TLB {
int get_size(const vm::CellSlice& cs) const override {
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);
}
int get_tag(const vm::CellSlice& cs) const override {
@ -653,7 +653,7 @@ extern const ComputeSkipReason t_ComputeSkipReason;
struct TrComputePhase final : TLB_Complex {
enum { tr_phase_compute_skipped = 0, tr_phase_compute_vm = 1 };
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 {
return (int)cs.prefetch_ulong(1);
}
@ -663,7 +663,7 @@ extern const TrComputePhase t_TrComputePhase;
struct TrActionPhase final : TLB_Complex {
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;
@ -671,7 +671,7 @@ extern const TrActionPhase t_TrActionPhase;
struct TrBouncePhase final : TLB_Complex {
enum { tr_phase_bounce_negfunds = 0, tr_phase_bounce_nofunds = 1, tr_phase_bounce_ok = 2 };
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;
};
@ -679,7 +679,7 @@ extern const TrBouncePhase t_TrBouncePhase;
struct SplitMergeInfo final : TLB_Complex {
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;
@ -695,7 +695,7 @@ struct TransactionDescr final : TLB_Complex {
trans_merge_install = 7
};
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;
bool skip_to_storage_phase(vm::CellSlice& cs, bool& found) 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 {
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;
struct Transaction final : TLB_Complex {
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_descr(Ref<vm::Cell> cell, 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 {
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);
}
};
@ -745,7 +745,7 @@ extern const RefTo<HashUpdate> t_Ref_HashUpdate;
struct AccountBlock final : TLB_Complex {
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;
};
@ -762,7 +762,7 @@ extern const HashmapAugE t_ShardAccountBlocks; // (HashmapAugE 256 AccountBlock
struct ImportFees final : TLB_Complex {
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 {
return cb.store_bits_same_bool(4 + 4 + 1, false);
}
@ -782,7 +782,7 @@ struct InMsg final : TLB_Complex {
msg_discard_tr = 7
};
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 {
return (int)cs.prefetch_ulong(3);
}
@ -802,7 +802,7 @@ struct OutMsg final : TLB_Complex {
msg_export_tr_req = 7
};
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 {
return (int)cs.prefetch_ulong(3);
}
@ -830,8 +830,8 @@ struct InMsgDescr final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override {
return dict_type.skip(cs);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(cs, weak);
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(ops, cs, weak);
}
};
@ -853,8 +853,8 @@ struct OutMsgDescr final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override {
return dict_type.skip(cs);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(cs, weak);
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(ops, cs, weak);
}
};
@ -867,7 +867,7 @@ struct EnqueuedMsg final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override {
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 {
return descr.unpack(cs);
}
@ -891,8 +891,8 @@ struct OutMsgQueue final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override {
return dict_type.skip(cs);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(cs, weak);
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return dict_type.validate_skip(ops, cs, weak);
}
};
@ -910,7 +910,7 @@ extern const HashmapE t_IhrPendingInfo;
struct OutMsgQueueInfo final : TLB_Complex {
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;
@ -946,7 +946,7 @@ struct ShardIdent final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override {
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 {
return 0;
}
@ -985,7 +985,7 @@ struct BlockIdExt final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override {
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 pack(vm::CellBuilder& cb, const ton::BlockIdExt& data) const;
};
@ -995,7 +995,7 @@ extern const BlockIdExt t_BlockIdExt;
struct ShardState final : TLB_Complex {
enum { shard_state = (int)0x9023afe2, split_state = 0x5f327da5 };
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 {
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 {
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 {
return 0;
}
@ -1016,7 +1016,7 @@ extern const ShardState_aux t_ShardState_aux;
struct LibDescr final : TLB_Complex {
enum { shared_lib_descr = 0 };
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 {
return (int)cs.prefetch_ulong(2);
}
@ -1029,7 +1029,7 @@ struct BlkPrevInfo final : TLB_Complex {
BlkPrevInfo(bool _merged) : merged(_merged) {
}
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;
@ -1037,7 +1037,7 @@ extern const BlkPrevInfo t_BlkPrevInfo_0;
struct McStateExtra final : TLB_Complex {
enum { masterchain_state_extra = 0xcc26 };
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;
@ -1074,7 +1074,7 @@ extern const Aug_OldMcBlocksInfo aug_OldMcBlocksInfo;
struct ShardFeeCreated final : TLB_Complex {
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 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
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 "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();
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(
-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(
-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";
auto old_total_balance = total_balance_;
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";
return td::Status::Error(
-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 {
return is_valid() && td::sgn(grams) >= 0 && validate_extra();
bool CurrencyCollection::validate(int max_cells) const {
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()) {
return true;
}
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) {
@ -1265,8 +1265,8 @@ bool CurrencyCollection::unpack(Ref<vm::CellSlice> csr) {
return unpack_CurrencyCollection(std::move(csr), grams, extra) || invalidate();
}
bool CurrencyCollection::validate_unpack(Ref<vm::CellSlice> csr) {
return (csr.not_null() && block::tlb::t_CurrencyCollection.validate(*csr) &&
bool CurrencyCollection::validate_unpack(Ref<vm::CellSlice> csr, int max_cells) {
return (csr.not_null() && block::tlb::t_CurrencyCollection.validate_upto(max_cells, *csr) &&
unpack_CurrencyCollection(std::move(csr), grams, extra)) ||
invalidate();
}
@ -1593,7 +1593,7 @@ bool check_one_config_param(Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, td::
} else if (idx < 0) {
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) {
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
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
#include "common/refcnt.hpp"
@ -323,8 +323,8 @@ struct CurrencyCollection {
grams.clear();
return false;
}
bool validate() const;
bool validate_extra() const;
bool validate(int max_cells = 1024) const;
bool validate_extra(int max_cells = 1024) const;
bool operator==(const CurrencyCollection& other) const;
bool operator!=(const CurrencyCollection& other) const {
return !operator==(other);
@ -360,7 +360,7 @@ struct CurrencyCollection {
bool fetch(vm::CellSlice& cs);
bool fetch_exact(vm::CellSlice& cs);
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;
bool pack_to(Ref<vm::CellSlice>& csr) const {
return (csr = pack()).not_null();

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
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 <algorithm>
@ -697,7 +697,7 @@ void interpret_tlb_skip(vm::Stack& stack) {
void interpret_tlb_validate_skip(vm::Stack& stack) {
auto tp = pop_tlb_type(stack);
auto cs = stack.pop_cellslice();
bool ok = (*tp)->validate_skip(cs.write());
bool ok = (*tp)->validate_skip_upto(1048576, cs.write());
if (ok) {
stack.push(std::move(cs));
}

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
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 "vm/boc.h"
@ -98,7 +98,7 @@ void test1() {
block::tlb::ShardIdent::Record shard_id;
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.dump(std::cerr, 7);
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;
}
}
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(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_upto(1024, csl) << std::endl;
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip_upto(1024, csl) << std::endl;
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(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 << cb << std::endl;
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(cs2) = " << block::tlb::t_Grams.validate(*cs2) << 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_upto(1024, *cs2) << std::endl;
//
block::gen::SplitMergeInfo::Record data;
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(cs2) = " << block::gen::t_Grams.validate(*cs2) << 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_upto(1024, *cs2) << std::endl;
std::cout << "[cs = " << cs << "]" << std::endl;
bool ok = tlb::csr_unpack_inexact(cs, data);
std::cout << "block::gen::SplitMergeInfo.unpack(cs, data) = " << ok << std::endl;
@ -182,12 +182,12 @@ void test1() {
}
void test2(vm::CellSlice& cs) {
std::cout << "Bool.validate() = " << block::tlb::t_Bool.validate(cs) << std::endl;
std::cout << "UInt16.validate() = " << block::tlb::t_uint16.validate(cs) << std::endl;
std::cout << "HashmapE(32,UInt16).validate() = " << block::tlb::HashmapE(32, block::tlb::t_uint16).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_upto(1024, cs) << std::endl;
std::cout << "HashmapE(32,UInt16).validate() = "
<< block::tlb::HashmapE(32, block::tlb::t_uint16).validate_upto(1024, cs) << std::endl;
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() {
@ -249,7 +249,7 @@ int main(int argc, char* const argv[]) {
}
type->print_ref(std::cout, boc);
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;
}
}

View file

@ -1239,7 +1239,7 @@ bool ShardConfig::new_workchain(ton::WorkchainId workchain, ton::BlockSeqno reg_
cb.store_zeroes_bool(
1 + 5 +
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);
}
@ -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) {
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 "
<< shard.to_str();
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;
}
} else {
due_payment = td::RefInt256{true, 0};
due_payment = td::zero_refint();
}
unsigned long long u = 0;
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;
last_paid = 0;
storage_stat.clear();
due_payment = td::RefInt256{true, 0};
due_payment = td::zero_refint();
balance.set_zero();
if (my_addr_exact.is_null()) {
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_);
end_lt = start_lt + 1;
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) {
@ -504,7 +507,7 @@ bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig*
if (ihr_delivered) {
in_fwd_fee = std::move(ihr_fee);
} else {
in_fwd_fee = td::RefInt256{true, 0};
in_fwd_fee = td::zero_refint();
msg_balance_remaining += std::move(ihr_fee);
}
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";
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) {
LOG(DEBUG) << "cannot pay for importing this external message";
return false;
@ -616,19 +619,19 @@ bool Transaction::prepare_storage_phase(const StoragePhaseConfig& cfg, bool forc
res->is_special = account.is_special;
last_paid = res->last_paid_updated = (res->is_special ? 0 : now);
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) {
res->fees_collected = to_pay;
res->fees_due = td::RefInt256{true, 0};
res->fees_due = td::zero_refint();
balance -= std::move(to_pay);
} else if (acc_status == Account::acc_frozen && !force_collect && to_pay + due_payment < cfg.delete_due_limit) {
// do not collect fee
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 {
res->fees_collected = 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) {
auto total_due = res->fees_due + due_payment;
switch (acc_status) {
@ -707,8 +710,8 @@ bool ComputePhaseConfig::parse_GasLimitsPrices_internal(Ref<vm::CellSlice> cs, t
special_gas_limit = spec_limit;
gas_credit = r.gas_credit;
gas_price = r.gas_price;
freeze_due_limit = td::RefInt256{true, r.freeze_due_limit};
delete_due_limit = td::RefInt256{true, r.delete_due_limit};
freeze_due_limit = td::make_refint(r.freeze_due_limit);
delete_due_limit = td::make_refint(r.delete_due_limit);
};
block::gen::GasLimitsPrices::Record_gas_prices_ext 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() {
gas_price256 = td::RefInt256{true, gas_price};
gas_price256 = td::make_refint(gas_price);
if (gas_limit > flat_gas_limit) {
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 {
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(
td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // actions:Integer
td::make_refint(0), // msgs_sent:Integer
td::zero_refint(), // actions:Integer
td::zero_refint(), // msgs_sent:Integer
td::make_refint(now), // unixtime:Integer
td::make_refint(account.block_lt), // block_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 (account.is_special) {
cp.gas_fees = td::RefInt256{true, 0};
cp.gas_fees = td::zero_refint();
} else {
cp.gas_fees = cfg.compute_gas_price(cp.gas_used);
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.remaining_balance = balance;
ap.end_lt = end_lt;
ap.total_fwd_fees = td::RefInt256{true, 0};
ap.total_action_fees = td::RefInt256{true, 0};
ap.total_fwd_fees = td::zero_refint();
ap.total_action_fees = td::zero_refint();
ap.reserved_balance.set_zero();
int n = 0;
@ -1429,7 +1432,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
info.ihr_disabled = true;
info.bounce = false;
info.bounced = false;
fwd_fee = ihr_fee = td::RefInt256{true, 0};
fwd_fee = ihr_fee = td::zero_refint();
} else {
// int_msg_info$0 constructor
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
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) {
ihr_fee = td::RefInt256{true, fees_c.second};
ihr_fee = td::make_refint(fees_c.second);
}
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 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
block::CurrencyCollection req;
@ -1757,10 +1760,10 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
balance -= msg_balance;
CHECK(balance.is_valid());
// 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 -= 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
info.created_lt = end_lt++;
info.created_at = now;

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include <vector>
@ -169,6 +169,8 @@ class PropagateConstSpan {
size_t size_{0};
};
struct Normalize {};
template <class Tr = BigIntInfo>
class AnyIntView {
public:
@ -308,6 +310,10 @@ class BigIntG {
explicit BigIntG(word_t x) : n(1) {
digits[0] = x;
}
BigIntG(Normalize, word_t x) : n(1) {
digits[0] = x;
normalize_bool();
}
BigIntG(const BigIntG& x) : n(x.n) {
std::memcpy(digits, x.digits, n * sizeof(word_t));
///std::cout << "(BiCC " << (const void*)&x << "->" << (void*)this << ")";
@ -2515,6 +2521,11 @@ extern template class AnyIntView<BigIntInfo>;
extern template class BigIntG<257, BigIntInfo>;
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 {
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;
}
RefInt256 operator+(RefInt256 x, const BigInt256& y) {
(x.write() += y).normalize();
return x;
}
RefInt256 operator-(RefInt256 x, RefInt256 y) {
(x.write() -= *y).normalize();
return x;
@ -48,6 +53,11 @@ RefInt256 operator-(RefInt256 x, long long y) {
return x;
}
RefInt256 operator-(RefInt256 x, const BigInt256& y) {
(x.write() -= y).normalize();
return x;
}
RefInt256 operator-(RefInt256 x) {
x.write().negate().normalize();
return x;
@ -69,6 +79,12 @@ RefInt256 operator*(RefInt256 x, long long y) {
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 quot{true};
x.write().mod_div(*y, quot.write());
@ -142,6 +158,11 @@ RefInt256& operator+=(RefInt256& x, long long y) {
return x;
}
RefInt256& operator+=(RefInt256& x, const BigInt256& y) {
(x.write() += y).normalize();
return x;
}
RefInt256& operator-=(RefInt256& x, RefInt256 y) {
(x.write() -= *y).normalize();
return x;
@ -152,6 +173,11 @@ RefInt256& operator-=(RefInt256& x, long long y) {
return x;
}
RefInt256& operator-=(RefInt256& x, const BigInt256& y) {
(x.write() -= y).normalize();
return x;
}
RefInt256& operator*=(RefInt256& x, RefInt256 y) {
RefInt256 z{true, 0};
z.write().add_mul(*x, *y).normalize();
@ -163,6 +189,12 @@ RefInt256& operator*=(RefInt256& x, long long y) {
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 quot{true};
x.write().mod_div(*y, quot.write());
@ -214,9 +246,13 @@ int sgn(RefInt256 x) {
}
RefInt256 make_refint(long long x) {
auto xx = td::RefInt256{true, x};
xx.unique_write().normalize();
return xx;
return td::RefInt256{true, td::Normalize(), x};
}
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) {

View file

@ -33,10 +33,13 @@ typedef Ref<CntInt256> RefInt256;
extern RefInt256 operator+(RefInt256 x, RefInt256 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, long long y);
extern RefInt256 operator-(RefInt256 x, const BigInt256& y);
extern RefInt256 operator*(RefInt256 x, RefInt256 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 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, long long y);
extern RefInt256& operator+=(RefInt256& x, const BigInt256& y);
extern RefInt256& operator-=(RefInt256& x, RefInt256 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, 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);
@ -100,7 +106,14 @@ extern int cmp(RefInt256 x, RefInt256 y);
extern int cmp(RefInt256 x, long long y);
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 zero_refint();
extern RefInt256 bits_to_refint(td::ConstBitPtr bits, int n, bool sgnd = false);
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 y = 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);
auto q = td::RefInt256{true};
auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize();
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 y = 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);
auto q = td::RefInt256{true};
auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize();
auto r = td::RefInt256{true, tmp};
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) {
auto z = stack.pop_int();
auto y = 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);
td::BigIntG<257 * 2> q;
tmp.mod_div(*z, q, round_mode);
auto r = td::RefInt256{true, tmp};
stack.push_int(std::move(r));
stack.push_int(td::make_refint(tmp));
}
void interpret_negate(vm::Stack& stack) {
@ -253,21 +250,21 @@ void interpret_fits(vm::Stack& stack, bool sgnd) {
void interpret_pow2(vm::Stack& stack) {
int x = stack.pop_smallint_range(255);
auto r = td::RefInt256{true};
auto r = td::make_refint();
r.unique_write().set_pow2(x);
stack.push_int(r);
}
void interpret_neg_pow2(vm::Stack& stack) {
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();
stack.push_int(r);
}
void interpret_pow2_minus1(vm::Stack& stack) {
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();
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);
auto y = 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();
auto q = td::RefInt256{true, tmp};
stack.push_int(std::move(q));
stack.push_int(td::make_refint(tmp));
}
void interpret_lshift_div(vm::Stack& stack, int round_mode) {
int z = stack.pop_smallint_range(256);
auto y = stack.pop_int();
auto x = stack.pop_int();
td::BigIntG<257 * 2> tmp{*x};
typename td::BigInt256::DoubleInt tmp{*x};
tmp <<= z;
auto q = td::RefInt256{true};
auto q = td::make_refint();
tmp.mod_div(*y, q.unique_write(), round_mode);
q.unique_write().normalize();
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();
int len = (int)s.size();
int frac = -1, base, *frac_ptr = allow_frac ? &frac : nullptr;
num = td::RefInt256{true};
num = td::make_refint();
auto& x = num.unique_write();
if (len >= 4 && str[0] == '-' && str[1] == '0' && (str[2] == 'x' || str[2] == 'b')) {
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) {
return 1;
} else {
denom = td::RefInt256{true, 1};
denom = td::make_refint(1);
while (frac-- > 0) {
if (!denom.unique_write().mul_tiny(base).normalize_bool()) {
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) {
return set_const(td::RefInt256{true, value});
return set_const(td::make_refint(value));
}
void VarDescr::set_const(td::RefInt256 value) {
@ -169,7 +169,7 @@ void VarDescr::set_const(td::RefInt256 value) {
}
void VarDescr::set_const_nan() {
set_const(td::RefInt256{true});
set_const(td::make_refint());
}
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()) {
x.unused();
y.unused();
r.set_const(td::RefInt256{true, 0});
r.set_const(td::zero_refint());
return push_const(r.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;
std::string to_string() const;
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 {
return t == a_none && op.empty();
@ -975,6 +975,9 @@ struct AsmOp {
*y = b;
return is_xchg();
}
bool is_xchg_short() const {
return is_xchg() && (a <= 1 || b <= 1);
}
bool is_swap() const {
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_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_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_rotrev() const;
bool is_push_rot(int i) const;
@ -1407,8 +1414,10 @@ struct Optimizer {
bool is_2swap();
bool is_2over();
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_pop(int* i);
bool is_pop_pop(int* i, int* j);
bool is_nop();
bool is_push_rot(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)); });
}
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) {
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; });
}
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) {
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;
pb_ = pb;
// 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()) ||
(!(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_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) &&
((is_rot() && rewrite(AsmOp::Custom("ROT", 3, 3))) || (is_rotrev() && rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
(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) {
op_list = AsmOpCons::cons(std::make_unique<AsmOp>(std::move(*it)), std::move(op_list));
}
op_list = optimize_code(std::move(op_list), 1);
op_list = optimize_code(std::move(op_list), 1);
op_list = optimize_code(std::move(op_list), 0);
op_list = optimize_code(std::move(op_list), 0);
for (int mode : {1, 1, 1, 1, 0, 0, 0, 0}) {
op_list = optimize_code(std::move(op_list), mode);
}
ops.list_.clear();
while (op_list) {
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;
}
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 {
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
// 1 0 3 4 .. = pop2
// 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 {
if (!is_valid() || d != 1 || n > 1 || i < 0) {
return false;
@ -443,6 +495,38 @@ bool StackTransform::is_pop(int *i) const {
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_rev{1, 2, 0, 3};
@ -519,10 +603,9 @@ bool StackTransform::is_xchg2(int *i, int *j) const {
if (*i < 0 || *j < 0) {
return false;
}
if (n != 3) {
return is_xchg2(*i, *j);
}
if (*i) {
if (n == 2 && !*i) {
*j = *i; // XCHG s0,s1 = XCHG2 s0,s0
} else if (n == 3 && *i) {
// XCHG2 s(i),s(i) = XCHG s1,s(i) ; XCHG s0,s(i) : 0->1, 1->i
*j = *i;
} // 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
;; burden on any random specific user request
;; 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
nhk = (mkey >> 128);
nhk = (mkey >> (256 - 32));
if (nhk < n) {
slice sdomain = domain.begin_parse();
(_, slice val, _, found?) = dd.pfxdict_get?(1023, sdomain);
int key = mkey % (1 << (256 - 32));
(slice val, found?) = dd.udict_get?(256 - 32, key);
if (found?) {
int exp = val.preload_uint(32);
if (exp <= n) {
dd~pfxdict_delete?(1023, sdomain);
dd~udict_delete?(256 - 32, key);
}
}
gc~udict_delete?(32 + 128, mkey);
(mkey, domain, found?) = gc.udict_get_min_ref?(32 + 128);
nhk = (found? ? mkey >> 32 : 0xffffffff);
gc~udict_delete?(256, mkey);
(mkey, _, found?) = gc.udict_get_min?(256);
nhk = (found? ? mkey >> (256 - 32) : 0xffffffff);
max_steps -= 1;
} else {
found? = false;
}
}
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;
}
int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
if (cat_table.null?()) { ;; domain not found: return notf | 2^31
int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int strict) inline_ref {
if (strict & cat_table.null?()) { ;; domain not found: return notf | 2^31
return 0xee6f7466;
}
cell cown = cat_table.idict_get_ref(16, -2);
if (cown.null?()) { ;; no owner on this domain: no-2
return 0xee6f2d32;
if (owner_info.null?()) { ;; no owner on this domain: no-2 (in strict mode), ok else
return strict & 0xee6f2d32;
}
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
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);
}
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();
cell cat_table = null();
int exp = 0;
if (found?) {
exp = val~load_uint(32);
if (n > exp) { ;; expired domains behave as not registered
found? = false;
} else {
cat_table = val.preload_ref();
cell cat_table = cell owner_info = null();
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?) {
exp = val~load_uint(32);
if (exp >= n) { ;; entry not expired
cell cat_table = val~load_ref();
val.end_parse();
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;
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);
}
int err = check_owner(cat_table, owner_info, src_wc, src_addr, qt != 1);
if (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();
;; basic integrity check of (client-provided) dictionary
ifnot (data.dict_empty?()) { ;; 1000 gas!
(_, _, int minok) = idict_get_min?(data, 16);
(_, _, int maxok) = idict_get_max?(data, 16);
var (oinfo, ok) = data.idict_get_ref?(16, -2);
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);
}
} 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
return send_error(0xf365726f);
}
slice value = begin_cell().store_uint(exp + stdper, 32).store_ref(data).end_cell().begin_parse();
ifnot (domdata~pfxdict_set?(1023, domain, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
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());
domdata~udict_set_builder(256 - 32, key, begin_cell().store_uint(exp + stdper, 32).store_ref(data));
int gckeyO = (exp << (256 - 32)) + key;
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());
housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1);
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 (found?) { ;; domain already exists: return alre | 2^31
ifnot (cat_table.null?()) { ;; domain already exists: return alre | 2^31
return send_error(0xe16c7265);
}
int expires_at = n + stdper;
slice value = begin_cell().store_uint(expires_at, 32).store_ref(data).end_cell().begin_parse();
ifnot (domdata~pfxdict_set?(1023, domain, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
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
domdata~udict_set_builder(256 - 32, key, begin_cell().store_uint(expires_at, 32).store_ref(data));
int gckey = (expires_at << (256 - 32)) | key;
gc~udict_set_builder(256, gckey, begin_cell());
housekeeping(ctl, domdata, gc, prices, min(nhk, expires_at), lhk, 1);
return send_ok(price);
}
;; ##########################################################################
if (qt == 4) { ;; 0x75706464 -> updd | update domain (data)
slice value = begin_cell().store_uint(exp, 32).store_ref(data).end_cell().begin_parse();
ifnot (domdata~pfxdict_set?(1023, domain, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
domdata~udict_set_builder(256 - 32, key, begin_cell().store_uint(exp, 32).store_ref(data));
housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1);
return send_ok(price);
}
@ -412,54 +407,46 @@ 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 bits = domain.slice_bits();
(int bits, int refs) = domain.slice_bits_refs();
throw_if(30, refs | (bits & 7)); ;; malformed input (~ 8n-bit)
ifnot (bits) {
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);
if (domain_last_byte) {
domain = begin_cell().store_slice(domain) ;; append zero byte
.store_uint(0, 8).end_cell().begin_parse();
.store_uint(0, 8).end_cell().begin_parse();
bits += 8;
}
if (bits == 8) {
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();
int exp = 0;
int tail_bits = -1;
slice tail = domain;
do {
slice pfxname = begin_cell().store_uint(zeros, 7)
.store_slice(domain).end_cell().begin_parse();
(pfx, val, tail, int succ) = root.pfxdict_get?(1023, pfxname);
if (succ) {
int exp = val~load_uint(32);
if (nowtime > exp) { ;; entry expired, skip
succ = false;
repeat (bits >> 3) {
if (tail~load_uint(8) == 0) {
var key = (string_hash(domain.skip_last_bits(tail.slice_bits())) >> 32);
var (v, found?) = root.udict_get?(256 - 32, key);
if (found?) {
if (v.preload_uint(32) >= nowtime) { ;; entry not expired
val = v;
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;
return (exp, val.preload_ref(), tail.slice_empty?(), pfx);
if (val.null?()) {
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
@ -472,12 +459,12 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
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
;; 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)
if (category == 0) {
ifnot (category) {
return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0
} else {
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' )
{ dup first
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 `adnl eq? { drop second swap x{ad01} s, swap 256 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, 0 8 u, } {
dup `text eq? { drop second swap x{1eda01} s, over $len 8 u, swap $, } {
abort"unknown value type"
} 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 -> X car(tuple list) asm "CAR";
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) unpair([X, Y] t) asm "UNPAIR";
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 -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
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 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) 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";

View file

@ -146,6 +146,23 @@ td::Result<DnsInterface::EntryData> DnsInterface::EntryData::from_cellslice(vm::
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 {
TRY_RESULT(raw_entries, resolve_raw(name, category));
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 {
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 {
@ -388,9 +405,7 @@ td::Result<std::vector<ManualDns::RawEntry>> ManualDns::resolve_raw_or_throw(td:
return td::Status::Error("Name is too long");
}
auto encoded_name = encode_name(name);
auto res = run_get_method(
"dnsresolve",
{vm::load_cell_slice_ref(vm::CellBuilder().store_bytes(encoded_name).finalize()), td::make_refint(category)});
auto res = run_get_method(resolve_args_raw(encoded_name, category));
if (!res.success) {
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));
}
std::string ManualDns::encode_name(td::Slice name) {
std::string DnsInterface::encode_name(td::Slice name) {
std::string res;
while (!name.empty()) {
auto pos = name.rfind('.');
@ -487,7 +502,7 @@ std::string ManualDns::encode_name(td::Slice name) {
return res;
}
std::string ManualDns::decode_name(td::Slice name) {
std::string DnsInterface::decode_name(td::Slice name) {
std::string res;
if (!name.empty() && name.back() == 0) {
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::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 {
@ -223,9 +232,6 @@ class ManualDns : public ton::SmartContract, public DnsInterface {
td::Ed25519::PrivateKey& pk, td::Span<Action> actions,
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>
struct CombinedActions {
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 "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
#include "vm/cells.h"

View file

@ -24,19 +24,21 @@
#include "block/block-auto.h"
#include "vm/cellslice.h"
#include "vm/cp0.h"
#include "vm/memo.h"
#include "vm/vm.h"
#include "td/utils/crypto.h"
namespace ton {
namespace {
td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) {
td::Ref<vm::Stack> stack_ref{true};
td::RefInt256 acc_addr{true};
//CHECK(acc_addr.write().import_bits(account.addr.cbits(), 256));
vm::Stack& stack = stack_ref.write();
stack.push_int(td::RefInt256{true, 10000000000});
stack.push_int(td::RefInt256{true, 10000000000});
stack.push_int(td::make_refint(10000000000));
stack.push_int(td::make_refint(10000000000));
stack.push_cell(vm::CellBuilder().finalize());
stack.push_cellslice(std::move(body));
return stack_ref;
@ -90,6 +92,11 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
}
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.set_c7(std::move(c7));
vm.set_chksig_always_succeed(ignore_chksig);
@ -124,6 +131,21 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
}
} // 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() {
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;
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 = {});

View file

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

View file

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

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "tlbc-gen-cpp.h"
#include "td/utils/bits.h"
@ -94,6 +94,7 @@ void init_forbidden_cpp_idents() {
l.insert("get_size");
l.insert("pack");
l.insert("unpack");
l.insert("ops");
l.insert("cs");
l.insert("cb");
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);
ss << '.';
}
ss << (validating ? "validate_skip(cs, weak" : "skip(cs");
ss << (validating ? "validate_skip(ops, cs, weak" : "skip(cs");
output_negative_type_arguments(ss, expr);
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);
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)};
return;
}
@ -2074,7 +2075,7 @@ void CppTypeCode::generate_skip_field(const Constructor& constr, const Field& fi
output_cpp_expr(ss, expr, 100);
ss << '.';
}
ss << "validate_skip_ref(cs, weak)" << tail;
ss << "validate_skip_ref(ops, cs, weak)" << tail;
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) {
bool validate = options & 1;
bool ret_ext = options & 2;
os << "\nbool " << cpp_type_class_name << "::" << (validate ? "validate_" : "") << "skip(vm::CellSlice& cs"
<< (validate ? ", bool weak" : "");
os << "\nbool " << cpp_type_class_name
<< "::" << (validate ? "validate_skip(int* ops, vm::CellSlice& cs, bool weak" : "skip(vm::CellSlice& cs");
if (ret_ext) {
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);
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);
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);
ss << '.';
}
ss << (validating ? "validate_" : "") << "fetch_" << (cvt == ct_enum ? "enum_" : "") << "to(cs, "
<< (validating ? "weak, " : "") << field_vars.at(i) << ")" << tail;
ss << (validating ? "validate_" : "") << "fetch_" << (cvt == ct_enum ? "enum_" : "")
<< (validating ? "to(ops, cs, weak, " : "to(cs, ") << field_vars.at(i) << ")" << tail;
field_var_set[i] = true;
actions += Action{std::move(ss)};
return;
@ -2540,7 +2541,7 @@ void CppTypeCode::generate_unpack_field(const CppTypeCode::ConsField& fi, const
output_cpp_expr(ss, expr, 100);
ss << '.';
}
ss << "validate_ref(" << field_vars.at(i) << "))" << tail;
ss << "validate_ref(ops, " << field_vars.at(i) << "))" << tail;
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 return " << (options & 1 ? "validate_" : "") << "unpack";
if (!(options & 8)) {
os << "(cs, data";
os << "(";
if (options & 1) {
os << "ops, ";
}
os << "cs, data";
} else {
os << "_" << cons_enum_name.at(rec.cons_idx) << "(cs";
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);
ss << '.';
}
ss << "validate_ref(" << field_vars.at(i) << "))" << tail;
ss << "validate_ref(ops, " << field_vars.at(i) << "))" << tail;
actions += Action{ss.str()};
}
@ -3093,7 +3098,7 @@ void CppTypeCode::generate_header(std::ostream& os, int options) {
if (ret_params) {
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) {
os << ";\n";
} else if (sz) {
@ -3102,7 +3107,7 @@ void CppTypeCode::generate_header(std::ostream& os, int options) {
os << " {\n return true;\n }\n";
}
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";
}
if (type.is_simple_enum) {

View file

@ -114,38 +114,43 @@ bool TupleT::skip(vm::CellSlice& cs) const {
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;
for (; i > 0; --i) {
if (!X.validate_skip(cs, weak)) {
if (!X.validate_skip(ops, cs, weak)) {
break;
}
}
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;
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 {
pp.open("raw@");
pp << *this << ' ';
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");
}
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 {
pp.open("raw@");
pp << *this << ' ';
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 {
@ -217,7 +222,7 @@ PrettyPrinter::~PrettyPrinter() {
}
bool PrettyPrinter::fail(std::string msg) {
os << "<FATAL: " << msg << ">";
os << "<FATAL: " << msg << ">" << std::endl;
failed = true;
return false;
}

View file

@ -30,6 +30,7 @@ struct PrettyPrinter;
class TLB {
public:
enum { default_validate_max_cells = 1024 };
virtual ~TLB() = default;
virtual int get_size(const vm::CellSlice& cs) const {
return -1;
@ -37,14 +38,26 @@ class TLB {
virtual bool skip(vm::CellSlice& cs) const {
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));
}
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);
}
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 {
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 {
return cs.fetch_subslice_ext(get_size(cs));
@ -52,67 +65,71 @@ class TLB {
Ref<vm::CellSlice> prefetch(const vm::CellSlice& cs) const {
return cs.prefetch_subslice_ext(get_size(cs));
}
virtual Ref<vm::CellSlice> validate_fetch(vm::CellSlice& cs, bool weak = false) const {
return validate(cs, weak) ? cs.fetch_subslice_ext(get_size(cs)) : Ref<vm::CellSlice>{};
virtual Ref<vm::CellSlice> validate_fetch(int* ops, vm::CellSlice& cs, bool weak = false) const {
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 {
return validate(cs, weak) ? cs.prefetch_subslice_ext(get_size(cs)) : Ref<vm::CellSlice>{};
virtual Ref<vm::CellSlice> validate_prefetch(int* ops, const vm::CellSlice& cs, bool weak = false) const {
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 {
return (res = fetch(cs)).not_null();
}
bool validate_fetch_to(vm::CellSlice& cs, Ref<vm::CellSlice>& res, bool weak = false) const {
return (res = validate_fetch(cs, weak)).not_null();
bool validate_fetch_to(int* ops, vm::CellSlice& cs, Ref<vm::CellSlice>& res, bool weak = false) const {
return (res = validate_fetch(ops, cs, weak)).not_null();
}
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));
}
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()) {
return false;
}
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 {
return cs.only_ext(get_size(cs));
}
virtual bool validate_extract(vm::CellSlice& cs, bool weak = false) const {
return validate(cs, weak) && extract(cs);
virtual bool validate_extract(int* ops, vm::CellSlice& cs, bool weak = false) const {
return validate(ops, cs, weak) && extract(cs);
}
int get_size_by_skip(const vm::CellSlice& cs) const {
vm::CellSlice copy{cs};
return skip(copy) ? copy.subtract_base_ext(cs) : -1;
}
virtual bool validate_skip(vm::CellSlice& cs, bool weak = false) const {
return validate(cs, weak) && skip(cs);
virtual bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const {
return validate(ops, cs, weak) && skip(cs);
}
bool validate_skip_exact(vm::CellSlice& cs, bool weak = false) const {
return validate_skip(cs, weak) && cs.empty_ext();
bool validate_skip_upto(int ops, vm::CellSlice& cs, bool weak = false) const {
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};
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};
return validate_skip_exact(copy, weak);
return validate_skip_exact(ops, copy, weak);
}
bool extract_by_skip(vm::CellSlice& cs) const {
vm::CellSlice copy{cs};
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};
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};
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};
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 {
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 {
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 {
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 {
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 {
return validate_ref(cs.fetch_ref(), weak);
bool validate_skip_ref(int* ops, vm::CellSlice& cs, bool weak = false) const {
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 {
return false;
@ -214,6 +246,9 @@ class TLB {
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(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(const vm::CellSlice& cs, int indent = 0) const;
std::string as_string(Ref<vm::CellSlice> cs_ref, int indent = 0) const {
@ -225,7 +260,7 @@ class TLB {
}
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) {
@ -234,29 +269,29 @@ static inline std::ostream& operator<<(std::ostream& os, const TLB& type) {
struct TLB_Complex : TLB {
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 {
return get_size_by_skip(cs);
}
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
return validate_by_skip(cs, weak);
bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return validate_by_skip(ops, cs, weak);
}
bool validate_exact(const vm::CellSlice& cs, bool weak = false) const override {
return validate_by_skip_exact(cs, weak);
bool validate_exact(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return validate_by_skip_exact(ops, cs, weak);
}
bool extract(vm::CellSlice& cs) const override {
return extract_by_skip(cs);
}
bool validate_extract(vm::CellSlice& cs, bool weak = false) const override {
return validate_extract_by_skip(cs, weak);
bool validate_extract(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return validate_extract_by_skip(ops, cs, weak);
}
Ref<vm::CellSlice> validate_fetch(vm::CellSlice& cs, bool weak = false) const override {
return validate_fetch_by_skip(cs, weak);
Ref<vm::CellSlice> validate_fetch(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return validate_fetch_by_skip(ops, cs, weak);
}
Ref<vm::CellSlice> validate_prefetch(const vm::CellSlice& cs, bool weak = false) const override {
return validate_prefetch_by_skip(cs, weak);
Ref<vm::CellSlice> validate_prefetch(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return validate_prefetch_by_skip(ops, cs, weak);
}
td::RefInt256 as_integer(const vm::CellSlice& cs) const override {
vm::CellSlice copy{cs};
@ -615,23 +650,23 @@ struct FwdT final : TLB {
bool skip(vm::CellSlice& cs) const override {
return X.skip(cs);
}
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
return X.validate(cs, weak);
bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return X.validate(ops, cs, weak);
}
Ref<vm::CellSlice> validate_fetch(vm::CellSlice& cs, bool weak = false) const override {
return X.validate_fetch(cs, weak);
Ref<vm::CellSlice> validate_fetch(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return X.validate_fetch(ops, cs, weak);
}
Ref<vm::CellSlice> validate_prefetch(const vm::CellSlice& cs, bool weak = false) const override {
return X.validate_prefetch(cs, weak);
Ref<vm::CellSlice> validate_prefetch(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return X.validate_prefetch(ops, cs, weak);
}
bool extract(vm::CellSlice& cs) const override {
return X.extract(cs);
}
bool validate_extract(vm::CellSlice& cs, bool weak = false) const override {
return X.validate_extract(cs, weak);
bool validate_extract(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return X.validate_extract(ops, cs, weak);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return X.validate_skip(cs, weak);
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return X.validate_skip(ops, cs, weak);
}
bool skip_copy(vm::CellBuilder& cb, vm::CellSlice& cs) const override {
return X.skip_copy(cb, cs);
@ -726,10 +761,10 @@ struct NatLess final : TLB {
int get_size(const vm::CellSlice& cs) const override {
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;
}
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;
}
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 {
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;
}
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;
}
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) {
}
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 {
return 0;
}
@ -786,8 +821,8 @@ struct CondT final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override {
return !n || X.skip(cs);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return !n || (n > 0 && X.validate_skip(cs, weak));
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return !n || (n > 0 && X.validate_skip(ops, cs, weak));
}
int get_tag(const vm::CellSlice& cs) const override {
return 0;
@ -808,8 +843,8 @@ struct Cond final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override {
return !n || field_type.skip(cs);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return !n || (n > 0 && field_type.validate_skip(cs, weak));
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return !n || (n > 0 && field_type.validate_skip(ops, cs, weak));
}
int get_tag(const vm::CellSlice& cs) const override {
return 0;
@ -905,7 +940,7 @@ struct Maybe : TLB_Complex {
Maybe(Args... args) : field_type(args...) {
}
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 {
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>
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);
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) {
return cs.advance(1);
} else {
@ -979,11 +1014,11 @@ struct RefTo final : TLB {
int get_size(const vm::CellSlice& cs) const override {
return 0x10000;
}
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
return cs.size_refs() ? ref_type.validate_ref(cs.prefetch_ref(), weak) : false;
bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
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 {
return ref_type.validate_skip_ref(cs, weak);
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return ref_type.validate_skip_ref(ops, cs, weak);
}
std::ostream& print_type(std::ostream& os) const override {
return os << '^' << ref_type;
@ -1000,11 +1035,11 @@ struct RefT final : TLB {
int get_size(const vm::CellSlice& cs) const override {
return 0x10000;
}
bool validate(const vm::CellSlice& cs, bool weak = false) const override {
return X.validate_ref(cs.prefetch_ref(), weak);
bool validate(int* ops, const vm::CellSlice& cs, bool weak = false) const override {
return X.validate_ref(ops, cs.prefetch_ref(), weak);
}
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override {
return X.validate_skip_ref(cs, weak);
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override {
return X.validate_skip_ref(ops, cs, weak);
}
std::ostream& print_type(std::ostream& os) const override {
return os << '^' << X;
@ -1021,9 +1056,10 @@ struct Either final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override {
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 {
return cs.have(1) ? (cs.fetch_ulong(1) ? right_type.validate_skip(cs, weak) : left_type.validate_skip(cs, weak))
: false;
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(ops, cs, weak) : left_type.validate_skip(ops, cs, weak))
: false;
}
int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(1);

View file

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

View file

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

View file

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

View file

@ -147,6 +147,15 @@ class StackEntry {
bool is_atom() const {
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 {
return tp == wanted;
}
@ -433,6 +442,12 @@ class Stack : public td::CntObject {
}
return *this;
}
std::vector<StackEntry> extract_contents() const & {
return stack;
}
std::vector<StackEntry> extract_contents() && {
return std::move(stack);
}
template <typename... Args>
const Stack& check_underflow(Args... args) const {
if (!at_least(args...)) {

View file

@ -260,7 +260,7 @@ int exec_rand_int(VmState* st) {
typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y);
tmp.rshift(256, -1).normalize();
stack.push_int(td::RefInt256{true, tmp});
stack.push_int(td::make_refint(tmp));
return 0;
}
@ -607,15 +607,15 @@ bool parse_maybe_anycast(CellSlice& cs, StackEntry& res) {
bool parse_message_addr(CellSlice& cs, std::vector<StackEntry>& res) {
res.clear();
switch ((unsigned)cs.fetch_ulong(2)) {
case 0: // addr_none$00 = MsgAddressExt;
res.emplace_back(td::RefInt256{true, 0}); // -> (0)
case 0: // addr_none$00 = MsgAddressExt;
res.emplace_back(td::zero_refint()); // -> (0)
return true;
case 1: { // addr_extern$01
unsigned len;
Ref<CellSlice> addr;
if (cs.fetch_uint_to(9, len) // len:(## 9)
&& 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));
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)
&& cs.fetch_int_to(8, workchain) // workchain_id:int8
&& 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(td::RefInt256{true, workchain});
res.emplace_back(td::make_refint(workchain));
res.emplace_back(std::move(addr));
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_int_to(32, workchain) // workchain_id:int32
&& 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(td::RefInt256{true, workchain});
res.emplace_back(td::make_refint(workchain));
res.emplace_back(std::move(addr));
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"
namespace vm {
@ -88,7 +106,7 @@ td::Result<vm::StackEntry> convert_stack_entry(td::Slice str) {
return vm::StackEntry{
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();
if (l >= 3 && str[0] == '0' && str[1] == 'x') {
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
#include "stack.hpp"

View file

@ -385,11 +385,13 @@ int VmState::step() {
if (code->size()) {
return dispatch->dispatch(this, code.write());
} 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()};
return jump(std::move(cont));
} else {
VM_LOG(this) << "execute implicit RET\n";
VM_LOG(this) << "execute implicit RET";
gas.consume_chk(implicit_ret_gas_price);
return ret();
}
}
@ -404,25 +406,27 @@ int VmState::run() {
// LOG(INFO) << "[BS] data cells: " << DataCell::get_total_data_cells();
try {
try {
res = step();
gas.check();
} catch (vm::CellBuilder::CellWriteError) {
throw VmError{Excno::cell_ov};
} catch (vm::CellBuilder::CellCreateError) {
throw VmError{Excno::cell_ov};
} catch (vm::CellSlice::CellReadError) {
throw VmError{Excno::cell_und};
}
} catch (const VmError& vme) {
VM_LOG(this) << "handling exception code " << vme.get_errno() << ": " << vme.get_msg();
try {
// LOG(INFO) << "[EX] data cells: " << DataCell::get_total_data_cells();
++steps;
res = throw_exception(vme.get_errno());
} catch (const VmError& vme2) {
VM_LOG(this) << "exception " << vme2.get_errno() << " while handling exception: " << vme.get_msg();
// LOG(INFO) << "[EXX] data cells: " << DataCell::get_total_data_cells();
return ~vme2.get_errno();
try {
res = step();
gas.check();
} catch (vm::CellBuilder::CellWriteError) {
throw VmError{Excno::cell_ov};
} catch (vm::CellBuilder::CellCreateError) {
throw VmError{Excno::cell_ov};
} catch (vm::CellSlice::CellReadError) {
throw VmError{Excno::cell_und};
}
} catch (const VmError& vme) {
VM_LOG(this) << "handling exception code " << vme.get_errno() << ": " << vme.get_msg();
try {
// LOG(INFO) << "[EX] data cells: " << DataCell::get_total_data_cells();
++steps;
res = throw_exception(vme.get_errno());
} catch (const VmError& vme2) {
VM_LOG(this) << "exception " << vme2.get_errno() << " while handling exception: " << vme.get_msg();
// LOG(INFO) << "[EXX] data cells: " << DataCell::get_total_data_cells();
return ~vme2.get_errno();
}
}
} catch (VmNoGas vmoog) {
++steps;

View file

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

View file

@ -49,8 +49,8 @@ class TonTest {
"workchain": -1,
"shard": -9223372036854775808,
"seqno": 0,
"root_hash": "VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=",
"file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo="
"root_hash": "F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk=",
"file_hash": "XplPz01CXAps5qeSWUtxcyBfdAo5zVb1N979KLSKD24="
}
}
}"""
@ -58,29 +58,27 @@ class TonTest {
fun createTestWallet() {
val client = ClientKotlin()
val dir = getContext().getExternalFilesDir(null).toString() + "/";
val words = getContext().getString(R.string.wallet_mnemonic_words).split(" ").toTypedArray();
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 walletAddress = client.send(TonApi.TestWalletGetAccountAddress(TonApi.TestWalletInitialAccountState(key.publicKey))) as TonApi.AccountAddress;
val testGiverState = client.send(TonApi.TestGiverGetAccountState()) as TonApi.TestGiverAccountState
val inputKey = TonApi.InputKeyRegular(key, "local password".toByteArray())
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)
}
val inputKey = TonApi.InputKey(key, "local password".toByteArray());
client.send(TonApi.TestWalletInit(inputKey)) as TonApi.Ok
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) {
val queryInfo2 = client.send(TonApi.CreateQuery(inputKey, walletAddress, 60, TonApi.ActionMsg(arrayOf(), true))) as TonApi.QueryInfo;
client.send(TonApi.QuerySend(queryInfo2.id)) as TonApi.Ok;
while ((client.send(TonApi.GetAccountState(walletAddress)) as TonApi.FullAccountState).accountState !is TonApi.WalletV3AccountState) {
delay(1000L)
}
}

View file

@ -53,29 +53,28 @@ public class TonTestJava {
return result[0];
}
}
String config = "{\n" +
" \"liteservers\": [\n" +
" {\n" +
" \"ip\": 1137658550,\n" +
" \"port\": 4924,\n" +
" \"id\": {\n" +
" \"@type\": \"pub.ed25519\",\n" +
" \"key\": \"peJTw/arlRfssgTuf9BMypJzqOi7SXEqSPSWiEw2U1M=\"\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"validator\": {\n" +
" \"@type\": \"validator.config.global\",\n" +
" \"zero_state\": {\n" +
" \"workchain\": -1,\n" +
" \"shard\": -9223372036854775808,\n" +
" \"seqno\": 0,\n" +
" \"root_hash\": \"VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=\",\n" +
" \"file_hash\": \"eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=\"\n" +
" }\n" +
" }\n" +
"}";
String config = "{\n"+
" \"liteservers\": [\n"+
" {\n"+
" \"ip\": 1137658550,\n"+
" \"port\": 4924,\n"+
" \"id\": {\n"+
" \"@type\": \"pub.ed25519\",\n"+
" \"key\": \"peJTw/arlRfssgTuf9BMypJzqOi7SXEqSPSWiEw2U1M=\"\n"+
" }\n"+
" }\n"+
" ],\n"+
" \"validator\": {\n"+
" \"@type\": \"validator.config.global\",\n"+
" \"zero_state\": {\n"+
" \"workchain\": -1,\n"+
" \"shard\": -9223372036854775808,\n"+
" \"seqno\": 0,\n"+
" \"root_hash\": \"F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk=\",\n"+
" \"file_hash\": \"XplPz01CXAps5qeSWUtxcyBfdAo5zVb1N979KLSKD24=\"\n"+
" }\n"+
" }\n"+
"}";
private void appendLog(String log) {
Log.w("XX", log);
@ -84,85 +83,45 @@ public class TonTestJava {
@Test
public void createTestWallet() {
appendLog("start...");
String dir = getContext().getExternalFilesDir(null) + "/";
String[] words = getContext().getString(R.string.wallet_mnemonic_words).split(" ");
JavaClient client = new JavaClient();
Object result = client.send(new TonApi.Init(new TonApi.Options(new TonApi.Config(config, "", false, false), new TonApi.KeyStoreTypeDirectory((dir)))));
if (!(result instanceof TonApi.OptionsInfo)) {
appendLog("failed to set config");
return;
}
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.InputKey inputKey = new TonApi.InputKeyRegular(key, "local password".getBytes());
TonApi.AccountAddress walletAddress = (TonApi.AccountAddress)client.send(new TonApi.GetAccountAddress(new TonApi.WalletV3InitialAccountState(key.publicKey, info.configInfo.defaultWalletId), 1));
JavaClient client = new JavaClient();
Object result = client.send(new TonApi.Init(new TonApi.Options(new TonApi.Config(config, "", false, false), getContext().getExternalFilesDir(null) + "/")));
if (!(result instanceof TonApi.Ok)) {
appendLog("failed to set config");
return;
}
appendLog("config set ok");
TonApi.Key key = (TonApi.Key) client.send(new TonApi.CreateNewKey("local password".getBytes(), "mnemonic password".getBytes(), "".getBytes()));
appendLog("got private key");
TonApi.AccountAddress walletAddress = (TonApi.AccountAddress) client.send(new TonApi.TestWalletGetAccountAddress(new TonApi.TestWalletInitialAccountState(key.publicKey)));
appendLog("got account address");
appendLog("sending grams...");
TonApi.TestGiverAccountState testGiverState = (TonApi.TestGiverAccountState) client.send(new TonApi.TestGiverGetAccountState());
result = client.send(new TonApi.TestGiverSendGrams(walletAddress, testGiverState.seqno, 6660000000L, "".getBytes()));
if (!(result instanceof TonApi.Ok)) {
appendLog("failed to send grams");
return;
}
appendLog("grams sent, getting balance");
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));
while (true) {
TonApi.GenericAccountStateUninited accountStateUninited = (TonApi.GenericAccountStateUninited) client.send(new TonApi.GenericGetAccountState(walletAddress));
if (accountStateUninited == null || accountStateUninited.accountState.balance <= 0L) {
try {
Thread.sleep(1000);
} catch (Throwable e) {
appendLog(e.toString());
}
} else {
appendLog(String.format("balance = %d", accountStateUninited.accountState.balance));
break;
appendLog("sending grams...");
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.QuerySend(queryInfo.id));
if (!(result instanceof TonApi.Ok)) {
appendLog("failed to send grams");
return;
}
appendLog("grams sent, getting balance");
while (true) {
TonApi.FullAccountState state = (TonApi.FullAccountState) client.send(new TonApi.GetAccountState(walletAddress));
if (state.balance <= 0L) {
try {
Thread.sleep(1000);
} catch (Throwable e) {
appendLog(e.toString());
}
} else {
appendLog(String.format("balance = %d", state.balance));
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>
<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>

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 "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
#include "crypto/block/block.h"

View file

@ -61,6 +61,7 @@
#include "ton/ton-shard.h"
#include "openssl/rand.hpp"
#include "crypto/vm/utils.h"
#include "crypto/common/util.h"
#if TD_DARWIN || TD_LINUX
#include <unistd.h>
@ -142,6 +143,8 @@ void TestNode::got_result() {
parse_line(std::move(data));
}
if (ex_mode_ && !running_queries_ && ex_queries_.size() == 0) {
std::cout.flush();
std::cerr.flush();
std::_Exit(0);
}
}
@ -179,6 +182,14 @@ bool TestNode::envelope_send_query(td::BufferSlice query, td::Promise<td::Buffer
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) {
for (const auto& id : known_blk_ids_) {
if (id == blkid) {
@ -570,8 +581,13 @@ bool TestNode::seekeoln() {
return eoln();
}
bool TestNode::parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr) {
return block::parse_std_account_addr(get_word(), wc, addr) || set_error("cannot parse account address");
bool TestNode::parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr, bool allow_none) {
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) {
@ -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");
}
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) {
if (error.is_ok()) {
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 "
"(StateInit) or just the code or data of specified account; <addr> is in "
"[<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"
"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 "
"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"
@ -872,21 +900,30 @@ bool TestNode::do_parse_line() {
(seekeoln()
? get_account_state(workchain, addr, mc_last_id_, 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;
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_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") {
return eoln() ? get_all_shards() : (parse_block_id_ext(blkid) && seekeoln() && get_all_shards(false, blkid));
} else if (word == "saveconfig") {
blkid = mc_last_id_;
std::string filename;
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") {
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") {
return parse_block_id_ext(blkid) && seekeoln() && get_block(blkid, false);
} 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());
}
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()) {
return set_error("must obtain last block information before making other queries");
}
@ -1039,31 +1086,33 @@ bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress a
return set_error("server connection not ready");
}
auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(workchain, addr);
int mode = (ext_mode ? 0x1f : 0);
if (!mode) {
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)),
true);
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";
return envelope_send_query(
std::move(b), [ Self = actor_id(this), workchain, addr, ref_blkid, method_name,
params = std::move(params) ](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) {
return;
}
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_accountState>(R.move_as_ok(), true);
if (F.is_error()) {
LOG(ERROR) << "cannot parse answer to liteServer.getAccountState";
} else {
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_),
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(params), td::BufferSlice(), td::BufferSlice(), td::BufferSlice(),
-0x10000);
}
});
return envelope_send_query(std::move(b), [
Self = actor_id(this), workchain, addr, ref_blkid, method_name, params = std::move(params),
promise = std::move(promise)
](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
return;
}
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_accountState>(R.move_as_ok(), true);
if (F.is_error()) {
LOG(ERROR) << "cannot parse answer to liteServer.getAccountState";
promise.set_error(td::Status::Error("cannot parse answer to liteServer.getAccountState"));
} else {
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_),
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(params), td::BufferSlice(), td::BufferSlice(), td::BufferSlice(),
-0x10000, std::move(promise));
}
});
} else {
td::int64 method_id = compute_method_id(method_name);
// set serialization limits
@ -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 "
<< params.size() << " parameters";
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 {
if (R.is_error()) {
promise.set_error(R.move_as_error());
return;
}
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_runMethodResult>(R.move_as_ok(), true);
if (F.is_error()) {
LOG(ERROR) << "cannot parse answer to liteServer.runSmcMethod";
promise.set_error(td::Status::Error("cannot parse answer to liteServer.runSmcMethod"));
} else {
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_),
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(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,
ton::LogicalTime lt, bool dump) {
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()) {
out << "account state is ";
std::ostringstream outp;
block::gen::t_Account.print_ref(outp, info.root);
vm::load_cell_slice(info.root).print_rec(outp);
block::gen::t_Account.print_ref(print_limit_, outp, info.root);
vm::load_cell_slice(info.root).print_rec(print_limit_, outp);
out << outp.str();
out << "last transaction lt = " << info.last_trans_lt << " hash = " << info.last_trans_hash.to_hex() << std::endl;
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,
ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method,
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()
<< " with respect to blocks " << 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));
if (r_info.is_error()) {
LOG(ERROR) << r_info.error().message();
promise.set_error(r_info.move_as_error());
return;
}
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()) {
LOG(ERROR) << "account state of " << workchain << ":" << addr.to_hex() << " is empty (cannot run method `"
<< method << "`)";
promise.set_error(td::Status::Error(PSLICE() << "account state of " << workchain << ":" << addr.to_hex()
<< " is empty (cannot run method `" << method << "`)"));
return;
}
if (false) {
// DEBUG (dump state)
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;
}
// 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);
val.dump(os);
// 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;
// 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;
}
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) &&
balance.validate_unpack(store.balance))) {
LOG(ERROR) << "error unpacking account state";
promise.set_error(td::Status::Error("error unpacking account state"));
return;
}
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) {
LOG(ERROR) << "VM terminated with error code " << exit_code;
out << "result: error " << exit_code << std::endl;
promise.set_error(td::Status::Error(PSLICE() << "VM terminated with non-zero exit code " << exit_code));
return;
}
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.flush();
promise.set_result(stack->extract_contents());
} catch (vm::VmVirtError& err) {
out << "virtualization error while parsing runSmcMethod result: " << err.get_msg();
} catch (vm::VmError& err) {
@ -1485,8 +1776,8 @@ void TestNode::got_one_transaction(ton::BlockIdExt req_blkid, ton::BlockIdExt bl
} else {
out << "transaction is ";
std::ostringstream outp;
block::gen::t_Transaction.print_ref(outp, root);
vm::load_cell_slice(root).print_rec(outp);
block::gen::t_Transaction.print_ref(print_limit_, outp, root, 0);
vm::load_cell_slice(root).print_rec(print_limit_, outp);
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");
if (dump) {
std::ostringstream outp;
block::gen::t_Transaction.print_ref(outp, info.transaction);
vm::load_cell_slice(info.transaction).print_rec(outp);
block::gen::t_Transaction.print_ref(print_limit_, outp, info.transaction);
vm::load_cell_slice(info.transaction).print_rec(print_limit_, outp);
out << outp.str();
}
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();
out << "shard configuration is ";
std::ostringstream outp;
block::gen::t_ShardHashes.print_ref(outp, root);
vm::load_cell_slice(root).print_rec(outp);
block::gen::t_ShardHashes.print_ref(print_limit_, outp, root);
vm::load_cell_slice(root).print_rec(print_limit_, outp);
out << outp.str();
block::ShardConfig sh_conf;
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();
}
bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string filename) {
std::vector<int> params;
if (mode >= 0 && !seekeoln()) {
bool TestNode::get_config_params(ton::BlockIdExt blkid, td::Promise<td::Unit> do_after, int mode, std::string filename,
std::vector<int> params) {
if (mode < 0) {
mode = 0x8000;
}
if (!(mode & 0x9000) && !seekeoln()) {
mode |= 0x1000;
while (!seekeoln()) {
int 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");
}
params.push_back(x);
}
}
if (!(ready_ && !client_.empty())) {
do_after.set_error(td::Status::Error("integer configuration parameter id expected"));
return set_error("server connection not ready");
}
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");
}
if (blkid == mc_last_id_) {
mode |= 0x2000;
}
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>(
0, ton::create_tl_lite_block_id(blkid), std::move(params_copy)),
true)
@ -1792,9 +2092,11 @@ bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string fi
true);
LOG(INFO) << "requesting " << params.size() << " configuration parameters with respect to masterchain block "
<< blkid.to_str();
return envelope_send_query(std::move(b), [ Self = actor_id(this), mode, filename, blkid,
params = std::move(params) ](td::Result<td::BufferSlice> R) mutable {
return envelope_send_query(std::move(b), [
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()) {
do_after.set_error(R.move_as_error());
return;
}
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();
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(params));
std::move(params), std::move(do_after));
}
});
}
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";
if (!blkid.is_masterchain_ext()) {
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;
}
auto config = res.move_as_ok();
if (mode < 0) {
if (mode & 0x8000) {
auto F = vm::std_boc_serialize(config->get_root_cell(), 2);
if (F.is_error()) {
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 {
std::ostringstream os;
if (i >= 0) {
block::gen::ConfigParam{i}.print_ref(os, value);
block::gen::ConfigParam{i}.print_ref(print_limit_, os, value);
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;
if (i == 4 && (mode & 0x2000)) {
register_config_param4(value);
}
}
}
} 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 << ") = ";
if (value.is_null()) {
out << "(null)\n";
} else {
std::ostringstream os;
if (i >= 0) {
block::gen::ConfigParam{i}.print_ref(os, value);
block::gen::ConfigParam{i}.print_ref(print_limit_, os, value);
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;
if (i == 4 && (mode & 0x2000)) {
register_config_param4(value);
}
}
return true;
});
@ -1888,6 +2197,25 @@ void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blki
} catch (vm::VmVirtError& err) {
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) {
@ -1981,8 +2309,8 @@ void TestNode::got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump)
auto out = td::TerminalIO::out();
out << "block contents is ";
std::ostringstream outp;
block::gen::t_Block.print_ref(outp, root);
vm::load_cell_slice(root).print_rec(outp);
block::gen::t_Block.print_ref(print_limit_, outp, root);
vm::load_cell_slice(root).print_rec(print_limit_, outp);
out << outp.str();
show_block_header(blkid, std::move(root), 0xffff);
} else {
@ -2037,8 +2365,8 @@ void TestNode::got_state(ton::BlockIdExt blkid, ton::RootHash root_hash, ton::Fi
auto out = td::TerminalIO::out();
out << "shard state contents is ";
std::ostringstream outp;
block::gen::t_ShardState.print_ref(outp, root);
vm::load_cell_slice(root).print_rec(outp);
block::gen::t_ShardState.print_ref(print_limit_, outp, root);
vm::load_cell_slice(root).print_rec(print_limit_, outp);
out << outp.str();
show_state_header(blkid, std::move(root), 0xffff);
} else {
@ -2173,7 +2501,7 @@ void TestNode::got_block_header(ton::BlockIdExt blkid, td::BufferSlice data, int
auto root = res.move_as_ok();
std::ostringstream outp;
vm::CellSlice cs{vm::NoVm(), root};
cs.print_rec(outp);
cs.print_rec(print_limit_, outp);
td::TerminalIO::out() << outp.str();
try {
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());
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) {
verbosity = td::to_integer<int>(arg);
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity);

View file

@ -50,6 +50,7 @@ class TestNode : public td::actor::Actor {
bool readline_enabled_ = true;
bool server_ok_ = false;
td::int32 liteserver_idx_ = -1;
int print_limit_ = 1024;
bool ready_ = false;
bool inited_ = false;
@ -66,6 +67,9 @@ class TestNode : public td::actor::Actor {
ton::BlockIdExt last_block_id_, last_state_id_;
td::BufferSlice last_block_data_, last_state_data_;
ton::StdSmcAddress dns_root_;
bool dns_root_queried_{false};
std::string line_;
const char *parse_ptr_, *parse_end_;
td::Status error_;
@ -118,16 +122,31 @@ class TestNode : public td::actor::Actor {
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,
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,
td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state,
ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method,
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 = {});
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,
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);
void got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump);
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(std::string err_msg);
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 bool parse_hash(const char* 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_lt(ton::LogicalTime& lt);
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_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;
@ -195,6 +216,7 @@ class TestNode : public td::actor::Actor {
bool register_blkid(const ton::BlockIdExt& blkid);
bool show_new_blkids(bool all = false);
bool complete_blkid(ton::BlockId partial_blkid, ton::BlockIdExt& complete_blkid) const;
td::Promise<td::Unit> trivial_promise();
public:
void conn_ready() {
@ -237,6 +259,11 @@ class TestNode : public td::actor::Actor {
fail_timeout_ = ts;
alarm_timestamp().relax(fail_timeout_);
}
void set_print_limit(int plimit) {
if (plimit >= 0) {
print_limit_ = plimit;
}
}
void add_cmd(td::BufferSlice data) {
ex_mode_ = true;
ex_queries_.push_back(std::move(data));

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement
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/HazardPointers.h"
@ -56,7 +56,10 @@ extern "C" {
#include "td/utils/Random.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/StealingQueue.h"
#include "td/utils/ThreadSafeCounter.h"
#include "td/utils/UInt.h"
#include "td/utils/VectorQueue.h"
#include <algorithm>
#include <array>
@ -796,10 +799,13 @@ class MpmcQueueBenchmark : public td::Benchmark {
std::vector<td::thread> m_threads(m_);
auto impl = std::make_unique<Impl>(n_ + m_ + 1);
size_t thread_id = 0;
td::ThreadSafeCounter counter;
CHECK(counter.sum() == 0);
for (auto &thread : m_threads) {
thread = td::thread([&, thread_id] {
while (true) {
size_t value = impl->pop(thread_id);
counter.add(-static_cast<td::int64>(value));
if (!value) {
break;
}
@ -811,6 +817,7 @@ class MpmcQueueBenchmark : public td::Benchmark {
thread = td::thread([&, thread_id] {
for (int i = 0; i < n / n_; i++) {
impl->push(static_cast<size_t>(i + 1), thread_id);
counter.add(i + 1);
}
});
thread_id++;
@ -818,7 +825,10 @@ class MpmcQueueBenchmark : public td::Benchmark {
for (auto &thread : n_threads) {
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);
}
for (auto &thread : m_threads) {
@ -832,6 +842,62 @@ class MpmcQueueBenchmark : public td::Benchmark {
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 {
public:
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>
class MoodyQueue {
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() {
return "moodycamel queue";
}
void push(Value v, size_t) {
q.enqueue(v);
void push(Value v, size_t tid) {
q.enqueue(p[tid], v);
}
Value pop(size_t) {
Value pop(size_t tid) {
Value res;
while (!q.try_dequeue(res)) {
while (!q.try_dequeue(c[tid], res)) {
}
//q.wait_dequeue(c[tid], res);
return res;
}
bool try_pop(Value &value, size_t) {
return q.try_dequeue(value);
bool try_pop(Value &value, size_t tid) {
return q.try_dequeue(c[tid], value);
}
private:
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
@ -1110,65 +1245,155 @@ class WaitQueue {
static std::string 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 res;
int yields = 0;
while (!impl.try_pop(res, thread_id)) {
yields = waiter.wait(yields, static_cast<uint32>(thread_id));
while (true) {
if (slots[thread_id].local_queue.try_pop(res)) {
break;
}
if (impl.try_pop(res, thread_id)) {
break;
}
waiter.wait(slots[thread_id].slot);
}
waiter.stop_wait(yields, static_cast<uint32>(thread_id));
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) {
impl.push(std::move(value), static_cast<uint32>(thread_id));
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;
};
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) {
bench(MpmcQueueBenchmark<WaitQueue<td::MpmcQueue<size_t>, td::MpmcWaiter, size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<td::MpmcQueue<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<MoodyQueue<size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<StealingWaitQueue<td::MpmcQueue<size_t>, td::MpmcEagerWaiter, size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<StealingWaitQueue<td::MpmcQueue<size_t>, td::MpmcSleepyWaiter, size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<WaitQueue<td::MpmcQueue<size_t>, td::MpmcEagerWaiter, size_t>>(n, m), 2);
bench(MpmcQueueBenchmark<WaitQueue<td::MpmcQueue<size_t>, td::MpmcSleepyWaiter, size_t>>(n, m), 2);
//bench(MpmcQueueBenchmark<td::MpmcQueue<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);
#if TG_LCR_QUEUE
bench(MpmcQueueBenchmark<CfQueue<LCRQueue<size_t>>>(n, m), 2);
#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 {
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};
};
//bench(MpmcQueueBenchmark<MpQueue>(n, m), 2);
#if TG_LCR_QUEUE
bench(MpmcQueueBenchmark2<CfQueue<LCRQueue<size_t>>>(n, k), 2);
#endif
}
class ChainedSpawn : public td::Benchmark {
public:
@ -1415,17 +1640,18 @@ class YieldMany : public td::Benchmark {
int main(int argc, char **argv) {
if (argc > 1) {
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<CfQueue<LCRQueue<size_t>>>(1, 40), 1 << 20);
} 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<FAAArrayQueue<size_t>>>(1, 1), 1 << 26);
}
return 0;
}
bench(YieldMany(false));
bench(YieldMany(true));
bench(SpawnMany(false));
@ -1436,6 +1662,22 @@ int main(int argc, char **argv) {
bench(ChainedSpawnInplace(true));
bench(ChainedSpawn(false));
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;
bench(ActorDummyQuery());
@ -1466,16 +1708,5 @@ int main(int argc, char **argv) {
bench(CalcHashSha256Benchmark<BlockSha256MpmcQueue<BoundedMpmcQueue<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;
}

View file

@ -269,17 +269,17 @@ static inline thread_id_t thread_id() {
namespace moodycamel {
namespace details {
#if defined(__GNUC__)
inline bool likely(bool x) {
static inline bool(likely)(bool x) {
return __builtin_expect((x), true);
}
inline bool unlikely(bool x) {
static inline bool(unlikely)(bool x) {
return __builtin_expect((x), false);
}
#else
inline bool likely(bool x) {
static inline bool(likely)(bool x) {
return x;
}
inline bool unlikely(bool x) {
static inline bool(unlikely)(bool x) {
return x;
}
#endif
@ -300,8 +300,8 @@ struct const_numeric_max {
: static_cast<T>(-1);
};
#if defined(__GNUC__) && !defined(__clang__)
typedef ::max_align_t std_max_align_t; // GCC forgot to add it to std:: for a while
#if defined(__GLIBCXX__)
typedef ::max_align_t std_max_align_t; // libstdc++ forgot to add it to std:: for a while
#else
typedef std::max_align_t std_max_align_t; // Others (e.g. MSVC) insist it can *only* be accessed via std::
#endif
@ -377,8 +377,8 @@ struct ConcurrentQueueDefaultTraits {
static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max<size_t>::value;
#ifndef MCDBGQ_USE_RELACY
// Memory allocation can be customized if needed.
// malloc should return nullptr on failure, and handle alignment like std::malloc.
// Memory allocation can be customized if needed.
// malloc should return nullptr on failure, and handle alignment like std::malloc.
#if defined(malloc) || defined(free)
// Gah, this is 2015, stop defining macros that break standard code already!
// Work around malloc/free being special macros:
@ -1140,7 +1140,7 @@ class ConcurrentQueue {
// 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
if (nonEmptyCount > 0) {
if (details::likely(best->dequeue(item))) {
if ((details::likely)(best->dequeue(item))) {
return true;
}
for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) {
@ -1335,7 +1335,10 @@ class ConcurrentQueue {
private:
friend struct ProducerToken;
friend struct ConsumerToken;
struct ExplicitProducer;
friend struct ExplicitProducer;
struct ImplicitProducer;
friend struct ImplicitProducer;
friend class ConcurrentQueueTests;
enum AllocationMode { CanAlloc, CannotAlloc };
@ -1380,7 +1383,7 @@ class ConcurrentQueue {
}
auto prodCount = producerCount.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.
// Figure out our local position
// 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;
inline void add(N* node) {
#if MCDBGQ_NOLOCKFREE_FREELIST
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
debug::DebugLock lock(mutex);
#endif
// 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() {
#if MCDBGQ_NOLOCKFREE_FREELIST
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
debug::DebugLock lock(mutex);
#endif
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 SHOULD_BE_ON_FREELIST = 0x80000000;
#if MCDBGQ_NOLOCKFREE_FREELIST
#ifdef MCDBGQ_NOLOCKFREE_FREELIST
debug::DebugMutex mutex;
#endif
};
@ -1548,7 +1551,7 @@ class ConcurrentQueue {
, freeListNext(nullptr)
, shouldBeOnFreeList(false)
, dynamicallyAllocated(true) {
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
owner = nullptr;
#endif
}
@ -1679,14 +1682,14 @@ class ConcurrentQueue {
std::atomic<bool> shouldBeOnFreeList;
bool dynamicallyAllocated; // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool'
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
void* owner;
#endif
};
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");
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
public:
struct MemStats;
@ -1756,7 +1759,7 @@ class ConcurrentQueue {
ConcurrentQueue* parent;
protected:
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
friend struct MemStats;
#endif
};
@ -1902,7 +1905,7 @@ class ConcurrentQueue {
if (newBlock == nullptr) {
return false;
}
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
newBlock->owner = this;
#endif
newBlock->ConcurrentQueue::Block::template reset_empty<explicit_context>();
@ -1916,7 +1919,7 @@ class ConcurrentQueue {
++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
// that case (without corrupting the queue):
MOODYCAMEL_TRY {
@ -1941,7 +1944,7 @@ class ConcurrentQueue {
blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, std::memory_order_release);
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);
return true;
}
@ -1985,13 +1988,14 @@ class ConcurrentQueue {
// 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
// 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
// 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
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!
// 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};
element = std::move(el);
element = std::move(el); // NOLINT
} else {
element = std::move(el);
el.~T();
element = std::move(el); // NOLINT
el.~T(); // NOLINT
block->ConcurrentQueue::Block::template set_empty<explicit_context>(index);
}
@ -2119,7 +2123,7 @@ class ConcurrentQueue {
return false;
}
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
newBlock->owner = this;
#endif
newBlock->ConcurrentQueue::Block::template set_all_empty<explicit_context>();
@ -2151,7 +2155,8 @@ class ConcurrentQueue {
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)
->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)) {
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) {
new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++);
}
@ -2186,9 +2192,10 @@ class ConcurrentQueue {
// 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
// be executed
new ((*this->tailBlock)[currentTailIndex]) T(
details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(
T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
new ((*this->tailBlock)[currentTailIndex])
T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(
T, decltype(*itemFirst),
new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
++currentTailIndex;
++itemFirst;
}
@ -2236,7 +2243,7 @@ class ConcurrentQueue {
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) {
blockIndex.load(std::memory_order_relaxed)
->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);
auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed);
assert(overcommit <= myDequeueCount);
;
tail = this->tailIndex.load(std::memory_order_acquire);
auto actualCount = static_cast<size_t>(tail - (myDequeueCount - overcommit));
@ -2418,7 +2425,7 @@ class ConcurrentQueue {
private:
#endif
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
friend struct MemStats;
#endif
};
@ -2500,7 +2507,7 @@ class ConcurrentQueue {
(MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) {
return false;
}
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex);
#endif
// 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);
return false;
}
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
newBlock->owner = this;
#endif
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
MOODYCAMEL_TRY {
new ((*newBlock)[currentTailIndex]) T(std::forward<U>(element));
@ -2539,7 +2546,7 @@ class ConcurrentQueue {
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);
return true;
}
@ -2562,9 +2569,8 @@ class ConcurrentQueue {
std::atomic_thread_fence(std::memory_order_acquire);
index_t myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed);
assert(overcommit <= myDequeueCount);
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);
// Determine which block the element is in
@ -2575,7 +2581,7 @@ class ConcurrentQueue {
auto& el = *((*block)[index]);
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
// is released is very sub-optimal, but it is, after all, purely debug code.
debug::DebugLock lock(producer->mutex);
@ -2595,14 +2601,14 @@ class ConcurrentQueue {
}
} guard = {block, index, entry, this->parent};
element = std::move(el);
element = std::move(el); // NOLINT
} else {
element = std::move(el);
el.~T();
element = std::move(el); // NOLINT
el.~T(); // NOLINT
if (block->ConcurrentQueue::Block::template set_empty<implicit_context>(index)) {
{
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex);
#endif
// 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));
index_t currentTailIndex = (startTailIndex - 1) & ~static_cast<index_t>(BLOCK_SIZE - 1);
if (blockBaseDiff > 0) {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex);
#endif
do {
@ -2679,7 +2685,7 @@ class ConcurrentQueue {
return false;
}
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
newBlock->owner = this;
#endif
newBlock->ConcurrentQueue::Block::template reset_empty<implicit_context>();
@ -2714,16 +2720,18 @@ class ConcurrentQueue {
if (details::circular_less_than<index_t>(newTailIndex, stopIndex)) {
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) {
new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++);
}
} else {
MOODYCAMEL_TRY {
while (currentTailIndex != stopIndex) {
new ((*this->tailBlock)[currentTailIndex]) T(
details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(
T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
new ((*this->tailBlock)[currentTailIndex])
T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(
T, decltype(*itemFirst),
new ((T*)nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst));
++currentTailIndex;
++itemFirst;
}
@ -2788,7 +2796,6 @@ class ConcurrentQueue {
std::atomic_thread_fence(std::memory_order_acquire);
auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed);
assert(overcommit <= myDequeueCount);
tail = this->tailIndex.load(std::memory_order_acquire);
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>(
blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex);
#endif
entry->value.store(nullptr, std::memory_order_relaxed);
@ -2865,7 +2872,7 @@ class ConcurrentQueue {
if (block->ConcurrentQueue::Block::template set_many_empty<implicit_context>(
blockStartIndex, static_cast<size_t>(endIndex - blockStartIndex))) {
{
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex);
#endif
// 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 {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
debug::DebugLock lock(mutex);
#endif
index &= ~static_cast<index_t>(BLOCK_SIZE - 1);
@ -3026,10 +3033,10 @@ class ConcurrentQueue {
private:
#endif
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX
mutable debug::DebugMutex mutex;
#endif
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
friend struct MemStats;
#endif
};
@ -3065,7 +3072,7 @@ class ConcurrentQueue {
}
inline void add_block_to_free_list(Block* block) {
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
block->owner = nullptr;
#endif
freeList.add(block);
@ -3103,7 +3110,7 @@ class ConcurrentQueue {
return nullptr;
}
#if MCDBGQ_TRACKMEM
#ifdef MCDBGQ_TRACKMEM
public:
struct MemStats {
size_t allocatedBlocks;
@ -3221,7 +3228,7 @@ class ConcurrentQueue {
}
ProducerBase* recycle_or_create_producer(bool isExplicit, bool& recycled) {
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
debug::DebugLock lock(implicitProdMutex);
#endif
// 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
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
debug::DebugLock lock(implicitProdMutex);
#endif
@ -3443,6 +3450,7 @@ class ConcurrentQueue {
// Insert!
auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed);
while (true) {
// NOLINTNEXTLINE(clang-analyzer-core.NullDereference)
if (newCount >= (mainHash->capacity >> 1) &&
!implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) {
// We've acquired the resize lock, try to allocate a bigger hash table.
@ -3538,8 +3546,8 @@ class ConcurrentQueue {
// Remove from thread exit listeners
details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener);
// Remove from hash
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
// Remove from hash
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
debug::DebugLock lock(implicitProdMutex);
#endif
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> globalExplicitConsumerOffset;
#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH
debug::DebugMutex implicitProdMutex;
#endif

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -41,6 +41,7 @@ class ActorInfo : private HeapNode, private ListNode {
}
~ActorInfo() {
VLOG(actor) << "Destroy actor [" << name_ << "]";
CHECK(!actor_);
}
bool is_alive() const {

View file

@ -14,13 +14,15 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/actor/core/CpuWorker.h"
#include "td/actor/core/ActorExecutor.h"
#include "td/actor/core/SchedulerContext.h"
#include "td/actor/core/Scheduler.h" // FIXME: afer LocalQueue is in a separate file
namespace td {
namespace actor {
namespace core {
@ -28,20 +30,64 @@ void CpuWorker::run() {
auto thread_id = get_thread_id();
auto &dispatcher = *SchedulerContext::get();
int yields = 0;
MpmcWaiter::Slot slot;
waiter_.init_slot(slot, thread_id);
while (true) {
SchedulerMessage message;
if (queue_.try_pop(message, thread_id)) {
if (try_pop(message, thread_id)) {
waiter_.stop_wait(slot);
if (!message) {
return;
}
ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue());
yields = waiter_.stop_wait(yields, thread_id);
} 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 actor
} // namespace td

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -22,19 +22,32 @@
#include "td/utils/MpmcQueue.h"
#include "td/utils/MpmcWaiter.h"
#include "td/utils/Span.h"
namespace td {
namespace actor {
namespace core {
template <class T>
struct LocalQueue;
class CpuWorker {
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();
private:
MpmcQueue<SchedulerMessage> &queue_;
MpmcQueue<SchedulerMessage::Raw *> &queue_;
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 actor

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/actor/core/Scheduler.h"
@ -24,6 +24,7 @@
namespace td {
namespace actor {
namespace core {
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_->active_scheduler_count++;
@ -31,17 +32,21 @@ Scheduler::Scheduler(std::shared_ptr<SchedulerGroupInfo> scheduler_group_info, S
info_->id = id;
if (cpu_threads_count != 0) {
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_local_queue = std::vector<LocalQueue<SchedulerMessage::Raw *>>(cpu_threads_count);
}
info_->io_queue = std::make_unique<MpscPollableQueue<SchedulerMessage>>();
info_->io_queue->init();
info_->cpu_workers.resize(cpu_threads_count);
td::uint8 cpu_worker_id = 0;
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();
io_worker_ = std::make_unique<IoWorker>(*info_->io_queue);
@ -62,8 +67,9 @@ Scheduler::~Scheduler() {
void Scheduler::start() {
for (size_t i = 0; i < cpu_threads_.size(); i++) {
cpu_threads_[i] = td::thread([this, i] {
this->run_in_context_impl(*this->info_->cpu_workers[i],
[this] { CpuWorker(*info_->cpu_queue, *info_->cpu_queue_waiter).run(); });
this->run_in_context_impl(*this->info_->cpu_workers[i], [this, i] {
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);
}
@ -121,9 +127,14 @@ void Scheduler::do_stop() {
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)
: 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 {
@ -138,7 +149,18 @@ void Scheduler::ContextImpl::add_to_queue(ActorInfoPtr actor_info_ptr, Scheduler
if (need_poll || !info.cpu_queue) {
info.io_queue->writer_put(std::move(actor_info_ptr));
} 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();
}
}
@ -254,13 +276,26 @@ void Scheduler::close_scheduler_group(SchedulerGroupInfo &group_info) {
}
// 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) {
auto &cpu_queue = *scheduler_info.cpu_queue;
while (true) {
SchedulerMessage message;
if (!cpu_queue.try_pop(message, get_thread_id())) {
SchedulerMessage::Raw *raw_message;
if (!cpu_queue.try_pop(raw_message, get_thread_id())) {
break;
}
SchedulerMessage(SchedulerMessage::acquire_t{}, raw_message);
// message's destructor is called
queues_are_empty = false;
}

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -38,9 +38,11 @@
#include "td/utils/List.h"
#include "td/utils/logging.h"
#include "td/utils/MpmcQueue.h"
#include "td/utils/StealingQueue.h"
#include "td/utils/MpmcWaiter.h"
#include "td/utils/MpscLinkQueue.h"
#include "td/utils/MpscPollableQueue.h"
#include "td/utils/optional.h"
#include "td/utils/port/Poll.h"
#include "td/utils/port/detail/Iocp.h"
#include "td/utils/port/thread.h"
@ -66,16 +68,52 @@ class IoWorker;
struct WorkerInfo {
enum class Type { Io, Cpu } type{Type::Io};
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;
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 {
SchedulerId id;
// 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::vector<LocalQueue<SchedulerMessage::Raw *>> cpu_local_queue;
//std::vector<td::StealingQueue<SchedulerMessage>> cpu_stealing_queue;
// only scheduler itself may read from io_queue_
std::unique_ptr<MpscPollableQueue<SchedulerMessage>> io_queue;
size_t cpu_threads_count{0};
@ -156,11 +194,12 @@ class Scheduler {
class ContextImpl : public SchedulerContext {
public:
ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, SchedulerGroupInfo *scheduler_group, Poll *poll,
KHeap<double> *heap);
ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id,
SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap);
SchedulerId get_scheduler_id() const override;
void add_to_queue(ActorInfoPtr actor_info_ptr, SchedulerId scheduler_id, bool need_poll) override;
ActorInfoCreator &get_actor_info_creator() override;
bool has_poll() override;
@ -181,6 +220,7 @@ class Scheduler {
ActorInfoCreator *creator_;
SchedulerId scheduler_id_;
CpuWorkerId cpu_worker_id_;
SchedulerGroupInfo *scheduler_group_;
Poll *poll_;
@ -193,8 +233,8 @@ class Scheduler {
td::detail::Iocp::Guard iocp_guard(&scheduler_group_info_->iocp);
#endif
bool is_io_worker = worker_info.type == WorkerInfo::Type::Io;
ContextImpl context(&worker_info.actor_info_creator, info_->id, scheduler_group_info_.get(),
is_io_worker ? &poll_ : nullptr, is_io_worker ? &heap_ : nullptr);
ContextImpl context(&worker_info.actor_info_creator, info_->id, worker_info.cpu_worker_id,
scheduler_group_info_.get(), is_io_worker ? &poll_ : nullptr, is_io_worker ? &heap_ : nullptr);
SchedulerContext::Guard guard(&context);
f();
}

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -26,8 +26,7 @@ namespace actor {
namespace core {
class SchedulerId {
public:
SchedulerId() : id_(-1) {
}
SchedulerId() = default;
explicit SchedulerId(uint8 id) : id_(id) {
}
bool is_valid() const {
@ -42,7 +41,27 @@ class SchedulerId {
}
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 actor

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "td/actor/core/ActorLocker.h"
#include "td/actor/actor.h"
@ -410,7 +410,7 @@ class Master : public Actor {
private:
uint32 l = 0;
uint32 r = 100000;
uint32 r = 1000;
core::ActorInfoPtr worker;
void start_up() override {
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};
scheduler.start();
scheduler.run_in_context([] {
global_cnt = 10;
for (int i = 0; i < 10; i++) {
global_cnt = 1000;
for (int i = 0; i < global_cnt; i++) {
detail::create_actor<Master>(ActorOptions().with_name("Master"));
}
});
@ -734,7 +734,7 @@ TEST(Actor2, actor_ping_pong) {
return;
}
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));
} else {
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_;
};
int N = td::Random::fast(2, 10);
int N = td::Random::fast(2, 100);
//N = 2;
std::vector<ActorOwn<PingPong>> actors;
for (int i = 0; i < N; i++) {

View file

@ -230,6 +230,7 @@ set(TDUTILS_SOURCE
td/utils/SpinLock.h
td/utils/StackAllocator.h
td/utils/Status.h
td/utils/StealingQueue.h
td/utils/Storer.h
td/utils/StorerBase.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/SharedObjectPool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/SharedSlice.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/StealingQueue.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/variant.cpp
PARENT_SCOPE
)

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -117,24 +117,28 @@ template <class T>
class OneValue<T *> {
public:
bool set_value(T *value) {
T *was = nullptr;
T *was = Empty();
return state_.compare_exchange_strong(was, value, std::memory_order_acq_rel);
}
bool get_value(T *&value) {
value = state_.exchange(Taken(), std::memory_order_acq_rel);
return value != nullptr;
return value != Empty();
}
void reset() {
state_ = nullptr;
state_ = Empty();
}
OneValue() {
}
private:
std::atomic<T *> state_{nullptr};
T *Taken() {
static T xxx;
return &xxx;
std::atomic<T *> state_{Empty()};
static T *Empty() {
static int64 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
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
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread.h"
#include <atomic>
#include <algorithm>
#include <condition_variable>
#include <mutex>
namespace td {
class MpmcWaiter {
class MpmcEagerWaiter {
public:
int wait(int yields, uint32 worker_id) {
if (yields < RoundsTillSleepy) {
struct Slot {
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();
return yields + 1;
} else if (yields == RoundsTillSleepy) {
slot.yields++;
return;
} else if (slot.yields == RoundsTillSleepy) {
auto state = state_.load(std::memory_order_relaxed);
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)) {
td::this_thread::yield();
return yields + 1;
slot.yields++;
return;
}
if (state == State::awake()) {
return 0;
slot.yields = 0;
return;
}
}
td::this_thread::yield();
return 0;
} else if (yields < RoundsTillAsleep) {
slot.yields = 0;
return;
} else if (slot.yields < RoundsTillAsleep) {
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();
return yields + 1;
slot.yields++;
return;
}
return 0;
slot.yields = 0;
return;
} else {
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_);
if (state_.compare_exchange_strong(state, State::asleep(), std::memory_order_acq_rel)) {
condition_variable_.wait(lock);
}
}
return 0;
slot.yields = 0;
return;
}
}
int stop_wait(int yields, uint32 worker_id) {
if (yields > RoundsTillSleepy) {
void stop_wait(Slot &slot) {
if (slot.yields > RoundsTillSleepy) {
notify_cold();
}
return 0;
slot.yields = 0;
return;
}
void close() {
}
void notify() {
@ -102,8 +125,8 @@ class MpmcWaiter {
return (state >> 1) == (worker + 1);
}
};
//enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 };
enum { RoundsTillSleepy = 1, RoundsTillAsleep = 2 };
enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 };
// enum { RoundsTillSleepy = 1, RoundsTillAsleep = 2 };
std::atomic<uint32> state_{State::awake()};
std::mutex mutex_;
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

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -49,9 +49,7 @@ class AtomicRefCnt {
};
template <class DataT, class DeleterT>
class SharedPtrRaw
: public DeleterT
, private MpscLinkQueueImpl::Node {
class SharedPtrRaw : public DeleterT, private MpscLinkQueueImpl::Node {
public:
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 {
public:
using Raw = detail::SharedPtrRaw<T, DeleterT>;
struct acquire_t {};
SharedPtr() = default;
~SharedPtr() {
if (!raw_) {
@ -112,6 +111,8 @@ class SharedPtr {
raw_->inc();
}
}
SharedPtr(acquire_t, Raw *raw) : raw_(raw) {
}
SharedPtr(const SharedPtr &other) : SharedPtr(other.raw_) {
}
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
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

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "TsFileLog.h"
@ -24,8 +24,10 @@ namespace td {
namespace detail {
class TsFileLog : public LogInterface {
public:
Status init(string path) {
Status init(string path, td::int64 rotate_threshold, bool redirect_stderr) {
path_ = std::move(path);
rotate_threshold_ = rotate_threshold;
redirect_stderr_ = redirect_stderr;
for (int i = 0; i < (int)logs_.size(); i++) {
logs_[i].id = i;
}
@ -54,6 +56,8 @@ class TsFileLog : public LogInterface {
int id;
};
static constexpr int MAX_THREAD_ID = 128;
td::int64 rotate_threshold_;
bool redirect_stderr_;
std::string path_;
std::array<Info, MAX_THREAD_ID> logs_;
@ -70,7 +74,7 @@ class TsFileLog : public LogInterface {
}
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;
return Status::OK();
}
@ -92,9 +96,9 @@ class TsFileLog : public LogInterface {
};
} // 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>();
TRY_STATUS(res->init(path));
TRY_STATUS(res->init(path, rotate_threshold, redirect_stderr));
return std::move(res);
}
} // namespace td

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -22,7 +22,10 @@
namespace td {
class TsFileLog {
static constexpr int64 DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
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

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"
#if TD_LINUX || TD_ANDROID
#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
#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"
#if TD_LINUX
#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
#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
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/port/sleep.h"
@ -25,21 +25,22 @@
#include <atomic>
#if !TD_THREAD_UNSUPPORTED
TEST(MpmcWaiter, stress_one_one) {
template <class W>
void test_waiter_stress_one_one() {
td::Stage run;
td::Stage check;
std::vector<td::thread> threads;
std::atomic<size_t> value{0};
size_t write_cnt = 10;
td::unique_ptr<td::MpmcWaiter> waiter;
td::unique_ptr<W> waiter;
size_t threads_n = 2;
for (size_t i = 0; i < threads_n; i++) {
threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
for (td::uint64 round = 1; round < 100000; round++) {
if (id == 0) {
value = 0;
waiter = td::make_unique<td::MpmcWaiter>();
waiter = td::make_unique<W>();
write_cnt = td::Random::fast(1, 10);
}
run.wait(round * threads_n);
@ -49,17 +50,19 @@ TEST(MpmcWaiter, stress_one_one) {
waiter->notify();
}
} else {
int yields = 0;
typename W::Slot slot;
waiter->init_slot(slot, id);
for (size_t i = 1; i <= write_cnt; i++) {
while (true) {
auto x = value.load(std::memory_order_relaxed);
if (x >= i) {
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);
}
@ -69,7 +72,15 @@ TEST(MpmcWaiter, stress_one_one) {
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 check;
@ -81,7 +92,7 @@ TEST(MpmcWaiter, stress) {
size_t end_pos;
size_t write_cnt;
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++) {
threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
for (td::uint64 round = 1; round < 1000; round++) {
@ -92,7 +103,7 @@ TEST(MpmcWaiter, stress) {
end_pos = write_n * write_cnt;
write_pos = 0;
read_pos = 0;
waiter = td::make_unique<td::MpmcWaiter>();
waiter = td::make_unique<W>();
}
run.wait(round * threads_n);
if (id <= write_n) {
@ -104,21 +115,26 @@ TEST(MpmcWaiter, stress) {
waiter->notify();
}
} else if (id > 10 && id - 10 <= read_n) {
int yields = 0;
typename W::Slot slot;
waiter->init_slot(slot, id);
while (true) {
auto x = read_pos.load(std::memory_order_relaxed);
if (x == end_pos) {
waiter->stop_wait(slot);
break;
}
if (x == write_pos.load(std::memory_order_relaxed)) {
yields = waiter->wait(yields, id);
waiter->wait(slot);
continue;
}
yields = waiter->stop_wait(yields, id);
waiter->stop_wait(slot);
read_pos.compare_exchange_strong(x, x + 1, std::memory_order_relaxed);
}
}
check.wait(round * threads_n);
if (id == 0) {
waiter->close();
}
}
}));
}
@ -126,4 +142,10 @@ TEST(MpmcWaiter, stress) {
thread.join();
}
}
TEST(MpmcEagerWaiter, stress_multi) {
test_waiter_stress<td::MpmcEagerWaiter>();
}
TEST(MpmcSleepyWaiter, stress_multi) {
test_waiter_stress<td::MpmcSleepyWaiter>();
}
#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
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/FileLog.h"
@ -80,7 +80,8 @@ void bench_log(std::string name, int threads_n, F &&f) {
};
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, [] {
class FileLog : public td::LogInterface {
public:

View file

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

View file

@ -32,7 +32,7 @@ class TerminalIOOutputter {
TerminalIOOutputter(bool is_err)
: buffer_(new char[BUFFER_SIZE])
, 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;
@ -56,6 +56,7 @@ class TerminalIOOutputter {
bool is_error() const {
return sb_->is_error();
}
void flush();
~TerminalIOOutputter();
private:
@ -80,7 +81,7 @@ class TerminalIO : public actor::Actor {
static void output(td::Slice slice);
static void output_stderr(std::string line);
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() {
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;
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.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.dataText text:string = msg.Data;
msg.dataEncryptedText text:string = msg.Data;
msg.dataRaw body:bytes = 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;
//
@ -111,7 +116,7 @@ dns.resolved entries:vector<dns.entry> = dns.Resolved;
//
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;
//actionMultisig actions:vector<multisig.order> = Action;
@ -205,11 +210,14 @@ sync = ton.BlockIdExt;
// revision = 0 -- use default revision
// 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;
guessAccountRevision initial_account_state:InitialAccountState = AccountRevisionList;
getAccountState account_address:accountAddress = FullAccountState;
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.forget id:int53 = Ok;
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/filesystem.h"
#include "td/utils/optional.h"
#include "td/utils/overloaded.h"
#include "td/utils/port/path.h"
#include "td/utils/PathView.h"
#include "td/utils/tests.h"
@ -70,7 +71,7 @@ TEST(Tonlib, Text) {
auto cs = vm::load_cell_slice(cb.finalize());
auto cs2 = cs;
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();
ASSERT_EQ(str, got_str);
}
@ -506,12 +507,32 @@ TEST(Tonlib, KeysApi) {
sync_send(client, make_object<tonlib_api::deleteKey>(
make_object<tonlib_api::key>(new_imported_key->public_key_, new_imported_key->secret_.copy())))
.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(),
std::move(exported_raw_key)))
.move_as_ok();
CHECK(raw_imported_key->public_key_ == key->public_key_);
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) {

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;
tonlib_api::object_ptr<tonlib_api::msg_Data> data;
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 {
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);
}
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,
bool fast = false) {
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;
auto r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast);
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;
r_query_id = create_send_grams_query(client, wallet, address, amount, encrypt, message, fast);
} else {
@ -363,7 +370,7 @@ td::Status transfer_grams(Client& client, const Wallet& wallet, std::string addr
const auto& txn = tr->transactions_[0];
CHECK(txn->in_msg_->body_hash_ == query_info.body_hash);
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_;
auto fee_difference = fees.first.sum() - 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_);
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 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) {
using namespace ton::tonlib_api;
auto address = dns.get_address();
while (true) {
auto resolved =
sync_send(client, make_object<::ton::tonlib_api::dns_resolve>(std::move(address), name, 1, 0)).move_as_ok();
CHECK(resolved->entries_.size() == 1);
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";
break;
}
auto resolved =
sync_send(client, make_object<::ton::tonlib_api::dns_resolve>(std::move(address), name, 1, 4)).move_as_ok();
CHECK(resolved->entries_.size() == 1);
LOG(INFO) << to_string(resolved);
LOG(INFO) << "OK";
}
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()))));
auto init_A = create_update_dns_query(client, A, std::move(actions)).move_as_ok();
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();
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();
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 "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
#include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h"

View file

@ -63,6 +63,24 @@ struct GetAccountState {
td::optional<ton::BlockIdExt> block_id;
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 {
KeyStorage::InputKey input_key;
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_);
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 {
td::int64 balance = -1;
@ -373,7 +401,6 @@ class AccountState {
if (o_revision) {
wallet_type_ = WalletType::WalletV3;
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_)});
return wallet_type_;
}
@ -381,7 +408,6 @@ class AccountState {
if (o_revision) {
wallet_type_ = WalletType::HighloadWalletV2;
wallet_revision_ = o_revision.value();
LOG(ERROR) << "!!!" << wallet_revision_;
set_new_state(
{ton::HighloadWalletV2::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id_)});
return wallet_type_;
@ -390,7 +416,6 @@ class AccountState {
if (o_revision) {
wallet_type_ = WalletType::ManualDns;
wallet_revision_ = o_revision.value();
LOG(ERROR) << "!!!" << wallet_revision_;
auto dns = ton::ManualDns::create(key, wallet_id_, wallet_revision_);
set_new_state(dns->get_state());
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 {
public:
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.info = std::move(info);
auto cell = res.info.root;
std::ostringstream outp;
block::gen::t_Account.print_ref(outp, cell);
//std::ostringstream outp;
//block::gen::t_Account.print_ref(outp, cell);
//LOG(INFO) << outp.str();
if (cell.is_null()) {
return res;
@ -1693,6 +1831,50 @@ struct ToRawTransactions {
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);
if (tag < 0) {
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(ihr_fee, to_balance(msg_info.ihr_fee));
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,
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: {
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");
}
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),
"", false);
std::move(data));
}
case block::gen::CommonMsgInfo::ext_out_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");
}
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 {
block::StdAddress destination;
td::int64 amount;
bool is_encrypted{false};
bool should_encrypt;
std::string message;
td::Ref<vm::Cell> body;
};
bool allow_send_to_uninited_{false};
std::vector<Action> actions_;
@ -2064,27 +2212,36 @@ class GenericCreateSendGrams : public TonlibQueryActor {
res.amount = message.amount_;
auto status =
downcast_call2<td::Status>(*message.data_, td::overloaded(
[&](tonlib_api::msg_dataText& text) {
// Use this limit as a preventive check
if (text.text_.size() > ton::Wallet::max_message_size) {
return TonlibError::MessageTooLong();
}
res.message = text.text_;
res.should_encrypt = false;
[&](tonlib_api::msg_dataRaw& text) {
TRY_RESULT(body, vm::std_boc_deserialize(text.body_));
res.body = std::move(body);
return td::Status::OK();
},
[&](tonlib_api::msg_dataEncryptedText& text) {
// Use this limit as a preventive check
if (text.text_.size() > ton::Wallet::max_message_size) {
return TonlibError::MessageTooLong();
}
[&](tonlib_api::msg_dataText& text) {
res.message = text.text_;
res.should_encrypt = false;
res.is_encrypted = false;
return td::Status::OK();
},
[&](tonlib_api::msg_dataDecryptedText& text) {
res.message = text.text_;
if (!has_private_key_) {
return TonlibError::EmptyField("input_key");
}
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();
}));
// Use this limit as a preventive check
if (res.message.size() > ton::Wallet::max_message_size) {
return TonlibError::MessageTooLong();
}
TRY_STATUS(std::move(status));
return res;
}
@ -2301,7 +2458,9 @@ class GenericCreateSendGrams : public TonlibQueryActor {
if (action.amount == source_->get_balance()) {
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";
if (!private_key_) {
return TonlibError::EmptyField("private_key");
@ -2311,7 +2470,8 @@ class GenericCreateSendGrams : public TonlibQueryActor {
}
auto wallet = destination->get_wallet();
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,
SimpleEncryptionV2::encrypt_data(action.message, public_key, private_key_.value()),
TonlibError::Internal());
@ -2319,7 +2479,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
gift.is_encrypted = true;
} else {
gift.message = action.message;
gift.is_encrypted = false;
gift.is_encrypted = action.is_encrypted;
}
i++;
gifts.push_back(gift);
@ -2349,7 +2509,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
if (source_->get_wallet_type() == AccountState::Giver) {
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());
@ -2387,6 +2547,40 @@ td::Status TonlibClient::do_request(tonlib_api::createQuery& request,
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::Promise<object_ptr<tonlib_api::query_info>>&& promise) {
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,
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) {
if (smc->get_wallet_type() != AccountState::WalletType::ManualDns) {
return promise.set_error(TonlibError::AccountTypeUnexpected("ManualDns"));
}
block_id = smc->get_block_id();
auto dns = ton::ManualDns::create(smc->get_smc_state());
block_id = dns_finish_data.block_id;
// TODO: check if the smartcontract supports Dns interface
// TODO: should we use some DnsInterface instead of ManualDns?
auto dns = ton::ManualDns::create(dns_finish_data.smc_state);
TRY_RESULT_PROMISE(promise, entries, dns->resolve(name, category));
if (entries.size() == 1 && entries[0].category == -1 && entries[0].name != name && ttl > 0 &&
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;
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;
@ -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::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise) {
auto block_id_copy = 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,
std::move(block_id)));
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)},
new_promise.wrap([](auto&& account_state) {
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,
@ -3015,6 +3242,14 @@ td::Status TonlibClient::do_request(int_api::GetAccountState request,
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) {
TRY_RESULT(pk, key_storage_.load_private_key(std::move(request.input_key)));
promise.set_value(std::move(pk));

View file

@ -42,6 +42,9 @@ struct GetAccountState;
struct GetPrivateKey;
struct GetDnsResolver;
struct SendMessage;
struct RemoteRunSmcMethod;
struct RemoteRunSmcMethodReturnType;
inline std::string to_string(const int_api::SendMessage&) {
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::msg_decrypt& request, td::Promise<object_ptr<tonlib_api::msg_dataArray>>&& promise);
td::int64 next_smc_id_{0};
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);
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);
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,
td::unique_ptr<AccountState> smc,
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
DnsFinishData dns_finish_data, 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::GetPrivateKey request, td::Promise<KeyStorage::PrivateKey>&&);
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(const tonlib_api::liteServer_getInfo& request,

View file

@ -345,10 +345,14 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out()
<< "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() << "transfer[f] <from_key_id> <to_key_id> <amount> - transfer <amount> of grams from "
"<from_key_id> to <to_key_id>.\n"
<< "\t<from_key_id> could also be 'giver'\n"
<< "\t<to_key_id> could also be 'giver' or smartcontract address\n";
td::TerminalIO::out() << "transfer[f][F][e][k][c] <from_key_id> (<to_key_id> <value> <message>|<file_name>) - "
"make transfer from <from_key_id>\n"
<< "\t 'f' modifier - allow send to uninited account\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") {
generate_key();
} else if (cmd == "exit" || cmd == "quit") {
@ -445,15 +449,6 @@ class TonlibCli : public td::actor::Actor {
promise.set_value(td::Unit());
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) {
td::TerminalIO::out() << "Redirect resolver\n";
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));
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) {
@ -531,7 +531,7 @@ class TonlibCli : public td::actor::Actor {
: nullptr;
send_query(tonlib_api::make_object<tonlib_api::createQuery>(std::move(key), std::move(address.address), 60,
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) {
@ -1389,13 +1389,21 @@ class TonlibCli : public td::actor::Actor {
} else {
sb << " From " << t->in_msg_->source_;
}
if (!t->in_msg_->message_.empty()) {
sb << " ";
if (t->in_msg_->is_message_encrypted_) {
sb << "e";
auto print_msg_data = [](td::StringBuilder& sb,
tonlib_api::object_ptr<tonlib_api::msg_Data>& msg_data) {
if (!msg_data) {
return;
}
sb << "msg{" << t->in_msg_->message_ << "}";
}
sb << " ";
downcast_call(*msg_data,
td::overloaded([&](tonlib_api::msg_dataRaw& raw) { sb << "<unknown message>"; },
[&](tonlib_api::msg_dataText& raw) { sb << "{" << raw.text_ << "}"; },
[&](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_) {
sb << "\n\t";
if (ot->destination_.empty()) {
@ -1404,13 +1412,7 @@ class TonlibCli : public td::actor::Actor {
sb << " To " << ot->destination_;
}
sb << " " << Grams{td::uint64(ot->value_)};
if (!ot->message_.empty()) {
sb << " ";
if (ot->is_message_encrypted_) {
sb << "e";
}
sb << "msg{" << ot->message_ << "}";
}
print_msg_data(sb, ot->msg_data_);
}
sb << "\n";
}
@ -1423,6 +1425,8 @@ class TonlibCli : public td::actor::Actor {
bool from_file = false;
bool force = false;
bool use_encryption = false;
bool use_fake_key = false;
bool estimate_fees = false;
if (cmd != "init") {
td::ConstParser cmd_parser(cmd);
cmd_parser.advance(td::Slice("transfer").size());
@ -1435,6 +1439,10 @@ class TonlibCli : public td::actor::Actor {
force = true;
} else if (c == 'e') {
use_encryption = true;
} else if (c == 'k') {
use_fake_key = true;
} else if (c == 'c') {
estimate_fees = true;
} else {
cmd_promise.set_error(td::Status::Error(PSLICE() << "Unknown suffix '" << c << "'"));
return;
@ -1464,7 +1472,7 @@ class TonlibCli : public td::actor::Actor {
tonlib_api::object_ptr<tonlib_api::msg_Data> data;
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 {
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
using tonlib_api::make_object;
auto key = !from_address.secret.empty()
? make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(from_address.public_key, from_address.secret.copy()),
td::SecureString(password))
: nullptr;
tonlib_api::object_ptr<tonlib_api::InputKey> key =
!from_address.secret.empty()
? make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(from_address.public_key, from_address.secret.copy()),
td::SecureString(password))
: nullptr;
if (use_fake_key) {
key = make_object<tonlib_api::inputKeyFake>();
}
bool allow_send_to_uninited = force;
send_query(make_object<tonlib_api::createQuery>(
std::move(key), std::move(from_address.address), 60,
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) {
send_query(tonlib_api::make_object<tonlib_api::query_send>(r_info.ok()->id_), cmd_promise.wrap([](auto&& info) {
td::TerminalIO::out() << "Transfer sent!\n";
return td::Unit();
}));
void transfer2(bool estimate_fees, td::Result<tonlib_api::object_ptr<tonlib_api::query_info>> r_info,
td::Promise<td::Unit> cmd_promise) {
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();
}));
} 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) {

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "apply-block.hpp"
#include "adnl/utils.hpp"
@ -79,8 +79,8 @@ void ApplyBlock::got_block_handle(BlockHandle handle) {
}
if (handle_->is_applied()) {
auto P =
td::PromiseCreator::lambda([SelfId = actor_id(this), seqno = handle_->id().id.seqno](td::Result<BlockIdExt> R) {
auto P = td::PromiseCreator::lambda(
[ SelfId = actor_id(this), seqno = handle_->id().id.seqno ](td::Result<BlockIdExt> R) {
R.ensure();
auto h = R.move_as_ok();
if (h.id.seqno < seqno) {
@ -119,14 +119,15 @@ void ApplyBlock::got_block_handle(BlockHandle handle) {
td::actor::send_closure(manager_, &ValidatorManager::set_block_data, handle_, block_, std::move(P));
} else {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle = handle_](td::Result<td::Ref<BlockData>> R) {
CHECK(handle->received());
if (R.is_error()) {
td::actor::send_closure(SelfId, &ApplyBlock::abort_query, R.move_as_error());
} else {
td::actor::send_closure(SelfId, &ApplyBlock::written_block_data);
}
});
auto P =
td::PromiseCreator::lambda([ SelfId = actor_id(this), handle = handle_ ](td::Result<td::Ref<BlockData>> R) {
CHECK(handle->received());
if (R.is_error()) {
td::actor::send_closure(SelfId, &ApplyBlock::abort_query, R.move_as_error());
} else {
td::actor::send_closure(SelfId, &ApplyBlock::written_block_data);
}
});
td::actor::send_closure(manager_, &ValidatorManager::wait_block_data, handle_, apply_block_priority(), timeout_,
std::move(P));

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 "td/actor/MultiPromise.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
#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
#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 "auto/tl/ton_api.hpp"
#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
#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
#include "validator/validator.h"

View file

@ -14,7 +14,7 @@
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-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "collator-impl.h"
#include "vm/boc.h"
@ -589,9 +589,9 @@ void Collator::got_neighbor_out_queue(int i, td::Result<Ref<MessageQueue>> res)
return;
}
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
CHECK(block::gen::t_OutMsgQueueInfo.validate_ref(outq_descr->root_cell()));
CHECK(block::tlb::t_OutMsgQueueInfo.validate_ref(outq_descr->root_cell()));
// comment the next two lines in the future when the output queues become huge
// CHECK(block::gen::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell()));
// CHECK(block::tlb::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell()));
// unpack ProcessedUpto
LOG(DEBUG) << "unpacking ProcessedUpto of neighbor " << descr.blk_.to_str();
if (verbosity >= 2) {
@ -1506,7 +1506,7 @@ bool Collator::fetch_config_params() {
// fetch block_grams_created
auto cell = config_->get_config_param(14);
if (cell.is_null()) {
basechain_create_fee_ = masterchain_create_fee_ = td::RefInt256{true, 0};
basechain_create_fee_ = masterchain_create_fee_ = td::zero_refint();
} else {
block::gen::BlockCreateFees::Record 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);
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);
}
@ -1853,13 +1853,13 @@ bool Collator::combine_account_transactions() {
block::gen::t_AccountBlock.print_ref(std::cerr, cell);
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);
csr->print_rec(std::cerr);
return fatal_error(std::string{"new AccountBlock for "} + z.first.to_hex() +
" 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);
csr->print_rec(std::cerr);
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_);
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");
}
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");
}
auto shard_accounts = account_dict->get_root();
@ -1937,10 +1937,10 @@ bool Collator::combine_account_transactions() {
}
if (verify >= 2) {
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");
}
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");
}
}
@ -3012,7 +3012,7 @@ bool Collator::create_mc_state_extra() {
std::cerr << "updated shard configuration to ";
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");
}
// 4. check extension flags
@ -3110,7 +3110,7 @@ bool Collator::create_mc_state_extra() {
state_extra.r1.block_create_stats = cs;
if (verify >= 2) {
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);
block::gen::t_BlockCreateStats.print(std::cerr, *cs);
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) {
LOG(INFO) << "verifying new McStateExtra";
CHECK(block::gen::t_McStateExtra.validate_ref(mc_state_extra_));
CHECK(block::tlb::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(1000000, mc_state_extra_));
}
LOG(INFO) << "McStateExtra created";
return true;
@ -3467,8 +3467,8 @@ bool Collator::create_shard_state() {
}
if (verify >= 2) {
LOG(INFO) << "verifying new ShardState";
CHECK(block::gen::t_ShardState.validate_ref(state_root));
CHECK(block::tlb::t_ShardState.validate_ref(state_root));
CHECK(block::gen::t_ShardState.validate_ref(1000000, state_root));
CHECK(block::tlb::t_ShardState.validate_ref(1000000, state_root));
}
LOG(INFO) << "creating Merkle update for the ShardState";
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) {
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");
}
}
@ -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");
}
}
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");
}
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");
}
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");
}
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");
}
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");
}
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
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 "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)) {
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");
}
if (top_shard_descr_dict_) {
@ -776,7 +776,7 @@ bool ValidateQuery::fetch_config_params() {
// fetch block_grams_created
auto cell = config_->get_config_param(14);
if (cell.is_null()) {
basechain_create_fee_ = masterchain_create_fee_ = td::RefInt256{true, 0};
basechain_create_fee_ = masterchain_create_fee_ = td::zero_refint();
} else {
block::gen::BlockCreateFees::Record 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));
// TODO: comment the next two lines in the future when the output queues become huge
// (do this carefully)
CHECK(block::gen::t_OutMsgQueueInfo.validate_ref(outq_descr->root_cell()));
CHECK(block::tlb::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(1000000, outq_descr->root_cell()));
// unpack ProcessedUpto
LOG(DEBUG) << "unpacking ProcessedUpto of neighbor " << descr.blk_.to_str();
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));
// run some hand-written checks from block::tlb::
// (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");
}
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");
}
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");
}
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");
}
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) +
" 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) +
" 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) +
" 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");
}
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");
}
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) {
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");
}
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");
}
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());
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
<< " in the new previous blocks dictionary failed to pass automated validity checks "
"form KeyMaxLt + KeyExtBlkRef");
@ -5039,7 +5040,7 @@ bool ValidateQuery::check_mc_state_extra() {
<< " while the block header claims is_key_block=" << is_key_block_);
}
// 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(
"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();
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");
}
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 "validator/db/fileref.hpp"
#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
#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
#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 "td/utils/port/path.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
#include "td/utils/int_types.h"