From e27fb1e09c7332d4eea0eb4f89ff0b7275257c10 Mon Sep 17 00:00:00 2001 From: ton Date: Fri, 28 Feb 2020 14:28:47 +0400 Subject: [PATCH] updated vm (breaking compatibility) - updated vm - new actor scheduler - updated tonlib - updated DNS smartcontract --- CMakeLists.txt | 2 +- common/int-to-string.hpp | 18 + crypto/block/block-parse.cpp | 534 +++++++++--------- crypto/block/block-parse.h | 140 ++--- crypto/block/block.cpp | 22 +- crypto/block/block.h | 8 +- crypto/block/create-state.cpp | 4 +- crypto/block/dump-block.cpp | 30 +- crypto/block/mc-config.cpp | 4 +- crypto/block/transaction.cpp | 49 +- crypto/common/bigint.hpp | 13 +- crypto/common/refint.cpp | 42 +- crypto/common/refint.h | 13 + crypto/fift/words.cpp | 36 +- crypto/func/abscode.cpp | 4 +- crypto/func/builtins.cpp | 2 +- crypto/func/func.h | 11 +- crypto/func/optimize.cpp | 23 +- crypto/func/stack-transform.cpp | 91 ++- crypto/smartcont/auto-dns.fif | 115 ++++ crypto/smartcont/dns-auto-code.fc | 183 +++--- crypto/smartcont/manual-dns-manage.fif | 4 +- crypto/smartcont/stdlib.fc | 9 +- crypto/smc-envelope/ManualDns.cpp | 27 +- crypto/smc-envelope/ManualDns.h | 12 +- crypto/smc-envelope/MultisigWallet.cpp | 18 + crypto/smc-envelope/MultisigWallet.h | 18 + crypto/smc-envelope/SmartContract.cpp | 26 +- crypto/smc-envelope/SmartContract.h | 8 + crypto/smc-envelope/SmartContractCode.cpp | 12 +- crypto/smc-envelope/WalletInterface.h | 10 + crypto/tl/tlbc-gen-cpp.cpp | 33 +- crypto/tl/tlblib.cpp | 21 +- crypto/tl/tlblib.hpp | 190 ++++--- crypto/vm/arithops.cpp | 20 +- crypto/vm/cells/CellSlice.cpp | 12 +- crypto/vm/stack.cpp | 6 +- crypto/vm/stack.hpp | 15 + crypto/vm/tonops.cpp | 16 +- crypto/vm/utils.cpp | 20 +- crypto/vm/utils.h | 18 + crypto/vm/vm.cpp | 46 +- crypto/vm/vm.h | 2 + .../java/drinkless/org/ton/TonTest.kt | 34 +- .../java/drinkless/org/ton/TonTestJava.java | 157 ++--- .../test/ton/src/main/res/values/strings.xml | 2 + lite-client/lite-client-common.cpp | 18 + lite-client/lite-client-common.h | 18 + lite-client/lite-client.cpp | 453 +++++++++++++-- lite-client/lite-client.h | 35 +- tdactor/benchmark/benchmark.cpp | 351 ++++++++++-- .../benchmark/third_party/MoodyCamelQueue.h | 136 ++--- tdactor/td/actor/core/ActorInfo.h | 3 +- tdactor/td/actor/core/CpuWorker.cpp | 56 +- tdactor/td/actor/core/CpuWorker.h | 19 +- tdactor/td/actor/core/Scheduler.cpp | 57 +- tdactor/td/actor/core/Scheduler.h | 54 +- tdactor/td/actor/core/SchedulerId.h | 27 +- tdactor/test/actors_core.cpp | 12 +- tdutils/CMakeLists.txt | 2 + tdutils/td/utils/MpmcQueue.h | 20 +- tdutils/td/utils/MpmcWaiter.h | 269 ++++++++- tdutils/td/utils/SharedObjectPool.h | 9 +- tdutils/td/utils/StealingQueue.h | 124 ++++ tdutils/td/utils/StringBuilder.h | 2 +- tdutils/td/utils/TsFileLog.cpp | 14 +- tdutils/td/utils/TsFileLog.h | 7 +- tdutils/td/utils/port/rlimit.cpp | 18 + tdutils/td/utils/port/rlimit.h | 18 + tdutils/td/utils/port/user.cpp | 18 + tdutils/td/utils/port/user.h | 18 + tdutils/test/MpmcWaiter.cpp | 48 +- tdutils/test/StealingQueue.cpp | 153 +++++ tdutils/test/log.cpp | 5 +- terminal/terminal.cpp | 32 +- terminal/terminal.h | 5 +- tl/generate/scheme/tonlib_api.tl | 16 +- tl/generate/scheme/tonlib_api.tlo | Bin 20684 -> 21096 bytes tonlib/test/offline.cpp | 23 +- tonlib/test/online.cpp | 37 +- tonlib/tonlib/KeyValue.cpp | 18 + tonlib/tonlib/KeyValue.h | 18 + tonlib/tonlib/TonlibClient.cpp | 381 ++++++++++--- tonlib/tonlib/TonlibClient.h | 14 +- tonlib/tonlib/tonlib-cli.cpp | 101 ++-- validator/apply-block.cpp | 23 +- validator/db/archive-mover.cpp | 18 + validator/db/archive-mover.hpp | 18 + validator/db/archive-slice.hpp | 18 + validator/db/fileref.cpp | 18 + validator/db/package.hpp | 18 + validator/full-node-shard-queries.hpp | 18 + validator/impl/collator.cpp | 42 +- validator/impl/external-message.cpp | 4 +- validator/impl/validate-query.cpp | 35 +- validator/import-db-slice.cpp | 18 + validator/import-db-slice.hpp | 18 + validator/interfaces/liteserver.h | 18 + validator/net/download-archive-slice.cpp | 18 + validator/stats-merger.h | 18 + 100 files changed, 3692 insertions(+), 1299 deletions(-) create mode 100644 crypto/smartcont/auto-dns.fif create mode 100644 tdutils/td/utils/StealingQueue.h create mode 100644 tdutils/test/StealingQueue.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cbb858e1..a005e915 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/common/int-to-string.hpp b/common/int-to-string.hpp index 414342cd..88aecc65 100644 --- a/common/int-to-string.hpp +++ b/common/int-to-string.hpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "td/utils/int_types.h" diff --git a/crypto/block/block-parse.cpp b/crypto/block/block-parse.cpp index ae1df343..a0322366 100644 --- a/crypto/block/block-parse.cpp +++ b/crypto/block/block-parse.cpp @@ -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 . - 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 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 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{5}.validate_skip(cs, weak) // split_depth:(Maybe (## 5)) - && Maybe{}.validate_skip(cs, weak) // special:(Maybe TickTock) - && Maybe{}.validate_skip(cs, weak) // code:(Maybe ^Cell) - && Maybe{}.validate_skip(cs, weak) // data:(Maybe ^Cell) - && Maybe{}.validate_skip(cs, weak); // library:(Maybe ^Cell) +bool StateInit::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const { + return Maybe{5}.validate_skip(ops, cs, weak) // split_depth:(Maybe (## 5)) + && Maybe{}.validate_skip(ops, cs, weak) // special:(Maybe TickTock) + && Maybe{}.validate_skip(ops, cs, weak) // code:(Maybe ^Cell) + && Maybe{}.validate_skip(ops, cs, weak) // data:(Maybe ^Cell) + && Maybe{}.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{5}.validate_skip(cs) && cs.fetch_bool_to(have_tt) && (!have_tt || cs.fetch_uint_to(2, ticktock)); + return Maybe{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>> init_type; static const Either 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 ref) const { const Message t_Message; const RefTo 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 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 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{3}.validate_skip(cs, weak) // gas_credit:(Maybe (VarUInteger 3)) - && cs.advance(8 + 32) // mode:int8 exit_code:int32 - && Maybe{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{3}.validate_skip(ops, cs, weak) // gas_credit:(Maybe (VarUInteger 3)) + && cs.advance(8 + 32) // mode:int8 exit_code:int32 + && Maybe{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{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{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{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) - && Maybe{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase) - && t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase - && Maybe>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase) - && cs.advance(1) // aborted:Bool - && Maybe{}.validate_skip(cs, weak) // bounce:(Maybe TrBouncePhase) - && cs.advance(1); // destroyed:Bool + return cs.advance(4 + 1) // trans_ord$0000 credit_first:Bool + && Maybe{}.validate_skip(ops, cs, weak) // storage_ph:(Maybe TrStoragePhase) + && Maybe{}.validate_skip(ops, cs, weak) // credit_ph:(Maybe TrCreditPhase) + && t_TrComputePhase.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase + && Maybe>{}.validate_skip(ops, cs, weak) // action:(Maybe ^TrActionPhase) + && cs.advance(1) // aborted:Bool + && Maybe{}.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>{}.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>{}.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{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) - && t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase - && Maybe>{}.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{}.validate_skip(ops, cs, weak) // storage_ph:(Maybe TrStoragePhase) + && t_TrComputePhase.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase + && Maybe>{}.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{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) - && Maybe{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase) - && Maybe{}.validate_skip(cs, weak) // compute_ph:TrComputePhase - && Maybe>{}.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{}.validate_skip(ops, cs, weak) // storage_ph:(Maybe TrStoragePhase) + && Maybe{}.validate_skip(ops, cs, weak) // credit_ph:(Maybe TrCreditPhase) + && Maybe{}.validate_skip(ops, cs, weak) // compute_ph:TrComputePhase + && Maybe>{}.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>{}.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>{}.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{}.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{}.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{}.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{}.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{}.validate_skip(ops, cs, weak); // description:^TransactionDescr } bool Transaction::get_storage_fees(Ref 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{}.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{}.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{}.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{}.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{}.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{}.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{}.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{}.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& 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>{}.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>{}.validate_skip(cs, weak); // custom:(Maybe ^McStateExtra) + && Maybe>{}.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{}.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{}.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{}.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 { diff --git a/crypto/block/block-parse.h b/crypto/block/block-parse.h index 8ea3325e..95906311 100644 --- a/crypto/block/block-parse.h +++ b/crypto/block/block-parse.h @@ -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 . - 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 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 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 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 cell, Ref& tdescr) const; bool get_descr(vm::CellSlice& cs, Ref& 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 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; }; diff --git a/crypto/block/block.cpp b/crypto/block/block.cpp index b2cad142..d9d3c267 100644 --- a/crypto/block/block.cpp +++ b/crypto/block/block.cpp @@ -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 . - 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 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 csr) { return unpack_CurrencyCollection(std::move(csr), grams, extra) || invalidate(); } -bool CurrencyCollection::validate_unpack(Ref csr) { - return (csr.not_null() && block::tlb::t_CurrencyCollection.validate(*csr) && +bool CurrencyCollection::validate_unpack(Ref 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 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"; } diff --git a/crypto/block/block.h b/crypto/block/block.h index 0cfbe24a..0d961320 100644 --- a/crypto/block/block.h +++ b/crypto/block/block.h @@ -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 . - 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 csr); - bool validate_unpack(Ref csr); + bool validate_unpack(Ref csr, int max_cells = 1024); Ref pack() const; bool pack_to(Ref& csr) const { return (csr = pack()).not_null(); diff --git a/crypto/block/create-state.cpp b/crypto/block/create-state.cpp index 02896e7b..5eb14528 100644 --- a/crypto/block/create-state.cpp +++ b/crypto/block/create-state.cpp @@ -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 #include @@ -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)); } diff --git a/crypto/block/dump-block.cpp b/crypto/block/dump-block.cpp index 25eb0eed..716aa3bc 100644 --- a/crypto/block/dump-block.cpp +++ b/crypto/block/dump-block.cpp @@ -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{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; } } diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index 719d76b0..555b2603 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -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& root, ton::ShardId shard, Ref val } bool ShardConfig::set_shard_info(ton::ShardIdFull shard, Ref 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); diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 1387e1f9..34abfff1 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -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 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 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 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 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; diff --git a/crypto/common/bigint.hpp b/crypto/common/bigint.hpp index 6734f07e..7f6cf971 100644 --- a/crypto/common/bigint.hpp +++ b/crypto/common/bigint.hpp @@ -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 . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include @@ -169,6 +169,8 @@ class PropagateConstSpan { size_t size_{0}; }; +struct Normalize {}; + template 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; extern template class BigIntG<257, BigIntInfo>; typedef BigIntG<257, BigIntInfo> BigInt256; +template +BigIntG make_bigint(long long x) { + return BigIntG{Normalize(), x}; +} + namespace literals { extern BigInt256 operator""_i256(const char* str, std::size_t str_len); diff --git a/crypto/common/refint.cpp b/crypto/common/refint.cpp index f252aba6..3a031a5a 100644 --- a/crypto/common/refint.cpp +++ b/crypto/common/refint.cpp @@ -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) { diff --git a/crypto/common/refint.h b/crypto/common/refint.h index 4c21aab8..ea1ae81b 100644 --- a/crypto/common/refint.h +++ b/crypto/common/refint.h @@ -33,10 +33,13 @@ typedef Ref 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 +RefInt256 make_refint(Args&&... args) { + return td::RefInt256{true, std::forward(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); diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 38cb2944..3afd687e 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -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) { diff --git a/crypto/func/abscode.cpp b/crypto/func/abscode.cpp index 8813bad2..8d73c805 100644 --- a/crypto/func/abscode.cpp +++ b/crypto/func/abscode.cpp @@ -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) { diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 93f47dec..066da460 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -628,7 +628,7 @@ AsmOp compile_mod(std::vector& res, std::vector& 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); diff --git a/crypto/func/func.h b/crypto/func/func.h index e9101c68..fd2bffd6 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -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); diff --git a/crypto/func/optimize.cpp b/crypto/func/optimize.cpp index 3d697e5e..85c1fdd3 100644 --- a/crypto/func/optimize.cpp +++ b/crypto/func/optimize.cpp @@ -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(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))); diff --git a/crypto/func/stack-transform.cpp b/crypto/func/stack-transform.cpp index bc8986b6..4d9b6a5f 100644 --- a/crypto/func/stack-transform.cpp +++ b/crypto/func/stack-transform.cpp @@ -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 diff --git a/crypto/smartcont/auto-dns.fif b/crypto/smartcont/auto-dns.fif new file mode 100644 index 00000000..6ef2ef0e --- /dev/null +++ b/crypto/smartcont/auto-dns.fif @@ -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 + " [-o] (add|update|prolong) ... " +cr +tab + +"Creates the internal message body containing a request to automatic DNS smart contract created by new-auto-dns.fif, " + +"to be sent later with a suitable payment from a wallet to , and saves it into ('" savefile $+ +"' by default). " + +"The operation to be performed is one of" +cr +tab + +"add { owner | cat (smc | next | adnl | text ) }" +cr +tab + +"update { owner | cat (smc | next | adnl | text ) }" +cr +tab + +"prolong " + 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" + 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 +{ } : serialize-smc-addr +{ } : serialize-next-resolver +{ } : serialize-adnl-addr +{ } : 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 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)" } { =: actions-builder + +// create an internal message +now 32 << actions-builder hashu 32 1<<1- and + =: query_id +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: " B dup Bx. cr +."Query_id is " query_id dup . ."= 0x" X. cr +savefile tuck B>file +."(Saved to file " type .")" cr diff --git a/crypto/smartcont/dns-auto-code.fc b/crypto/smartcont/dns-auto-code.fc index 854f4f25..e548a70a 100644 --- a/crypto/smartcont/dns-auto-code.fc +++ b/crypto/smartcont/dns-auto-code.fc @@ -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); diff --git a/crypto/smartcont/manual-dns-manage.fif b/crypto/smartcont/manual-dns-manage.fif index f8d4a3fb..ef18127c 100644 --- a/crypto/smartcont/manual-dns-manage.fif +++ b/crypto/smartcont/manual-dns-manage.fif @@ -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 diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 2d1a47c4..ca8337e1 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -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"; diff --git a/crypto/smc-envelope/ManualDns.cpp b/crypto/smc-envelope/ManualDns.cpp index 8acba1c8..00f1becb 100644 --- a/crypto/smc-envelope/ManualDns.cpp +++ b/crypto/smc-envelope/ManualDns.cpp @@ -146,6 +146,23 @@ td::Result 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 DnsInterface::resolve_args(td::Slice name, td::int32 category_big) { + TRY_RESULT(category, td::narrow_cast_safe(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> DnsInterface::resolve(td::Slice name, td::int32 category) const { TRY_RESULT(raw_entries, resolve_raw(name, category)); std::vector entries; @@ -375,7 +392,7 @@ td::Ref 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> ManualDns::resolve_raw(td::Slice name, td::int32 category_big) const { @@ -388,9 +405,7 @@ td::Result> 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> 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); diff --git a/crypto/smc-envelope/ManualDns.h b/crypto/smc-envelope/ManualDns.h index 74977c31..fbf12e74 100644 --- a/crypto/smc-envelope/ManualDns.h +++ b/crypto/smc-envelope/ManualDns.h @@ -165,6 +165,15 @@ class DnsInterface { td::uint32 valid_until = std::numeric_limits::max()) const = 0; td::Result> 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 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 actions, td::uint32 valid_until = std::numeric_limits::max()) const override; - static std::string encode_name(td::Slice name); - static std::string decode_name(td::Slice name); - template struct CombinedActions { std::string name; diff --git a/crypto/smc-envelope/MultisigWallet.cpp b/crypto/smc-envelope/MultisigWallet.cpp index ce1795ee..5a9fab6a 100644 --- a/crypto/smc-envelope/MultisigWallet.cpp +++ b/crypto/smc-envelope/MultisigWallet.cpp @@ -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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ #include "MultisigWallet.h" #include "SmartContractCode.h" diff --git a/crypto/smc-envelope/MultisigWallet.h b/crypto/smc-envelope/MultisigWallet.h index 17395e47..96da297a 100644 --- a/crypto/smc-envelope/MultisigWallet.h +++ b/crypto/smc-envelope/MultisigWallet.h @@ -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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ #pragma once #include "vm/cells.h" diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp index 17477de9..36ba2730 100644 --- a/crypto/smc-envelope/SmartContract.cpp +++ b/crypto/smc-envelope/SmartContract.cpp @@ -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 prepare_vm_stack(td::Ref body) { td::Ref 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= 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 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 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 SmartContract::empty_slice() { return vm::load_cell_slice_ref(vm::CellBuilder().finalize()); } diff --git a/crypto/smc-envelope/SmartContract.h b/crypto/smc-envelope/SmartContract.h index 14b5b947..24c2d132 100644 --- a/crypto/smc-envelope/SmartContract.h +++ b/crypto/smc-envelope/SmartContract.h @@ -90,6 +90,14 @@ class SmartContract : public td::CntObject { this->ignore_chksig = ignore_chksig; return std::move(*this); } + + td::Result get_method_id() const { + if (!method_id) { + return td::Status::Error("Args has no method id"); + } + return method_id.value(); + } + td::Result get_serialized_stack(); }; Answer run_method(Args args = {}); diff --git a/crypto/smc-envelope/SmartContractCode.cpp b/crypto/smc-envelope/SmartContractCode.cpp index eea5ac21..6256dcc0 100644 --- a/crypto/smc-envelope/SmartContractCode.cpp +++ b/crypto/smc-envelope/SmartContractCode.cpp @@ -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::less<>> map; diff --git a/crypto/smc-envelope/WalletInterface.h b/crypto/smc-envelope/WalletInterface.h index e0e439c3..2c7760a1 100644 --- a/crypto/smc-envelope/WalletInterface.h +++ b/crypto/smc-envelope/WalletInterface.h @@ -31,8 +31,11 @@ class WalletInterface { struct Gift { block::StdAddress destination; td::int64 gramms; + bool is_encrypted{false}; std::string message; + + td::Ref 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 { diff --git a/crypto/tl/tlbc-gen-cpp.cpp b/crypto/tl/tlbc-gen-cpp.cpp index 3de92bd5..dedec15d 100644 --- a/crypto/tl/tlbc-gen-cpp.cpp +++ b/crypto/tl/tlbc-gen-cpp.cpp @@ -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 . - 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& res" << skip_extra_args << ") const;\n"; } if (type.is_simple_enum) { diff --git a/crypto/tl/tlblib.cpp b/crypto/tl/tlblib.cpp index 4a9475ea..617c298b 100644 --- a/crypto/tl/tlblib.cpp +++ b/crypto/tl/tlblib.cpp @@ -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 cell_ref, bool weak) const { +bool TLB::validate_ref_internal(int* ops, Ref 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 cell_ref) const { @@ -217,7 +222,7 @@ PrettyPrinter::~PrettyPrinter() { } bool PrettyPrinter::fail(std::string msg) { - os << ""; + os << "" << std::endl; failed = true; return false; } diff --git a/crypto/tl/tlblib.hpp b/crypto/tl/tlblib.hpp index 407df292..7543d38f 100644 --- a/crypto/tl/tlblib.hpp +++ b/crypto/tl/tlblib.hpp @@ -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 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 cs_ref, bool weak = false) const { + return validate_csr(&ops, std::move(cs_ref), weak); + } bool validate_csr(Ref 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 fetch(vm::CellSlice& cs) const { return cs.fetch_subslice_ext(get_size(cs)); @@ -52,67 +65,71 @@ class TLB { Ref prefetch(const vm::CellSlice& cs) const { return cs.prefetch_subslice_ext(get_size(cs)); } - virtual Ref validate_fetch(vm::CellSlice& cs, bool weak = false) const { - return validate(cs, weak) ? cs.fetch_subslice_ext(get_size(cs)) : Ref{}; + virtual Ref validate_fetch(int* ops, vm::CellSlice& cs, bool weak = false) const { + return validate(ops, cs, weak) ? cs.fetch_subslice_ext(get_size(cs)) : Ref{}; } - virtual Ref validate_prefetch(const vm::CellSlice& cs, bool weak = false) const { - return validate(cs, weak) ? cs.prefetch_subslice_ext(get_size(cs)) : Ref{}; + virtual Ref 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{}; } bool fetch_to(vm::CellSlice& cs, Ref& res) const { return (res = fetch(cs)).not_null(); } - bool validate_fetch_to(vm::CellSlice& cs, Ref& res, bool weak = false) const { - return (res = validate_fetch(cs, weak)).not_null(); + bool validate_fetch_to(int* ops, vm::CellSlice& cs, Ref& res, bool weak = false) const { + return (res = validate_fetch(ops, cs, weak)).not_null(); } bool store_from(vm::CellBuilder& cb, Ref 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 field, bool weak = false) const { + bool validate_store_from(int* ops, vm::CellBuilder& cb, Ref 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 validate_fetch_by_skip(vm::CellSlice& cs, bool weak = false) const { + Ref validate_fetch_by_skip(int* ops, vm::CellSlice& cs, bool weak = false) const { Ref copy{true, cs}; - return validate_skip(cs, weak) && copy.unique_write().cut_tail(cs) ? copy : Ref{}; + return validate_skip(ops, cs, weak) && copy.unique_write().cut_tail(cs) ? copy : Ref{}; } - Ref validate_prefetch_by_skip(const vm::CellSlice& cs, bool weak = false) const { + Ref 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{}; + return validate_skip(ops, copy, false) ? cs.prefetch_subslice_ext(copy.subtract_base_ext(cs)) + : Ref{}; } 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 cs_ref, td::RefInt256& res) const { return (res = as_integer(std::move(cs_ref))).not_null(); } + bool validate_ref(int* ops, Ref 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 cell_ref, bool weak = false) const { + return validate_ref(&ops, std::move(cell_ref), weak); + } bool validate_ref(Ref 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 cell_ref) const { + return cell_ref.not_null() && validate_ref_internal(ops, std::move(cell_ref), false); + } + bool force_validate_ref(int ops, Ref cell_ref) const { + return force_validate_ref(&ops, std::move(cell_ref)); } bool force_validate_ref(Ref 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 cell_ref, int indent = 0, int rec_limit = 0) const; + bool print_ref(int rec_limit, std::ostream& os, Ref 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 cs_ref, int indent = 0) const { @@ -225,7 +260,7 @@ class TLB { } protected: - bool validate_ref_internal(Ref cell_ref, bool weak = false) const; + bool validate_ref_internal(int* ops, Ref 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 validate_fetch(vm::CellSlice& cs, bool weak = false) const override { - return validate_fetch_by_skip(cs, weak); + Ref validate_fetch(int* ops, vm::CellSlice& cs, bool weak = false) const override { + return validate_fetch_by_skip(ops, cs, weak); } - Ref validate_prefetch(const vm::CellSlice& cs, bool weak = false) const override { - return validate_prefetch_by_skip(cs, weak); + Ref 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 validate_fetch(vm::CellSlice& cs, bool weak = false) const override { - return X.validate_fetch(cs, weak); + Ref validate_fetch(int* ops, vm::CellSlice& cs, bool weak = false) const override { + return X.validate_fetch(ops, cs, weak); } - Ref validate_prefetch(const vm::CellSlice& cs, bool weak = false) const override { - return X.validate_prefetch(cs, weak); + Ref 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::skip(vm::CellSlice& cs) const { } template -bool Maybe::validate_skip(vm::CellSlice& cs, bool weak) const { +bool Maybe::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); diff --git a/crypto/vm/arithops.cpp b/crypto/vm/arithops.cpp index 69e6f083..25b67353 100644 --- a/crypto/vm/arithops.cpp +++ b/crypto/vm/arithops.cpp @@ -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; } diff --git a/crypto/vm/cells/CellSlice.cpp b/crypto/vm/cells/CellSlice.cpp index 8f1e606f..0d539c74 100644 --- a/crypto/vm/cells/CellSlice.cpp +++ b/crypto/vm/cells/CellSlice.cpp @@ -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; diff --git a/crypto/vm/stack.cpp b/crypto/vm/stack.cpp index 84ed4b83..43099927 100644 --- a/crypto/vm/stack.cpp +++ b/crypto/vm/stack.cpp @@ -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 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; diff --git a/crypto/vm/stack.hpp b/crypto/vm/stack.hpp index 75540fb0..3fadce6b 100644 --- a/crypto/vm/stack.hpp +++ b/crypto/vm/stack.hpp @@ -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 extract_contents() const & { + return stack; + } + std::vector extract_contents() && { + return std::move(stack); + } template const Stack& check_underflow(Args... args) const { if (!at_least(args...)) { diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index 84ff5b6b..ca8e4b00 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -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& 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 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& 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& 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; } diff --git a/crypto/vm/utils.cpp b/crypto/vm/utils.cpp index 25e01f21..783bf132 100644 --- a/crypto/vm/utils.cpp +++ b/crypto/vm/utils.cpp @@ -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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ #include "utils.h" namespace vm { @@ -88,7 +106,7 @@ td::Result convert_stack_entry(td::Slice str) { return vm::StackEntry{ Ref{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) { diff --git a/crypto/vm/utils.h b/crypto/vm/utils.h index e1afe60c..34d761b1 100644 --- a/crypto/vm/utils.h +++ b/crypto/vm/utils.h @@ -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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ #pragma once #include "stack.hpp" diff --git a/crypto/vm/vm.cpp b/crypto/vm/vm.cpp index 5ddc16b8..395e01d3 100644 --- a/crypto/vm/vm.cpp +++ b/crypto/vm/vm.cpp @@ -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 cont = Ref{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; diff --git a/crypto/vm/vm.h b/crypto/vm/vm.h index 78087d88..19c3829f 100644 --- a/crypto/vm/vm.h +++ b/crypto/vm/vm.h @@ -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(); diff --git a/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTest.kt b/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTest.kt index f2b94f9d..84f703e1 100644 --- a/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTest.kt +++ b/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTest.kt @@ -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) } } diff --git a/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTestJava.java b/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTestJava.java index 0c61abc4..3c34099f 100644 --- a/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTestJava.java +++ b/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTestJava.java @@ -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"); - + } } } diff --git a/example/android/test/ton/src/main/res/values/strings.xml b/example/android/test/ton/src/main/res/values/strings.xml index b9200763..76b1bac9 100644 --- a/example/android/test/ton/src/main/res/values/strings.xml +++ b/example/android/test/ton/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ tonlib + + diff --git a/lite-client/lite-client-common.cpp b/lite-client/lite-client-common.cpp index 184dfba3..5bc8ca73 100644 --- a/lite-client/lite-client-common.cpp +++ b/lite-client/lite-client-common.cpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #include "lite-client-common.h" #include "auto/tl/lite_api.hpp" diff --git a/lite-client/lite-client-common.h b/lite-client/lite-client-common.h index 54948ead..a73dab91 100644 --- a/lite-client/lite-client-common.h +++ b/lite-client/lite-client-common.h @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "crypto/block/block.h" diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index ed1d9782..3dedabe2 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -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 @@ -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 TestNode::trivial_promise() { + return td::PromiseCreator::lambda([Self = actor_id(this)](td::Result 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] []\tSaves into specified file the most recent state " "(StateInit) or just the code or data of specified account; is in " "[:] format\n" - "runmethod[x] [] ...\tRuns GET method of account " + "runmethod[full] [] ...\tRuns GET method of account " + " " "with specified parameters\n" + "dnsresolve [] []\tResolves a domain starting from root dns smart contract\n" + "dnsresolvestep [] []\tResolves a subdomain using dns smart contract " + "\n" "allshards []\tShows shard configuration from the most recent masterchain " "state or from masterchain state corresponding to \n" "getconfig [...]\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> 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 params, int mode, + td::Promise> 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(workchain, addr); - int mode = (ext_mode ? 0x1f : 0); if (!mode) { auto b = ton::serialize_tl_object(ton::create_tl_object( 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 R) mutable { - if (R.is_error()) { - return; - } - auto F = ton::fetch_tl_object(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 R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + return; + } + auto F = ton::fetch_tl_object(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 R) mutable { if (R.is_error()) { + promise.set_error(R.move_as_error()); return; } auto F = ton::fetch_tl_object(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 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 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 cell; + if (!(cb.store_bytes_bool(td::Slice(qdomain0)) && cb.finalize_to(cell))) { + return set_error("cannot store domain name into slice"); + } + std::vector 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> 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 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 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(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 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 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 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> 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 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 params; - if (mode >= 0 && !seekeoln()) { +bool TestNode::get_config_params(ton::BlockIdExt blkid, td::Promise do_after, int mode, std::string filename, + std::vector 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( 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 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 R) mutable { if (R.is_error()) { + do_after.set_error(R.move_as_error()); return; } auto F = ton::fetch_tl_object(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 params) { + td::BufferSlice cfg_proof, int mode, std::string filename, std::vector params, + td::Promise 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 value) { + config->foreach_config_param([this, &out, mode](int i, Ref 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 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(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(arg); SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity); diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index 0984f67d..7ebaaf17 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -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 params, int mode, + td::Promise> 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 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> promise); + bool register_config_param4(Ref 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 value); + bool show_dns_record(std::ostream& os, int cat, Ref 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 do_after, int mode = 0, std::string filename = "", + std::vector 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 params); + td::BufferSlice cfg_proof, int mode, std::string filename, std::vector params, + td::Promise 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 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)); diff --git a/tdactor/benchmark/benchmark.cpp b/tdactor/benchmark/benchmark.cpp index 442bb84c..deddcb22 100644 --- a/tdactor/benchmark/benchmark.cpp +++ b/tdactor/benchmark/benchmark.cpp @@ -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 #include @@ -796,10 +799,13 @@ class MpmcQueueBenchmark : public td::Benchmark { std::vector m_threads(m_); auto impl = std::make_unique(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(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(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 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 n_threads(n_); + auto impl = std::make_unique(n_ + 1); + size_t thread_id = 0; + std::atomic 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(thread_n)) { @@ -959,26 +1025,95 @@ std::string CfQueueT, Cell>::get_description() { template 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 q; + std::vector p; + std::vector c; +}; + +struct Sem { + public: + void post() { + if (++cnt_ == 0) { + { + std::unique_lock lk(mutex_); + } + cnd_.notify_one(); + } + } + void wait(int cnt = 1) { + auto was = cnt_.fetch_sub(cnt); + if (was >= cnt) { + return; + } + std::unique_lock lk(mutex_); + cnd_.wait(lk, [&] { return cnt_ >= 0; }); + } + + private: + std::mutex mutex_; + std::condition_variable cnd_; + std::atomic cnt_{0}; +}; + +template +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> 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(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(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(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(thread_id)); + impl.push(value, static_cast(thread_id)); waiter.notify(); } private: W waiter; Q impl; + struct Slot { + typename W::Slot slot; + td::actor::core::LocalQueue local_queue; + }; + std::vector slots; +}; +template +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(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(thread_id)); }); + waiter.notify(); + } + + private: + W waiter; + Q impl; + struct Slot { + typename W::Slot slot; + td::actor::core::LocalQueue local_queue; + td::StealingQueue stealing_queue; + }; + std::vector slots; }; void run_queue_bench(int n, int m) { - bench(MpmcQueueBenchmark, td::MpmcWaiter, size_t>>(n, m), 2); - bench(MpmcQueueBenchmark>(n, m), 2); - bench(MpmcQueueBenchmark>(n, m), 2); - bench(MpmcQueueBenchmark(n, m), 2); - bench(MpmcQueueBenchmark>>(n, m), 2); - bench(MpmcQueueBenchmark>>(n, m), 2); - bench(MpmcQueueBenchmark>(n, m), 2); + bench(MpmcQueueBenchmark>(n, m), 2); + bench(MpmcQueueBenchmark, td::MpmcEagerWaiter, size_t>>(n, m), 2); + bench(MpmcQueueBenchmark, td::MpmcSleepyWaiter, size_t>>(n, m), 2); + bench(MpmcQueueBenchmark, td::MpmcEagerWaiter, size_t>>(n, m), 2); + bench(MpmcQueueBenchmark, td::MpmcSleepyWaiter, size_t>>(n, m), 2); + //bench(MpmcQueueBenchmark>(n, m), 2); + //bench(MpmcQueueBenchmark>(n, m), 2); + //bench(MpmcQueueBenchmark(n, m), 2); + //bench(MpmcQueueBenchmark>>(n, m), 2); + //bench(MpmcQueueBenchmark>>(n, m), 2); + //bench(MpmcQueueBenchmark>(n, m), 2); + //bench(MpmcQueueBenchmark(n, m), 2); #if TG_LCR_QUEUE bench(MpmcQueueBenchmark>>(n, m), 2); #endif } +void run_queue_bench2(int n, int k) { + bench(MpmcQueueBenchmark2, td::MpmcSleepyWaiter, size_t>>(n, k), 2); + bench(MpmcQueueBenchmark2, td::MpmcEagerWaiter, size_t>>(n, k), 2); + bench(MpmcQueueBenchmark2>(n, k), 2); + bench(MpmcQueueBenchmark2>(n, k), 2); + bench(MpmcQueueBenchmark2, td::MpmcEagerWaiter, size_t>>(n, k), 2); + bench(MpmcQueueBenchmark2, td::MpmcSleepyWaiter, size_t>>(n, k), 2); + //bench(MpmcQueueBenchmark2>(n, k), 2); + //bench(MpmcQueueBenchmark2>(n, k), 2); + //bench(MpmcQueueBenchmark2(n, k), 2); + //bench(MpmcQueueBenchmark2>>(n, k), 2); + //bench(MpmcQueueBenchmark2>>(n, k), 2); + //bench(MpmcQueueBenchmark2>(n, k), 2); -struct Sem { - public: - void post() { - if (++cnt_ == 0) { - { - std::unique_lock lk(mutex_); - } - cnd_.notify_one(); - } - } - void wait(int cnt = 1) { - auto was = cnt_.fetch_sub(cnt); - if (was >= cnt) { - return; - } - std::unique_lock lk(mutex_); - cnd_.wait(lk, [&] { return cnt_ >= 0; }); - } - - private: - std::mutex mutex_; - std::condition_variable cnd_; - std::atomic cnt_{0}; -}; + //bench(MpmcQueueBenchmark(n, m), 2); +#if TG_LCR_QUEUE + bench(MpmcQueueBenchmark2>>(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>(1, 1), 1 << 26); + bench_n(MpmcQueueBenchmark2, td::MpmcEagerWaiter, size_t>>(50, 1), 1 << 26); + //bench_n(MpmcQueueBenchmark>(1, 1), 1 << 26); //bench_n(MpmcQueueBenchmark(1, 40), 1 << 20); //bench_n(MpmcQueueBenchmark>>(1, 40), 1 << 20); } else { - bench_n(MpmcQueueBenchmark>(1, 1), 1 << 26); + bench_n(MpmcQueueBenchmark2, td::MpmcSleepyWaiter, size_t>>(50, 1), 1 << 26); + //bench_n(MpmcQueueBenchmark>(1, 1), 1 << 26); //bench_n(MpmcQueueBenchmark>>(1, 40), 1 << 20); //bench_n(MpmcQueueBenchmark>>(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>>>()); bench(CalcHashSha256Benchmark>>>()); - 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; } diff --git a/tdactor/benchmark/third_party/MoodyCamelQueue.h b/tdactor/benchmark/third_party/MoodyCamelQueue.h index 936a2f5f..1c7cf801 100644 --- a/tdactor/benchmark/third_party/MoodyCamelQueue.h +++ b/tdactor/benchmark/third_party/MoodyCamelQueue.h @@ -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(-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::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 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::value >= std::alignment_of::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(); @@ -1916,7 +1919,7 @@ class ConcurrentQueue { ++pr_blockIndexSlotsUsed; } - if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { + if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward(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(element)))) { + if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward(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(myDequeueCount - overcommit, tail))) { + if ((details::likely)(details::circular_less_than(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(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(); @@ -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(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(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(); - if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { + if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward(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(element)); @@ -2539,7 +2546,7 @@ class ConcurrentQueue { this->tailBlock = newBlock; - if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { + if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new ((T*)nullptr) T(std::forward(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(myDequeueCount - overcommit, tail))) { + if ((details::likely)(details::circular_less_than(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(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(BLOCK_SIZE - 1)); index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(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(); @@ -2714,16 +2720,18 @@ class ConcurrentQueue { if (details::circular_less_than(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(tail - (myDequeueCount - overcommit)); @@ -2843,7 +2850,7 @@ class ConcurrentQueue { if (block->ConcurrentQueue::Block::template set_many_empty( blockStartIndex, static_cast(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( blockStartIndex, static_cast(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(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 nextExplicitConsumerId; std::atomic globalExplicitConsumerOffset; -#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH +#ifdef MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH debug::DebugMutex implicitProdMutex; #endif diff --git a/tdactor/td/actor/core/ActorInfo.h b/tdactor/td/actor/core/ActorInfo.h index e2574c0d..97419293 100644 --- a/tdactor/td/actor/core/ActorInfo.h +++ b/tdactor/td/actor/core/ActorInfo.h @@ -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 . - 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 { diff --git a/tdactor/td/actor/core/CpuWorker.cpp b/tdactor/td/actor/core/CpuWorker.cpp index e047eab1..d752560d 100644 --- a/tdactor/td/actor/core/CpuWorker.cpp +++ b/tdactor/td/actor/core/CpuWorker.cpp @@ -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 . - 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 diff --git a/tdactor/td/actor/core/CpuWorker.h b/tdactor/td/actor/core/CpuWorker.h index 63cb337b..d9f32513 100644 --- a/tdactor/td/actor/core/CpuWorker.h +++ b/tdactor/td/actor/core/CpuWorker.h @@ -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 . - 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 +struct LocalQueue; class CpuWorker { public: - CpuWorker(MpmcQueue &queue, MpmcWaiter &waiter) : queue_(queue), waiter_(waiter) { + CpuWorker(MpmcQueue &queue, MpmcWaiter &waiter, size_t id, + MutableSpan> local_queues) + : queue_(queue), waiter_(waiter), id_(id), local_queues_(local_queues) { } void run(); private: - MpmcQueue &queue_; + MpmcQueue &queue_; MpmcWaiter &waiter_; + size_t id_; + MutableSpan> 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 diff --git a/tdactor/td/actor/core/Scheduler.cpp b/tdactor/td/actor/core/Scheduler.cpp index 9569fac3..0863bda0 100644 --- a/tdactor/td/actor/core/Scheduler.cpp +++ b/tdactor/td/actor/core/Scheduler.cpp @@ -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 . - 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 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 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>(1024, max_thread_count()); + info_->cpu_queue = std::make_unique>(1024, max_thread_count()); info_->cpu_queue_waiter = std::make_unique(); + + info_->cpu_local_queue = std::vector>(cpu_threads_count); } info_->io_queue = std::make_unique>(); 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::Type::Cpu, true); + worker = std::make_unique(WorkerInfo::Type::Cpu, true, CpuWorkerId{cpu_worker_id}); + cpu_worker_id++; } - info_->io_worker = std::make_unique(WorkerInfo::Type::Io, !info_->cpu_workers.empty()); + info_->io_worker = std::make_unique(WorkerInfo::Type::Io, !info_->cpu_workers.empty(), CpuWorkerId{}); poll_.init(); io_worker_ = std::make_unique(*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 *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; } diff --git a/tdactor/td/actor/core/Scheduler.h b/tdactor/td/actor/core/Scheduler.h index 4ebfc912..3cc627ee 100644 --- a/tdactor/td/actor/core/Scheduler.h +++ b/tdactor/td/actor/core/Scheduler.h @@ -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 . - 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 +struct LocalQueue { + public: + template + 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 &other) { + return queue_.steal(message, other.queue_); + } + + private: + td::optional next_; + StealingQueue queue_; + char pad[TD_CONCURRENCY_PAD - sizeof(optional)]; }; struct SchedulerInfo { SchedulerId id; // will be read by all workers is any thread - std::unique_ptr> cpu_queue; + std::unique_ptr> cpu_queue; std::unique_ptr cpu_queue_waiter; + + std::vector> cpu_local_queue; + //std::vector> cpu_stealing_queue; + // only scheduler itself may read from io_queue_ std::unique_ptr> 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 *heap); + ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id, + SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap *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(); } diff --git a/tdactor/td/actor/core/SchedulerId.h b/tdactor/td/actor/core/SchedulerId.h index 124e4092..289adbd4 100644 --- a/tdactor/td/actor/core/SchedulerId.h +++ b/tdactor/td/actor/core/SchedulerId.h @@ -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 . - 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(id_); + } + bool operator==(CpuWorkerId other) const { + return id_ == other.id_; + } + + private: + int32 id_{-1}; }; } // namespace core } // namespace actor diff --git a/tdactor/test/actors_core.cpp b/tdactor/test/actors_core.cpp index 2e44d3bb..1ec9dda2 100644 --- a/tdactor/test/actors_core.cpp +++ b/tdactor/test/actors_core.cpp @@ -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 . - 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(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(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 watcher_; }; - int N = td::Random::fast(2, 10); + int N = td::Random::fast(2, 100); //N = 2; std::vector> actors; for (int i = 0; i < N; i++) { diff --git a/tdutils/CMakeLists.txt b/tdutils/CMakeLists.txt index 8927bd05..721a32b7 100644 --- a/tdutils/CMakeLists.txt +++ b/tdutils/CMakeLists.txt @@ -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 ) diff --git a/tdutils/td/utils/MpmcQueue.h b/tdutils/td/utils/MpmcQueue.h index 2daf6147..e6504e35 100644 --- a/tdutils/td/utils/MpmcQueue.h +++ b/tdutils/td/utils/MpmcQueue.h @@ -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 . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once @@ -117,24 +117,28 @@ template class OneValue { 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 state_{nullptr}; - T *Taken() { - static T xxx; - return &xxx; + std::atomic state_{Empty()}; + static T *Empty() { + static int64 xxx; + return reinterpret_cast(&xxx); + } + static T *Taken() { + static int64 xxx; + return reinterpret_cast(&xxx); } }; diff --git a/tdutils/td/utils/MpmcWaiter.h b/tdutils/td/utils/MpmcWaiter.h index 42864cae..8fb1a511 100644 --- a/tdutils/td/utils/MpmcWaiter.h +++ b/tdutils/td/utils/MpmcWaiter.h @@ -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 . - 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 +#include #include #include 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 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 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 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 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 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 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 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(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 state_{0}; + + std::mutex sleepers_mutex_; + std::vector sleepers_; + + bool closed_ = false; +}; + +using MpmcWaiter = MpmcSleepyWaiter; + } // namespace td diff --git a/tdutils/td/utils/SharedObjectPool.h b/tdutils/td/utils/SharedObjectPool.h index 5f606142..0a9123d3 100644 --- a/tdutils/td/utils/SharedObjectPool.h +++ b/tdutils/td/utils/SharedObjectPool.h @@ -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 . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once @@ -49,9 +49,7 @@ class AtomicRefCnt { }; template -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 SharedPtr { public: using Raw = detail::SharedPtrRaw; + 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) { diff --git a/tdutils/td/utils/StealingQueue.h b/tdutils/td/utils/StealingQueue.h new file mode 100644 index 00000000..20a39dc0 --- /dev/null +++ b/tdutils/td/utils/StealingQueue.h @@ -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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ +#pragma once + +#include "td/utils/Status.h" +#include "td/utils/Span.h" +namespace td { +template +class StealingQueue { + public: + // tries to put a value + // returns if succeeded + // only owner is alowed to to do this + template + 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(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& 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(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 head_{0}; + std::atomic tail_{0}; + static constexpr size_t MASK{N - 1}; + std::array, N> buf_; +}; +}; // namespace td diff --git a/tdutils/td/utils/StringBuilder.h b/tdutils/td/utils/StringBuilder.h index 229b967e..5fe1e942 100644 --- a/tdutils/td/utils/StringBuilder.h +++ b/tdutils/td/utils/StringBuilder.h @@ -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 . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once diff --git a/tdutils/td/utils/TsFileLog.cpp b/tdutils/td/utils/TsFileLog.cpp index e36b4fc5..865faa4b 100644 --- a/tdutils/td/utils/TsFileLog.cpp +++ b/tdutils/td/utils/TsFileLog.cpp @@ -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 . - 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 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::max(), info->id == 0)); + TRY_STATUS(info->log.init(get_path(info), std::numeric_limits::max(), info->id == 0 && redirect_stderr_)); info->is_inited = true; return Status::OK(); } @@ -92,9 +96,9 @@ class TsFileLog : public LogInterface { }; } // namespace detail -Result> TsFileLog::create(string path) { +Result> TsFileLog::create(string path, td::int64 rotate_threshold, bool redirect_stderr) { auto res = td::make_unique(); - TRY_STATUS(res->init(path)); + TRY_STATUS(res->init(path, rotate_threshold, redirect_stderr)); return std::move(res); } } // namespace td diff --git a/tdutils/td/utils/TsFileLog.h b/tdutils/td/utils/TsFileLog.h index a34572af..0ebac8eb 100644 --- a/tdutils/td/utils/TsFileLog.h +++ b/tdutils/td/utils/TsFileLog.h @@ -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 . - 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> create(string path); + static Result> create(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD, + bool redirect_stderr = true); }; } // namespace td diff --git a/tdutils/td/utils/port/rlimit.cpp b/tdutils/td/utils/port/rlimit.cpp index bc7c6e32..5833961c 100644 --- a/tdutils/td/utils/port/rlimit.cpp +++ b/tdutils/td/utils/port/rlimit.cpp @@ -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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ #include "rlimit.h" #if TD_LINUX || TD_ANDROID #include diff --git a/tdutils/td/utils/port/rlimit.h b/tdutils/td/utils/port/rlimit.h index b217dac8..9f6a6524 100644 --- a/tdutils/td/utils/port/rlimit.h +++ b/tdutils/td/utils/port/rlimit.h @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "td/utils/port/config.h" diff --git a/tdutils/td/utils/port/user.cpp b/tdutils/td/utils/port/user.cpp index 859bb703..2e77eff8 100644 --- a/tdutils/td/utils/port/user.cpp +++ b/tdutils/td/utils/port/user.cpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #include "user.h" #if TD_LINUX #include diff --git a/tdutils/td/utils/port/user.h b/tdutils/td/utils/port/user.h index 4c5d48a4..9c53c2fc 100644 --- a/tdutils/td/utils/port/user.h +++ b/tdutils/td/utils/port/user.h @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "td/utils/port/config.h" diff --git a/tdutils/test/MpmcWaiter.cpp b/tdutils/test/MpmcWaiter.cpp index cf75b525..9cb5b363 100644 --- a/tdutils/test/MpmcWaiter.cpp +++ b/tdutils/test/MpmcWaiter.cpp @@ -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 . - 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 #if !TD_THREAD_UNSUPPORTED -TEST(MpmcWaiter, stress_one_one) { +template +void test_waiter_stress_one_one() { td::Stage run; td::Stage check; std::vector threads; std::atomic value{0}; size_t write_cnt = 10; - td::unique_ptr waiter; + td::unique_ptr waiter; size_t threads_n = 2; for (size_t i = 0; i < threads_n; i++) { threads.push_back(td::thread([&, id = static_cast(i)] { for (td::uint64 round = 1; round < 100000; round++) { if (id == 0) { value = 0; - waiter = td::make_unique(); + waiter = td::make_unique(); 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(); +} +TEST(MpmcSleepyWaiter, stress_one_one) { + test_waiter_stress_one_one(); +} + +template +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 waiter; + td::unique_ptr waiter; for (size_t i = 0; i < threads_n; i++) { threads.push_back(td::thread([&, id = static_cast(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(); + waiter = td::make_unique(); } 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(); +} +TEST(MpmcSleepyWaiter, stress_multi) { + test_waiter_stress(); +} #endif // !TD_THREAD_UNSUPPORTED diff --git a/tdutils/test/StealingQueue.cpp b/tdutils/test/StealingQueue.cpp new file mode 100644 index 00000000..6aa2aae6 --- /dev/null +++ b/tdutils/test/StealingQueue.cpp @@ -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 . + + 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 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 threads; + + int x{0}; + std::atomic version{0}; + + int64 res = 0; + for (size_t i = 0; i < threads_n; i++) { + threads.push_back(td::thread([&, id = static_cast(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 got_sum; + + td::Stage run; + td::Stage check; + + size_t threads_n = 10; + std::vector threads; + std::vector> lq(threads_n); + MpmcQueue 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(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 diff --git a/tdutils/test/log.cpp b/tdutils/test/log.cpp index 99be2d59..707bb070 100644 --- a/tdutils/test/log.cpp +++ b/tdutils/test/log.cpp @@ -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 . - 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::max(), false).move_as_ok(); }); bench_log("TsFileLog", 8, [] { class FileLog : public td::LogInterface { public: diff --git a/terminal/terminal.cpp b/terminal/terminal.cpp index b3c6cc6f..9b4ae374 100644 --- a/terminal/terminal.cpp +++ b/terminal/terminal.cpp @@ -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_; } } diff --git a/terminal/terminal.h b/terminal/terminal.h index c85cbd6b..baa5df2e 100644 --- a/terminal/terminal.h +++ b/terminal/terminal.h @@ -32,7 +32,7 @@ class TerminalIOOutputter { TerminalIOOutputter(bool is_err) : buffer_(new char[BUFFER_SIZE]) , is_err_(is_err) - , sb_(std::make_unique(td::MutableSlice{buffer_, BUFFER_SIZE})) { + , sb_(std::make_unique(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}; } diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 21f68e7f..beb5d64a 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -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.Transaction; raw.transactions transactions:vector 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.DataArray; + msg.message destination:accountAddress amount:int64 data:msg.Data = msg.Message; // @@ -111,7 +116,7 @@ dns.resolved entries:vector = dns.Resolved; // actionNoop = Action; -actionMsg messages:vector allow_send_to_uninited:Bool = Action; +actionMsg messages:vector allow_send_to_uninited:Bool = Action; actionDns actions:vector = Action; //actionMultisig actions:vector = 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; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 992d735c9433beea9b4dbafcc5834b8183ee49b4..6a730d89e12188541e71d228f12c42ccf3111558 100644 GIT binary patch delta 654 zcmX@JknzP5M&3uW^{p77z;z<;dX`tOmAEz@c*7=mC#7!-M{aSto=aj$B2Wd(llFJ73aV^L9J<>WX)9)7_&D2<$BIy0W$LD zF(Dt2HAT*BIvlob2SH*`YZR0}fvX29Vg~2PP&G$c}>rBv^8aRs#!2aCj*S#RfKm!O?g^pNA0~p_6YIm_TAyz%YcJ(PDF|ML!cOI2I@K t>dJ4Hu%5ytqRE`?42cRzK!M%HVQDaVhOU4Fh{FiA6WQ^b7r5$h0{|gO)Sv(W delta 416 zcmaE{gz?NmM&3uW^{p77z-1!udY0TTMgAKPykX+6{BeytR&n2-WadIuQ^dt_E ziC;t}Yj8YZ`RN&ZcQX&C3=?DeW=-zrEEXVLH;)PVfJ`cKX4B!YZ951OgBg8Qjw|MR z#xrlu#N?99{5;>{bOr{H{A3RS3r2>?jRN*6=*krh)ZL-VUGj>d%AW|hfL*v*NAQJ& z#M}^hSssuRQ-DqkNv$XWS_g9WhglNjSMH+3a=qNt;^M^gR1hEJRnEz0^*?j4Lq#WF(321q$t;eC^5aus vHcYk@mWKq`8v`CjFl+K3LlcOQiBSkUqxt4e%YG(^zhrE-aBcQ*SKtN!X&#UP diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index 58b86beb..223c8eda 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -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( make_object(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(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> elements; + elements.push_back(make_object( + SimpleEncryptionV2::encrypt_data(text, other_public_key, pkey).move_as_ok().as_slice().str())); + + auto decrypted = + sync_send(client, make_object( + make_object( + make_object(key->public_key_, raw_imported_key->secret_.copy()), + new_local_password.copy()), + make_object(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) { diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index a1544888..d6d4a894 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -234,7 +234,7 @@ td::Result create_send_grams_query(Client& client, const Wallet& source std::vector> msgs; tonlib_api::object_ptr data; if (encrypted) { - data = tonlib_api::make_object(std::move(message)); + data = tonlib_api::make_object(std::move(message)); } else { data = tonlib_api::make_object(std::move(message)); } @@ -310,6 +310,13 @@ td::Result> 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(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("A", -1, make_object(A_B.get_address())))); auto init_A = create_update_dns_query(client, A, std::move(actions)).move_as_ok(); actions.push_back(make_object( - make_object("B.A", -1, make_object(A_B_C.get_address())))); + make_object("B", -1, make_object(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(make_object("C.B.A", 1, make_object("Hello dns")))); + make_object(make_object("C", 1, make_object("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"; diff --git a/tonlib/tonlib/KeyValue.cpp b/tonlib/tonlib/KeyValue.cpp index 5943ea47..cdb0f5ee 100644 --- a/tonlib/tonlib/KeyValue.cpp +++ b/tonlib/tonlib/KeyValue.cpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #include "KeyValue.h" #include "td/utils/filesystem.h" diff --git a/tonlib/tonlib/KeyValue.h b/tonlib/tonlib/KeyValue.h index 7d2020ac..33aef44e 100644 --- a/tonlib/tonlib/KeyValue.h +++ b/tonlib/tonlib/KeyValue.h @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "td/utils/SharedSlice.h" #include "td/utils/Slice.h" diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 13db5314..f0315961 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -63,6 +63,24 @@ struct GetAccountState { td::optional block_id; using ReturnType = td::unique_ptr; }; + +struct RemoteRunSmcMethod { + block::StdAddress address; + td::optional 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_ptrstate_); return res; } +static block::AccountState create_account_state(ton::tl_object_ptr& 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&& 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 promise_; + td::actor::ActorShared<> parent_; + ExtClient client_; + + void with_run_method_result( + td::Result> 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> 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 do_with_run_method_result( + ton::tl_object_ptr 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 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(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 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 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 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 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 data; + if (body->size() >= 32 && static_cast(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(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 { + 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(r_decrypted_message.move_as_ok()); + } else { + data = tonlib_api::make_object(encrypted_message); + } + } + } + } + if (!data) { + data = tonlib_api::make_object(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(msg_info.created_lt); - td::Ref 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 { - 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(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 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("", 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(std::move(src), "", 0, 0, 0, 0, "", "", false); + return tonlib_api::make_object(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 body; }; bool allow_send_to_uninited_{false}; std::vector actions_; @@ -2064,27 +2212,36 @@ class GenericCreateSendGrams : public TonlibQueryActor { res.amount = message.amount_; auto status = downcast_call2(*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>&& 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; + return downcast_call2( + *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( + 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>&& promise) { if (!request.destination_) { @@ -2698,19 +2892,32 @@ td::Result> to_tonlib_api( } void TonlibClient::finish_dns_resolve(std::string name, td::int32 category, td::int32 ttl, - td::optional block_id, td::unique_ptr smc, + td::optional block_id, DnsFinishData dns_finish_data, td::Promise>&& 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().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> api_entries; @@ -2726,9 +2933,29 @@ void TonlibClient::do_dns_request(std::string name, td::int32 category, td::int3 td::optional block_id, block::StdAddress address, td::Promise>&& 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 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&& promise) { + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "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&& promise) { TRY_RESULT(pk, key_storage_.load_private_key(std::move(request.input_key))); promise.set_value(std::move(pk)); diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index fd68071b..786e4e77 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -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>&& promise); + td::Status do_request(tonlib_api::msg_decrypt& request, td::Promise>&& promise); + td::int64 next_smc_id_{0}; std::map> smcs_; @@ -299,13 +304,18 @@ class TonlibClient : public td::actor::Actor { td::Promise>&& promise); void do_dns_request(std::string name, td::int32 category, td::int32 ttl, td::optional block_id, block::StdAddress address, td::Promise>&& 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 block_id, - td::unique_ptr smc, - td::Promise>&& promise); + DnsFinishData dns_finish_data, td::Promise>&& promise); td::Status do_request(int_api::GetAccountState request, td::Promise>&&); td::Status do_request(int_api::GetPrivateKey request, td::Promise&&); td::Status do_request(int_api::GetDnsResolver request, td::Promise&&); + td::Status do_request(int_api::RemoteRunSmcMethod request, + td::Promise&& promise); td::Status do_request(int_api::SendMessage request, td::Promise&& promise); td::Status do_request(const tonlib_api::liteServer_getInfo& request, diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 06bb90cd..2f37df68 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -345,10 +345,14 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "gethistory - get history fo simple wallet with requested key (last 10 transactions)\n"; td::TerminalIO::out() << "init - init simple wallet with requested key\n"; - td::TerminalIO::out() << "transfer[f] - transfer of grams from " - " to .\n" - << "\t could also be 'giver'\n" - << "\t could also be 'giver' or smartcontract address\n"; + td::TerminalIO::out() << "transfer[f][F][e][k][c] ( |) - " + "make transfer from \n" + << "\t 'f' modifier - allow send to uninited account\n" + << "\t 'F' modifier - read list of messages from (in same format " + " , 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(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 promise) { @@ -531,7 +531,7 @@ class TonlibCli : public td::actor::Actor { : nullptr; send_query(tonlib_api::make_object(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 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& 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 << ""; }, + [&](tonlib_api::msg_dataText& raw) { sb << "{" << raw.text_ << "}"; }, + [&](tonlib_api::msg_dataEncryptedText& raw) { sb << ""; }, + [&](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 data; if (use_encryption) { - data = tonlib_api::make_object(message.str()); + data = tonlib_api::make_object(message.str()); } else { data = tonlib_api::make_object(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( - make_object(from_address.public_key, from_address.secret.copy()), - td::SecureString(password)) - : nullptr; + tonlib_api::object_ptr key = + !from_address.secret.empty() + ? make_object( + make_object(from_address.public_key, from_address.secret.copy()), + td::SecureString(password)) + : nullptr; + if (use_fake_key) { + key = make_object(); + } bool allow_send_to_uninited = force; send_query(make_object( std::move(key), std::move(from_address.address), 60, make_object(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> r_info, td::Promise cmd_promise) { - send_query(tonlib_api::make_object(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> r_info, + td::Promise cmd_promise) { + if (estimate_fees) { + send_query(tonlib_api::make_object(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(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) { diff --git a/validator/apply-block.cpp b/validator/apply-block.cpp index 9da8635d..c01b0a54 100644 --- a/validator/apply-block.cpp +++ b/validator/apply-block.cpp @@ -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 . - 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 R) { + auto P = td::PromiseCreator::lambda( + [ SelfId = actor_id(this), seqno = handle_->id().id.seqno ](td::Result 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> 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> 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)); diff --git a/validator/db/archive-mover.cpp b/validator/db/archive-mover.cpp index ec9448ab..aa89570d 100644 --- a/validator/db/archive-mover.cpp +++ b/validator/db/archive-mover.cpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #include "archive-mover.hpp" #include "td/actor/MultiPromise.h" #include "validator/fabric.h" diff --git a/validator/db/archive-mover.hpp b/validator/db/archive-mover.hpp index f81ee03a..be0d8dc7 100644 --- a/validator/db/archive-mover.hpp +++ b/validator/db/archive-mover.hpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "td/actor/actor.h" diff --git a/validator/db/archive-slice.hpp b/validator/db/archive-slice.hpp index 45a40a12..dc7b2341 100644 --- a/validator/db/archive-slice.hpp +++ b/validator/db/archive-slice.hpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "validator/interfaces/db.h" diff --git a/validator/db/fileref.cpp b/validator/db/fileref.cpp index 968ec552..2e2a3b6b 100644 --- a/validator/db/fileref.cpp +++ b/validator/db/fileref.cpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #include "fileref.hpp" #include "auto/tl/ton_api.hpp" #include "td/utils/overloaded.h" diff --git a/validator/db/package.hpp b/validator/db/package.hpp index 60e4d8c0..b2dd4e9f 100644 --- a/validator/db/package.hpp +++ b/validator/db/package.hpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "td/actor/actor.h" diff --git a/validator/full-node-shard-queries.hpp b/validator/full-node-shard-queries.hpp index 7743d42f..1a2cd716 100644 --- a/validator/full-node-shard-queries.hpp +++ b/validator/full-node-shard-queries.hpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "validator/validator.h" diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 3f7983b2..0961cb04 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -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 . - 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> 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 Collator::register_external_message_cell(Ref 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; diff --git a/validator/impl/external-message.cpp b/validator/impl/external-message.cpp index 2688b306..6af721b2 100644 --- a/validator/impl/external-message.cpp +++ b/validator/impl/external-message.cpp @@ -56,10 +56,10 @@ td::Result> 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; diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index b8737873..fdeb345a 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -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 . - 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 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> 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(std::move(inmsg_cs), 256, block::tlb::aug_InMsgDescr); @@ -2274,11 +2274,11 @@ bool ValidateQuery::precheck_one_account_update(td::ConstBitPtr acc_id, Ref old_conf_params, Ref 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. + + Copyright 2019-2020 Telegram Systems LLP +*/ #include "import-db-slice.hpp" #include "validator/db/fileref.hpp" #include "td/utils/overloaded.h" diff --git a/validator/import-db-slice.hpp b/validator/import-db-slice.hpp index b7f383d8..0993d4bb 100644 --- a/validator/import-db-slice.hpp +++ b/validator/import-db-slice.hpp @@ -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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ #pragma once #include "td/actor/actor.h" diff --git a/validator/interfaces/liteserver.h b/validator/interfaces/liteserver.h index ff1bf165..3e803029 100644 --- a/validator/interfaces/liteserver.h +++ b/validator/interfaces/liteserver.h @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "td/actor/actor.h" diff --git a/validator/net/download-archive-slice.cpp b/validator/net/download-archive-slice.cpp index 8b6e7188..e3acb4ba 100644 --- a/validator/net/download-archive-slice.cpp +++ b/validator/net/download-archive-slice.cpp @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #include "download-archive-slice.hpp" #include "td/utils/port/path.h" #include "td/utils/overloaded.h" diff --git a/validator/stats-merger.h b/validator/stats-merger.h index e2c7f242..bc5b7085 100644 --- a/validator/stats-merger.h +++ b/validator/stats-merger.h @@ -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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ #pragma once #include "td/utils/int_types.h"