mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
updated block header
1. Updated block header, proofs now contain more data Notice, that old proofs may become invalid in the future 2. Fixed message routing 3. Fixed block creator id in block header 4. Support for full proofs in tonlib 5. Support for partial state download 6. Some other bugfixes
This commit is contained in:
parent
bce33f588a
commit
13140ddf29
73 changed files with 2084 additions and 304 deletions
|
@ -9,6 +9,7 @@ set(TON_CRYPTO_SOURCE
|
|||
common/bigint.cpp
|
||||
common/refcnt.cpp
|
||||
common/refint.cpp
|
||||
common/bigexp.cpp
|
||||
common/bitstring.cpp
|
||||
common/util.cpp
|
||||
ellcurve/Ed25519.cpp
|
||||
|
@ -42,6 +43,7 @@ set(TON_CRYPTO_SOURCE
|
|||
common/bitstring.h
|
||||
common/refcnt.hpp
|
||||
common/refint.h
|
||||
common/bigexp.h
|
||||
common/util.h
|
||||
|
||||
ellcurve/Ed25519.h
|
||||
|
@ -227,7 +229,7 @@ target_link_libraries(ton_crypto PUBLIC ${OPENSSL_CRYPTO_LIBRARY} tdutils)
|
|||
if (NOT WIN32)
|
||||
target_link_libraries(ton_crypto PUBLIC dl z)
|
||||
endif()
|
||||
target_include_directories(ton_crypto SYSTEM PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
target_include_directories(ton_crypto SYSTEM PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
|
||||
|
||||
add_library(ton_db STATIC ${TON_DB_SOURCE})
|
||||
target_include_directories(ton_db PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
|
@ -324,10 +326,10 @@ if (WINGETOPT_FOUND)
|
|||
target_link_libraries_system(create-state wingetopt)
|
||||
endif()
|
||||
|
||||
add_executable(test-block block/test-block.cpp)
|
||||
target_include_directories(test-block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
add_executable(dump-block block/dump-block.cpp)
|
||||
target_include_directories(dump-block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||
target_link_libraries(test-block PUBLIC ton_crypto fift-lib ton_block)
|
||||
target_link_libraries(dump-block PUBLIC ton_crypto fift-lib ton_block)
|
||||
if (WINGETOPT_FOUND)
|
||||
target_link_libraries_system(test-block wingetopt)
|
||||
target_link_libraries_system(dump-block wingetopt)
|
||||
endif()
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "block/block-auto.h"
|
||||
#include "block/block-parse.h"
|
||||
#include "ton/ton-shard.h"
|
||||
#include "common/bigexp.h"
|
||||
#include "common/util.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "td/utils/tl_storers.h"
|
||||
|
@ -367,14 +368,14 @@ std::unique_ptr<MsgProcessedUptoCollection> MsgProcessedUptoCollection::unpack(t
|
|||
return v && v->valid ? std::move(v) : std::unique_ptr<MsgProcessedUptoCollection>{};
|
||||
}
|
||||
|
||||
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const& {
|
||||
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const & {
|
||||
return ton::shard_is_ancestor(shard, other.shard) && mc_seqno >= other.mc_seqno &&
|
||||
(last_inmsg_lt > other.last_inmsg_lt ||
|
||||
(last_inmsg_lt == other.last_inmsg_lt && !(last_inmsg_hash < other.last_inmsg_hash)));
|
||||
}
|
||||
|
||||
bool MsgProcessedUpto::contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
|
||||
ton::BlockSeqno other_mc_seqno) const& {
|
||||
ton::BlockSeqno other_mc_seqno) const & {
|
||||
return ton::shard_is_ancestor(shard, other_shard) && mc_seqno >= other_mc_seqno &&
|
||||
(last_inmsg_lt > other_lt || (last_inmsg_lt == other_lt && !(last_inmsg_hash < other_hash)));
|
||||
}
|
||||
|
@ -788,6 +789,14 @@ td::Status ShardState::unpack_state(ton::BlockIdExt blkid, Ref<vm::Cell> prev_st
|
|||
if (!global_balance_.validate_unpack(extra.global_balance)) {
|
||||
return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid global_balance");
|
||||
}
|
||||
if (extra.r1.flags & 1) {
|
||||
if (extra.r1.block_create_stats->prefetch_ulong(8) != 0x17) {
|
||||
return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid BlockCreateStats");
|
||||
}
|
||||
block_create_stats_ = std::make_unique<vm::Dictionary>(extra.r1.block_create_stats->prefetch_ref(), 256);
|
||||
} else {
|
||||
block_create_stats_ = std::make_unique<vm::Dictionary>(256);
|
||||
}
|
||||
}
|
||||
return unpack_out_msg_queue_info(std::move(state.out_msg_queue_info));
|
||||
}
|
||||
|
@ -1387,6 +1396,114 @@ std::ostream& operator<<(std::ostream& os, const ValueFlow& vflow) {
|
|||
return os;
|
||||
}
|
||||
|
||||
bool DiscountedCounter::increase_by(unsigned count, ton::UnixTime now) {
|
||||
if (!validate()) {
|
||||
return false;
|
||||
}
|
||||
td::uint64 scaled = (td::uint64(count) << 32);
|
||||
if (!total) {
|
||||
last_updated = now;
|
||||
total = count;
|
||||
cnt2048 = scaled;
|
||||
cnt65536 = scaled;
|
||||
return true;
|
||||
}
|
||||
if (count > ~total || cnt2048 > ~scaled || cnt65536 > ~scaled) {
|
||||
return false /* invalidate() */; // overflow
|
||||
}
|
||||
unsigned dt = (now >= last_updated ? now - last_updated : 0);
|
||||
if (dt > 0) {
|
||||
// more precise version of cnt2048 = llround(cnt2048 * exp(-dt / 2048.));
|
||||
// (rounding error has absolute value < 1)
|
||||
cnt2048 = (dt >= 48 * 2048 ? 0 : td::umulnexps32(cnt2048, dt << 5));
|
||||
// more precise version of cnt65536 = llround(cnt65536 * exp(-dt / 65536.));
|
||||
// (rounding error has absolute value < 1)
|
||||
cnt65536 = td::umulnexps32(cnt65536, dt);
|
||||
}
|
||||
total += count;
|
||||
cnt2048 += scaled;
|
||||
cnt65536 += scaled;
|
||||
last_updated = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiscountedCounter::validate() {
|
||||
if (!is_valid()) {
|
||||
return false;
|
||||
}
|
||||
if (!total) {
|
||||
if (cnt2048 | cnt65536) {
|
||||
return invalidate();
|
||||
}
|
||||
} else if (!last_updated) {
|
||||
return invalidate();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiscountedCounter::fetch(vm::CellSlice& cs) {
|
||||
valid = (cs.fetch_uint_to(32, last_updated) && cs.fetch_uint_to(64, total) && cs.fetch_uint_to(64, cnt2048) &&
|
||||
cs.fetch_uint_to(64, cnt65536));
|
||||
return validate() || invalidate();
|
||||
}
|
||||
|
||||
bool DiscountedCounter::unpack(Ref<vm::CellSlice> csr) {
|
||||
return (csr.not_null() && fetch(csr.write()) && csr->empty_ext()) || invalidate();
|
||||
}
|
||||
|
||||
bool DiscountedCounter::store(vm::CellBuilder& cb) const {
|
||||
return is_valid() && cb.store_long_bool(last_updated, 32) && cb.store_long_bool(total, 64) &&
|
||||
cb.store_long_bool(cnt2048, 64) && cb.store_long_bool(cnt65536, 64);
|
||||
}
|
||||
|
||||
Ref<vm::CellSlice> DiscountedCounter::pack() const {
|
||||
vm::CellBuilder cb;
|
||||
if (store(cb)) {
|
||||
return vm::load_cell_slice_ref(cb.finalize());
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool DiscountedCounter::show(std::ostream& os) const {
|
||||
if (!is_valid()) {
|
||||
os << "<invalid-counter>";
|
||||
return false;
|
||||
}
|
||||
os << "(counter last_updated:" << last_updated << " total:" << total << " cnt2048: " << (double)cnt2048 / (1LL << 32)
|
||||
<< " cnt65536: " << (double)cnt65536 / (1LL << 32) << ")";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string DiscountedCounter::to_str() const {
|
||||
std::ostringstream stream;
|
||||
if (show(stream)) {
|
||||
return stream.str();
|
||||
} else {
|
||||
return "<invalid-counter>";
|
||||
}
|
||||
}
|
||||
|
||||
bool fetch_CreatorStats(vm::CellSlice& cs, DiscountedCounter& mc_cnt, DiscountedCounter& shard_cnt) {
|
||||
return cs.fetch_ulong(4) == 4 // creator_info#4
|
||||
&& mc_cnt.fetch(cs) // mc_blocks:Counters
|
||||
&& shard_cnt.fetch(cs); // shard_blocks:Counters
|
||||
}
|
||||
|
||||
bool store_CreatorStats(vm::CellBuilder& cb, const DiscountedCounter& mc_cnt, const DiscountedCounter& shard_cnt) {
|
||||
return cb.store_long_bool(4, 4) // creator_info#4
|
||||
&& mc_cnt.store(cb) // mc_blocks:Counters
|
||||
&& shard_cnt.store(cb); // shard_blocks:Counters
|
||||
}
|
||||
|
||||
bool unpack_CreatorStats(Ref<vm::CellSlice> cs, DiscountedCounter& mc_cnt, DiscountedCounter& shard_cnt) {
|
||||
if (cs.is_null()) {
|
||||
return mc_cnt.set_zero() && shard_cnt.set_zero();
|
||||
} else {
|
||||
return fetch_CreatorStats(cs.write(), mc_cnt, shard_cnt) && cs->empty_ext();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Other block-related functions
|
||||
|
@ -1565,8 +1682,8 @@ std::pair<int, int> perform_hypercube_routing(ton::AccountIdPrefixFull src, ton:
|
|||
if (!ton::shard_contains(cur, transit)) {
|
||||
return {-1, -1};
|
||||
}
|
||||
if (transit.account_id_prefix == dest.account_id_prefix || ton::shard_contains(cur, dest)) {
|
||||
// if destination is already reached, or is in this shard, set cur:=next_hop:=dest
|
||||
if (ton::shard_contains(cur, dest)) {
|
||||
// if destination is in this shard, set cur:=next_hop:=dest
|
||||
return {96, 96};
|
||||
}
|
||||
if (transit.workchain == ton::masterchainId || dest.workchain == ton::masterchainId) {
|
||||
|
|
|
@ -159,12 +159,12 @@ struct MsgProcessedUpto {
|
|||
MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash)
|
||||
: shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) {
|
||||
}
|
||||
bool operator<(const MsgProcessedUpto& other) const& {
|
||||
bool operator<(const MsgProcessedUpto& other) const & {
|
||||
return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno);
|
||||
}
|
||||
bool contains(const MsgProcessedUpto& other) const&;
|
||||
bool contains(const MsgProcessedUpto& other) const &;
|
||||
bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
|
||||
ton::BlockSeqno other_mc_seqno) const&;
|
||||
ton::BlockSeqno other_mc_seqno) const &;
|
||||
// NB: this is for checking whether we have already imported an internal message
|
||||
bool already_processed(const EnqueuedMsgDescr& msg) const;
|
||||
};
|
||||
|
@ -392,6 +392,7 @@ struct ShardState {
|
|||
CurrencyCollection total_balance_, total_validator_fees_, global_balance_;
|
||||
std::unique_ptr<vm::AugmentedDictionary> out_msg_queue_;
|
||||
std::unique_ptr<vm::Dictionary> ihr_pending_;
|
||||
std::unique_ptr<vm::Dictionary> block_create_stats_;
|
||||
std::shared_ptr<block::MsgProcessedUptoCollection> processed_upto_;
|
||||
|
||||
bool is_valid() const {
|
||||
|
@ -467,6 +468,67 @@ struct ValueFlow {
|
|||
|
||||
std::ostream& operator<<(std::ostream& os, const ValueFlow& vflow);
|
||||
|
||||
struct DiscountedCounter {
|
||||
struct SetZero {};
|
||||
bool valid;
|
||||
ton::UnixTime last_updated;
|
||||
td::uint64 total;
|
||||
td::uint64 cnt2048;
|
||||
td::uint64 cnt65536;
|
||||
DiscountedCounter() : valid(false) {
|
||||
}
|
||||
DiscountedCounter(SetZero) : valid(true), last_updated(0), total(0), cnt2048(0), cnt65536(0) {
|
||||
}
|
||||
DiscountedCounter(ton::UnixTime _lastupd, td::uint64 _total, td::uint64 _cnt2048, td::uint64 _cnt65536)
|
||||
: valid(true), last_updated(_lastupd), total(_total), cnt2048(_cnt2048), cnt65536(_cnt65536) {
|
||||
}
|
||||
static DiscountedCounter Zero() {
|
||||
return SetZero();
|
||||
}
|
||||
bool is_valid() const {
|
||||
return valid;
|
||||
}
|
||||
bool invalidate() {
|
||||
return (valid = false);
|
||||
}
|
||||
bool set_zero() {
|
||||
last_updated = 0;
|
||||
total = cnt2048 = cnt65536 = 0;
|
||||
return (valid = true);
|
||||
}
|
||||
bool is_zero() const {
|
||||
return !total;
|
||||
}
|
||||
bool almost_zero() const {
|
||||
return (cnt2048 | cnt65536) <= 1;
|
||||
}
|
||||
bool operator==(const DiscountedCounter& other) const {
|
||||
return last_updated == other.last_updated && total == other.total && cnt2048 == other.cnt2048 &&
|
||||
cnt65536 == other.cnt65536;
|
||||
}
|
||||
bool almost_equals(const DiscountedCounter& other) const {
|
||||
return last_updated == other.last_updated && total == other.total && cnt2048 <= other.cnt2048 + 1 &&
|
||||
other.cnt2048 <= cnt2048 + 1 && cnt65536 <= other.cnt65536 + 1 && other.cnt65536 <= cnt65536 + 1;
|
||||
}
|
||||
bool validate();
|
||||
bool increase_by(unsigned count, ton::UnixTime now);
|
||||
bool fetch(vm::CellSlice& cs);
|
||||
bool unpack(Ref<vm::CellSlice> csr);
|
||||
bool store(vm::CellBuilder& cb) const;
|
||||
Ref<vm::CellSlice> pack() const;
|
||||
bool show(std::ostream& os) const;
|
||||
std::string to_str() const;
|
||||
};
|
||||
|
||||
static inline std::ostream& operator<<(std::ostream& os, const DiscountedCounter& dcount) {
|
||||
dcount.show(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
bool fetch_CreatorStats(vm::CellSlice& cs, DiscountedCounter& mc_cnt, DiscountedCounter& shard_cnt);
|
||||
bool store_CreatorStats(vm::CellBuilder& cb, const DiscountedCounter& mc_cnt, const DiscountedCounter& shard_cnt);
|
||||
bool unpack_CreatorStats(Ref<vm::CellSlice> cs, DiscountedCounter& mc_cnt, DiscountedCounter& shard_cnt);
|
||||
|
||||
struct BlockProofLink {
|
||||
ton::BlockIdExt from, to;
|
||||
bool is_key{false}, is_fwd{false};
|
||||
|
|
|
@ -499,14 +499,20 @@ _ key:Bool blk_ref:ExtBlkRef = KeyExtBlkRef;
|
|||
|
||||
_ (HashmapAugE 32 KeyExtBlkRef KeyMaxLt) = OldMcBlocksInfo;
|
||||
|
||||
|
||||
counters#_ last_updated:uint32 total:uint64 cnt2048:uint64 cnt65536:uint64 = Counters;
|
||||
creator_info#4 mc_blocks:Counters shard_blocks:Counters = CreatorStats;
|
||||
block_create_stats#17 counters:(HashmapE 256 CreatorStats) = BlockCreateStats;
|
||||
|
||||
masterchain_state_extra#cc26
|
||||
shard_hashes:ShardHashes
|
||||
config:ConfigParams
|
||||
^[ flags:(## 16) { flags = 0 }
|
||||
^[ flags:(## 16) { flags <= 1 }
|
||||
validator_info:ValidatorInfo
|
||||
prev_blocks:OldMcBlocksInfo
|
||||
after_key_block:Bool
|
||||
last_key_block:(Maybe ExtBlkRef) ]
|
||||
last_key_block:(Maybe ExtBlkRef)
|
||||
block_create_stats:(flags . 0)?BlockCreateStats ]
|
||||
global_balance:CurrencyCollection
|
||||
= McStateExtra;
|
||||
|
||||
|
|
|
@ -80,4 +80,15 @@ struct TransactionList {
|
|||
td::Result<Info> validate() const;
|
||||
};
|
||||
|
||||
struct BlockChain {
|
||||
ton::BlockIdExt from;
|
||||
ton::BlockIdExt to;
|
||||
td::int32 mode;
|
||||
td::BufferSlice proof;
|
||||
|
||||
using Info = std::unique_ptr<block::BlockProofChain>;
|
||||
|
||||
td::Result<Info> validate() const;
|
||||
};
|
||||
|
||||
} // namespace block
|
||||
|
|
252
crypto/block/dump-block.cpp
Normal file
252
crypto/block/dump-block.cpp
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
This file is part of TON Blockchain source code.
|
||||
|
||||
TON Blockchain is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
TON Blockchain is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
You must obey the GNU General Public License in all respects for all
|
||||
of the code used other than OpenSSL. If you modify file(s) with this
|
||||
exception, you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. If you delete this exception statement
|
||||
from all source files in the program, then also delete it here.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "block/block.h"
|
||||
#include "vm/boc.h"
|
||||
#include <iostream>
|
||||
#include "block-db.h"
|
||||
#include "block-auto.h"
|
||||
#include "block-parse.h"
|
||||
#include "vm/cp0.h"
|
||||
#include <getopt.h>
|
||||
|
||||
using td::Ref;
|
||||
|
||||
int verbosity;
|
||||
|
||||
struct IntError {
|
||||
std::string err_msg;
|
||||
IntError(std::string _msg) : err_msg(_msg) {
|
||||
}
|
||||
IntError(const char* _msg) : err_msg(_msg) {
|
||||
}
|
||||
};
|
||||
|
||||
td::Ref<vm::Cell> load_boc(std::string filename) {
|
||||
std::cerr << "loading bag-of-cell file " << filename << std::endl;
|
||||
auto bytes_res = block::load_binary_file(filename);
|
||||
if (bytes_res.is_error()) {
|
||||
throw IntError{PSTRING() << "cannot load file `" << filename << "` : " << bytes_res.move_as_error()};
|
||||
}
|
||||
vm::BagOfCells boc;
|
||||
auto res = boc.deserialize(bytes_res.move_as_ok());
|
||||
if (res.is_error()) {
|
||||
throw IntError{PSTRING() << "cannot deserialize bag-of-cells " << res.move_as_error()};
|
||||
}
|
||||
if (res.move_as_ok() <= 0 || boc.get_root_cell().is_null()) {
|
||||
throw IntError{"cannot deserialize bag-of-cells "};
|
||||
}
|
||||
return boc.get_root_cell();
|
||||
}
|
||||
|
||||
void test1() {
|
||||
block::ShardId id{ton::masterchainId}, id2{ton::basechainId, 0x11efULL << 48};
|
||||
std::cout << '[' << id << "][" << id2 << ']' << std::endl;
|
||||
vm::CellBuilder cb;
|
||||
cb << id << id2;
|
||||
std::cout << "ShardIdent.pack() = " << block::tlb::t_ShardIdent.pack(cb, {12, 3, 0x3aeULL << 52}) << std::endl;
|
||||
std::cout << cb << std::endl;
|
||||
auto cref = cb.finalize();
|
||||
td::Ref<vm::CellSlice> cs{true, cref}, cs2;
|
||||
block::ShardId id3{cs.write()}, id4, id5;
|
||||
cs >> id4 >> id5;
|
||||
std::cout << '[' << id3 << "][" << id4 << "][" << id5 << ']' << std::endl;
|
||||
vm::CellSlice csl{std::move(cref)};
|
||||
std::cout << "ShardIdent.get_size() = " << block::tlb::t_ShardIdent.get_size(csl) << std::endl;
|
||||
std::cout << "MsgAddress.get_size() = " << block::tlb::t_MsgAddress.get_size(csl) << std::endl;
|
||||
std::cout << "Grams.get_size() = " << block::tlb::t_Grams.get_size(csl) << std::endl;
|
||||
std::cout << "Grams.as_integer() = " << block::tlb::t_Grams.as_integer(csl) << std::endl;
|
||||
(csl + 8).print_rec(std::cout);
|
||||
std::cout << "Grams.get_size() = " << block::tlb::t_Grams.get_size(csl + 8) << std::endl;
|
||||
std::cout << "Grams.as_integer() = " << block::tlb::t_Grams.as_integer(csl + 8) << std::endl;
|
||||
|
||||
vm::CellSlice csl2{csl};
|
||||
block::gen::ShardIdent::Record sh_id;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
std::cout << csl2 << std::endl;
|
||||
bool ok = tlb::unpack(csl2, sh_id);
|
||||
std::cout << "block::gen::ShardIdent.unpack() = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << " (shard_ident shard_pfx_bits:" << sh_id.shard_pfx_bits << " workchain_id:" << sh_id.workchain_id
|
||||
<< " shard_prefix:" << std::hex << sh_id.shard_prefix << std::dec << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
csl.print_rec(std::cerr);
|
||||
csl.dump(std::cerr, 7);
|
||||
std::cout << "ShardIdent.unpack() = " << block::tlb::t_ShardIdent.unpack(csl, shard_id) << std::endl;
|
||||
if (shard_id.is_valid()) {
|
||||
std::cout << "shard_pfx_bits:" << shard_id.shard_pfx_bits << " workchain_id:" << shard_id.workchain_id
|
||||
<< " 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;
|
||||
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;
|
||||
std::cout << "Grams.store_intval(-17) = " << block::tlb::t_Grams.store_integer_value(cb, "-17"_i256) << std::endl;
|
||||
std::cout << "Grams.store_intval(0) = " << block::tlb::t_Grams.store_integer_value(cb, "0"_i256) << std::endl;
|
||||
std::cout << cb << std::endl;
|
||||
cs = td::Ref<vm::CellSlice>{true, cb.finalize()};
|
||||
std::cout << "Grams.store_intval(666) = " << block::tlb::t_Grams.store_integer_value(cb, "666"_i256) << std::endl;
|
||||
std::cout << cb << std::endl;
|
||||
cs2 = td::Ref<vm::CellSlice>{true, cb.finalize()};
|
||||
std::cout << "Grams.validate(cs) = " << block::tlb::t_Grams.validate(*cs) << std::endl;
|
||||
std::cout << "Grams.validate(cs2) = " << block::tlb::t_Grams.validate(*cs2) << std::endl;
|
||||
//
|
||||
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 << "[cs = " << cs << "]" << std::endl;
|
||||
bool ok = tlb::csr_unpack_inexact(cs, data);
|
||||
std::cout << "block::gen::SplitMergeInfo.unpack(cs, data) = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << " cur_shard_pfx_len = " << data.cur_shard_pfx_len << "; acc_split_depth = " << data.acc_split_depth
|
||||
<< "; this_addr = " << data.this_addr << "; sibling_addr = " << data.sibling_addr << std::endl;
|
||||
}
|
||||
ok = tlb::csr_unpack_inexact(cs, data2);
|
||||
std::cout << "block::gen::Grams.unpack(cs, data2) = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << " amount = " << data2.amount << std::endl;
|
||||
block::gen::VarUInteger::Record data3;
|
||||
ok = tlb::csr_type_unpack(data2.amount, block::gen::t_VarUInteger_16, data3);
|
||||
std::cout << " block::gen::VarUInteger16.unpack(amount, data3) = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << " len = " << data3.len << "; value = " << data3.value << std::endl;
|
||||
vm::CellBuilder cb;
|
||||
std::cout << " block::gen::VarUInteger16.pack(cb, data3) = "
|
||||
<< tlb::type_pack(cb, block::gen::t_VarUInteger_16, data3) << std::endl;
|
||||
std::cout << " cb = " << cb.finalize() << std::endl;
|
||||
}
|
||||
}
|
||||
/*
|
||||
{
|
||||
vm::CellBuilder cb;
|
||||
td::BitArray<256> hash;
|
||||
std::memset(hash.data(), 0x69, 32);
|
||||
bool ok = tlb::pack(
|
||||
cb, block::gen::Test::Record{1000000000000, {170239, -888, {239017, "1000000000000000000"_ri256}, hash}, 17});
|
||||
std::cout << " block::gen::Test::pack(cb, {1000000000000, ...}) = " << ok << std::endl;
|
||||
std::cout << " cb = " << cb << std::endl;
|
||||
auto cell = cb.finalize();
|
||||
vm::CellSlice cs{cell};
|
||||
cs.print_rec(std::cout);
|
||||
block::gen::Test::Record data;
|
||||
std::cout << " block::gen::Test::validate_ref(cell) = " << block::gen::t_Test.validate_ref(cell) << std::endl;
|
||||
ok = tlb::unpack(cs, data);
|
||||
std::cout << " block::gen::Test::unpack(cs, data) = " << ok << std::endl;
|
||||
if (ok) {
|
||||
std::cout << "a:" << data.a << " b:" << data.r1.b << " c:" << data.r1.c << " d:" << data.r1.r1.d
|
||||
<< " e:" << data.r1.r1.e << " f:" << data.r1.f << " g:" << data.g << std::endl;
|
||||
}
|
||||
std::cout << " block::gen::Test::print_ref(cell) = ";
|
||||
block::gen::t_Test.print_ref(std::cout, cell, 2);
|
||||
block::gen::t_CurrencyCollection.print_ref(std::cout, cell, 2);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
*/
|
||||
std::cout << "Grams.add_values() = " << block::tlb::t_Grams.add_values(cb, cs.write(), cs2.write()) << std::endl;
|
||||
std::cout << cb << std::endl;
|
||||
std::cout << "block::gen::t_HashmapAug_64_...print_type() = "
|
||||
<< block::gen::t_HashmapAug_64_Ref_Transaction_CurrencyCollection << std::endl;
|
||||
}
|
||||
|
||||
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 << "block::gen::HashmapE(32,UInt16).validate() = "
|
||||
<< block::gen::HashmapE{32, block::gen::t_uint16}.validate(cs) << std::endl;
|
||||
}
|
||||
|
||||
void usage() {
|
||||
std::cout << "usage: test-block [-S][<boc-file>]\n\tor test-block -h\n\tDumps specified blockchain block or state "
|
||||
"from <boc-file>, or runs some tests\n\t-S\tDump a blockchain state\n";
|
||||
std::exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char* const argv[]) {
|
||||
int i;
|
||||
int new_verbosity_level = VERBOSITY_NAME(INFO);
|
||||
bool dump_state = false;
|
||||
auto zerostate = std::make_unique<block::ZerostateInfo>();
|
||||
while ((i = getopt(argc, argv, "Shv:")) != -1) {
|
||||
switch (i) {
|
||||
case 'S':
|
||||
dump_state = true;
|
||||
break;
|
||||
case 'v':
|
||||
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
std::exit(2);
|
||||
default:
|
||||
usage();
|
||||
std::exit(2);
|
||||
}
|
||||
}
|
||||
SET_VERBOSITY_LEVEL(new_verbosity_level);
|
||||
try {
|
||||
bool done = false;
|
||||
while (optind < argc) {
|
||||
auto boc = load_boc(argv[optind++]);
|
||||
if (boc.is_null()) {
|
||||
std::cerr << "(invalid boc)" << std::endl;
|
||||
std::exit(2);
|
||||
} else {
|
||||
done = true;
|
||||
vm::CellSlice cs{vm::NoVm(), boc};
|
||||
cs.print_rec(std::cout);
|
||||
std::cout << std::endl;
|
||||
auto& type = dump_state ? (const tlb::TLB&)block::gen::t_ShardStateUnsplit : block::gen::t_Block;
|
||||
std::string type_name = dump_state ? "ShardState" : "Block";
|
||||
type.print_ref(std::cout, boc);
|
||||
std::cout << std::endl;
|
||||
bool ok = type.validate_ref(boc);
|
||||
std::cout << "(" << (ok ? "" : "in") << "valid " << type_name << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
test1();
|
||||
}
|
||||
} catch (IntError& err) {
|
||||
std::cerr << "caught internal error " << err.err_msg << std::endl;
|
||||
return 1;
|
||||
} catch (vm::VmError& err) {
|
||||
std::cerr << "caught vm error " << err.get_msg() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -260,6 +260,22 @@ td::Status Config::unpack() {
|
|||
workchains_ = std::move(pair.first);
|
||||
workchains_dict_ = std::move(pair.second);
|
||||
}
|
||||
if (mode & needCapabilities) {
|
||||
auto cell = get_config_param(8);
|
||||
if (cell.is_null()) {
|
||||
version_ = 0;
|
||||
capabilities_ = 0;
|
||||
} else {
|
||||
block::gen::GlobalVersion::Record gv;
|
||||
if (!tlb::unpack_cell(std::move(cell), gv)) {
|
||||
return td::Status::Error(
|
||||
"cannot extract global blockchain version and capabilities from GlobalVersion in configuration parameter "
|
||||
"#8");
|
||||
}
|
||||
version_ = gv.version;
|
||||
capabilities_ = gv.capabilities;
|
||||
}
|
||||
}
|
||||
// ...
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
@ -339,7 +355,7 @@ bool ConfigInfo::get_last_key_block(ton::BlockIdExt& blkid, ton::LogicalTime& bl
|
|||
blkid = block_id;
|
||||
blklt = lt;
|
||||
}
|
||||
return blkid.is_valid() && blkid.seqno();
|
||||
return blkid.is_valid();
|
||||
}
|
||||
|
||||
td::Result<std::pair<WorkchainSet, std::unique_ptr<vm::Dictionary>>> Config::unpack_workchain_list_ext(
|
||||
|
|
|
@ -441,7 +441,7 @@ class Config {
|
|||
};
|
||||
|
||||
public:
|
||||
enum { needValidatorSet = 16, needSpecialSmc = 32, needWorkchainInfo = 256 };
|
||||
enum { needValidatorSet = 16, needSpecialSmc = 32, needWorkchainInfo = 256, needCapabilities = 512 };
|
||||
int mode{0};
|
||||
ton::BlockIdExt block_id;
|
||||
|
||||
|
@ -452,6 +452,8 @@ class Config {
|
|||
std::unique_ptr<ValidatorSet> cur_validators_;
|
||||
std::unique_ptr<vm::Dictionary> workchains_dict_;
|
||||
WorkchainSet workchains_;
|
||||
int version_{-1};
|
||||
long long capabilities_{-1};
|
||||
|
||||
protected:
|
||||
std::unique_ptr<vm::Dictionary> special_smc_dict;
|
||||
|
@ -474,6 +476,24 @@ class Config {
|
|||
bool is_masterchain() const {
|
||||
return block_id.is_masterchain();
|
||||
}
|
||||
bool has_capabilities() const {
|
||||
return capabilities_ >= 0;
|
||||
}
|
||||
long long get_capabilities() const {
|
||||
return capabilities_;
|
||||
}
|
||||
int get_global_version() const {
|
||||
return version_;
|
||||
}
|
||||
bool has_capability(long long cap_set) const {
|
||||
return has_capabilities() && (capabilities_ & cap_set) == cap_set;
|
||||
}
|
||||
bool ihr_enabled() const {
|
||||
return has_capability(ton::capIhrEnabled);
|
||||
}
|
||||
bool create_stats_enabled() const {
|
||||
return has_capability(ton::capCreateStatsEnabled);
|
||||
}
|
||||
bool set_block_id_ext(const ton::BlockIdExt& block_id_ext);
|
||||
td::Result<std::vector<ton::StdSmcAddress>> get_special_smartcontracts(bool without_config = false) const;
|
||||
bool is_special_smartcontract(const ton::StdSmcAddress& addr) const;
|
||||
|
|
261
crypto/common/bigexp.cpp
Normal file
261
crypto/common/bigexp.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "bigexp.h"
|
||||
#include "td/utils/bits.h"
|
||||
#include "td/utils/as.h"
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
bool NegExpBinTable::init() {
|
||||
init_one();
|
||||
int k;
|
||||
for (k = minpw2; k <= 0; k++) {
|
||||
exp_pw2_table.emplace_back(series_exp(-k));
|
||||
exp_pw2_ref_table.emplace_back(true, exp_pw2_table.back());
|
||||
}
|
||||
for (; k < maxpw2; k++) {
|
||||
td::BigIntG<257 * 2> tmp{0};
|
||||
auto& x = exp_pw2_table.back();
|
||||
tmp.add_mul(x, x).rshift(precision, 0).normalize();
|
||||
exp_pw2_table.emplace_back(tmp);
|
||||
exp_pw2_ref_table.emplace_back(true, exp_pw2_table.back());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NegExpBinTable::adjust_precision(int new_precision, int rmode) {
|
||||
if (new_precision <= 0 || new_precision > precision) {
|
||||
return false;
|
||||
}
|
||||
if (new_precision == precision) {
|
||||
return true;
|
||||
}
|
||||
int s = precision - new_precision;
|
||||
for (auto& x : exp_pw2_table) {
|
||||
x.rshift(s, rmode).normalize();
|
||||
}
|
||||
for (auto& x : exp_pw2_ref_table) {
|
||||
x.write().rshift(s, rmode).normalize();
|
||||
}
|
||||
precision = new_precision;
|
||||
return init_one();
|
||||
}
|
||||
|
||||
bool NegExpBinTable::init_one() {
|
||||
One.set_pow2(precision);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NegExpBinTable::nexpf(td::BigInt256& res, long long x, int k) const { // res := 2^precision * exp(-x * 2^k)
|
||||
if (!x) {
|
||||
res.set_pow2(precision);
|
||||
return true;
|
||||
}
|
||||
if (x < 0) {
|
||||
return false;
|
||||
}
|
||||
int s = td::count_trailing_zeroes64(x);
|
||||
x >>= s;
|
||||
k -= s;
|
||||
if (k + minpw2 > 0) {
|
||||
return false;
|
||||
}
|
||||
int t = 63 - td::count_leading_zeroes64(x);
|
||||
if (t - k >= maxpw2) {
|
||||
return false;
|
||||
}
|
||||
res.set_pow2(precision);
|
||||
while (true) {
|
||||
td::BigIntG<257 * 2> tmp{0};
|
||||
tmp.add_mul(res, exp_pw2_table.at(t - k - minpw2)).rshift(precision, 0).normalize();
|
||||
res = tmp;
|
||||
x -= (1LL << t);
|
||||
if (!x) {
|
||||
return true;
|
||||
}
|
||||
t = 63 - td::count_leading_zeroes64(x);
|
||||
}
|
||||
}
|
||||
|
||||
td::RefInt256 NegExpBinTable::nexpf(long long x, int k) const {
|
||||
td::RefInt256 res{true};
|
||||
if (nexpf(res.unique_write(), x, k)) {
|
||||
return res;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
td::BigInt256 NegExpBinTable::series_exp(int k) const { // returns 2^precision * exp(-2^(-k)), k >= 0
|
||||
td::BigIntG<257 * 2> s{0}, q;
|
||||
const int prec = 52 * 6;
|
||||
q.set_pow2(prec);
|
||||
int i = 0;
|
||||
do {
|
||||
s += q;
|
||||
--i;
|
||||
q.rshift(k).add_tiny(i / 2).divmod_short(i);
|
||||
q.normalize();
|
||||
} while (q.sgn());
|
||||
s.rshift(prec - precision).normalize();
|
||||
return s;
|
||||
}
|
||||
|
||||
NegExpInt64Table::NegExpInt64Table() {
|
||||
NegExpBinTable t{252, 8, -32};
|
||||
CHECK(t.is_valid());
|
||||
table0[0] = 0;
|
||||
table0_shift[0] = 0;
|
||||
for (int i = 1; i <= max_exp; i++) {
|
||||
SuperFloat v(*t.nexpf(i, 0)); // compute exp(-i)
|
||||
CHECK(!v.is_nan());
|
||||
if (v.is_zero()) {
|
||||
table0[i] = 0;
|
||||
table0_shift[i] = 0;
|
||||
} else {
|
||||
CHECK(v.normalize());
|
||||
int k = v.s + 64 - 252;
|
||||
CHECK(k <= -64);
|
||||
if (k > -128) {
|
||||
table0[i] = v.top();
|
||||
table0_shift[i] = td::narrow_cast<unsigned char>(-k - 1);
|
||||
} else {
|
||||
table0[i] = 0;
|
||||
table0_shift[i] = 0;
|
||||
}
|
||||
}
|
||||
// std::cerr << "table0[" << i << "] = exp(-" << i << ") : " << table0[i] << " / 2^" << table0_shift[i] + 1 << std::endl;
|
||||
}
|
||||
td::BigInt256 One;
|
||||
One.set_pow2(252);
|
||||
for (int i = 0; i < 256; i++) {
|
||||
td::BigInt256 x;
|
||||
CHECK(t.nexpf(x, i, 8));
|
||||
(x.negate() += One).rshift(252 - 64, 0).normalize();
|
||||
table1[i] = SuperFloat::as_uint64(x);
|
||||
// std::cerr << "table1[" << i << "] = 1 - exp(-" << i << "/256) : " << table1[i] << " / 2^64" << std::endl;
|
||||
}
|
||||
for (int i = 0; i < 256; i++) {
|
||||
td::BigInt256 x;
|
||||
CHECK(t.nexpf(x, i, 16));
|
||||
(x.negate() += One).rshift(252 - 64 - 8, 0).normalize();
|
||||
table2[i] = SuperFloat::as_uint64(x);
|
||||
// std::cerr << "table2[" << i << "] = 1 - exp(-" << i << "/2^16) : " << table2[i] << " / 2^72" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
td::uint64 NegExpInt64Table::umulnexps32(td::uint64 x, unsigned k, bool trunc) const { // compute x * exp(-k / 2^16)
|
||||
if (!k || !x) {
|
||||
return x;
|
||||
}
|
||||
unsigned k0 = (k >> 16);
|
||||
if (k0 > max_exp) {
|
||||
return 0;
|
||||
}
|
||||
unsigned s = td::count_leading_zeroes_non_zero64(x);
|
||||
x <<= s;
|
||||
unsigned k1 = (k >> 8) & 0xff;
|
||||
unsigned k2 = k & 0xff;
|
||||
if (k2) {
|
||||
x -= ((td::uint128::from_unsigned(x).mult(table2[k2]).rounded_hi() + 0x80) >> 8);
|
||||
}
|
||||
if (k1) {
|
||||
x -= td::uint128::from_unsigned(x).mult(table1[k1]).rounded_hi();
|
||||
}
|
||||
if (k0) {
|
||||
if (trunc) {
|
||||
return td::uint128::from_unsigned(x).mult(table0[k0]).shr(table0_shift[k0] + s + 1).lo();
|
||||
} else {
|
||||
return (td::uint128::from_unsigned(x).mult(table0[k0]).shr(table0_shift[k0] + s).lo() + 1) >> 1;
|
||||
}
|
||||
}
|
||||
if (!s) {
|
||||
return x;
|
||||
} else if (trunc) {
|
||||
return x >> s;
|
||||
} else {
|
||||
return ((x >> (s - 1)) + 1) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
td::int64 NegExpInt64Table::mulnexps32(td::int64 x, unsigned k, bool trunc) const {
|
||||
return x >= 0 ? umulnexps32(x, k, trunc) : -umulnexps32(-x, k, trunc);
|
||||
}
|
||||
|
||||
const NegExpInt64Table& NegExpInt64Table::table() {
|
||||
static NegExpInt64Table tab;
|
||||
return tab;
|
||||
}
|
||||
|
||||
td::uint64 umulnexps32(td::uint64 x, unsigned k, bool trunc) { // compute x * exp(-k / 2^16)
|
||||
return NegExpInt64Table::table().umulnexps32(x, k, trunc);
|
||||
}
|
||||
|
||||
td::int64 mulnexps32(td::int64 x, unsigned k, bool trunc) {
|
||||
return NegExpInt64Table::table().mulnexps32(x, k, trunc);
|
||||
}
|
||||
|
||||
td::uint128 SuperFloat::as_uint128(const td::BigInt256& x) {
|
||||
td::uint64 t[2];
|
||||
if (!x.export_bytes_lsb((unsigned char*)(void*)t, sizeof(t), false)) {
|
||||
return {std::numeric_limits<uint64>::max(), 0};
|
||||
} else {
|
||||
return {t[1], t[0]};
|
||||
}
|
||||
}
|
||||
|
||||
td::uint64 SuperFloat::as_uint64(const td::BigInt256& x) {
|
||||
td::uint64 t;
|
||||
if (!x.export_bytes_lsb((unsigned char*)&t, sizeof(t), false)) {
|
||||
return std::numeric_limits<uint64>::max();
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
SuperFloat::SuperFloat(td::BigInt256 x) {
|
||||
if (x.unsigned_fits_bits(128)) {
|
||||
m = as_uint128(x);
|
||||
s = 0;
|
||||
} else if (x.sgn() == 1) {
|
||||
s = x.bit_size(false) - 128;
|
||||
x.rshift(s, 0).normalize();
|
||||
m = as_uint128(x);
|
||||
} else {
|
||||
set_nan();
|
||||
}
|
||||
}
|
||||
|
||||
bool SuperFloat::normalize() {
|
||||
if (is_nan()) {
|
||||
return false;
|
||||
}
|
||||
if (is_zero()) {
|
||||
s = 0;
|
||||
return true;
|
||||
}
|
||||
auto hi = m.hi();
|
||||
int t = (hi ? td::count_leading_zeroes_non_zero64(hi) : 64 + td::count_leading_zeroes_non_zero64(m.lo()));
|
||||
m.shl(t);
|
||||
s -= t;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace td
|
145
crypto/common/bigexp.h
Normal file
145
crypto/common/bigexp.h
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/refint.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
class NegExpBinTable {
|
||||
int precision, maxpw2, minpw2;
|
||||
std::vector<td::BigInt256> exp_pw2_table; // table of 2^precision * exp(- 2^k) for k = max_pw2-1 .. min_pw2
|
||||
std::vector<td::RefInt256> exp_pw2_ref_table; // same data
|
||||
td::BigInt256 One;
|
||||
|
||||
public:
|
||||
NegExpBinTable(int _precision, int _maxpw2, int _minpw2) : precision(255), maxpw2(_maxpw2), minpw2(_minpw2) {
|
||||
(_precision > 0 && _precision < 256 && _minpw2 <= 0 && _maxpw2 > 0 && _maxpw2 <= 256 && _minpw2 >= -256 && init() &&
|
||||
adjust_precision(_precision)) ||
|
||||
invalidate();
|
||||
}
|
||||
bool is_valid() const {
|
||||
return minpw2 < maxpw2;
|
||||
}
|
||||
int get_precision() const {
|
||||
return precision;
|
||||
}
|
||||
int get_exponent_precision() const {
|
||||
return -minpw2;
|
||||
}
|
||||
int get_exponent_max_log2() const {
|
||||
return maxpw2;
|
||||
}
|
||||
const td::BigInt256* exp_pw2(int k) const { // returns 2^precision * exp(-2^k) or null
|
||||
return (k >= minpw2 && k < maxpw2) ? &exp_pw2_table[k - minpw2] : nullptr;
|
||||
}
|
||||
td::RefInt256 exp_pw2_ref(int k) const {
|
||||
if (k >= minpw2 && k < maxpw2) {
|
||||
return exp_pw2_ref_table[k - minpw2];
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
bool nexpf(td::BigInt256& res, long long x, int k) const; // res := 2^precision * exp(-x * 2^k)
|
||||
td::RefInt256 nexpf(long long x, int k) const;
|
||||
|
||||
private:
|
||||
bool init();
|
||||
bool init_one();
|
||||
bool adjust_precision(int new_precision, int rmode = 0);
|
||||
bool invalidate() {
|
||||
minpw2 = maxpw2 = 0;
|
||||
return false;
|
||||
}
|
||||
td::BigInt256 series_exp(int k) const; // returns 2^precision * exp(-2^(-k)), k >= 0
|
||||
};
|
||||
|
||||
struct SuperFloat {
|
||||
struct SetZero {};
|
||||
struct SetOne {};
|
||||
struct SetNan {};
|
||||
td::uint128 m;
|
||||
int s;
|
||||
SuperFloat() = default;
|
||||
SuperFloat(SetZero) : m(0, 0), s(0) {
|
||||
}
|
||||
SuperFloat(SetOne) : m(0, 1), s(0) {
|
||||
}
|
||||
SuperFloat(SetNan) : m(0, 0), s(std::numeric_limits<int>::min()) {
|
||||
}
|
||||
SuperFloat(td::uint128 _m, int _s = 0) : m(_m), s(_s) {
|
||||
}
|
||||
SuperFloat(td::uint64 _m, int _s = 0) : m(0, _m), s(_s) {
|
||||
}
|
||||
explicit SuperFloat(BigInt256 x);
|
||||
static SuperFloat Zero() {
|
||||
return SetZero{};
|
||||
}
|
||||
static SuperFloat One() {
|
||||
return SetOne{};
|
||||
}
|
||||
static SuperFloat NaN() {
|
||||
return SetNan{};
|
||||
}
|
||||
void set_zero() {
|
||||
m = td::uint128(0, 0);
|
||||
s = 0;
|
||||
}
|
||||
void set_one() {
|
||||
m = td::uint128(0, 1);
|
||||
s = 0;
|
||||
}
|
||||
void set_nan() {
|
||||
s = std::numeric_limits<int>::min();
|
||||
}
|
||||
bool is_nan() const {
|
||||
return s == std::numeric_limits<int>::min();
|
||||
}
|
||||
bool is_zero() const {
|
||||
return m.is_zero();
|
||||
}
|
||||
bool normalize();
|
||||
td::uint64 top() const {
|
||||
return m.rounded_hi();
|
||||
}
|
||||
static td::uint128 as_uint128(const td::BigInt256& x);
|
||||
static td::uint64 as_uint64(const td::BigInt256& x);
|
||||
};
|
||||
|
||||
class NegExpInt64Table {
|
||||
enum { max_exp = 45 };
|
||||
unsigned char table0_shift[max_exp + 1];
|
||||
td::uint64 table0[max_exp + 1], table1[256], table2[256];
|
||||
|
||||
public:
|
||||
NegExpInt64Table();
|
||||
// compute x * exp(-k / 2^16);
|
||||
// more precisely: computes 0 <= y <= x for 0 <= x < 2^60, s.that |y - x * exp(-k / 2^16)| < 1
|
||||
// two different implementations of this functions would return values differing by at most one
|
||||
td::uint64 umulnexps32(td::uint64 x, unsigned k, bool trunc = false) const;
|
||||
td::int64 mulnexps32(td::int64 x, unsigned k, bool trunc = false) const;
|
||||
static const NegExpInt64Table& table();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
td::uint64 umulnexps32(td::uint64 x, unsigned k, bool trunc = false); // compute x * exp(-k / 2^16)
|
||||
td::int64 mulnexps32(td::int64 x, unsigned k, bool trunc = false);
|
||||
|
||||
} // namespace td
|
|
@ -128,6 +128,9 @@ struct BitPtrGen {
|
|||
std::size_t scan(bool value, std::size_t len) const {
|
||||
return bitstring::bits_memscan(*this, len, value);
|
||||
}
|
||||
bool is_zero(std::size_t len) const {
|
||||
return scan(false, len) == len;
|
||||
}
|
||||
long long get_int(unsigned bits) const {
|
||||
return bitstring::bits_load_long(*this, bits);
|
||||
}
|
||||
|
@ -279,7 +282,7 @@ class BitSliceGen {
|
|||
ensure_throw(set_size_bool(bits));
|
||||
return *this;
|
||||
}
|
||||
BitSliceGen subslice(unsigned from, unsigned bits) const & {
|
||||
BitSliceGen subslice(unsigned from, unsigned bits) const& {
|
||||
return BitSliceGen(*this, from, bits);
|
||||
}
|
||||
BitSliceGen subslice(unsigned from, unsigned bits) && {
|
||||
|
@ -467,7 +470,7 @@ class BitArray {
|
|||
unsigned char* data() {
|
||||
return bytes.data();
|
||||
}
|
||||
unsigned size() const {
|
||||
static unsigned size() {
|
||||
return n;
|
||||
}
|
||||
const byte_array_t& as_array() const {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// { bl word (forget) } : forget
|
||||
{ bl word 1 ' (forget) } :: [forget]
|
||||
{ char " word 1 ' type } ::_ ."
|
||||
{ char } word x>B 1 'nop } ::_ B{
|
||||
{ swap ({) over 2+ -roll swap (compile) (}) } : does
|
||||
{ 1 'nop does create } : constant
|
||||
{ 2 'nop does create } : 2constant
|
||||
|
|
|
@ -625,14 +625,69 @@ void interpret_bytes_len(vm::Stack& stack) {
|
|||
stack.push_smallint((long long)stack.pop_bytes().size());
|
||||
}
|
||||
|
||||
void interpret_bytes_hex_print_raw(IntCtx& ctx) {
|
||||
const char hex_digits[] = "0123456789ABCDEF";
|
||||
const char hex_digits[] = "0123456789abcdef";
|
||||
const char HEX_digits[] = "0123456789ABCDEF";
|
||||
|
||||
static inline const char* hex_digits_table(bool upcase) {
|
||||
return upcase ? HEX_digits : hex_digits;
|
||||
}
|
||||
|
||||
void interpret_bytes_hex_print_raw(IntCtx& ctx, bool upcase) {
|
||||
auto hex_digits = hex_digits_table(upcase);
|
||||
std::string str = ctx.stack.pop_bytes();
|
||||
for (unsigned c : str) {
|
||||
*ctx.output_stream << hex_digits[(c >> 4) & 15] << hex_digits[c & 15];
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_bytes_to_hex(vm::Stack& stack, bool upcase) {
|
||||
auto hex_digits = hex_digits_table(upcase);
|
||||
std::string str = stack.pop_bytes();
|
||||
std::string t(str.size() * 2, 0);
|
||||
for (std::size_t i = 0; i < str.size(); i++) {
|
||||
unsigned c = str[i];
|
||||
t[2 * i] = hex_digits[(c >> 4) & 15];
|
||||
t[2 * i + 1] = hex_digits[c & 15];
|
||||
}
|
||||
stack.push_string(std::move(t));
|
||||
}
|
||||
|
||||
void interpret_hex_to_bytes(vm::Stack& stack, bool partial) {
|
||||
std::string str = stack.pop_string(), t;
|
||||
if (!partial) {
|
||||
if (str.size() & 1) {
|
||||
throw IntError{"not a hex string"};
|
||||
}
|
||||
t.reserve(str.size() >> 1);
|
||||
}
|
||||
std::size_t i;
|
||||
unsigned f = 0;
|
||||
for (i = 0; i < str.size(); i++) {
|
||||
int c = str[i];
|
||||
if (c >= '0' && c <= '9') {
|
||||
c -= '0';
|
||||
} else {
|
||||
c |= 0x20;
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
c -= 'a' - 10;
|
||||
} else {
|
||||
if (!partial) {
|
||||
throw IntError{"not a hex string"};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
f = (f << 4) + c;
|
||||
if (i & 1) {
|
||||
t += (char)(f & 0xff);
|
||||
}
|
||||
}
|
||||
stack.push_bytes(t);
|
||||
if (partial) {
|
||||
stack.push_smallint(i & -2);
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_bytes_split(vm::Stack& stack) {
|
||||
stack.check_underflow(2);
|
||||
unsigned sz = stack.pop_smallint_range(0x7fffffff);
|
||||
|
@ -1525,39 +1580,6 @@ void interpret_pfx_dict_get(vm::Stack& stack) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_bytes_hex_literal(IntCtx& ctx) {
|
||||
auto s = ctx.scan_word_to('}');
|
||||
std::string t;
|
||||
t.reserve(s.size() >> 1);
|
||||
int v = 1;
|
||||
for (char c : s) {
|
||||
if (c == ' ' || c == '\t') {
|
||||
continue;
|
||||
}
|
||||
v <<= 4;
|
||||
if (c >= '0' && c <= '9') {
|
||||
v += c - '0';
|
||||
} else {
|
||||
c |= 0x20;
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
v += c - ('a' - 10);
|
||||
} else {
|
||||
v = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (v & 0x100) {
|
||||
t.push_back((char)v);
|
||||
v = 1;
|
||||
}
|
||||
}
|
||||
if (v != 1) {
|
||||
throw IntError{"Invalid bytes hexstring constant"};
|
||||
}
|
||||
ctx.stack.push_bytes(std::move(t));
|
||||
push_argcount(ctx.stack, 1);
|
||||
}
|
||||
|
||||
void interpret_bitstring_hex_literal(IntCtx& ctx) {
|
||||
auto s = ctx.scan_word_to('}');
|
||||
unsigned char buff[128];
|
||||
|
@ -2430,7 +2452,11 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("-trailing0 ", std::bind(interpret_str_remove_trailing_int, _1, '0'));
|
||||
d.def_stack_word("$len ", interpret_str_len);
|
||||
d.def_stack_word("Blen ", interpret_bytes_len);
|
||||
d.def_ctx_word("Bx. ", interpret_bytes_hex_print_raw);
|
||||
d.def_ctx_word("Bx. ", std::bind(interpret_bytes_hex_print_raw, _1, true));
|
||||
d.def_stack_word("B>X ", std::bind(interpret_bytes_to_hex, _1, true));
|
||||
d.def_stack_word("B>x ", std::bind(interpret_bytes_to_hex, _1, false));
|
||||
d.def_stack_word("x>B ", std::bind(interpret_hex_to_bytes, _1, false));
|
||||
d.def_stack_word("x>B? ", std::bind(interpret_hex_to_bytes, _1, true));
|
||||
d.def_stack_word("B| ", interpret_bytes_split);
|
||||
d.def_stack_word("B+ ", interpret_bytes_concat);
|
||||
d.def_stack_word("B= ", interpret_bytes_equal);
|
||||
|
@ -2542,7 +2568,6 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_ctx_word("dictmerge ", interpret_dict_merge);
|
||||
d.def_ctx_word("dictdiff ", interpret_dict_diff);
|
||||
// slice/bitstring constants
|
||||
d.def_active_word("B{", interpret_bytes_hex_literal);
|
||||
d.def_active_word("x{", interpret_bitstring_hex_literal);
|
||||
d.def_active_word("b{", interpret_bitstring_binary_literal);
|
||||
// boxes/holes/variables
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <openssl/bn.h>
|
||||
#include "td/utils/bits.h"
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
namespace arith {
|
||||
struct dec_string {
|
||||
|
@ -36,6 +37,12 @@ struct hex_string {
|
|||
explicit hex_string(const std::string& s) : str(s) {
|
||||
}
|
||||
};
|
||||
|
||||
struct bin_string {
|
||||
std::string str;
|
||||
explicit bin_string(const std::string& s) : str(s) {
|
||||
}
|
||||
};
|
||||
} // namespace arith
|
||||
|
||||
namespace arith {
|
||||
|
@ -70,6 +77,10 @@ class Bignum {
|
|||
~Bignum() {
|
||||
BN_free(val);
|
||||
}
|
||||
Bignum(const bin_string& bs) {
|
||||
val = BN_new();
|
||||
set_dec_str(bs.str);
|
||||
}
|
||||
Bignum(const dec_string& ds) {
|
||||
val = BN_new();
|
||||
set_dec_str(ds.str);
|
||||
|
@ -150,6 +161,11 @@ class Bignum {
|
|||
return *this;
|
||||
}
|
||||
|
||||
Bignum& set_raw_bytes(std::string s) {
|
||||
CHECK(BN_bin2bn(reinterpret_cast<const td::uint8*>(s.c_str()), td::narrow_cast<td::uint32>(s.size()), val));
|
||||
return *this;
|
||||
}
|
||||
|
||||
Bignum& set_hex_str(std::string s) {
|
||||
bn_assert(BN_hex2bn(&val, s.c_str()));
|
||||
return *this;
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
|
||||
{ (configdict) 0= abort"Configuration dictionary is empty" } : configdict
|
||||
|
||||
// version capabilities --
|
||||
{ <b x{c4} s, rot 32 u, swap 64 u, b> 8 config! } : config.version!
|
||||
1 constant capIhr
|
||||
2 constant capCreateStats
|
||||
|
||||
// max-validators masterchain-validators min-validators --
|
||||
{ swap rot <b swap 16 u, swap 16 u, swap 16 u, b> 16 config! } : config.validator_num!
|
||||
|
||||
|
|
|
@ -150,6 +150,8 @@ Masterchain swap
|
|||
* Configuration Parameters
|
||||
*
|
||||
*/
|
||||
// version capabilities
|
||||
0 capCreateStats config.version!
|
||||
// max-validators max-main-validators min-validators
|
||||
// 9 4 1 config.validator_num!
|
||||
1000 100 5 config.validator_num!
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "common/refcnt.hpp"
|
||||
#include "common/bigint.hpp"
|
||||
#include "common/refint.h"
|
||||
#include "common/bigexp.h"
|
||||
#include "common/bitstring.h"
|
||||
#include "common/util.h"
|
||||
#include "vm/cells.h"
|
||||
|
@ -569,3 +570,87 @@ TEST(bits256_scan, main) {
|
|||
os << "bits256_scan test OK";
|
||||
REGRESSION_VERIFY(os.str());
|
||||
}
|
||||
|
||||
bool check_exp(std::ostream& stream, const td::NegExpBinTable& tab, double x) {
|
||||
long long xx = lround(x * (1LL << 52));
|
||||
td::BigInt256 yy;
|
||||
if (!tab.nexpf(yy, -xx, 52)) {
|
||||
stream << "cannot compute exp(" << x << ") = exp(" << xx << " * 2^(-52))" << std::endl;
|
||||
return false;
|
||||
}
|
||||
double y = yy.to_double() * exp2(-252);
|
||||
double y0 = exp(x);
|
||||
bool ok = (abs(y - y0) < 1e-15);
|
||||
if (!ok) {
|
||||
stream << "exp(" << x << ") = exp(" << xx << " * 2^(-52)) = " << yy << " / 2^252 = " << y << " (correct value is "
|
||||
<< y0 << ") " << (ok ? "match" : "incorrect") << std::endl;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
TEST(bigexp, main) {
|
||||
os = create_ss();
|
||||
td::NegExpBinTable tab(252, 32, -128);
|
||||
bool ok = true;
|
||||
if (!tab.is_valid()) {
|
||||
os << "cannot initialize td::NegExpBinTable(252, 32, -128)" << std::endl;
|
||||
ok = false;
|
||||
} else {
|
||||
// for (int i = -128; i < 32; i++) {
|
||||
// os << "exp(-2^" << i << ") = " << tab.exp_pw2_ref(i) << " / 2^252 = " << tab.exp_pw2_ref(i)->to_double() * exp2(-252) << " (correct value is " << exp(-exp2(i)) << ")" << std::endl;
|
||||
// }
|
||||
ok &= check_exp(os, tab, -2.39);
|
||||
ok &= check_exp(os, tab, 0);
|
||||
ok &= check_exp(os, tab, -1);
|
||||
ok &= check_exp(os, tab, -2);
|
||||
ok &= check_exp(os, tab, -16);
|
||||
ok &= check_exp(os, tab, -17);
|
||||
ok &= check_exp(os, tab, -0.5);
|
||||
ok &= check_exp(os, tab, -0.25);
|
||||
ok &= check_exp(os, tab, -3.1415926535);
|
||||
ok &= check_exp(os, tab, -1e-9);
|
||||
}
|
||||
if (ok) {
|
||||
os << "bigexp test OK\n";
|
||||
} else {
|
||||
os << "bigexp test FAILED\n";
|
||||
}
|
||||
REGRESSION_VERIFY(os.str());
|
||||
}
|
||||
|
||||
bool check_intexp(std::ostream& stream, td::uint64 x, unsigned k, td::uint64 yc = 0) {
|
||||
td::uint64 y = td::umulnexps32(x, k);
|
||||
long long delta = (long long)(y - yc);
|
||||
bool ok = (y <= x && std::abs(delta) <= 1);
|
||||
if (!ok) {
|
||||
stream << x << "*exp(-" << k << "/65536) = " << y << " (correct value " << yc << ", delta = " << delta << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
TEST(uint64_exp, main) {
|
||||
os = create_ss();
|
||||
bool ok = true;
|
||||
ok &= check_intexp(os, 3167801306015831286, 4003, 2980099890648636481);
|
||||
ok &= check_intexp(os, 1583900653007915643, 4003, 1490049945324318240);
|
||||
ok &= check_intexp(os, 9094494907266047891, 17239, 6990995826652297465);
|
||||
ok &= check_intexp(os, 5487867407433215099, 239017, 143048684491504152);
|
||||
ok &= check_intexp(os, 46462010749955243, 239017, 1211095134625318); // up
|
||||
ok &= check_intexp(os, 390263500024095125, 2700001, 1);
|
||||
ok &= check_intexp(os, 390263500024095124, 2700001, 1);
|
||||
ok &= check_intexp(os, std::numeric_limits<td::uint64>::max(), 2952601, 1);
|
||||
ok &= check_intexp(os, std::numeric_limits<td::uint64>::max(), 2952696, 1);
|
||||
ok &= check_intexp(os, std::numeric_limits<td::uint64>::max(), 2952697, 0);
|
||||
ok &= check_intexp(os, std::numeric_limits<td::uint64>::max(), 2952800, 0);
|
||||
ok &= check_intexp(os, std::numeric_limits<td::uint64>::max(), 295269700, 0);
|
||||
ok &= check_intexp(os, std::numeric_limits<td::uint64>::max(), 2000018, 1028453);
|
||||
ok &= check_intexp(os, 1ULL << 60, 2770991, 1);
|
||||
ok &= check_intexp(os, 1ULL << 60, 2770992, 0);
|
||||
if (ok) {
|
||||
os << "uint64_exp test OK\n";
|
||||
} else {
|
||||
os << "uint64_exp test FAILED\n";
|
||||
}
|
||||
REGRESSION_VERIFY(os.str());
|
||||
}
|
||||
|
|
|
@ -30,7 +30,19 @@ struct Type;
|
|||
struct Constructor;
|
||||
|
||||
struct TypeExpr {
|
||||
enum { te_Unknown, te_Type, te_Param, te_Apply, te_Add, te_MulConst, te_IntConst, te_Tuple, te_Ref, te_CondType };
|
||||
enum {
|
||||
te_Unknown,
|
||||
te_Type,
|
||||
te_Param,
|
||||
te_Apply,
|
||||
te_Add,
|
||||
te_GetBit,
|
||||
te_MulConst,
|
||||
te_IntConst,
|
||||
te_Tuple,
|
||||
te_Ref,
|
||||
te_CondType
|
||||
};
|
||||
enum { max_const_expr = 100000, const_htable_size = 170239 };
|
||||
int tp;
|
||||
int value;
|
||||
|
|
|
@ -1464,6 +1464,28 @@ void CppTypeCode::output_cpp_expr(std::ostream& os, const TypeExpr* expr, int pr
|
|||
os << ")";
|
||||
}
|
||||
return;
|
||||
case TypeExpr::te_GetBit:
|
||||
if (prio > 0) {
|
||||
os << "(";
|
||||
}
|
||||
output_cpp_expr(os, expr->args[0], 5);
|
||||
os << " & ";
|
||||
if (expr->args[1]->tp == TypeExpr::te_IntConst && (unsigned)expr->args[1]->value <= 31) {
|
||||
int v = expr->args[1]->value;
|
||||
if (v > 1024) {
|
||||
os << "0x" << std::hex << (1 << v) << std::dec;
|
||||
} else {
|
||||
os << (1 << v);
|
||||
}
|
||||
} else {
|
||||
os << "(1 << ";
|
||||
output_cpp_expr(os, expr->args[1], 5);
|
||||
os << ")";
|
||||
}
|
||||
if (prio > 0) {
|
||||
os << ")";
|
||||
}
|
||||
return;
|
||||
case TypeExpr::te_IntConst:
|
||||
os << expr->value;
|
||||
return;
|
||||
|
|
|
@ -81,6 +81,7 @@ void define_keywords() {
|
|||
.add_kw_char('=')
|
||||
.add_kw_char('_')
|
||||
.add_kw_char('?')
|
||||
.add_kw_char('.')
|
||||
.add_kw_char('~')
|
||||
.add_kw_char('^');
|
||||
|
||||
|
@ -1128,6 +1129,19 @@ void TypeExpr::show(std::ostream& os, const Constructor* cs, int prio, int mode)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case te_GetBit: {
|
||||
assert(args.size() == 2);
|
||||
if (prio > 97) {
|
||||
os << '(';
|
||||
}
|
||||
args[0]->show(os, cs, 98, mode);
|
||||
os << "."; // priority 20
|
||||
args[1]->show(os, cs, 98, mode);
|
||||
if (prio > 97) {
|
||||
os << ')';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case te_IntConst: {
|
||||
assert(args.empty());
|
||||
os << value;
|
||||
|
@ -1202,11 +1216,12 @@ int abstract_nat_const(int value) {
|
|||
}
|
||||
|
||||
unsigned char abstract_add_base_table[4][4] = {{0, 1, 2, 3}, {1, 2, 3, 2}, {2, 3, 2, 3}, {3, 2, 3, 2}};
|
||||
|
||||
unsigned char abstract_mul_base_table[4][4] = {{0, 0, 0, 0}, {0, 1, 2, 3}, {0, 2, 2, 2}, {0, 3, 2, 3}};
|
||||
unsigned char abstract_getbit_b_table[4][4] = {{1, 1, 1, 1}, {2, 1, 1, 1}, {1, 3, 3, 3}, {2, 3, 3, 3}};
|
||||
|
||||
unsigned char abstract_add_table[16][16];
|
||||
unsigned char abstract_mul_table[16][16];
|
||||
unsigned char abstract_getbit_table[16][16];
|
||||
|
||||
void compute_semilat_table(unsigned char table[16][16], const unsigned char base_table[4][4]) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
|
@ -1226,9 +1241,28 @@ void compute_semilat_table(unsigned char table[16][16], const unsigned char base
|
|||
}
|
||||
}
|
||||
|
||||
void compute_semilat_b_table(unsigned char table[16][16], const unsigned char b_table[4][4]) {
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
int res = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((x >> i) & 1) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if ((y >> j) & 1) {
|
||||
res |= b_table[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
table[x][y] = (unsigned char)res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_abstract_tables() {
|
||||
compute_semilat_table(abstract_add_table, abstract_add_base_table);
|
||||
compute_semilat_table(abstract_mul_table, abstract_mul_base_table);
|
||||
compute_semilat_b_table(abstract_getbit_table, abstract_getbit_b_table);
|
||||
}
|
||||
|
||||
int abstract_add(int x, int y) {
|
||||
|
@ -1239,6 +1273,10 @@ int abstract_mul(int x, int y) {
|
|||
return abstract_mul_table[x & 15][y & 15];
|
||||
}
|
||||
|
||||
int abstract_getbit(int x, int y) {
|
||||
return abstract_getbit_table[x & 15][y & 15];
|
||||
}
|
||||
|
||||
int TypeExpr::abstract_interpret_nat() const {
|
||||
if (!is_nat || tchk_only) {
|
||||
return 0;
|
||||
|
@ -1249,6 +1287,9 @@ int TypeExpr::abstract_interpret_nat() const {
|
|||
case te_Add:
|
||||
assert(args.size() == 2);
|
||||
return abstract_add(args[0]->abstract_interpret_nat(), args[1]->abstract_interpret_nat());
|
||||
case te_GetBit:
|
||||
assert(args.size() == 2);
|
||||
return abstract_getbit(args[0]->abstract_interpret_nat(), args[1]->abstract_interpret_nat());
|
||||
case te_IntConst:
|
||||
return abstract_nat_const(value);
|
||||
case te_MulConst:
|
||||
|
@ -1464,6 +1505,11 @@ void TypeExpr::const_type_name(std::ostream& os) const {
|
|||
os << "_plus";
|
||||
args[1]->const_type_name(os);
|
||||
return;
|
||||
case te_GetBit:
|
||||
args[0]->const_type_name(os);
|
||||
os << "_bit";
|
||||
args[1]->const_type_name(os);
|
||||
return;
|
||||
case te_IntConst:
|
||||
os << "_" << value;
|
||||
return;
|
||||
|
@ -1573,6 +1619,13 @@ bool TypeExpr::bind_value(bool value_negated, Constructor& cs, bool checking_typ
|
|||
args[0]->bind_value(value_negated, cs);
|
||||
return true;
|
||||
}
|
||||
case te_GetBit: {
|
||||
assert(is_nat && args.size() == 2 && !args[0]->negated && !args[1]->negated);
|
||||
assert(!negated);
|
||||
args[0]->bind_value(false, cs);
|
||||
args[1]->bind_value(false, cs);
|
||||
return true;
|
||||
}
|
||||
case te_Type: {
|
||||
assert(!is_nat && !negated);
|
||||
return true;
|
||||
|
@ -2057,10 +2110,34 @@ TypeExpr* parse_term(Lexer& lex, Constructor& cs, int mode) {
|
|||
}
|
||||
}
|
||||
|
||||
// E ? E [ : E ]
|
||||
|
||||
TypeExpr* parse_expr95(Lexer& lex, Constructor& cs, int mode) {
|
||||
// E[.E]
|
||||
TypeExpr* parse_expr97(Lexer& lex, Constructor& cs, int mode) {
|
||||
TypeExpr* expr = parse_term(lex, cs, mode | 3);
|
||||
if (lex.tp() == '.') {
|
||||
src::SrcLocation where = lex.cur().loc;
|
||||
expr->close(lex.cur().loc);
|
||||
// std::cerr << "parse ., mode " << mode << std::endl;
|
||||
if (!(mode & 2)) {
|
||||
throw src::ParseError{where, "bitfield expression cannot be used instead of a type expression"};
|
||||
}
|
||||
if (!expr->is_nat) {
|
||||
throw src::ParseError{where, "cannot apply bit selection operator `.` to types"};
|
||||
}
|
||||
lex.next();
|
||||
TypeExpr* expr2 = parse_term(lex, cs, mode & ~1);
|
||||
expr2->close(lex.cur().loc);
|
||||
if (expr->negated || expr2->negated) {
|
||||
throw src::ParseError{where, "cannot apply bit selection operator `.` to values of negative polarity"};
|
||||
}
|
||||
expr = TypeExpr::mk_apply(where, TypeExpr::te_GetBit, expr, expr2);
|
||||
}
|
||||
expr->check_mode(lex.cur().loc, mode);
|
||||
return expr;
|
||||
}
|
||||
|
||||
// E ? E [ : E ]
|
||||
TypeExpr* parse_expr95(Lexer& lex, Constructor& cs, int mode) {
|
||||
TypeExpr* expr = parse_expr97(lex, cs, mode | 3);
|
||||
if (lex.tp() != '?') {
|
||||
expr->check_mode(lex.cur().loc, mode);
|
||||
return expr;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue