mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
liteclient signature check support
1. update liteclient/liteserver. Now liteserver sends signatures of blocks and liteclient checks them. I.e. liteclient completely checks received data. 2. validator-engine: more GC options 3. blockchain-explorer: show all block transactions (instead of 256) 4. some bugfixes
This commit is contained in:
parent
d8244eff53
commit
9d6853ef24
58 changed files with 1480 additions and 325 deletions
|
@ -1643,6 +1643,37 @@ td::Status unpack_block_prev_blk_ext(Ref<vm::Cell> block_root, const ton::BlockI
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status check_block_header(Ref<vm::Cell> block_root, const ton::BlockIdExt& id, ton::Bits256* store_shard_hash_to) {
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockInfo::Record info;
|
||||
ton::ShardIdFull shard;
|
||||
if (!(tlb::unpack_cell(block_root, blk) && tlb::unpack_cell(blk.info, info) && !info.version &&
|
||||
block::tlb::t_ShardIdent.unpack(info.shard.write(), shard) && !info.vert_seq_no)) {
|
||||
return td::Status::Error("cannot unpack block header");
|
||||
}
|
||||
ton::BlockId hdr_id{shard, (unsigned)info.seq_no};
|
||||
if (id.id != hdr_id) {
|
||||
return td::Status::Error("block header contains block id "s + hdr_id.to_str() + ", expected " + id.id.to_str());
|
||||
}
|
||||
if (id.root_hash != block_root->get_hash().bits()) {
|
||||
return td::Status::Error("block header has incorrect root hash "s + block_root->get_hash().bits().to_hex(256) +
|
||||
" instead of expected " + id.root_hash.to_hex());
|
||||
}
|
||||
if (info.not_master != !shard.is_masterchain()) {
|
||||
return td::Status::Error("block has invalid not_master flag in its (Merkelized) header");
|
||||
}
|
||||
if (store_shard_hash_to) {
|
||||
vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update};
|
||||
if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update
|
||||
&& upd_cs.size_ext() == 0x20228)) {
|
||||
return td::Status::Error("invalid Merkle update in block header");
|
||||
}
|
||||
auto upd_hash = upd_cs.prefetch_ref(1)->get_hash(0);
|
||||
*store_shard_hash_to = upd_hash.bits();
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
std::unique_ptr<vm::AugmentedDictionary> get_prev_blocks_dict(Ref<vm::Cell> state_root) {
|
||||
block::gen::ShardStateUnsplit::Record info;
|
||||
block::gen::McStateExtra::Record extra_info;
|
||||
|
|
|
@ -457,27 +457,47 @@ struct ValueFlow {
|
|||
|
||||
std::ostream& operator<<(std::ostream& os, const ValueFlow& vflow);
|
||||
|
||||
struct BlkProofLink {
|
||||
struct BlockProofLink {
|
||||
ton::BlockIdExt from, to;
|
||||
bool is_key{false}, is_fwd{false};
|
||||
Ref<vm::Cell> dest_proof, shard_proof, proof;
|
||||
Ref<vm::Cell> dest_proof, state_proof, proof;
|
||||
ton::CatchainSeqno cc_seqno{0};
|
||||
td::uint32 validator_set_hash{0};
|
||||
std::vector<ton::BlockSignature> signatures;
|
||||
BlkProofLink(ton::BlockIdExt _from, ton::BlockIdExt _to, bool _iskey = false)
|
||||
BlockProofLink(ton::BlockIdExt _from, ton::BlockIdExt _to, bool _iskey = false)
|
||||
: from(_from), to(_to), is_key(_iskey), is_fwd(to.seqno() > from.seqno()) {
|
||||
}
|
||||
bool incomplete() const {
|
||||
return dest_proof.is_null();
|
||||
}
|
||||
td::Status validate() const;
|
||||
};
|
||||
|
||||
struct BlkProofChain {
|
||||
struct BlockProofChain {
|
||||
ton::BlockIdExt from, to;
|
||||
int mode;
|
||||
std::vector<BlkProofLink> links;
|
||||
bool complete{false}, has_key_block{false}, valid{false};
|
||||
ton::BlockIdExt key_blkid;
|
||||
std::vector<BlockProofLink> links;
|
||||
std::size_t link_count() const {
|
||||
return links.size();
|
||||
}
|
||||
BlkProofChain(ton::BlockIdExt _from, ton::BlockIdExt _to, int _mode) : from(_from), to(_to), mode(_mode) {
|
||||
BlockProofChain(ton::BlockIdExt _from, ton::BlockIdExt _to, int _mode = 0) : from(_from), to(_to), mode(_mode) {
|
||||
}
|
||||
BlockProofLink& new_link(const ton::BlockIdExt& cur, const ton::BlockIdExt& next, bool iskey = false) {
|
||||
links.emplace_back(cur, next, iskey);
|
||||
return links.back();
|
||||
}
|
||||
const BlockProofLink& last_link() const {
|
||||
return links.back();
|
||||
}
|
||||
BlockProofLink& last_link() {
|
||||
return links.back();
|
||||
}
|
||||
bool last_link_incomplete() const {
|
||||
return !links.empty() && last_link().incomplete();
|
||||
}
|
||||
td::Status validate();
|
||||
};
|
||||
|
||||
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard);
|
||||
|
@ -529,6 +549,8 @@ td::Status unpack_block_prev_blk_ext(Ref<vm::Cell> block_root, const ton::BlockI
|
|||
td::Status unpack_block_prev_blk_try(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
|
||||
std::vector<ton::BlockIdExt>& prev, ton::BlockIdExt& mc_blkid, bool& after_split,
|
||||
ton::BlockIdExt* fetch_blkid = nullptr);
|
||||
td::Status check_block_header(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
|
||||
ton::Bits256* store_shard_hash_to = nullptr);
|
||||
|
||||
std::unique_ptr<vm::AugmentedDictionary> get_prev_blocks_dict(Ref<vm::Cell> state_root);
|
||||
bool get_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
|
||||
|
|
|
@ -25,8 +25,12 @@
|
|||
#include "ton/ton-shard.h"
|
||||
|
||||
#include "vm/cells/MerkleProof.h"
|
||||
#include "openssl/digest.h"
|
||||
#include "Ed25519.h"
|
||||
|
||||
namespace block {
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to,
|
||||
bool check_state_hash) {
|
||||
ton::RootHash vhash{root->get_hash().bits()};
|
||||
|
@ -291,4 +295,234 @@ td::Result<TransactionList::Info> TransactionList::validate() const {
|
|||
}
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
td::Status BlockProofLink::validate() const {
|
||||
if (!(from.is_masterchain_ext() && to.is_masterchain_ext())) {
|
||||
return td::Status::Error("BlockProofLink must have both source and destination blocks in the masterchain");
|
||||
}
|
||||
if (from.seqno() == to.seqno()) {
|
||||
return td::Status::Error("BlockProofLink connects two masterchain blocks "s + from.to_str() + " and " +
|
||||
to.to_str() + " of equal height");
|
||||
}
|
||||
if (is_fwd != (from.seqno() < to.seqno())) {
|
||||
return td::Status::Error("BlockProofLink from "s + from.to_str() + " to " + to.to_str() +
|
||||
" is incorrectly declared as a " + (is_fwd ? "forward" : "backward") + " link");
|
||||
}
|
||||
if (dest_proof.is_null() && to.seqno()) {
|
||||
return td::Status::Error("BlockProofLink contains no proof for destination block "s + to.to_str());
|
||||
}
|
||||
if (proof.is_null()) {
|
||||
return td::Status::Error("BlockProofLink contains no proof for source block "s + from.to_str());
|
||||
}
|
||||
if (!is_fwd && state_proof.is_null()) {
|
||||
return td::Status::Error("a backward BlockProofLink contains no proof for the source state of "s + from.to_str());
|
||||
}
|
||||
if (is_fwd && signatures.empty()) {
|
||||
return td::Status::Error("a forward BlockProofLink from "s + from.to_str() + " to " + to.to_str() +
|
||||
" contains no signatures");
|
||||
}
|
||||
try {
|
||||
// virtualize Merkle proof roots
|
||||
auto vs_root = vm::MerkleProof::virtualize(proof, 1);
|
||||
if (vs_root.is_null()) {
|
||||
return td::Status::Error("BlockProofLink contains an invalid Merkle proof for source block "s + from.to_str());
|
||||
}
|
||||
ton::Bits256 state_hash;
|
||||
if (from.seqno()) {
|
||||
TRY_STATUS(check_block_header(vs_root, from, is_fwd ? nullptr : &state_hash));
|
||||
}
|
||||
auto vd_root = dest_proof.not_null() ? vm::MerkleProof::virtualize(dest_proof, 1) : Ref<vm::Cell>{};
|
||||
if (vd_root.is_null() && to.seqno()) {
|
||||
return td::Status::Error("BlockProofLink contains an invalid Merkle proof for destination block "s + to.to_str());
|
||||
}
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockInfo::Record info;
|
||||
if (to.seqno()) {
|
||||
TRY_STATUS(check_block_header(vd_root, to));
|
||||
if (!(tlb::unpack_cell(vd_root, blk) && tlb::unpack_cell(blk.info, info))) {
|
||||
return td::Status::Error("cannot unpack header for block "s + from.to_str());
|
||||
}
|
||||
if (info.key_block != is_key) {
|
||||
return td::Status::Error(PSTRING() << "incorrect is_key_block value " << is_key << " for destination block "
|
||||
<< to.to_str());
|
||||
}
|
||||
} else if (!is_key) {
|
||||
// return td::Status::Error("Zerostate destination block "s + to.to_str() + " does not have is_key_block set");
|
||||
}
|
||||
if (!is_fwd) {
|
||||
// check a backward link
|
||||
auto vstate_root = vm::MerkleProof::virtualize(state_proof, 1);
|
||||
if (vstate_root.is_null()) {
|
||||
return td::Status::Error("backward BlockProofLink contains an invalid Merkle proof for source state "s +
|
||||
from.to_str());
|
||||
}
|
||||
if (state_hash != vstate_root->get_hash().bits()) {
|
||||
return td::Status::Error("BlockProofLink contains a state proof for "s + from.to_str() +
|
||||
" with incorrect root hash");
|
||||
}
|
||||
TRY_RESULT(config, block::ConfigInfo::extract_config(vstate_root, block::ConfigInfo::needPrevBlocks));
|
||||
if (!config->check_old_mc_block_id(to, true)) {
|
||||
return td::Status::Error("cannot check that "s + to.to_str() + " is indeed a previous masterchain block of " +
|
||||
from.to_str() + " using the presented Merkle proof of masterchain state");
|
||||
}
|
||||
return td::Status::OK();
|
||||
} else {
|
||||
// check a forward link
|
||||
// extract configuration from source key block or zerostate
|
||||
auto cfg_res = from.seqno() ? block::Config::extract_from_key_block(vs_root, block::ConfigInfo::needValidatorSet)
|
||||
: block::Config::extract_from_state(vs_root, block::ConfigInfo::needValidatorSet);
|
||||
if (cfg_res.is_error()) {
|
||||
return td::Status::Error("cannot extract configuration from source key block "s + from.to_str() +
|
||||
" of a forward BlockProofLink: " + cfg_res.move_as_error().to_string());
|
||||
}
|
||||
auto config = cfg_res.move_as_ok();
|
||||
// compute validator set
|
||||
ton::ShardIdFull shard{ton::masterchainId};
|
||||
auto nodes = config->compute_validator_set(shard, info.gen_utime, info.gen_catchain_seqno);
|
||||
if (nodes.empty()) {
|
||||
return td::Status::Error(PSTRING()
|
||||
<< "while checking a forward BlockProofLink: cannot compute validator set for block "
|
||||
<< to.to_str() << " with utime " << info.gen_utime << " and cc_seqno "
|
||||
<< info.gen_catchain_seqno << " starting from previous key block " << from.to_str());
|
||||
}
|
||||
// check computed validator set hash
|
||||
auto vset_hash = compute_validator_set_hash(cc_seqno, shard, nodes);
|
||||
if (vset_hash != info.gen_validator_list_hash_short) {
|
||||
return td::Status::Error(
|
||||
PSTRING() << "while checking a forward BlockProofLink: computed validator set for block " << to.to_str()
|
||||
<< " with utime " << info.gen_utime << " and cc_seqno " << info.gen_catchain_seqno
|
||||
<< " starting from previous key block " << from.to_str() << " has hash " << vset_hash
|
||||
<< " different from " << info.gen_validator_list_hash_short << " stated in block header");
|
||||
}
|
||||
// check signatures
|
||||
auto err = check_block_signatures(nodes, signatures, to);
|
||||
if (err.is_error()) {
|
||||
return td::Status::Error("error checking signatures for block "s + to.to_str() +
|
||||
" in a forward BlockProofLink: " + err.to_string());
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
} catch (vm::VmError& err) {
|
||||
return td::Status::Error("vm error while checking BlockProofLink from "s + from.to_str() + " to " + to.to_str() +
|
||||
" : " + err.get_msg());
|
||||
} catch (vm::VmVirtError& err) {
|
||||
return td::Status::Error("virtualization error while checking BlockProofLink from "s + from.to_str() + " to " +
|
||||
to.to_str() + " : " + err.get_msg());
|
||||
}
|
||||
}
|
||||
|
||||
td::Status BlockProofChain::validate() {
|
||||
valid = false;
|
||||
has_key_block = false;
|
||||
key_blkid.invalidate();
|
||||
if (!(from.is_masterchain_ext() && to.is_masterchain_ext())) {
|
||||
return td::Status::Error("BlockProofChain must have both source and destination blocks in the masterchain");
|
||||
}
|
||||
if (!link_count()) {
|
||||
if (from != to) {
|
||||
return td::Status::Error("BlockProofChain has no links, but its source block "s + from.to_str() +
|
||||
" and destination block " + to.to_str() + " differ");
|
||||
}
|
||||
valid = true;
|
||||
return td::Status::OK();
|
||||
}
|
||||
ton::BlockIdExt cur = from;
|
||||
int i = 0;
|
||||
for (const auto& link : links) {
|
||||
++i;
|
||||
if (link.from != cur) {
|
||||
return td::Status::Error(PSTRING() << "link #" << i << " in a BlockProofChain begins with block "
|
||||
<< link.from.to_str() << " but the previous link ends at different block "
|
||||
<< cur.to_str());
|
||||
}
|
||||
auto err = link.validate();
|
||||
if (err.is_error()) {
|
||||
return td::Status::Error(PSTRING() << "link #" << i << " in BlockProofChain is invalid: " << err.to_string());
|
||||
}
|
||||
if (link.is_key && (!has_key_block || key_blkid.seqno() < link.to.seqno())) {
|
||||
key_blkid = link.to;
|
||||
has_key_block = true;
|
||||
}
|
||||
cur = link.to;
|
||||
}
|
||||
if (cur != to) {
|
||||
return td::Status::Error("last link of BlockProofChain ends at block "s + cur.to_str() +
|
||||
" different from declared chain destination block " + to.to_str());
|
||||
}
|
||||
valid = true;
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Bits256 compute_node_id_short(td::Bits256 ed25519_pubkey) {
|
||||
// pub.ed25519#4813b4c6 key:int256 = PublicKey;
|
||||
struct pubkey {
|
||||
int magic = 0x4813b4c6;
|
||||
unsigned char ed25519_key[32];
|
||||
} PK;
|
||||
std::memcpy(PK.ed25519_key, ed25519_pubkey.data(), 32);
|
||||
static_assert(sizeof(pubkey) == 36, "PublicKey structure is not 36 bytes long");
|
||||
td::Bits256 hash;
|
||||
digest::hash_str<digest::SHA256>(hash.data(), (void*)&PK, sizeof(pubkey));
|
||||
return hash;
|
||||
}
|
||||
|
||||
td::Status check_block_signatures(const std::vector<ton::ValidatorDescr>& nodes,
|
||||
const std::vector<ton::BlockSignature>& signatures, const ton::BlockIdExt& blkid) {
|
||||
if (nodes.empty()) {
|
||||
return td::Status::Error("empty validator public keys set");
|
||||
}
|
||||
if (signatures.empty()) {
|
||||
return td::Status::Error("empty validator signature set");
|
||||
}
|
||||
// compute the string to be signed and its hash
|
||||
unsigned char to_sign[68];
|
||||
td::as<td::uint32>(to_sign) = 0xc50b6e70; // ton.blockId root_cell_hash:int256 file_hash:int256 = ton.BlockId;
|
||||
memcpy(to_sign + 4, blkid.root_hash.data(), 32);
|
||||
memcpy(to_sign + 36, blkid.file_hash.data(), 32);
|
||||
// unsigned char hash[32];
|
||||
// digest::hash_str<digest::SHA256>(hash, (void*)to_sign, sizeof(to_sign));
|
||||
|
||||
ton::ValidatorWeight total_weight = 0, signed_weight = 0;
|
||||
std::vector<std::pair<td::Bits256, unsigned>> node_map;
|
||||
for (unsigned i = 0; i < nodes.size(); i++) {
|
||||
total_weight += nodes[i].weight;
|
||||
node_map.emplace_back(compute_node_id_short(nodes[i].key), i);
|
||||
}
|
||||
std::sort(node_map.begin(), node_map.end());
|
||||
std::vector<unsigned> seen;
|
||||
for (auto& sig : signatures) {
|
||||
// lookup node in validator set
|
||||
auto& id = sig.node;
|
||||
auto it = std::lower_bound(node_map.begin(), node_map.end(), id,
|
||||
[](const auto& p, const auto& x) { return p.first < x; });
|
||||
if (it == node_map.end() || it->first != id) {
|
||||
return td::Status::Error("signature set contains unknown NodeIdShort "s + id.to_hex());
|
||||
}
|
||||
unsigned i = it->second;
|
||||
seen.emplace_back(i);
|
||||
// check one signature
|
||||
td::Ed25519::PublicKey pub_key{td::SecureString{nodes.at(i).key.as_slice()}};
|
||||
auto res = pub_key.verify_signature(td::Slice{to_sign, 68}, sig.signature.as_slice());
|
||||
if (res.is_error()) {
|
||||
return res;
|
||||
}
|
||||
signed_weight += nodes[i].weight;
|
||||
if (signed_weight > total_weight) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::sort(seen.begin(), seen.end());
|
||||
for (std::size_t i = 1; i < seen.size(); i++) {
|
||||
if (seen[i] == seen[i - 1]) {
|
||||
return td::Status::Error("signature set contains duplicate signature for NodeIdShort "s +
|
||||
compute_node_id_short(nodes.at(seen[i]).key).to_hex());
|
||||
}
|
||||
}
|
||||
if (3 * signed_weight <= 2 * total_weight) {
|
||||
return td::Status::Error(PSTRING() << "insufficient total signature weight: only " << signed_weight << " out of "
|
||||
<< total_weight);
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
} // namespace block
|
||||
|
|
|
@ -33,6 +33,9 @@ td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const
|
|||
td::Result<td::Bits256> check_state_proof(ton::BlockIdExt blkid, td::Slice proof);
|
||||
td::Result<Ref<vm::Cell>> check_extract_state_proof(ton::BlockIdExt blkid, td::Slice proof, td::Slice data);
|
||||
|
||||
td::Status check_block_signatures(const std::vector<ton::ValidatorDescr>& nodes,
|
||||
const std::vector<ton::BlockSignature>& signatures, const ton::BlockIdExt& blkid);
|
||||
|
||||
struct AccountState {
|
||||
ton::BlockIdExt blk;
|
||||
ton::BlockIdExt shard_blk;
|
||||
|
|
|
@ -1003,47 +1003,47 @@ std::vector<ton::BlockId> ShardConfig::get_shard_hash_ids(
|
|||
std::vector<ton::BlockId> res;
|
||||
bool mcout = mc_shard_hash_.is_null() || !mc_shard_hash_->seqno(); // include masterchain as a shard if seqno > 0
|
||||
bool ok = shard_hashes_dict_->check_for_each(
|
||||
[&res, &mcout, mc_shard_hash_ = mc_shard_hash_, &filter](Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key,
|
||||
int n) -> bool {
|
||||
int workchain = (int)key.get_int(n);
|
||||
if (workchain >= 0 && !mcout) {
|
||||
if (filter(ton::ShardIdFull{ton::masterchainId}, true)) {
|
||||
res.emplace_back(mc_shard_hash_->blk_.id);
|
||||
}
|
||||
mcout = true;
|
||||
}
|
||||
if (!cs_ref->have_refs()) {
|
||||
return false;
|
||||
}
|
||||
std::stack<std::pair<Ref<vm::Cell>, unsigned long long>> stack;
|
||||
stack.emplace(cs_ref->prefetch_ref(), ton::shardIdAll);
|
||||
while (!stack.empty()) {
|
||||
vm::CellSlice cs{vm::NoVm{}, std::move(stack.top().first)};
|
||||
unsigned long long shard = stack.top().second;
|
||||
stack.pop();
|
||||
int t = (int)cs.fetch_ulong(1);
|
||||
if (t < 0) {
|
||||
return false;
|
||||
}
|
||||
if (!filter(ton::ShardIdFull{workchain, shard}, !t)) {
|
||||
continue;
|
||||
}
|
||||
if (!t) {
|
||||
if (!(cs.advance(4) && cs.have(32))) {
|
||||
[&res, &mcout, mc_shard_hash_ = mc_shard_hash_, &filter ](Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, int n)
|
||||
->bool {
|
||||
int workchain = (int)key.get_int(n);
|
||||
if (workchain >= 0 && !mcout) {
|
||||
if (filter(ton::ShardIdFull{ton::masterchainId}, true)) {
|
||||
res.emplace_back(mc_shard_hash_->blk_.id);
|
||||
}
|
||||
mcout = true;
|
||||
}
|
||||
if (!cs_ref->have_refs()) {
|
||||
return false;
|
||||
}
|
||||
res.emplace_back(workchain, shard, (int)cs.prefetch_ulong(32));
|
||||
continue;
|
||||
}
|
||||
unsigned long long delta = (td::lower_bit64(shard) >> 1);
|
||||
if (!delta || cs.size_ext() != 0x20000) {
|
||||
return false;
|
||||
}
|
||||
stack.emplace(cs.prefetch_ref(1), shard + delta);
|
||||
stack.emplace(cs.prefetch_ref(0), shard - delta);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
std::stack<std::pair<Ref<vm::Cell>, unsigned long long>> stack;
|
||||
stack.emplace(cs_ref->prefetch_ref(), ton::shardIdAll);
|
||||
while (!stack.empty()) {
|
||||
vm::CellSlice cs{vm::NoVm{}, std::move(stack.top().first)};
|
||||
unsigned long long shard = stack.top().second;
|
||||
stack.pop();
|
||||
int t = (int)cs.fetch_ulong(1);
|
||||
if (t < 0) {
|
||||
return false;
|
||||
}
|
||||
if (!filter(ton::ShardIdFull{workchain, shard}, !t)) {
|
||||
continue;
|
||||
}
|
||||
if (!t) {
|
||||
if (!(cs.advance(4) && cs.have(32))) {
|
||||
return false;
|
||||
}
|
||||
res.emplace_back(workchain, shard, (int)cs.prefetch_ulong(32));
|
||||
continue;
|
||||
}
|
||||
unsigned long long delta = (td::lower_bit64(shard) >> 1);
|
||||
if (!delta || cs.size_ext() != 0x20000) {
|
||||
return false;
|
||||
}
|
||||
stack.emplace(cs.prefetch_ref(1), shard + delta);
|
||||
stack.emplace(cs.prefetch_ref(0), shard - delta);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
true);
|
||||
if (!ok) {
|
||||
return {};
|
||||
|
@ -1399,8 +1399,8 @@ td::Result<std::vector<ton::StdSmcAddress>> Config::get_special_smartcontracts(b
|
|||
return td::Status::Error(-666, "configuration loaded without fundamental smart contract list");
|
||||
}
|
||||
std::vector<ton::StdSmcAddress> res;
|
||||
if (!special_smc_dict->check_for_each([&res, &without_config, conf_addr = config_addr.bits()](
|
||||
Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, int n) {
|
||||
if (!special_smc_dict->check_for_each([&res, &without_config, conf_addr = config_addr.bits() ](
|
||||
Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, int n) {
|
||||
if (cs_ref->size_ext() || n != 256) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1565,7 +1565,7 @@ std::vector<ton::ValidatorDescr> Config::do_compute_validator_set(const block::C
|
|||
ton::ShardIdFull shard,
|
||||
const block::ValidatorSet& vset, ton::UnixTime time,
|
||||
ton::CatchainSeqno cc_seqno) {
|
||||
LOG(DEBUG) << "in Config::do_compute_validator_set() for " << shard.to_str() << " ; cc_seqno=" << cc_seqno;
|
||||
// LOG(DEBUG) << "in Config::do_compute_validator_set() for " << shard.to_str() << " ; cc_seqno=" << cc_seqno;
|
||||
std::vector<ton::ValidatorDescr> nodes;
|
||||
bool is_mc = shard.is_masterchain();
|
||||
unsigned count = std::min<unsigned>(is_mc ? vset.main : ccv_conf.shard_val_num, vset.total);
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "block/block.h"
|
||||
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/optional.h"
|
||||
#include "td/utils/PathView.h"
|
||||
#include "td/utils/port/thread.h"
|
||||
|
@ -1037,7 +1038,7 @@ void interpret_tuple_index(vm::Stack& stack) {
|
|||
if ((td::uint64)idx >= tuple->size()) {
|
||||
throw vm::VmError{vm::Excno::range_chk, "array index out of range"};
|
||||
}
|
||||
stack.push((*tuple)[idx]);
|
||||
stack.push((*tuple)[td::narrow_cast<size_t>(idx)]);
|
||||
}
|
||||
|
||||
void interpret_tuple_set(vm::Stack& stack) {
|
||||
|
@ -1047,7 +1048,7 @@ void interpret_tuple_set(vm::Stack& stack) {
|
|||
if ((td::uint64)idx >= tuple->size()) {
|
||||
throw vm::VmError{vm::Excno::range_chk, "array index out of range"};
|
||||
}
|
||||
tuple.write()[idx] = std::move(val);
|
||||
tuple.write()[td::narrow_cast<size_t>(idx)] = std::move(val);
|
||||
stack.push_tuple(std::move(tuple));
|
||||
}
|
||||
|
||||
|
@ -1095,7 +1096,7 @@ void interpret_allot(vm::Stack& stack) {
|
|||
auto n = stack.pop_long_range(0xffffffff);
|
||||
Ref<vm::Tuple> ref{true};
|
||||
auto& tuple = ref.unique_write();
|
||||
tuple.reserve(n);
|
||||
tuple.reserve(td::narrow_cast<size_t>(n));
|
||||
while (n-- > 0) {
|
||||
tuple.emplace_back(Ref<vm::Box>{true});
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
var cs = begin_parse(get_data());
|
||||
var cfg_dict = cs~load_ref();
|
||||
int kl = 32;
|
||||
cfg_dict~idict_set_ref(kl, -17, begin_cell().store_uint(now() >> 8, 32).end_cell());
|
||||
;; cfg_dict~idict_set_ref(kl, -17, begin_cell().store_uint(now() >> 16, 32).end_cell());
|
||||
var next_vset = cfg_dict.idict_get_ref(kl, 36);
|
||||
ifnot (next_vset.null?()) {
|
||||
;; check whether we have to set next_vset as the current validator set
|
||||
|
|
|
@ -519,11 +519,11 @@ TEST(base64, main) {
|
|||
REGRESSION_VERIFY(os.str());
|
||||
}
|
||||
|
||||
void check_bits256_scan(std::ostream& os, td::Bits256 a, td::Bits256 b) {
|
||||
void check_bits256_scan(std::ostream& stream, td::Bits256 a, td::Bits256 b) {
|
||||
auto c = a ^ b;
|
||||
auto bit = c.count_leading_zeroes();
|
||||
auto bit2 = a.count_matching(b);
|
||||
// os << a.to_hex() << " and " << b.to_hex() << " match in " << bit << " or " << bit2 << " first bits" << std::endl;
|
||||
// stream << a.to_hex() << " and " << b.to_hex() << " match in " << bit << " or " << bit2 << " first bits" << std::endl;
|
||||
// std::cerr << a.to_hex() << " and " << b.to_hex() << " match in " << bit << " or " << bit2 << " first bits (a XOR b = " << c.to_hex() << ")" << std::endl;
|
||||
CHECK((int)bit >= 0 && bit <= 256);
|
||||
for (td::uint32 i = 0; i < bit; i++) {
|
||||
|
@ -533,7 +533,7 @@ void check_bits256_scan(std::ostream& os, td::Bits256 a, td::Bits256 b) {
|
|||
CHECK(bit == bit2);
|
||||
}
|
||||
|
||||
void check_bits_scan(std::ostream& os, td::ConstBitPtr a, bool value) {
|
||||
void check_bits_scan(std::ostream& stream, td::ConstBitPtr a, bool value) {
|
||||
auto bit = (unsigned)a.scan(value, 256);
|
||||
CHECK((int)bit >= 0 && bit <= 256);
|
||||
for (td::uint32 i = 0; i < bit; i++) {
|
||||
|
|
|
@ -717,7 +717,7 @@ td::Result<td::Slice> BagOfCells::get_cell_slice(int idx, td::Slice data) {
|
|||
return td::Status::Error(PSLICE() << "invalid index entry [" << offs << "; " << offs_end << "], "
|
||||
<< td::tag("data.size()", data.size()));
|
||||
}
|
||||
return data.substr(offs, offs_end - offs);
|
||||
return data.substr(offs, td::narrow_cast<size_t>(offs_end - offs));
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::DataCell>> BagOfCells::deserialize_cell(int idx, td::Slice cells_slice,
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "vm/cells/MerkleProof.h"
|
||||
#include "vm/cells/CellBuilder.h"
|
||||
#include "vm/cells/CellSlice.h"
|
||||
#include "vm/boc.h"
|
||||
|
||||
#include "td/utils/HashMap.h"
|
||||
#include "td/utils/HashSet.h"
|
||||
|
@ -260,4 +261,39 @@ Ref<Cell> MerkleProof::combine(Ref<Cell> a, Ref<Cell> b) {
|
|||
}
|
||||
return res.move_as_ok();
|
||||
}
|
||||
|
||||
MerkleProofBuilder::MerkleProofBuilder(Ref<Cell> root)
|
||||
: usage_tree(std::make_shared<CellUsageTree>()), orig_root(std::move(root)) {
|
||||
usage_root = UsageCell::create(orig_root, usage_tree->root_ptr());
|
||||
}
|
||||
|
||||
void MerkleProofBuilder::reset(Ref<Cell> root) {
|
||||
usage_tree = std::make_shared<CellUsageTree>();
|
||||
orig_root = std::move(root);
|
||||
usage_root = UsageCell::create(orig_root, usage_tree->root_ptr());
|
||||
}
|
||||
|
||||
void MerkleProofBuilder::clear() {
|
||||
usage_tree.reset();
|
||||
orig_root.clear();
|
||||
usage_root.clear();
|
||||
}
|
||||
|
||||
Ref<Cell> MerkleProofBuilder::extract_proof() const {
|
||||
return MerkleProof::generate(orig_root, usage_tree.get());
|
||||
}
|
||||
|
||||
bool MerkleProofBuilder::extract_proof_to(Ref<Cell> &proof_root) const {
|
||||
return orig_root.not_null() && (proof_root = extract_proof()).not_null();
|
||||
}
|
||||
|
||||
td::Result<td::BufferSlice> MerkleProofBuilder::extract_proof_boc() const {
|
||||
Ref<Cell> proof_root = extract_proof();
|
||||
if (proof_root.is_null()) {
|
||||
return td::Status::Error("cannot create Merkle proof");
|
||||
} else {
|
||||
return std_boc_serialize(std::move(proof_root));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace vm
|
||||
|
|
|
@ -18,11 +18,13 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include "vm/cells/Cell.h"
|
||||
#include "td/utils/buffer.h"
|
||||
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
||||
namespace vm {
|
||||
|
||||
class MerkleProof {
|
||||
public:
|
||||
using IsPrunnedFunction = std::function<bool(const Ref<Cell> &)>;
|
||||
|
@ -43,4 +45,21 @@ class MerkleProof {
|
|||
static Ref<Cell> generate_raw(Ref<Cell> cell, CellUsageTree *usage_tree);
|
||||
static Ref<Cell> virtualize_raw(Ref<Cell> cell, Cell::VirtualizationParameters virt);
|
||||
};
|
||||
|
||||
class MerkleProofBuilder {
|
||||
std::shared_ptr<CellUsageTree> usage_tree;
|
||||
Ref<vm::Cell> orig_root, usage_root;
|
||||
|
||||
public:
|
||||
MerkleProofBuilder(Ref<Cell> root);
|
||||
void reset(Ref<Cell> root);
|
||||
void clear();
|
||||
Ref<Cell> root() const {
|
||||
return usage_root;
|
||||
}
|
||||
Ref<Cell> extract_proof() const;
|
||||
bool extract_proof_to(Ref<Cell> &proof_root) const;
|
||||
td::Result<td::BufferSlice> extract_proof_boc() const;
|
||||
};
|
||||
|
||||
} // namespace vm
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue