mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Add method listBlockTransactionsExt to liteserver (#399)
* Verify proof for method blocks.getTransactions * check completeness of response * fix start_lt * fix mode & 128, check bTxes->ids_ out of bounds * Improve gitactions; separate cpp-check (#346) * Use ninja build tool and compile blockchain-explorer Ninja builds TON much faster; * Use clang instead of gcc * remove blockchain-explorer since target not found on github action * move ccpcheck to other gitaction * run nativelib-java only against wallets branch for now * rename gitaction * Update windows2019x64-tonlib-java.yml * Update windows2019x64-tonlib-java.yml * Update macos-10.15-tonlib-java.yml * Update windows2019x64-tonlib-java.yml * Update windows2019x64-tonlib-java.yml * rebase * update tlo's * Revert "Improve gitactions; separate cpp-check (#346)" This reverts commit bd1d96e6d391e48840d81cfcf10d2692848e504e. * add checks, simplify ls response * Revert workflows * Add verifying proofs * fix win build --------- Co-authored-by: neodiX42 <namlem@gmail.com>
This commit is contained in:
parent
4db7ad039a
commit
078aabe50e
10 changed files with 502 additions and 16 deletions
|
@ -315,6 +315,113 @@ td::Result<TransactionList::Info> TransactionList::validate() const {
|
|||
return std::move(res);
|
||||
}
|
||||
|
||||
td::Result<BlockTransaction::Info> BlockTransaction::validate(bool check_proof) const {
|
||||
if (root.is_null()) {
|
||||
return td::Status::Error("transactions are expected to be non-empty");
|
||||
}
|
||||
if (check_proof && proof->get_hash().bits().compare(root->get_hash().bits(), 256)) {
|
||||
return td::Status::Error(PSLICE() << "transaction hash mismatch: Merkle proof expects "
|
||||
<< proof->get_hash().bits().to_hex(256)
|
||||
<< " but received data has " << root->get_hash().bits().to_hex(256));
|
||||
}
|
||||
block::gen::Transaction::Record trans;
|
||||
if (!tlb::unpack_cell(root, trans)) {
|
||||
return td::Status::Error("cannot unpack transaction cell");
|
||||
}
|
||||
Info res;
|
||||
res.blkid = blkid;
|
||||
res.now = trans.now;
|
||||
res.lt = trans.lt;
|
||||
res.hash = root->get_hash().bits();
|
||||
res.transaction = root;
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
td::Result<BlockTransactionList::Info> BlockTransactionList::validate(bool check_proof) const {
|
||||
constexpr int max_answer_transactions = 256;
|
||||
|
||||
TRY_RESULT_PREFIX(list, vm::std_boc_deserialize_multi(std::move(transactions_boc)), "cannot deserialize transactions boc: ");
|
||||
std::vector<td::Ref<vm::Cell>> tx_proofs(list.size());
|
||||
|
||||
if (check_proof) {
|
||||
try {
|
||||
TRY_RESULT(proof_cell, vm::std_boc_deserialize(std::move(proof_boc)));
|
||||
auto virt_root = vm::MerkleProof::virtualize(proof_cell, 1);
|
||||
|
||||
if (blkid.root_hash != virt_root->get_hash().bits()) {
|
||||
return td::Status::Error("Invalid block proof root hash");
|
||||
}
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockExtra::Record extra;
|
||||
if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(std::move(blk.extra), extra))) {
|
||||
return td::Status::Error("Error unpacking proof cell");
|
||||
}
|
||||
vm::AugmentedDictionary acc_dict{vm::load_cell_slice_ref(extra.account_blocks), 256,
|
||||
block::tlb::aug_ShardAccountBlocks};
|
||||
|
||||
bool eof = false;
|
||||
ton::LogicalTime reverse = reverse_mode ? ~0ULL : 0;
|
||||
ton::LogicalTime trans_lt = static_cast<ton::LogicalTime>(start_lt);
|
||||
td::Bits256 cur_addr = start_addr;
|
||||
bool allow_same = true;
|
||||
int count = 0;
|
||||
while (!eof && count < req_count && count < max_answer_transactions) {
|
||||
auto value = acc_dict.extract_value(
|
||||
acc_dict.vm::DictionaryFixed::lookup_nearest_key(cur_addr.bits(), 256, !reverse, allow_same));
|
||||
if (value.is_null()) {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
allow_same = false;
|
||||
if (cur_addr != start_addr) {
|
||||
trans_lt = reverse;
|
||||
}
|
||||
|
||||
block::gen::AccountBlock::Record acc_blk;
|
||||
if (!tlb::csr_unpack(std::move(value), acc_blk) || acc_blk.account_addr != cur_addr) {
|
||||
return td::Status::Error("Error unpacking proof account block");
|
||||
}
|
||||
vm::AugmentedDictionary trans_dict{vm::DictNonEmpty(), std::move(acc_blk.transactions), 64,
|
||||
block::tlb::aug_AccountTransactions};
|
||||
td::BitArray<64> cur_trans{(long long)trans_lt};
|
||||
while (count < req_count && count < max_answer_transactions) {
|
||||
auto tvalue = trans_dict.extract_value_ref(
|
||||
trans_dict.vm::DictionaryFixed::lookup_nearest_key(cur_trans.bits(), 64, !reverse));
|
||||
if (tvalue.is_null()) {
|
||||
trans_lt = reverse;
|
||||
break;
|
||||
}
|
||||
if (static_cast<size_t>(count) < tx_proofs.size()) {
|
||||
tx_proofs[count] = std::move(tvalue);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (static_cast<size_t>(count) != list.size()) {
|
||||
return td::Status::Error(PSLICE() << "Txs count mismatch in proof (" << count << ") and response (" << list.size() << ")");
|
||||
}
|
||||
} catch (vm::VmError& err) {
|
||||
return err.as_status("Couldn't verify proof: ");
|
||||
} catch (vm::VmVirtError& err) {
|
||||
return err.as_status("Couldn't verify proof: ");
|
||||
} catch (...) {
|
||||
return td::Status::Error("Unknown exception raised while verifying proof");
|
||||
}
|
||||
}
|
||||
|
||||
Info res;
|
||||
for (int i = 0; i < static_cast<int>(list.size()); i++) {
|
||||
auto& root = list[i];
|
||||
BlockTransaction transaction;
|
||||
transaction.root = root;
|
||||
transaction.blkid = blkid;
|
||||
transaction.proof = tx_proofs[i];
|
||||
TRY_RESULT(info, transaction.validate(check_proof));
|
||||
res.transactions.push_back(std::move(info));
|
||||
}
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
td::Status BlockProofLink::validate(td::uint32* save_utime) const {
|
||||
if (save_utime) {
|
||||
*save_utime = 0;
|
||||
|
|
|
@ -88,4 +88,36 @@ struct TransactionList {
|
|||
td::Result<Info> validate() const;
|
||||
};
|
||||
|
||||
struct BlockTransaction {
|
||||
ton::BlockIdExt blkid;
|
||||
td::Ref<vm::Cell> root;
|
||||
td::Ref<vm::Cell> proof;
|
||||
|
||||
struct Info {
|
||||
ton::BlockIdExt blkid;
|
||||
td::uint32 now;
|
||||
ton::LogicalTime lt;
|
||||
ton::Bits256 hash;
|
||||
td::Ref<vm::Cell> transaction;
|
||||
};
|
||||
td::Result<Info> validate(bool check_proof) const;
|
||||
};
|
||||
|
||||
struct BlockTransactionList {
|
||||
ton::BlockIdExt blkid;
|
||||
td::BufferSlice transactions_boc;
|
||||
td::BufferSlice proof_boc;
|
||||
ton::LogicalTime start_lt;
|
||||
td::Bits256 start_addr;
|
||||
bool reverse_mode;
|
||||
int req_count;
|
||||
|
||||
struct Info {
|
||||
ton::BlockIdExt blkid;
|
||||
std::vector<BlockTransaction::Info> transactions;
|
||||
};
|
||||
|
||||
td::Result<Info> validate(bool check_proof) const;
|
||||
};
|
||||
|
||||
} // namespace block
|
||||
|
|
|
@ -44,6 +44,7 @@ liteServer.transactionList ids:(vector tonNode.blockIdExt) transactions:bytes =
|
|||
liteServer.transactionId mode:# account:mode.0?int256 lt:mode.1?long hash:mode.2?int256 = liteServer.TransactionId;
|
||||
liteServer.transactionId3 account:int256 lt:long = liteServer.TransactionId3;
|
||||
liteServer.blockTransactions id:tonNode.blockIdExt req_count:# incomplete:Bool ids:(vector liteServer.transactionId) proof:bytes = liteServer.BlockTransactions;
|
||||
liteServer.blockTransactionsExt id:tonNode.blockIdExt req_count:# incomplete:Bool transactions:bytes proof:bytes = liteServer.BlockTransactionsExt;
|
||||
liteServer.signature node_id_short:int256 signature:bytes = liteServer.Signature;
|
||||
liteServer.signatureSet validator_set_hash:int catchain_seqno:int signatures:(vector liteServer.signature) = liteServer.SignatureSet;
|
||||
liteServer.blockLinkBack to_key_block:Bool from:tonNode.blockIdExt to:tonNode.blockIdExt dest_proof:bytes proof:bytes state_proof:bytes = liteServer.BlockLink;
|
||||
|
@ -76,6 +77,7 @@ liteServer.getOneTransaction id:tonNode.blockIdExt account:liteServer.accountId
|
|||
liteServer.getTransactions count:# account:liteServer.accountId lt:long hash:int256 = liteServer.TransactionList;
|
||||
liteServer.lookupBlock mode:# id:tonNode.blockId lt:mode.1?long utime:mode.2?int = liteServer.BlockHeader;
|
||||
liteServer.listBlockTransactions id:tonNode.blockIdExt mode:# count:# after:mode.7?liteServer.transactionId3 reverse_order:mode.6?true want_proof:mode.5?true = liteServer.BlockTransactions;
|
||||
liteServer.listBlockTransactionsExt id:tonNode.blockIdExt mode:# count:# after:mode.7?liteServer.transactionId3 reverse_order:mode.6?true want_proof:mode.5?true = liteServer.BlockTransactionsExt;
|
||||
liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
|
||||
liteServer.getConfigAll mode:# id:tonNode.blockIdExt = liteServer.ConfigInfo;
|
||||
liteServer.getConfigParams mode:# id:tonNode.blockIdExt param_list:(vector int) = liteServer.ConfigInfo;
|
||||
|
|
Binary file not shown.
|
@ -52,7 +52,7 @@ ton.blockIdExt workchain:int32 shard:int64 seqno:int32 root_hash:bytes file_hash
|
|||
|
||||
raw.fullAccountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId block_id:ton.blockIdExt frozen_hash:bytes sync_utime:int53 = raw.FullAccountState;
|
||||
raw.message source:accountAddress destination:accountAddress value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes msg_data:msg.Data = raw.Message;
|
||||
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
|
||||
raw.transaction address:accountAddress utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
|
||||
raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
|
||||
|
||||
raw.extMessageInfo hash:bytes = raw.ExtMessageInfo;
|
||||
|
@ -215,6 +215,7 @@ blocks.shards shards:vector<ton.BlockIdExt> = blocks.Shards;
|
|||
blocks.accountTransactionId account:bytes lt:int64 = blocks.AccountTransactionId;
|
||||
blocks.shortTxId mode:# account:mode.0?bytes lt:mode.1?int64 hash:mode.2?bytes = liteServer.TransactionId;
|
||||
blocks.transactions id:ton.blockIdExt req_count:int32 incomplete:Bool transactions:vector<blocks.shortTxId> = blocks.Transactions;
|
||||
blocks.transactionsExt id:ton.blockIdExt req_count:int32 incomplete:Bool transactions:vector<raw.transaction> = blocks.TransactionsExt;
|
||||
blocks.header id:ton.blockIdExt global_id:int32 version:int32 flags:# after_merge:Bool after_split:Bool before_split:Bool want_merge:Bool want_split:Bool validator_list_hash_short:int32 catchain_seqno:int32 min_ref_mc_seqno:int32 is_key_block:Bool prev_key_block_seqno:int32 start_lt:int64 end_lt:int64 gen_utime:int53 vert_seqno:# prev_blocks:vector<ton.blockIdExt> = blocks.Header;
|
||||
//blocks.shortData header:blocks.Header transactions:blocks.Header = blocks.BlockData;
|
||||
|
||||
|
@ -319,6 +320,7 @@ blocks.getMasterchainInfo = blocks.MasterchainInfo;
|
|||
blocks.getShards id:ton.blockIdExt = blocks.Shards;
|
||||
blocks.lookupBlock mode:int32 id:ton.blockId lt:int64 utime:int32 = ton.BlockIdExt;
|
||||
blocks.getTransactions id:ton.blockIdExt mode:# count:# after:blocks.accountTransactionId = blocks.Transactions;
|
||||
blocks.getTransactionsExt id:ton.blockIdExt mode:# count:# after:blocks.accountTransactionId = blocks.TransactionsExt;
|
||||
blocks.getBlockHeader id:ton.blockIdExt = blocks.Header;
|
||||
blocks.getMasterchainBlockSignatures seqno:int32 = blocks.BlockSignatures;
|
||||
blocks.getShardBlockProof id:ton.blockIdExt mode:# from:mode.0?ton.blockIdExt = blocks.ShardBlockProof;
|
||||
|
|
Binary file not shown.
|
@ -2966,6 +2966,7 @@ struct ToRawTransactions {
|
|||
std::vector<tonlib_api::object_ptr<tonlib_api::raw_message>> out_msgs;
|
||||
td::int64 fees = 0;
|
||||
td::int64 storage_fee = 0;
|
||||
td::string address;
|
||||
if (info.transaction.not_null()) {
|
||||
data = to_bytes(info.transaction);
|
||||
block::gen::Transaction::Record trans;
|
||||
|
@ -2974,11 +2975,6 @@ struct ToRawTransactions {
|
|||
}
|
||||
|
||||
TRY_RESULT_ASSIGN(fees, to_balance(trans.total_fees));
|
||||
//LOG(ERROR) << fees;
|
||||
|
||||
//std::ostringstream outp;
|
||||
//block::gen::t_Transaction.print_ref(outp, info.transaction);
|
||||
//LOG(INFO) << outp.str();
|
||||
|
||||
auto is_just = trans.r1.in_msg->prefetch_long(1);
|
||||
if (is_just == trans.r1.in_msg->fetch_long_eof) {
|
||||
|
@ -3004,8 +3000,11 @@ struct ToRawTransactions {
|
|||
return td::Status::Error("Failed to fetch storage fee from transaction");
|
||||
}
|
||||
storage_fee = storage_fees->to_long();
|
||||
auto std_address = block::StdAddress(info.blkid.id.workchain, trans.account_addr);
|
||||
address = std_address.rserialize(true);
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::raw_transaction>(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(std::move(address)),
|
||||
info.now, data,
|
||||
tonlib_api::make_object<tonlib_api::internal_transactionId>(info.prev_trans_lt,
|
||||
info.prev_trans_hash.as_slice().str()),
|
||||
|
@ -3032,6 +3031,74 @@ struct ToRawTransactions {
|
|||
|
||||
return tonlib_api::make_object<tonlib_api::raw_transactions>(std::move(transactions), std::move(transaction_id));
|
||||
}
|
||||
|
||||
td::Result<tonlib_api::object_ptr<tonlib_api::raw_transaction>> to_raw_transaction_or_throw(
|
||||
block::BlockTransaction::Info&& info) {
|
||||
std::string data;
|
||||
|
||||
tonlib_api::object_ptr<tonlib_api::raw_message> in_msg;
|
||||
std::vector<tonlib_api::object_ptr<tonlib_api::raw_message>> out_msgs;
|
||||
td::int64 fees = 0;
|
||||
td::int64 storage_fee = 0;
|
||||
td::string address;
|
||||
if (info.transaction.not_null()) {
|
||||
data = to_bytes(info.transaction);
|
||||
block::gen::Transaction::Record trans;
|
||||
if (!tlb::unpack_cell(info.transaction, trans)) {
|
||||
return td::Status::Error("Failed to unpack Transaction");
|
||||
}
|
||||
|
||||
TRY_RESULT_ASSIGN(fees, to_balance(trans.total_fees));
|
||||
|
||||
auto is_just = trans.r1.in_msg->prefetch_long(1);
|
||||
if (is_just == trans.r1.in_msg->fetch_long_eof) {
|
||||
return td::Status::Error("Failed to parse long");
|
||||
}
|
||||
if (is_just == -1) {
|
||||
auto msg = trans.r1.in_msg->prefetch_ref();
|
||||
TRY_RESULT(in_msg_copy, to_raw_message(trans.r1.in_msg->prefetch_ref()));
|
||||
in_msg = std::move(in_msg_copy);
|
||||
}
|
||||
|
||||
if (trans.outmsg_cnt != 0) {
|
||||
vm::Dictionary dict{trans.r1.out_msgs, 15};
|
||||
for (int x = 0; x < trans.outmsg_cnt; x++) {
|
||||
TRY_RESULT(out_msg, to_raw_message(dict.lookup_ref(td::BitArray<15>{x})));
|
||||
fees += out_msg->fwd_fee_;
|
||||
fees += out_msg->ihr_fee_;
|
||||
out_msgs.push_back(std::move(out_msg));
|
||||
}
|
||||
}
|
||||
td::RefInt256 storage_fees;
|
||||
if (!block::tlb::t_TransactionDescr.get_storage_fees(trans.description, storage_fees)) {
|
||||
return td::Status::Error("Failed to fetch storage fee from transaction");
|
||||
}
|
||||
storage_fee = storage_fees->to_long();
|
||||
auto std_address = block::StdAddress(info.blkid.id.workchain, trans.account_addr);
|
||||
address = std_address.rserialize(true);
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::raw_transaction>(
|
||||
tonlib_api::make_object<tonlib_api::accountAddress>(std::move(address)),
|
||||
info.now, data,
|
||||
tonlib_api::make_object<tonlib_api::internal_transactionId>(info.lt,
|
||||
info.hash.as_slice().str()),
|
||||
fees, storage_fee, fees - storage_fee, std::move(in_msg), std::move(out_msgs));
|
||||
}
|
||||
|
||||
td::Result<tonlib_api::object_ptr<tonlib_api::raw_transaction>> to_raw_transaction(block::BlockTransaction::Info&& info) {
|
||||
return TRY_VM(to_raw_transaction_or_throw(std::move(info)));
|
||||
}
|
||||
|
||||
td::Result<std::vector<tonlib_api::object_ptr<tonlib_api::raw_transaction>>> to_raw_transactions(
|
||||
block::BlockTransactionList::Info&& info) {
|
||||
std::vector<tonlib_api::object_ptr<tonlib_api::raw_transaction>> transactions;
|
||||
for (auto& transaction : info.transactions) {
|
||||
TRY_RESULT(raw_transaction, to_raw_transaction(std::move(transaction)));
|
||||
transactions.push_back(std::move(raw_transaction));
|
||||
}
|
||||
|
||||
return std::move(transactions);
|
||||
}
|
||||
};
|
||||
|
||||
// Raw
|
||||
|
@ -5215,25 +5282,116 @@ auto to_tonlib_api(const ton::lite_api::liteServer_transactionId& txid)
|
|||
td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactions& request,
|
||||
td::Promise<object_ptr<tonlib_api::blocks_transactions>>&& promise) {
|
||||
TRY_RESULT(block, to_lite_api(*request.id_))
|
||||
TRY_RESULT(account, to_bits256((*request.after_).account_, "account"));
|
||||
auto after = ton::lite_api::make_object<ton::lite_api::liteServer_transactionId3>(account, (*request.after_).lt_);
|
||||
auto root_hash = block->root_hash_;
|
||||
bool check_proof = request.mode_ & 32;
|
||||
bool reverse_mode = request.mode_ & 64;
|
||||
bool has_starting_tx = request.mode_ & 128;
|
||||
|
||||
td::Bits256 start_addr;
|
||||
ton::LogicalTime start_lt;
|
||||
ton::lite_api::object_ptr<ton::lite_api::liteServer_transactionId3> after;
|
||||
if (has_starting_tx) {
|
||||
if (!request.after_) {
|
||||
return td::Status::Error("Missing field `after`");
|
||||
}
|
||||
TRY_RESULT_ASSIGN(start_addr, to_bits256(request.after_->account_, "account"));
|
||||
start_lt = request.after_->lt_;
|
||||
after = ton::lite_api::make_object<ton::lite_api::liteServer_transactionId3>(start_addr, start_lt);
|
||||
} else {
|
||||
start_addr = reverse_mode ? td::Bits256::ones() : td::Bits256::zero();
|
||||
start_lt = reverse_mode ? ~0ULL : 0;
|
||||
after = nullptr;
|
||||
}
|
||||
|
||||
client_.send_query(ton::lite_api::liteServer_listBlockTransactions(
|
||||
std::move(block),
|
||||
request.mode_,
|
||||
request.count_,
|
||||
std::move(after),
|
||||
false,
|
||||
false),
|
||||
promise.wrap([](lite_api_ptr<ton::lite_api::liteServer_blockTransactions>&& bTxes) {
|
||||
const auto& id = bTxes->id_;
|
||||
//for (auto id : ids) {
|
||||
reverse_mode,
|
||||
check_proof),
|
||||
promise.wrap([check_proof, reverse_mode, root_hash, req_count = request.count_, start_addr, start_lt, mode = request.mode_]
|
||||
(lite_api_ptr<ton::lite_api::liteServer_blockTransactions>&& bTxes) -> td::Result<object_ptr<tonlib_api::blocks_transactions>> {
|
||||
if (check_proof) {
|
||||
try {
|
||||
constexpr int max_answer_transactions = 256;
|
||||
TRY_RESULT(proof_cell, vm::std_boc_deserialize(std::move(bTxes->proof_)));
|
||||
auto virt_root = vm::MerkleProof::virtualize(proof_cell, 1);
|
||||
|
||||
if (root_hash != virt_root->get_hash().bits()) {
|
||||
return td::Status::Error("Invalid block proof root hash");
|
||||
}
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockExtra::Record extra;
|
||||
if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(std::move(blk.extra), extra))) {
|
||||
return td::Status::Error("Error unpacking proof cell");
|
||||
}
|
||||
vm::AugmentedDictionary acc_dict{vm::load_cell_slice_ref(extra.account_blocks), 256,
|
||||
block::tlb::aug_ShardAccountBlocks};
|
||||
|
||||
bool eof = false;
|
||||
ton::LogicalTime reverse = reverse_mode ? ~0ULL : 0;
|
||||
ton::LogicalTime trans_lt = static_cast<ton::LogicalTime>(start_lt);
|
||||
td::Bits256 cur_addr = start_addr;
|
||||
bool allow_same = true;
|
||||
int count = 0;
|
||||
while (!eof && count < req_count && count < max_answer_transactions) {
|
||||
auto value = acc_dict.extract_value(
|
||||
acc_dict.vm::DictionaryFixed::lookup_nearest_key(cur_addr.bits(), 256, !reverse, allow_same));
|
||||
if (value.is_null()) {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
allow_same = false;
|
||||
if (cur_addr != start_addr) {
|
||||
trans_lt = reverse;
|
||||
}
|
||||
|
||||
block::gen::AccountBlock::Record acc_blk;
|
||||
if (!tlb::csr_unpack(std::move(value), acc_blk) || acc_blk.account_addr != cur_addr) {
|
||||
return td::Status::Error("Error unpacking proof account block");
|
||||
}
|
||||
vm::AugmentedDictionary trans_dict{vm::DictNonEmpty(), std::move(acc_blk.transactions), 64,
|
||||
block::tlb::aug_AccountTransactions};
|
||||
td::BitArray<64> cur_trans{(long long)trans_lt};
|
||||
while (count < req_count && count < max_answer_transactions) {
|
||||
auto tvalue = trans_dict.extract_value_ref(
|
||||
trans_dict.vm::DictionaryFixed::lookup_nearest_key(cur_trans.bits(), 64, !reverse));
|
||||
if (tvalue.is_null()) {
|
||||
trans_lt = reverse;
|
||||
break;
|
||||
}
|
||||
if (static_cast<size_t>(count) < bTxes->ids_.size()) {
|
||||
if (mode & 4 && !tvalue->get_hash().bits().equals(bTxes->ids_[count]->hash_.bits(), 256)) {
|
||||
return td::Status::Error("Couldn't verify proof (hash)");
|
||||
}
|
||||
if (mode & 2 && cur_trans != td::BitArray<64>(bTxes->ids_[count]->lt_)) {
|
||||
return td::Status::Error("Couldn't verify proof (lt)");
|
||||
}
|
||||
if (mode & 1 && cur_addr != bTxes->ids_[count]->account_) {
|
||||
return td::Status::Error("Couldn't verify proof (account)");
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (static_cast<size_t>(count) != bTxes->ids_.size()) {
|
||||
return td::Status::Error(PSLICE() << "Txs count mismatch in proof (" << count << ") and response (" << bTxes->ids_.size() << ")");
|
||||
}
|
||||
} catch (vm::VmError& err) {
|
||||
return err.as_status("Couldn't verify proof: ");
|
||||
} catch (vm::VmVirtError& err) {
|
||||
return err.as_status("Couldn't verify proof: ");
|
||||
} catch (...) {
|
||||
return td::Status::Error("Unknown exception raised while verifying proof");
|
||||
}
|
||||
}
|
||||
|
||||
tonlib_api::blocks_transactions r;
|
||||
r.id_ = to_tonlib_api(*id);
|
||||
r.id_ = to_tonlib_api(*bTxes->id_);
|
||||
r.req_count_ = bTxes->req_count_;
|
||||
r.incomplete_ = bTxes->incomplete_;
|
||||
for (auto& id: bTxes->ids_) {
|
||||
//tonlib_api::blocks_shortTxId txid = tonlib_api::blocks_shortTxId(id->mode_, id->account_.as_slice().str(), id->lt_, id->hash_.as_slice().str());
|
||||
//r.transactions_.push_back(txid);
|
||||
r.transactions_.push_back(to_tonlib_api(*id));
|
||||
}
|
||||
return tonlib_api::make_object<tonlib_api::blocks_transactions>(std::move(r));
|
||||
|
@ -5241,6 +5399,70 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactions& re
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::blocks_getTransactionsExt& request,
|
||||
td::Promise<object_ptr<tonlib_api::blocks_transactionsExt>>&& promise) {
|
||||
TRY_RESULT(block, to_lite_api(*request.id_))
|
||||
bool check_proof = request.mode_ & 32;
|
||||
bool reverse_mode = request.mode_ & 64;
|
||||
bool has_starting_tx = request.mode_ & 128;
|
||||
|
||||
td::Bits256 start_addr;
|
||||
ton::LogicalTime start_lt;
|
||||
ton::lite_api::object_ptr<ton::lite_api::liteServer_transactionId3> after;
|
||||
if (has_starting_tx) {
|
||||
if (!request.after_) {
|
||||
return td::Status::Error("Missing field `after`");
|
||||
}
|
||||
TRY_RESULT_ASSIGN(start_addr, to_bits256(request.after_->account_, "account"));
|
||||
start_lt = request.after_->lt_;
|
||||
after = ton::lite_api::make_object<ton::lite_api::liteServer_transactionId3>(start_addr, start_lt);
|
||||
} else {
|
||||
start_addr = reverse_mode ? td::Bits256::ones() : td::Bits256::zero();
|
||||
start_lt = reverse_mode ? ~0ULL : 0;
|
||||
after = nullptr;
|
||||
}
|
||||
auto block_id = ton::create_block_id(block);
|
||||
client_.send_query(ton::lite_api::liteServer_listBlockTransactionsExt(
|
||||
std::move(block),
|
||||
request.mode_,
|
||||
request.count_,
|
||||
std::move(after),
|
||||
reverse_mode,
|
||||
check_proof),
|
||||
promise.wrap([block_id, check_proof, reverse_mode, start_addr, start_lt, req_count = request.count_]
|
||||
(lite_api_ptr<ton::lite_api::liteServer_blockTransactionsExt>&& bTxes) -> td::Result<tonlib_api::object_ptr<tonlib_api::blocks_transactionsExt>> {
|
||||
if (block_id != create_block_id(bTxes->id_)) {
|
||||
return td::Status::Error("Liteserver responded with wrong block");
|
||||
}
|
||||
|
||||
block::BlockTransactionList list;
|
||||
list.blkid = block_id;
|
||||
list.transactions_boc = std::move(bTxes->transactions_);
|
||||
list.proof_boc = std::move(bTxes->proof_);
|
||||
list.reverse_mode = reverse_mode;
|
||||
list.start_lt = start_lt;
|
||||
list.start_addr = start_addr;
|
||||
list.req_count = req_count;
|
||||
auto info = list.validate(check_proof);
|
||||
if (info.is_error()) {
|
||||
return info.move_as_error_prefix("Validation of block::BlockTransactionList failed: ");
|
||||
}
|
||||
|
||||
auto raw_transactions = ToRawTransactions(td::optional<td::Ed25519::PrivateKey>()).to_raw_transactions(info.move_as_ok());
|
||||
if (raw_transactions.is_error()) {
|
||||
return raw_transactions.move_as_error_prefix("Error occured while creating tonlib_api::raw_transaction: ");
|
||||
}
|
||||
|
||||
tonlib_api::blocks_transactionsExt r;
|
||||
r.id_ = to_tonlib_api(*bTxes->id_);
|
||||
r.req_count_ = bTxes->req_count_;
|
||||
r.incomplete_ = bTxes->incomplete_;
|
||||
r.transactions_ = raw_transactions.move_as_ok();
|
||||
return tonlib_api::make_object<tonlib_api::blocks_transactionsExt>(std::move(r));
|
||||
}));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status TonlibClient::do_request(const tonlib_api::blocks_getBlockHeader& request,
|
||||
td::Promise<object_ptr<tonlib_api::blocks_header>>&& promise) {
|
||||
TRY_RESULT(block, to_lite_api(*request.id_))
|
||||
|
|
|
@ -375,6 +375,8 @@ class TonlibClient : public td::actor::Actor {
|
|||
td::Promise<object_ptr<tonlib_api::ton_blockIdExt>>&& promise);
|
||||
td::Status do_request(const tonlib_api::blocks_getTransactions& block_data,
|
||||
td::Promise<object_ptr<tonlib_api::blocks_transactions>>&& promise);
|
||||
td::Status do_request(const tonlib_api::blocks_getTransactionsExt& request,
|
||||
td::Promise<object_ptr<tonlib_api::blocks_transactionsExt>>&& promise);
|
||||
td::Status do_request(const tonlib_api::blocks_getBlockHeader& request,
|
||||
td::Promise<object_ptr<tonlib_api::blocks_header>>&& promise);
|
||||
td::Status do_request(const tonlib_api::blocks_getMasterchainBlockSignatures& request,
|
||||
|
|
|
@ -182,6 +182,11 @@ void LiteQuery::start_up() {
|
|||
(q.mode_ & 128) ? q.after_->account_ : td::Bits256::zero(),
|
||||
static_cast<LogicalTime>((q.mode_ & 128) ? (q.after_->lt_) : 0));
|
||||
},
|
||||
[&](lite_api::liteServer_listBlockTransactionsExt& q) {
|
||||
this->perform_listBlockTransactionsExt(ton::create_block_id(q.id_), q.mode_, q.count_,
|
||||
(q.mode_ & 128) ? q.after_->account_ : td::Bits256::zero(),
|
||||
static_cast<LogicalTime>((q.mode_ & 128) ? (q.after_->lt_) : 0));
|
||||
},
|
||||
[&](lite_api::liteServer_getConfigParams& q) {
|
||||
this->perform_getConfigParams(ton::create_block_id(q.id_), (q.mode_ & 0xffff) | 0x10000, q.param_list_);
|
||||
},
|
||||
|
@ -1964,6 +1969,118 @@ void LiteQuery::finish_listBlockTransactions(int mode, int req_count) {
|
|||
finish_query(std::move(b));
|
||||
}
|
||||
|
||||
void LiteQuery::perform_listBlockTransactionsExt(BlockIdExt blkid, int mode, int count, Bits256 account, LogicalTime lt) {
|
||||
LOG(INFO) << "started a listBlockTransactionsExt(" << blkid.to_str() << ", " << mode << ", " << count << ", "
|
||||
<< account.to_hex() << ", " << lt << ") liteserver query";
|
||||
base_blk_id_ = blkid;
|
||||
acc_addr_ = account;
|
||||
trans_lt_ = lt;
|
||||
set_continuation([this, mode, count]() -> void { finish_listBlockTransactionsExt(mode, count); });
|
||||
request_block_data(blkid);
|
||||
}
|
||||
|
||||
void LiteQuery::finish_listBlockTransactionsExt(int mode, int req_count) {
|
||||
LOG(INFO) << "completing a listBlockTransactionsExt(" << base_blk_id_.to_str() << ", " << mode << ", " << req_count
|
||||
<< ", " << acc_addr_.to_hex() << ", " << trans_lt_ << ") liteserver query";
|
||||
constexpr int max_answer_transactions = 256;
|
||||
CHECK(block_.not_null());
|
||||
auto block_root = block_->root_cell();
|
||||
CHECK(block_root.not_null());
|
||||
RootHash rhash{block_root->get_hash().bits()};
|
||||
CHECK(rhash == base_blk_id_.root_hash);
|
||||
vm::MerkleProofBuilder pb;
|
||||
auto virt_root = block_root;
|
||||
if (mode & 32) {
|
||||
// proof requested
|
||||
virt_root = pb.init(std::move(virt_root));
|
||||
}
|
||||
if ((mode & 192) == 64) { // reverse order, no starting point
|
||||
acc_addr_.set_ones();
|
||||
trans_lt_ = ~0ULL;
|
||||
}
|
||||
std::vector<Ref<vm::Cell>> trans_roots;
|
||||
bool eof = false;
|
||||
ton::LogicalTime reverse = (mode & 64) ? ~0ULL : 0;
|
||||
try {
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockExtra::Record extra;
|
||||
if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(std::move(blk.extra), extra))) {
|
||||
fatal_error("cannot find account transaction data in block "s + base_blk_id_.to_str());
|
||||
return;
|
||||
}
|
||||
vm::AugmentedDictionary acc_dict{vm::load_cell_slice_ref(extra.account_blocks), 256,
|
||||
block::tlb::aug_ShardAccountBlocks};
|
||||
int count = 0;
|
||||
bool allow_same = true;
|
||||
td::Bits256 cur_addr = acc_addr_;
|
||||
while (!eof && count < req_count && count < max_answer_transactions) {
|
||||
Ref<vm::CellSlice> value;
|
||||
try {
|
||||
value = acc_dict.extract_value(
|
||||
acc_dict.vm::DictionaryFixed::lookup_nearest_key(cur_addr.bits(), 256, !reverse, allow_same));
|
||||
} catch (vm::VmError err) {
|
||||
fatal_error("error while traversing account block dictionary: "s + err.get_msg());
|
||||
return;
|
||||
}
|
||||
if (value.is_null()) {
|
||||
eof = true;
|
||||
break;
|
||||
}
|
||||
allow_same = false;
|
||||
if (cur_addr != acc_addr_) {
|
||||
trans_lt_ = reverse;
|
||||
}
|
||||
block::gen::AccountBlock::Record acc_blk;
|
||||
if (!(tlb::csr_unpack(std::move(value), acc_blk) && acc_blk.account_addr == cur_addr)) {
|
||||
fatal_error("invalid AccountBlock for account "s + cur_addr.to_hex());
|
||||
return;
|
||||
}
|
||||
vm::AugmentedDictionary trans_dict{vm::DictNonEmpty(), std::move(acc_blk.transactions), 64,
|
||||
block::tlb::aug_AccountTransactions};
|
||||
td::BitArray<64> cur_trans{(long long)trans_lt_};
|
||||
while (count < req_count && count < max_answer_transactions) {
|
||||
Ref<vm::Cell> tvalue;
|
||||
try {
|
||||
tvalue = trans_dict.extract_value_ref(
|
||||
trans_dict.vm::DictionaryFixed::lookup_nearest_key(cur_trans.bits(), 64, !reverse));
|
||||
} catch (vm::VmError err) {
|
||||
fatal_error("error while traversing transaction dictionary of an AccountBlock: "s + err.get_msg());
|
||||
return;
|
||||
}
|
||||
if (tvalue.is_null()) {
|
||||
trans_lt_ = reverse;
|
||||
break;
|
||||
}
|
||||
trans_roots.push_back(std::move(tvalue));
|
||||
++count;
|
||||
}
|
||||
}
|
||||
} catch (vm::VmError err) {
|
||||
fatal_error("error while parsing AccountBlocks of block "s + base_blk_id_.to_str() + " : " + err.get_msg());
|
||||
return;
|
||||
}
|
||||
td::BufferSlice proof_data;
|
||||
if (mode & 32) {
|
||||
// create proof
|
||||
auto proof_boc = pb.extract_proof_boc();
|
||||
if (proof_boc.is_error()) {
|
||||
fatal_error(proof_boc.move_as_error());
|
||||
return;
|
||||
}
|
||||
proof_data = proof_boc.move_as_ok();
|
||||
}
|
||||
auto res = vm::std_boc_serialize_multi(std::move(trans_roots));
|
||||
if (res.is_error()) {
|
||||
fatal_error(res.move_as_error());
|
||||
return;
|
||||
}
|
||||
|
||||
auto b = ton::create_serialize_tl_object<ton::lite_api::liteServer_blockTransactionsExt>(
|
||||
ton::create_tl_lite_block_id(base_blk_id_), req_count, !eof, res.move_as_ok(), std::move(proof_data));
|
||||
LOG(INFO) << "listBlockTransactionsExt() query completed";
|
||||
finish_query(std::move(b));
|
||||
}
|
||||
|
||||
void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, int mode) {
|
||||
if (!(mode & 1)) {
|
||||
to.invalidate_clear();
|
||||
|
|
|
@ -133,6 +133,8 @@ class LiteQuery : public td::actor::Actor {
|
|||
void perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, UnixTime utime);
|
||||
void perform_listBlockTransactions(BlockIdExt blkid, int mode, int count, Bits256 account, LogicalTime lt);
|
||||
void finish_listBlockTransactions(int mode, int count);
|
||||
void perform_listBlockTransactionsExt(BlockIdExt blkid, int mode, int count, Bits256 account, LogicalTime lt);
|
||||
void finish_listBlockTransactionsExt(int mode, int count);
|
||||
void perform_getBlockProof(BlockIdExt from, BlockIdExt to, int mode);
|
||||
void continue_getBlockProof(BlockIdExt from, BlockIdExt to, int mode, BlockIdExt baseblk,
|
||||
Ref<MasterchainStateQ> state);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue