1
0
Fork 0
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:
ton 2019-09-14 18:14:55 +04:00
parent d8244eff53
commit 9d6853ef24
58 changed files with 1480 additions and 325 deletions

View file

@ -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;

View file

@ -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,

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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});
}

View file

@ -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

View file

@ -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++) {

View file

@ -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,

View file

@ -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

View file

@ -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