1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

updated tonlib, fixed bugs

updated tonlib
fixed bugs in func
validator: partial support for hardforks
liteserver: support for waitMasterchainBlock prefix
transactions: support for gas flat rate
This commit is contained in:
ton 2019-10-03 17:04:52 +04:00
parent 841d5ebac2
commit 7ea00ebfcf
89 changed files with 1922 additions and 608 deletions

View file

@ -1143,6 +1143,20 @@ bool TrStoragePhase::validate_skip(vm::CellSlice& cs, bool weak) const {
&& t_AccStatusChange.validate_skip(cs, weak); // status_change:AccStatusChange
}
bool TrStoragePhase::get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const {
return t_Grams.as_integer_skip_to(cs, storage_fees); // storage_fees_collected:Grams
}
bool TrStoragePhase::maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const {
auto z = cs.fetch_ulong(1);
if (!z) {
storage_fees = td::make_refint(0);
return true;
} else {
return z == 1 && get_storage_fees(cs, storage_fees);
}
}
const TrStoragePhase t_TrStoragePhase;
bool TrCreditPhase::skip(vm::CellSlice& cs) const {
@ -1322,13 +1336,14 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
&& t_TrStoragePhase.skip(cs); // storage_ph:TrStoragePhase
case trans_tick_tock:
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
&& t_TrStoragePhase.skip(cs) // storage:TrStoragePhase
&& t_TrStoragePhase.skip(cs) // storage_ph:TrStoragePhase
&& t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.skip(cs) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
case trans_split_prepare:
return cs.advance(4) // trans_split_prepare$0100
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& Maybe<TrStoragePhase>{}.skip(cs) // storage_ph:(Maybe TrStoragePhase)
&& t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.skip(cs) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
@ -1346,6 +1361,7 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
return cs.advance(4) // trans_merge_install$0111
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction
&& Maybe<TrStoragePhase>{}.skip(cs) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.skip(cs) // credit_ph:(Maybe TrCreditPhase)
&& Maybe<TrComputePhase>{}.skip(cs) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.skip(cs) // action:(Maybe ^TrActionPhase)
@ -1370,13 +1386,14 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
&& t_TrStoragePhase.validate_skip(cs, weak); // storage_ph:TrStoragePhase
case trans_tick_tock:
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
&& t_TrStoragePhase.validate_skip(cs, weak) // storage:TrStoragePhase
&& t_TrStoragePhase.validate_skip(cs, weak) // storage_ph:TrStoragePhase
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
case trans_split_prepare:
return cs.advance(4) // trans_split_prepare$0100
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
@ -1394,6 +1411,7 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
return cs.advance(4) // trans_merge_install$0111
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
&& t_Ref_Transaction.validate_skip(cs, weak) // prepare_transaction:^Transaction
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase)
&& Maybe<TrComputePhase>{}.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
@ -1407,6 +1425,53 @@ int TransactionDescr::get_tag(const vm::CellSlice& cs) const {
return (t >= 0 && t <= 7) ? (t == 3 ? 2 : t) : -1;
}
bool TransactionDescr::skip_to_storage_phase(vm::CellSlice& cs, bool& found) const {
found = false;
switch (get_tag(cs)) {
case trans_ord:
return cs.advance(4 + 1) // trans_ord$0000 storage_first:Bool
&& cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase)
case trans_storage:
return cs.advance(4) // trans_storage$0001
&& (found = true); // storage_ph:TrStoragePhase
case trans_tick_tock:
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
&& (found = true); // storage_ph:TrStoragePhase
case trans_split_prepare:
return cs.advance(4) // trans_split_prepare$0100
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase)
case trans_split_install:
return true;
case trans_merge_prepare:
return cs.advance(4) // trans_merge_prepare$0110
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& (found = true); // storage_ph:TrStoragePhase
case trans_merge_install:
return cs.advance(4) // trans_merge_install$0111
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction
&& cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase)
}
return false;
}
bool TransactionDescr::get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const {
if (cell.is_null()) {
return false;
}
auto cs = vm::load_cell_slice(std::move(cell));
bool found;
if (!skip_to_storage_phase(cs, found)) {
return false;
} else if (found) {
return t_TrStoragePhase.get_storage_fees(cs, storage_fees);
} else {
storage_fees = td::make_refint(0);
return true;
}
}
const TransactionDescr t_TransactionDescr;
bool Transaction_aux::skip(vm::CellSlice& cs) const {
@ -1447,6 +1512,32 @@ bool Transaction::validate_skip(vm::CellSlice& cs, bool weak) const {
&& RefTo<TransactionDescr>{}.validate_skip(cs, weak); // description:^TransactionDescr
}
bool Transaction::get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const {
Ref<vm::Cell> tdescr;
return get_descr(std::move(cell), tdescr) && t_TransactionDescr.get_storage_fees(std::move(tdescr), storage_fees);
}
bool Transaction::get_descr(Ref<vm::Cell> cell, Ref<vm::Cell>& tdescr) const {
if (cell.is_null()) {
return false;
} else {
auto cs = vm::load_cell_slice(std::move(cell));
return cs.is_valid() && get_descr(cs, tdescr) && cs.empty_ext();
}
}
bool Transaction::get_descr(vm::CellSlice& cs, Ref<vm::Cell>& tdescr) const {
return cs.advance(
4 + 256 + 64 + 256 + 64 + 32 +
15) // transaction$0111 account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15
&& t_AccountStatus.skip(cs) // orig_status:AccountStatus
&& t_AccountStatus.skip(cs) // end_status:AccountStatus
&& cs.advance_refs(1) // ^[ in_msg:(Maybe ^Message) out_msgs:(HashmapE 15 ^Message) ]
&& t_CurrencyCollection.skip(cs) // total_fees:CurrencyCollection
&& cs.advance_refs(1) // state_update:^(MERKLE_UPDATE Account)
&& cs.fetch_ref_to(tdescr); // description:^TransactionDescr
}
bool Transaction::get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const {
return cs.is_valid() && cs.fetch_ulong(4) == 7 // transaction$0111
&&

View file

@ -614,6 +614,8 @@ extern const AccStatusChange t_AccStatusChange;
struct TrStoragePhase final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
bool get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const;
bool maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const;
};
extern const TrStoragePhase t_TrStoragePhase;
@ -693,6 +695,8 @@ struct TransactionDescr final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override;
bool skip_to_storage_phase(vm::CellSlice& cs, bool& found) const;
bool get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const;
};
extern const TransactionDescr t_TransactionDescr;
@ -708,6 +712,9 @@ struct Transaction final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
bool get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const;
bool get_descr(Ref<vm::Cell> cell, Ref<vm::Cell>& tdescr) const;
bool get_descr(vm::CellSlice& cs, Ref<vm::Cell>& tdescr) const;
bool get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const;
};
extern const Transaction t_Transaction;

View file

@ -325,7 +325,7 @@ trans_ord$0000 credit_first:Bool
trans_storage$0001 storage_ph:TrStoragePhase
= TransactionDescr;
trans_tick_tock$001 is_tock:Bool storage:TrStoragePhase
trans_tick_tock$001 is_tock:Bool storage_ph:TrStoragePhase
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool destroyed:Bool = TransactionDescr;
//
@ -333,6 +333,7 @@ split_merge_info$_ cur_shard_pfx_len:(## 6)
acc_split_depth:(## 6) this_addr:bits256 sibling_addr:bits256
= SplitMergeInfo;
trans_split_prepare$0100 split_info:SplitMergeInfo
storage_ph:(Maybe TrStoragePhase)
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool destroyed:Bool
= TransactionDescr;
@ -345,6 +346,7 @@ trans_merge_prepare$0110 split_info:SplitMergeInfo
= TransactionDescr;
trans_merge_install$0111 split_info:SplitMergeInfo
prepare_transaction:^Transaction
storage_ph:(Maybe TrStoragePhase)
credit_ph:(Maybe TrCreditPhase)
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool destroyed:Bool
@ -609,6 +611,9 @@ gas_prices_ext#de gas_price:uint64 gas_limit:uint64 special_gas_limit:uint64 gas
block_gas_limit:uint64 freeze_due_limit:uint64 delete_due_limit:uint64
= GasLimitsPrices;
gas_flat_pfx#d1 flat_gas_limit:uint64 flat_gas_price:uint64 other:GasLimitsPrices
= GasLimitsPrices;
config_mc_gas_prices#_ GasLimitsPrices = ConfigParam 20;
config_gas_prices#_ GasLimitsPrices = ConfigParam 21;

View file

@ -672,9 +672,56 @@ bool Transaction::prepare_credit_phase() {
return true;
}
bool ComputePhaseConfig::parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit,
td::RefInt256& delete_due_limit) {
return cell.not_null() &&
parse_GasLimitsPrices(vm::load_cell_slice_ref(std::move(cell)), freeze_due_limit, delete_due_limit);
}
bool ComputePhaseConfig::parse_GasLimitsPrices(Ref<vm::CellSlice> cs, td::RefInt256& freeze_due_limit,
td::RefInt256& delete_due_limit) {
if (cs.is_null()) {
return false;
}
block::gen::GasLimitsPrices::Record_gas_flat_pfx flat;
if (tlb::csr_unpack(cs, flat)) {
bool ok = parse_GasLimitsPrices(std::move(flat.other), freeze_due_limit, delete_due_limit);
flat_gas_limit = flat.flat_gas_limit;
flat_gas_price = flat.flat_gas_price;
return ok;
}
flat_gas_limit = flat_gas_price = 0;
auto f = [&](const auto& r, td::uint64 spec_limit) {
gas_limit = r.gas_limit;
special_gas_limit = spec_limit;
gas_credit = r.gas_credit;
gas_price = r.gas_price;
freeze_due_limit = td::RefInt256{true, r.freeze_due_limit};
delete_due_limit = td::RefInt256{true, r.delete_due_limit};
};
block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
if (tlb::csr_unpack(cs, rec)) {
f(rec, rec.special_gas_limit);
} else {
block::gen::GasLimitsPrices::Record_gas_prices rec0;
if (tlb::csr_unpack(std::move(cs), rec0)) {
f(rec0, rec0.gas_limit);
} else {
return false;
}
}
compute_threshold();
return true;
}
void ComputePhaseConfig::compute_threshold() {
gas_price256 = td::RefInt256{true, gas_price};
max_gas_threshold = td::rshift(gas_price256 * gas_limit, 16, 1);
if (gas_limit > flat_gas_limit) {
max_gas_threshold =
td::rshift(gas_price256 * (gas_limit - flat_gas_limit), 16, 1) + td::make_refint(flat_gas_price);
} else {
max_gas_threshold = td::make_refint(flat_gas_price);
}
}
td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const {
@ -684,8 +731,11 @@ td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const {
if (nanograms >= max_gas_threshold) {
return gas_limit;
}
auto res = td::div(std::move(nanograms) << 16, gas_price256);
return res->to_long();
if (nanograms < flat_gas_price) {
return 0;
}
auto res = td::div((std::move(nanograms) - flat_gas_price) << 16, gas_price256);
return res->to_long() + flat_gas_limit;
}
td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
@ -855,6 +905,16 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
cp.skip_reason = ComputePhase::sk_no_gas;
return true;
}
// Compute gas limits
if (!compute_gas_limits(cp, cfg)) {
compute_phase.reset();
return false;
}
if (!cp.gas_limit && !cp.gas_credit) {
// no gas
cp.skip_reason = ComputePhase::sk_no_gas;
return true;
}
if (in_msg_state.not_null()) {
LOG(DEBUG) << "HASH(in_msg_state) = " << in_msg_state->get_hash().bits().to_hex(256)
<< ", account_state_hash = " << account.state_hash.to_hex();
@ -883,11 +943,6 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
} else if (in_msg_state.not_null()) {
unpack_msg_state(true); // use only libraries
}
// Compute gas limits
if (!compute_gas_limits(cp, cfg)) {
compute_phase.reset();
return false;
}
// initialize VM
Ref<vm::Stack> stack = prepare_vm_stack(cp);
if (stack.is_null()) {

View file

@ -98,6 +98,8 @@ struct ComputePhaseConfig {
td::uint64 gas_limit;
td::uint64 special_gas_limit;
td::uint64 gas_credit;
td::uint64 flat_gas_limit = 0;
td::uint64 flat_gas_price = 0;
static constexpr td::uint64 gas_infty = (1ULL << 63) - 1;
td::RefInt256 gas_price256;
td::RefInt256 max_gas_threshold;
@ -126,6 +128,8 @@ struct ComputePhaseConfig {
Ref<vm::Cell> get_lib_root() const {
return libraries ? libraries->get_root_cell() : Ref<vm::Cell>{};
}
bool parse_GasLimitsPrices(Ref<vm::CellSlice> cs, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit);
bool parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit);
};
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms

View file

@ -764,6 +764,10 @@ void interpret_string_to_bytes(vm::Stack& stack) {
stack.push_bytes(stack.pop_string());
}
void interpret_bytes_to_string(vm::Stack& stack) {
stack.push_string(stack.pop_bytes());
}
void interpret_bytes_hash(vm::Stack& stack, bool as_uint) {
std::string str = stack.pop_bytes();
unsigned char buffer[32];
@ -796,7 +800,9 @@ void interpret_store_str(vm::Stack& stack) {
stack.check_underflow(2);
auto str = stack.pop_string();
auto cell = stack.pop_builder();
cell.write().store_bytes(str); // may throw CellWriteError
if (!cell.write().store_bytes_bool(str)) {
throw IntError{"string does not fit into cell"};
}
stack.push(cell);
}
@ -804,14 +810,18 @@ void interpret_store_bytes(vm::Stack& stack) {
stack.check_underflow(2);
auto str = stack.pop_bytes();
auto cell = stack.pop_builder();
cell.write().store_bytes(str); // may throw CellWriteError
if (!cell.write().store_bytes_bool(str)) {
throw IntError{"byte string does not fit into cell"};
}
stack.push(cell);
}
void interpret_string_to_cellslice(vm::Stack& stack) {
auto str = stack.pop_string();
vm::CellBuilder cb;
cb.store_bytes(str); // may throw CellWriteError
if (!cb.store_bytes_bool(str)) {
throw IntError{"string does not fit into cell"};
}
stack.push_cellslice(td::Ref<vm::CellSlice>{true, cb.finalize()});
}
@ -819,7 +829,9 @@ void interpret_store_cellslice(vm::Stack& stack) {
stack.check_underflow(2);
auto cs = stack.pop_cellslice();
auto cb = stack.pop_builder();
vm::cell_builder_add_slice(cb.write(), *cs);
if (!vm::cell_builder_add_slice_bool(cb.write(), *cs)) {
throw IntError{"slice does not fit into cell"};
}
stack.push(std::move(cb));
}
@ -840,9 +852,11 @@ void interpret_concat_cellslice(vm::Stack& stack) {
auto cs2 = stack.pop_cellslice();
auto cs1 = stack.pop_cellslice();
vm::CellBuilder cb;
vm::cell_builder_add_slice(cb, *cs1);
vm::cell_builder_add_slice(cb, *cs2);
stack.push_cellslice(td::Ref<vm::CellSlice>{true, cb.finalize()});
if (vm::cell_builder_add_slice_bool(cb, *cs1) && vm::cell_builder_add_slice_bool(cb, *cs2)) {
stack.push_cellslice(td::Ref<vm::CellSlice>{true, cb.finalize()});
} else {
throw IntError{"concatenation of two slices does not fit into a cell"};
}
}
void interpret_concat_cellslice_ref(vm::Stack& stack) {
@ -862,7 +876,9 @@ void interpret_concat_builders(vm::Stack& stack) {
stack.check_underflow(2);
auto cb2 = stack.pop_builder();
auto cb1 = stack.pop_builder();
cb1.write().append_builder(std::move(cb2));
if (!cb1.write().append_builder_bool(std::move(cb2))) {
throw IntError{"cannot concatenate two builders"};
}
stack.push_builder(std::move(cb1));
}
@ -2490,6 +2506,7 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("B>Lu@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x12));
d.def_stack_word("B>Li@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x13));
d.def_stack_word("$>B ", interpret_string_to_bytes);
d.def_stack_word("B>$ ", interpret_bytes_to_string);
d.def_stack_word("Bhash ", std::bind(interpret_bytes_hash, _1, true));
d.def_stack_word("Bhashu ", std::bind(interpret_bytes_hash, _1, true));
d.def_stack_word("BhashB ", std::bind(interpret_bytes_hash, _1, false));

View file

@ -149,6 +149,10 @@ AsmOp AsmOp::IntConst(td::RefInt256 x) {
return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT");
}
AsmOp AsmOp::BoolConst(bool f) {
return AsmOp::Const(f ? "TRUE" : "FALSE");
}
AsmOp AsmOp::Parse(std::string custom_op) {
if (custom_op == "NOP") {
return AsmOp::Nop();

View file

@ -620,7 +620,7 @@ int compute_compare(td::RefInt256 x, td::RefInt256 y, int mode) {
if (mode == 7) {
return s;
} else {
return (mode >> (1 - s)) & 1;
return -((mode >> (1 - s)) & 1);
}
}
@ -641,8 +641,8 @@ int compute_compare(const VarDescr& x, const VarDescr& y, int mode) {
return x.always_less(y) ? 1 : (x.always_geq(y) ? 2 : 3);
case 5: // <>
return x.always_neq(y) ? 1 : (x.always_equal(y) ? 2 : 3);
case 6: // >=
return x.always_geq(y) ? 1 : (x.always_less(y) ? 2 : 3);
case 6: // <=
return x.always_leq(y) ? 1 : (x.always_greater(y) ? 2 : 3);
case 7: // <=>
return x.always_less(y)
? 1
@ -661,10 +661,11 @@ AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, i
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
if (x.is_int_const() && y.is_int_const()) {
r.set_const(compute_compare(x.int_const, y.int_const, mode));
int v = compute_compare(x.int_const, y.int_const, mode);
r.set_const(v);
x.unused();
y.unused();
return push_const(r.int_const);
return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v != 0);
}
int v = compute_compare(x, y, mode);
assert(v);
@ -672,7 +673,7 @@ AsmOp compile_cmp_int(std::vector<VarDescr>& res, std::vector<VarDescr>& args, i
r.set_const(v - (v >> 2) - 2);
x.unused();
y.unused();
return push_const(r.int_const);
return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v & 1);
}
r.val = ~0;
if (v & 1) {
@ -957,12 +958,11 @@ void define_builtins() {
AsmOp::Nop());
define_builtin_func("~dump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
AsmOp::Custom("s0 DUMP", 1, 1));
define_builtin_func(
"run_method0", TypeExpr::new_map(Int, Unit),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 0, false); }, true);
define_builtin_func_x(
"run_method1", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 1, false); }, {1, 0}, {}, true);
define_builtin_func("run_method0", TypeExpr::new_map(Int, Unit),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 0, false); }, true);
define_builtin_func_x("run_method1",
TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 1, false); }, {1, 0}, {}, true);
define_builtin_func_x(
"run_method2", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X, Y}), Unit)),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 2, false); }, {1, 2, 0}, {}, true);

View file

@ -950,6 +950,7 @@ struct AsmOp {
static AsmOp make_stk2(int a, int b, const char* str, int delta);
static AsmOp make_stk3(int a, int b, int c, const char* str, int delta);
static AsmOp IntConst(td::RefInt256 value);
static AsmOp BoolConst(bool f);
static AsmOp Const(std::string push_op) {
return AsmOp(a_const, 0, 1, std::move(push_op));
}

View file

@ -101,8 +101,12 @@ dictnew constant special-dict
1 'nop
} ::_ sg~
// gas_price gas_limit spec_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit -- c
{ 7 0 reverse <b x{de} s, { swap 64 u, } 7 times b>
// gas_price gas_limit spec_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit b -- c
{ 7 1 reverse x{de} s, { swap 64 u, } 7 times b>
} : make-gas-prices-basic
// gas_price ... delete_due_limit flat_gas_limit flat_gas_rate -- c
{ 2dup or { <b x{d1} s, rot 64 u, swap 64 u, } { 2drop <b } cond
make-gas-prices-basic
} : make-gas-prices
{ make-gas-prices 20 config! } : config.mc_gas_prices!
{ make-gas-prices 21 config! } : config.gas_prices!

View file

@ -171,9 +171,9 @@ elector_addr config.elector_smc!
1 500 1000 500000 config.storage_prices!
config.special!
// gas_price gas_limit special_gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit --
1000 sg* 1 *M dup 10000 10 *M GR$0.1 GR$1.0 config.gas_prices!
10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 config.mc_gas_prices!
// gas_price gas_limit special_gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit flat_gas_limit flat_gas_price --
1000 sg* 1 *M dup 10000 10 *M GR$0.1 GR$1.0 100 100000 config.gas_prices!
10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 0 0 config.mc_gas_prices!
// lump_price bit_price cell_price ihr_factor first_frac next_frac
1000000 1000 sg* 100000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.fwd_prices!
10000000 10000 sg* 1000000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.mc_fwd_prices!

View file

@ -745,6 +745,26 @@ Ref<Cell> CellSlice::fetch_ref() {
}
}
bool CellSlice::prefetch_maybe_ref(Ref<vm::Cell>& res) const {
auto z = prefetch_ulong(1);
if (!z) {
res.clear();
return true;
} else {
return z == 1 && prefetch_ref_to(res);
}
}
bool CellSlice::fetch_maybe_ref(Ref<vm::Cell>& res) {
auto z = prefetch_ulong(1);
if (!z) {
res.clear();
return advance(1);
} else {
return z == 1 && prefetch_ref_to(res) && advance_ext(1, 1);
}
}
bool CellSlice::begins_with(unsigned bits, unsigned long long value) const {
return have(bits) && !((prefetch_ulong(bits) ^ value) & ((1ULL << bits) - 1));
}

View file

@ -185,6 +185,8 @@ class CellSlice : public td::CntObject {
bool prefetch_ref_to(Ref<Cell>& ref, unsigned offset = 0) const {
return (ref = prefetch_ref(offset)).not_null();
}
bool fetch_maybe_ref(Ref<Cell>& ref);
bool prefetch_maybe_ref(Ref<Cell>& ref) const;
td::BitSlice fetch_bits(unsigned bits);
td::BitSlice prefetch_bits(unsigned bits) const;
td::Ref<CellSlice> fetch_subslice(unsigned bits, unsigned refs = 0);

View file

@ -1,6 +1,6 @@
The aim of this document is to provide step-by-step instructions for setting up a full node for the TON Blockchain. We assume some familiarity with the TON Blockchain Lite Client, at least to the extent explained in the Lite Client HOWTO.
Notice that you need a machine with a public IP address and a high-bandwidth network connection to run a TON Blockchain Full Node. Typically you'll need a sufficiently powerful server in a datacenter. It is a bad idea to run a Full Node on your home computer; instead, you could run a Full Node on a remote server, and use the TON Blockchain Lite Client to connect to it from home.
Note that you need a machine with a public IP address and a high-bandwidth network connection to run a TON Blockchain Full Node. Typically you'll need a sufficiently powerful server in a datacenter with good network connectivity, using at least a 1 Gbit/s connection to reliably accommodate peak loads (the average load is expected to be approximately 100 Mbit/s). We recommend a dual-processor server with at least eight cores in each processor, at least 256 MiB RAM, at least 8 TB of conventional HDD storage and at least 512 GB of faster SSD storage. It is a bad idea to run a Full Node on your home computer; instead, you could run a Full Node on a remote server, and use the TON Blockchain Lite Client to connect to it from home.
0. Downloading and compiling
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -1,6 +1,6 @@
The aim of this document is to provide step-by-step instructions for setting up a full node for the TON Blockchain as a validator. We assume that a TON Blockchain Full Node is already up and running as explained in FullNode-HOWTO. We also assume some familiarity with the TON Blockchain Lite Client.
Notice that a validator must be run on a dedicated high-performance server with high network bandwidth installed in a reliable datacenter, and that you'll need a large amount of Grams (test Grams, if you want to run a validator in the "testnet") as stakes for your validator. If your validator works incorrectly or is not available for prolonged periods of time, you may lose part or all of your stake, so it makes sense to use high-performance, reliable servers.
Note that a validator must be run on a dedicated high-performance server with high network bandwidth installed in a reliable datacenter, and that you'll need a large amount of Grams (test Grams, if you want to run a validator in the "testnet") as stakes for your validator. If your validator works incorrectly or is not available for prolonged periods of time, you may lose part or all of your stake, so it makes sense to use high-performance, reliable servers. We recommend a dual-processor server with at least eight cores in each processor, at least 256 MiB RAM, at least 8 TB of conventional HDD storage and at least 512 GB of faster SSD storage, with 1 Gbit/s network (and Internet) connectivity to reliably accomodate peak loads.
0. Downloading and compiling
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -12,9 +12,9 @@ The basic instructions are the same as for a TON Blockchain Full Node, as explai
In order to run a Validator, you'll need a Full Node that is already up and running (and completely synchronized with the current blockchain state), and a wallet in the masterchain holding a large amount of Grams (or test Grams, if you want to run a validator in the "testnet" TON Blockchain instance). Typically you'll need at least 100,000 Grams.
Each validator is identified by its (Ed25519) public key. During the validator elections, the validator (or rather its public key) is also associated with a smart contract residing in the masterchain. For simplicity, we say that the validator is "controlled" by this smart contract (e.g., a wallet smart contract). Stakes are accepted on behalf of this validator only if they arrive from its associated smart contract, and only that associated smart contract is entitled to collect the validator's stake after it is unfrozen, along with the validator's share of bonuses (e.g., block mining fees, transaction and message forwarding fees collected from the users of the TON Blockchain by the validator pool). Typically the bonuses are distributed proportionally to the (effective) stakes of the validators. On the other hand, validators with higher stakes are assigned a larger amount of work to perform (i.e., they have to create and validate blocks for more shardchains), so it is important not to get too greedy.
Each validator is identified by its (Ed25519) public key. During the validator elections, the validator (or rather its public key) is also associated with a smart contract residing in the masterchain. For simplicity, we say that the validator is "controlled" by this smart contract (e.g., a wallet smart contract). Stakes are accepted on behalf of this validator only if they arrive from its associated smart contract, and only that associated smart contract is entitled to collect the validator's stake after it is unfrozen, along with the validator's share of bonuses (e.g., block mining fees, transaction and message forwarding fees collected from the users of the TON Blockchain by the validator pool). Typically the bonuses are distributed proportionally to the (effective) stakes of the validators. On the other hand, validators with higher stakes are assigned a larger amount of work to perform (i.e., they have to create and validate blocks for more shardchains), so it is important not to stake an amount that will yield more validation work than your node is capable of handling.
Notice that each validator (identified by its public key) can be associated with at most one controlling smart contract (residing in the masterchain), but the same controlling smart contract may be associated with several validators. In this way you can run several validators (on different physical servers) and make stakes for them from the same smart contract. If one of these validators breaks down and you lose its stake, at least the other validators will continue working and will keep their stakes and receive bonuses.
Notice that each validator (identified by its public key) can be associated with at most one controlling smart contract (residing in the masterchain), but the same controlling smart contract may be associated with several validators. In this way you can run several validators (on different physical servers) and make stakes for them from the same smart contract. If one of these validators stops functioning and you lose its stake, the other validators should continue operating and will keep their stakes and potentially receive bonuses.
2. Creating the controlling smart contract
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -231,4 +231,4 @@ If you have done everything correctly (in particular indicated the correct seqno
7. Participating in the next elections
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notice that even before the term of the validator group containing your elected validator finishes, new elections for the next validator group will be announced. You'll probably want to participate in them as well. For this, you can use the same validator, but you must generate a new validator key and new ADNL address. You'll also have to make a new stake before your previous stake is returned (because your previous stake will be unfrozen and returned only some time after the next validator group becomes active), so it does not make sense to stake more than half of your Grams.
Notice that even before the term of the validator group containing your elected validator finishes, new elections for the next validator group will be announced. You'll probably want to participate in them as well. For this, you can use the same validator, but you must generate a new validator key and new ADNL address. You'll also have to make a new stake before your previous stake is returned (because your previous stake will be unfrozen and returned only some time after the next validator group becomes active), so if you want to participate in concurrent elections, it likely does not make sense to stake more than half of your Grams.

View file

@ -364,7 +364,7 @@ inline void register_actor_info_ptr(core::ActorInfoPtr actor_info_ptr) {
}
template <class T, class... ArgsT>
core::ActorInfoPtr create_actor(core::ActorOptions &options, ArgsT &&... args) {
core::ActorInfoPtr create_actor(core::ActorOptions &options, ArgsT &&... args) noexcept {
auto *scheduler_context = core::SchedulerContext::get();
if (!options.has_scheduler()) {
options.on_scheduler(scheduler_context->get_scheduler_id());

View file

@ -75,7 +75,7 @@ void ActorExecutor::send(ActorSignals signals) {
pending_signals_.add_signals(signals);
}
void ActorExecutor::start() {
void ActorExecutor::start() noexcept {
//LOG(ERROR) << "START " << actor_info_.get_name() << " " << tag("from_queue", options.from_queue);
if (is_closed()) {
return;
@ -126,7 +126,7 @@ void ActorExecutor::start() {
}
}
void ActorExecutor::finish() {
void ActorExecutor::finish() noexcept {
//LOG(ERROR) << "FINISH " << actor_info_.get_name() << " " << tag("own_lock", actor_locker_.own_lock());
if (!actor_locker_.own_lock()) {
if (!pending_signals_.empty() && actor_locker_.add_signals(pending_signals_)) {

View file

@ -106,8 +106,8 @@ class ActorExecutor {
return flags_;
}
void start();
void finish();
void start() noexcept;
void finish() noexcept;
bool flush_one(ActorSignals &signals);
bool flush_one_signal(ActorSignals &signals);

View file

@ -50,6 +50,9 @@
#define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
#define TRY_RESULT_PROMISE(promise_name, name, result) \
TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
#define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result)
#define TRY_RESULT_PREFIX(name, result, prefix) \
@ -78,10 +81,18 @@
} \
name = r_name.move_as_ok();
#define TRY_RESULT_PROMISE_IMPL(promise_name, r_name, name, result) \
auto r_name = (result); \
if (r_name.is_error()) { \
promise_name.set_error(r_name.move_as_error()); \
return; \
} \
name = r_name.move_as_ok();
#define TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, r_name, name, result, prefix) \
auto r_name = (result); \
if (r_name.is_error()) { \
promise.set_error(r_name.move_as_error()); \
promise_name.set_error(r_name.move_as_error_prefix(prefix)); \
return; \
} \
name = r_name.move_as_ok();
@ -298,14 +309,31 @@ class Status {
return std::move(*this);
}
Status move_as_error_prefix(Slice prefix) TD_WARN_UNUSED_RESULT {
Status move_as_error_prefix(const Status &status) const TD_WARN_UNUSED_RESULT {
return status.move_as_error_suffix(message());
}
Status move_as_error_prefix(Slice prefix) const TD_WARN_UNUSED_RESULT {
CHECK(is_error());
Info info = get_info();
switch (info.error_type) {
case ErrorType::general:
return Error(code(), PSLICE() << prefix << message());
return Error(code(), PSLICE() << prefix << " " << message());
case ErrorType::os:
return Status(false, ErrorType::os, code(), PSLICE() << prefix << message());
return Status(false, ErrorType::os, code(), PSLICE() << prefix << " " << message());
default:
UNREACHABLE();
return {};
}
}
Status move_as_error_suffix(Slice suffix) const TD_WARN_UNUSED_RESULT {
CHECK(is_error());
Info info = get_info();
switch (info.error_type) {
case ErrorType::general:
return Error(code(), PSLICE() << message() << " " << suffix);
case ErrorType::os:
return Status(false, ErrorType::os, code(), PSLICE() << message() << " " << suffix);
default:
UNREACHABLE();
return {};
@ -488,6 +516,18 @@ class Result {
};
return status_.move_as_error_prefix(prefix);
}
Status move_as_error_prefix(const Status &prefix) TD_WARN_UNUSED_RESULT {
SCOPE_EXIT {
status_ = Status::Error<-5>();
};
return status_.move_as_error_prefix(prefix);
}
Status move_as_error_suffix(Slice suffix) TD_WARN_UNUSED_RESULT {
SCOPE_EXIT {
status_ = Status::Error<-5>();
};
return status_.move_as_error_suffix(suffix);
}
const T &ok() const {
LOG_CHECK(status_.is_ok()) << status_;
return value_;

View file

@ -32,10 +32,10 @@
#include <type_traits>
#include <unordered_set>
#define BEGIN_STORE_FLAGS() \
do { \
uint32 flags_store = 0; \
uint32 bit_offset_store = 0
#define BEGIN_STORE_FLAGS() \
do { \
td::uint32 flags_store = 0; \
td::uint32 bit_offset_store = 0
#define STORE_FLAG(flag) \
flags_store |= (flag) << bit_offset_store; \
@ -47,10 +47,10 @@
} \
while (false)
#define BEGIN_PARSE_FLAGS() \
do { \
uint32 flags_parse; \
uint32 bit_offset_parse = 0; \
#define BEGIN_PARSE_FLAGS() \
do { \
td::uint32 flags_parse; \
td::uint32 bit_offset_parse = 0; \
td::parse(flags_parse, parser)
#define PARSE_FLAG(flag) \

View file

@ -99,6 +99,16 @@ class TlParser {
}
}
bool can_prefetch_int() const {
return get_left_len() >= sizeof(int32);
}
int32 prefetch_int_unsafe() const {
int32 result;
std::memcpy(&result, data, sizeof(int32));
return result;
}
int32 fetch_int_unsafe() {
int32 result;
std::memcpy(&result, data, sizeof(int32));

View file

@ -74,3 +74,4 @@ liteServer.getConfigParams mode:# id:tonNode.blockIdExt param_list:(vector int)
liteServer.queryPrefix = Object;
liteServer.query data:bytes = Object;
liteServer.waitMasterchainSeqno seqno:int timeout_ms:int = Object; // query prefix

Binary file not shown.

View file

@ -444,12 +444,14 @@ db.state.initBlockId block:tonNode.blockIdExt = db.state.InitBlockId;
db.state.gcBlockId block:tonNode.blockIdExt = db.state.GcBlockId;
db.state.shardClient block:tonNode.blockIdExt = db.state.ShardClient;
db.state.asyncSerializer block:tonNode.blockIdExt last:tonNode.blockIdExt last_ts:int = db.state.AsyncSerializer;
db.state.hardforks blocks:(vector tonNode.blockIdExt) = db.state.Hardforks;
db.state.key.destroyedSessions = db.state.Key;
db.state.key.initBlockId = db.state.Key;
db.state.key.gcBlockId = db.state.Key;
db.state.key.shardClient = db.state.Key;
db.state.key.asyncSerializer = db.state.Key;
db.state.key.hardforks = db.state.Key;
db.lt.el.key workchain:int shard:long idx:int = db.lt.Key;
db.lt.desc.key workchain:int shard:long = db.lt.Key;

Binary file not shown.

View file

@ -39,8 +39,8 @@ internal.transactionId lt:int64 hash:bytes = internal.TransactionId;
raw.initialAccountState code:bytes data:bytes = raw.InitialAccountState;
raw.accountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId sync_utime:int53 = raw.AccountState;
raw.message source:string destination:string value:int64 message:bytes = raw.Message;
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
raw.message source:string destination:string value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes message:bytes = 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.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
testWallet.initialAccountState public_key:string = testWallet.InitialAccountState;
@ -63,7 +63,7 @@ generic.accountStateWallet account_state:wallet.accountState = generic.AccountSt
generic.accountStateTestGiver account_state:testGiver.accountState = generic.AccountState;
generic.accountStateUninited account_state:uninited.accountState = generic.AccountState;
sendGramsResult sent_until:int53 = SendGramsResult;
sendGramsResult sent_until:int53 body_hash:bytes = SendGramsResult;
updateSendLiteServerQuery id:int64 data:bytes = Update;
@ -94,6 +94,7 @@ options.setConfig config:config = Ok;
createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key;
deleteKey key:key = Ok;
deleteAllKeys = Ok;
exportKey input_key:inputKey = ExportedKey;
exportPemKey input_key:inputKey key_password:secureBytes = ExportedPemKey;
exportEncryptedKey input_key:inputKey key_password:secureBytes = ExportedEncryptedKey;

Binary file not shown.

View file

@ -68,7 +68,7 @@ int TD_TL_writer::get_storer_type(const tl::tl_combinator *t, const std::string
tl::TL_writer::Mode TD_TL_writer::get_parser_mode(int type) const {
if (tl_name == "tonlib_api") {
#ifndef TD_ENABLE_JNI // we need to parse all types in order to implement toString
#ifndef TONLIB_ENABLE_JNI // we need to parse all types in order to implement toString
return Server;
#endif
}

View file

@ -80,7 +80,7 @@ add_executable(tonlib-cli tonlib/tonlib-cli.cpp)
target_link_libraries(tonlib-cli tonlib tdactor tdutils terminal)
if (NOT CMAKE_CROSSCOMPILING)
if (TD_ENABLE_JNI)
if (TONLIB_ENABLE_JNI)
#FIXME
#add_dependencies(tonlib tonlib_generate_java_api)
endif()

View file

@ -420,9 +420,14 @@ TEST(Tonlib, ParseAddres) {
ASSERT_EQ(-1, addr->workchain_id_);
ASSERT_EQ(true, addr->bounceable_);
ASSERT_EQ(false, addr->testnet_);
auto raw = addr->addr_;
auto addr_str = sync_send(client, make_object<tonlib_api::packAccountAddress>(std::move(addr))).move_as_ok();
ASSERT_EQ("Ef9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX", addr_str->account_address_);
auto addr_str2 = sync_send(client, make_object<tonlib_api::packAccountAddress>(
make_object<tonlib_api::unpackedAccountAddress>(-1, false, false, raw)))
.move_as_ok();
ASSERT_EQ("Uf9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfdyS", addr_str2->account_address_);
}
TEST(Tonlib, KeysApi) {
@ -550,7 +555,7 @@ TEST(Tonlib, KeysApi) {
make_object<tonlib_api::inputKey>(
make_object<tonlib_api::key>(key->public_key_, imported_key->secret_.copy()), new_local_password.copy()),
pem_password.copy()));
if (r_exported_pem_key.is_error() && r_exported_pem_key.error().message() == "Not supported") {
if (r_exported_pem_key.is_error() && r_exported_pem_key.error().message() == "INTERNAL Not supported") {
return;
}
auto exported_pem_key = r_exported_pem_key.move_as_ok();

View file

@ -30,7 +30,7 @@ void ExtClient::with_last_block(td::Promise<LastBlockState> promise) {
});
};
if (client_.last_block_actor_.empty()) {
return P.set_error(td::Status::Error(500, "No lite clients"));
return P.set_error(TonlibError::NoLiteServers());
}
td::actor::send_closure(client_.last_block_actor_, &LastBlock::get_last_block, std::move(P));
}
@ -44,7 +44,7 @@ void ExtClient::send_raw_query(td::BufferSlice query, td::Promise<td::BufferSlic
});
};
if (client_.andl_ext_client_.empty()) {
return P.set_error(td::Status::Error(500, "No lite clients"));
return P.set_error(TonlibError::NoLiteServers());
}
td::actor::send_closure(client_.andl_ext_client_, &ton::adnl::AdnlExtClient::send_query, "query", std::move(query),
td::Timestamp::in(10.0), std::move(P));

View file

@ -26,6 +26,10 @@
#include "td/actor/actor.h"
#include "td/utils/Container.h"
#include "td/utils/Random.h"
#include "TonlibError.h"
#include "utils.h"
namespace tonlib {
class LastBlock;
@ -53,23 +57,37 @@ class ExtClient {
void with_last_block(td::Promise<LastBlockState> promise);
template <class QueryT>
void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise) {
void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise, td::int32 seq_no = -1) {
auto raw_query = ton::serialize_tl_object(&query, true);
LOG(ERROR) << "send query to liteserver: " << to_string(query);
td::uint32 tag = td::Random::fast_uint32();
VLOG(lite_server) << "send query to liteserver: " << tag << " " << to_string(query);
td::BufferSlice liteserver_query =
ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_query>(std::move(raw_query)), true);
send_raw_query(std::move(liteserver_query), [promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
promise.set_result([&]() -> td::Result<typename QueryT::ReturnType> {
TRY_RESULT(data, std::move(R));
auto r_error = ton::fetch_tl_object<ton::lite_api::liteServer_error>(data.clone(), true);
if (r_error.is_ok()) {
auto f = r_error.move_as_ok();
return td::Status::Error(f->code_, f->message_);
}
return ton::fetch_result<QueryT>(std::move(data));
}());
});
if (seq_no >= 0) {
auto wait = ton::lite_api::liteServer_waitMasterchainSeqno(seq_no, 5000);
VLOG(lite_server) << " with prefix " << to_string(wait);
auto prefix = ton::serialize_tl_object(&wait, true);
liteserver_query = td::BufferSlice(PSLICE() << prefix.as_slice() << liteserver_query.as_slice());
}
send_raw_query(
std::move(liteserver_query), [promise = std::move(promise), tag](td::Result<td::BufferSlice> R) mutable {
auto res = [&]() -> td::Result<typename QueryT::ReturnType> {
TRY_RESULT_PREFIX(data, std::move(R), TonlibError::LiteServerNetwork());
auto r_error = ton::fetch_tl_object<ton::lite_api::liteServer_error>(data.clone(), true);
if (r_error.is_ok()) {
auto f = r_error.move_as_ok();
return TonlibError::LiteServer(f->code_, f->message_);
}
return ton::fetch_result<QueryT>(std::move(data));
}
();
VLOG_IF(lite_server, res.is_ok())
<< "got result from liteserver: " << tag << " " << td::Slice(to_string(res.ok())).truncate(1 << 12);
VLOG_IF(lite_server, res.is_error()) << "got error from liteserver: " << tag << " " << res.error();
promise.set_result(std::move(res));
});
}
private:

View file

@ -18,6 +18,7 @@
Copyright 2017-2019 Telegram Systems LLP
*/
#include "ExtClientOutbound.h"
#include "TonlibError.h"
#include <map>
namespace tonlib {
@ -40,7 +41,7 @@ class ExtClientOutboundImp : public ExtClientOutbound {
void on_query_result(td::int64 id, td::Result<td::BufferSlice> r_data, td::Promise<td::Unit> promise) override {
auto it = queries_.find(id);
if (it == queries_.end()) {
promise.set_error(td::Status::Error(400, "Unknown query id"));
promise.set_error(TonlibError::Internal("Unknown query id"));
}
it->second.set_result(std::move(r_data));
queries_.erase(it);
@ -54,7 +55,7 @@ class ExtClientOutboundImp : public ExtClientOutbound {
void tear_down() override {
for (auto &it : queries_) {
it.second.set_error(td::Status::Error(400, "Query cancelled"));
it.second.set_error(TonlibError::Cancelled());
}
queries_.clear();
}

View file

@ -20,18 +20,19 @@
#include "tonlib/utils.h"
#include "block/block-auto.h"
namespace tonlib {
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) {
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept {
return vm::CellBuilder()
.append_cellslice(binary_bitstring_to_cellslice("b{00110}").move_as_ok())
.store_ref(std::move(code))
.store_ref(std::move(data))
.finalize();
}
block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) {
block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id,
const td::Ref<vm::Cell>& init_state) noexcept {
return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/);
}
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body) {
td::Ref<vm::Cell> body) noexcept {
block::gen::Message::Record message;
/*info*/ {
block::gen::CommonMsgInfo::Record_ext_in_msg_info info;

View file

@ -22,9 +22,9 @@
namespace tonlib {
class GenericAccount {
public:
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data);
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state);
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept;
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) noexcept;
static td::Ref<vm::Cell> create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body);
td::Ref<vm::Cell> body) noexcept;
};
} // namespace tonlib

View file

@ -22,9 +22,12 @@
#include "tonlib/keys/DecryptedKey.h"
#include "tonlib/keys/EncryptedKey.h"
#include "tonlib/TonlibError.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
#include "td/utils/crypto.h"
#include "td/utils/PathView.h"
namespace tonlib {
namespace {
@ -47,7 +50,7 @@ td::Result<KeyStorage::Key> KeyStorage::save_key(const DecryptedKey &decrypted_k
Key res;
res.public_key = encrypted_key.public_key.as_octet_string();
res.secret = std::move(encrypted_key.secret);
TRY_STATUS(kv_->set(to_file_name(res), encrypted_key.encrypted_data));
TRY_STATUS_PREFIX(kv_->set(to_file_name(res), encrypted_key.encrypted_data), TonlibError::Internal());
return std::move(res);
}
@ -68,14 +71,16 @@ td::Result<DecryptedKey> KeyStorage::export_decrypted_key(InputKey input_key) {
if (r_encrypted_data.is_ok()) {
LOG(WARNING) << "Restore private from deprecated location " << to_file_name_old(input_key.key) << " --> "
<< to_file_name(input_key.key);
TRY_STATUS(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok()));
TRY_STATUS_PREFIX(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok()), TonlibError::Internal());
kv_->erase(to_file_name_old(input_key.key)).ignore();
}
}
TRY_RESULT(encrypted_data, std::move(r_encrypted_data));
TRY_RESULT_PREFIX(encrypted_data, std::move(r_encrypted_data), TonlibError::KeyUnknown());
EncryptedKey encrypted_key{std::move(encrypted_data), td::Ed25519::PublicKey(std::move(input_key.key.public_key)),
std::move(input_key.key.secret)};
return encrypted_key.decrypt(std::move(input_key.local_password));
TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(std::move(input_key.local_password)),
TonlibError::KeyDecrypt());
return std::move(decrypted_key);
}
td::Result<KeyStorage::ExportedKey> KeyStorage::export_key(InputKey input_key) {
@ -93,24 +98,43 @@ td::Result<KeyStorage::PrivateKey> KeyStorage::load_private_key(InputKey input_k
}
td::Status KeyStorage::delete_key(const Key &key) {
LOG(WARNING) << "Delete private key stored at " << to_file_name(key);
return kv_->erase(to_file_name(key));
}
td::Status KeyStorage::delete_all_keys() {
std::vector<std::string> keys;
kv_->foreach_key([&](td::Slice key) {
if (td::PathView(key).extension().empty()) {
keys.push_back(key.str());
}
});
td::Status status;
for (auto key : keys) {
LOG(WARNING) << "Delete private key stored at " << key;
auto err = kv_->erase(key);
if (err.is_error() && status.is_ok()) {
status = std::move(err);
}
}
return status;
}
td::Result<KeyStorage::Key> KeyStorage::import_key(td::Slice local_password, td::Slice mnemonic_password,
ExportedKey exported_key) {
TRY_RESULT(mnemonic, Mnemonic::create(std::move(exported_key.mnemonic_words), td::SecureString(mnemonic_password)));
if (!mnemonic.is_basic_seed()) {
if (mnemonic_password.empty() && mnemonic.is_password_seed()) {
return td::Status::Error("Mnemonic password is expected");
return TonlibError::NeedMnemonicPassword();
}
return td::Status::Error("Invalid mnemonic words or password (invalid checksum)");
return TonlibError::InvalidMnemonic();
}
return save_key(DecryptedKey(std::move(mnemonic)), local_password);
}
td::Result<KeyStorage::ExportedPemKey> KeyStorage::export_pem_key(InputKey input_key, td::Slice key_password) {
TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key)));
TRY_RESULT(pem, decrypted_key.private_key.as_pem(key_password));
TRY_RESULT_PREFIX(pem, decrypted_key.private_key.as_pem(key_password), TonlibError::Internal());
return ExportedPemKey{std::move(pem)};
}
@ -120,14 +144,15 @@ td::Result<KeyStorage::Key> KeyStorage::change_local_password(InputKey input_key
Key res;
res.public_key = std::move(input_key.key.public_key);
res.secret = std::move(new_secret);
TRY_RESULT(value, kv_->get(to_file_name(input_key.key)));
TRY_STATUS(kv_->add(to_file_name(res), value));
TRY_RESULT_PREFIX(value, kv_->get(to_file_name(input_key.key)), TonlibError::KeyUnknown());
TRY_STATUS_PREFIX(kv_->add(to_file_name(res), value), TonlibError::Internal());
return std::move(res);
}
td::Result<KeyStorage::Key> KeyStorage::import_pem_key(td::Slice local_password, td::Slice key_password,
ExportedPemKey exported_key) {
TRY_RESULT(key, td::Ed25519::PrivateKey::from_pem(exported_key.pem, key_password));
TRY_RESULT_PREFIX(key, td::Ed25519::PrivateKey::from_pem(exported_key.pem, key_password),
TonlibError::InvalidPemKey());
return save_key(DecryptedKey({}, std::move(key)), local_password);
}
@ -143,7 +168,7 @@ td::Result<KeyStorage::Key> KeyStorage::import_encrypted_key(td::Slice local_pas
ExportedEncryptedKey exported_key) {
EncryptedKey encrypted_key{std::move(exported_key.data), td::Ed25519::PublicKey(td::SecureString()),
td::SecureString(dummy_secret)};
TRY_RESULT(decrypted_key, encrypted_key.decrypt(key_password, false));
TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(key_password, false), TonlibError::KeyDecrypt());
return save_key(std::move(decrypted_key), local_password);
}

View file

@ -60,6 +60,7 @@ class KeyStorage {
td::Result<Key> change_local_password(InputKey input_key, td::Slice new_local_password);
td::Status delete_key(const Key& key);
td::Status delete_all_keys();
td::Result<Key> import_key(td::Slice local_password, td::Slice mnemonic_password, ExportedKey exported_key);
td::Result<Key> import_pem_key(td::Slice local_password, td::Slice key_password, ExportedPemKey exported_key);

View file

@ -42,6 +42,22 @@ class KeyValueDir : public KeyValue {
return td::unlink(key.str());
}
void foreach_key(std::function<void(td::Slice)> f) override {
int cnt = 0;
td::WalkPath::run(directory_, [&](td::Slice path, td::WalkPath::Type type) {
cnt++;
if (type == td::WalkPath::Type::EnterDir) {
if (cnt != 1) {
return td::WalkPath::Action::SkipDir;
}
} else if (type == td::WalkPath::Type::NotDir) {
f(path);
}
return td::WalkPath::Action::Continue;
}).ignore();
}
private:
std::string directory_;
@ -82,6 +98,11 @@ class KeyValueInmemory : public KeyValue {
map_.erase(it);
return td::Status::OK();
}
void foreach_key(std::function<void(td::Slice)> f) override {
for (auto &it : map_) {
f(it.first);
}
}
private:
class Cmp : public std::less<> {

View file

@ -3,6 +3,8 @@
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <functional>
namespace tonlib {
class KeyValue {
public:
@ -11,6 +13,7 @@ class KeyValue {
virtual td::Status set(td::Slice key, td::Slice value) = 0;
virtual td::Status erase(td::Slice key) = 0;
virtual td::Result<td::SecureString> get(td::Slice key) = 0;
virtual void foreach_key(std::function<void(td::Slice)> f) = 0;
static td::Result<td::unique_ptr<KeyValue>> create_dir(td::CSlice dir);
static td::Result<td::unique_ptr<KeyValue>> create_inmemory();

View file

@ -18,12 +18,18 @@
*/
#include "tonlib/LastBlock.h"
#include "tonlib/utils.h"
#include "ton/lite-tl.hpp"
#include "lite-client/lite-client-common.h"
namespace tonlib {
// init_state <-> last_key_block
// state.valitated_init_state
// last_key_block ->
//
td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state) {
return sb << td::tag("last_block", state.last_block_id.to_str())
<< td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime);
@ -32,9 +38,10 @@ td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state
LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr<Callback> callback)
: state_(std::move(state)), config_(std::move(config)), callback_(std::move(callback)) {
client_.set_client(client);
if (!config_.init_block_id.is_valid()) {
check_init_block_state_ = QueryState::Done;
}
state_.last_block_id = state_.last_key_block_id;
VLOG(last_block) << "check_init_block: skip - FIXME before release";
check_init_block_state_ = QueryState::Done;
}
void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
@ -42,9 +49,13 @@ void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
promise.set_error(fatal_error_.clone());
return;
}
if (promises_.empty() && get_last_block_state_ == QueryState::Done) {
VLOG(last_block) << "sync: start";
VLOG(last_block) << "get_last_block: reset";
get_last_block_state_ = QueryState::Empty;
}
promises_.push_back(std::move(promise));
sync_loop();
}
@ -54,36 +65,43 @@ void LastBlock::sync_loop() {
return;
}
update_zero_state(state_.zero_state_id);
update_zero_state(state_.zero_state_id, "cache");
update_zero_state(ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash,
config_.zero_state_id.file_hash));
config_.zero_state_id.file_hash),
"config");
if (get_mc_info_state_ == QueryState::Empty) {
VLOG(last_block) << "get_masterchain_info: start";
get_mc_info_state_ = QueryState::Active;
client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(),
[this](auto r_info) { this->on_masterchain_info(std::move(r_info)); });
}
if (get_last_block_state_ == QueryState::Empty) {
get_last_block_state_ = QueryState::Active;
total_sync_ = td::Timer();
validate_ = td::Timer(true);
queries_ = 0;
LOG(INFO) << "Begin last block synchronization " << state_;
do_get_last_block();
if (check_init_block_state_ == QueryState::Empty) {
if (!config_.init_block_id.is_valid()) {
check_init_block_state_ = QueryState::Done;
VLOG(last_block) << "check_init_block: skip - no init_block in config";
} else if (config_.init_block_id == state_.init_block_id) {
check_init_block_state_ = QueryState::Done;
VLOG(last_block) << "check_init_block: skip - was checked before";
} else {
check_init_block_state_ = QueryState::Active;
check_init_block_stats_.start();
if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) {
VLOG(last_block) << "check_init_block: start - init_block -> last_block";
do_check_init_block(config_.init_block_id, state_.last_key_block_id);
} else {
VLOG(last_block) << "check_init_block: start - last_block -> init_block";
do_check_init_block(state_.last_key_block_id, config_.init_block_id);
}
}
}
if (check_init_block_state_ == QueryState::Empty) {
if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) {
check_init_block_state_ = QueryState::Active;
// validate
//total_sync_ = td::Timer();
//validate_ = td::Timer(true);
//queries_ = 0;
LOG(INFO) << "Begin last block synchronization (check init_block)" << state_;
do_check_init_block(state_.last_key_block_id);
} else {
}
if (get_last_block_state_ == QueryState::Empty && check_init_block_state_ == QueryState::Done) {
VLOG(last_block) << "get_last_block: start";
get_last_block_stats_.start();
get_last_block_state_ = QueryState::Active;
do_get_last_block();
}
if (get_mc_info_state_ == QueryState::Done && get_last_block_state_ == QueryState::Done &&
@ -94,7 +112,8 @@ void LastBlock::sync_loop() {
void LastBlock::do_get_last_block() {
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
queries_++;
VLOG(last_block) << "get_last_block: continue " << state_.last_key_block_id.to_str() << " -> ?";
get_last_block_stats_.queries_++;
client_.send_query(
ton::lite_api::liteServer_getBlockProof(0, create_tl_lite_block_id(state_.last_key_block_id), nullptr),
[this, from = state_.last_key_block_id](auto r_block_proof) {
@ -102,64 +121,69 @@ void LastBlock::do_get_last_block() {
});
}
void LastBlock::do_check_init_block(ton::BlockIdExt from) {
void LastBlock::do_check_init_block(ton::BlockIdExt from, ton::BlockIdExt to) {
VLOG(last_block) << "check_init_block: continue " << from.to_str() << " -> " << to.to_str();
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
//queries_++;
client_.send_query(ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from),
create_tl_lite_block_id(config_.init_block_id)),
[this, from = state_.last_key_block_id](auto r_block_proof) {
this->on_init_block_proof(from, std::move(r_block_proof));
});
check_init_block_stats_.queries_++;
client_.send_query(
ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from), create_tl_lite_block_id(to)),
[this, from, to](auto r_block_proof) { this->on_init_block_proof(from, to, std::move(r_block_proof)); });
}
td::Result<std::unique_ptr<block::BlockProofChain>> LastBlock::process_block_proof(
ton::BlockIdExt from,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
TRY_RESULT(block_proof, std::move(r_block_proof));
LOG(DEBUG) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_);
TRY_RESULT(block_proof, std::move(r_block_proof)); //TODO: it is fatal?
TRY_RESULT_PREFIX(chain, TRY_VM(process_block_proof(from, std::move(block_proof))),
TonlibError::ValidateBlockProof());
return std::move(chain);
}
td::Result<std::unique_ptr<block::BlockProofChain>> LastBlock::process_block_proof(
ton::BlockIdExt from, ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof> block_proof) {
VLOG(last_block) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_);
TRY_RESULT(chain, liteclient::deserialize_proof_chain(std::move(block_proof)));
if (chain->from != from) {
return td::Status::Error(PSLICE() << "block proof chain starts from block " << chain->from.to_str()
<< ", not from requested block " << from.to_str());
}
TRY_STATUS(chain->validate());
return std::move(chain);
}
void LastBlock::update_state(block::BlockProofChain& chain) {
// Update state_
bool is_changed = false;
is_changed |= update_mc_last_block(chain->to);
if (chain->has_key_block) {
is_changed |= update_mc_last_key_block(chain->key_blkid);
is_changed |= update_mc_last_block(chain.to);
if (chain.has_key_block) {
is_changed |= update_mc_last_key_block(chain.key_blkid);
}
if (chain->has_utime) {
update_utime(chain->last_utime);
if (chain.has_utime) {
update_utime(chain.last_utime);
}
if (is_changed) {
callback_->on_state_changed(state_);
}
return std::move(chain);
}
void LastBlock::on_block_proof(
ton::BlockIdExt from,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
validate_.resume();
get_last_block_stats_.validate_.resume();
auto r_chain = process_block_proof(from, std::move(r_block_proof));
validate_.pause();
bool is_ready;
get_last_block_stats_.validate_.pause();
if (r_chain.is_error()) {
LOG(WARNING) << "Error during last block synchronization " << r_chain.error();
if (config_.init_block_id.is_valid()) {
if (state_.last_key_block_id.id.seqno < config_.init_block_id.id.seqno) {
on_sync_error(td::Status::Error(PSLICE() << "Sync failed and we can't validate config.init_block: "
<< r_chain.move_as_error()));
}
}
is_ready = true;
} else {
is_ready = r_chain.ok()->complete;
get_last_block_state_ = QueryState::Empty;
VLOG(last_block) << "get_last_block: error " << r_chain.error();
on_sync_error(r_chain.move_as_error_suffix("(during last block synchronization)"));
return;
}
if (is_ready) {
LOG(INFO) << "End last block synchronization " << state_ << "\n"
<< " net queries: " << queries_ << "\n"
<< " total: " << total_sync_ << " validation: " << validate_;
auto chain = r_chain.move_as_ok();
CHECK(chain);
update_state(*chain);
if (chain->complete) {
VLOG(last_block) << "get_last_block: done\n" << get_last_block_stats_;
get_last_block_state_ = QueryState::Done;
sync_loop();
} else {
@ -168,26 +192,26 @@ void LastBlock::on_block_proof(
}
void LastBlock::on_init_block_proof(
ton::BlockIdExt from,
ton::BlockIdExt from, ton::BlockIdExt to,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
validate_.resume();
check_init_block_stats_.validate_.resume();
auto r_chain = process_block_proof(from, std::move(r_block_proof));
validate_.pause();
check_init_block_stats_.validate_.pause();
if (r_chain.is_error()) {
check_init_block_state_ = QueryState::Empty;
on_sync_error(
td::Status::Error(PSLICE() << "Error during last block synchronization (check init_block)" << r_chain.error()));
VLOG(last_block) << "check_init_block: error " << r_chain.error();
on_sync_error(r_chain.move_as_error_suffix("(during check init block)"));
return;
}
auto chain = r_chain.move_as_ok();
CHECK(chain);
update_state(*chain);
if (chain->complete) {
LOG(INFO) << "End last block synchronization " << state_ << "\n"
<< " net queries: " << queries_ << "\n"
<< " total: " << total_sync_ << " validation: " << validate_;
get_last_block_state_ = QueryState::Done;
VLOG(last_block) << "check_init_block: done\n" << check_init_block_stats_;
check_init_block_state_ = QueryState::Done;
sync_loop();
} else {
do_check_init_block(chain->to);
do_check_init_block(chain->to, to);
}
}
@ -195,28 +219,30 @@ void LastBlock::on_masterchain_info(
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info) {
if (r_info.is_ok()) {
auto info = r_info.move_as_ok();
update_zero_state(create_zero_state_id(info->init_));
update_mc_last_block(create_block_id(info->last_));
update_zero_state(create_zero_state_id(info->init_), "masterchain info");
// last block is not validated! Do not update it
get_mc_info_state_ = QueryState::Done;
VLOG(last_block) << "get_masterchain_info: done";
} else {
get_mc_info_state_ = QueryState::Empty;
VLOG(last_block) << "get_masterchain_info: error " << r_info.error();
LOG(WARNING) << "Failed liteServer_getMasterchainInfo " << r_info.error();
on_sync_error(r_info.move_as_error());
}
sync_loop();
}
void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice source) {
if (has_fatal_error()) {
return;
}
if (!zero_state_id.is_valid()) {
LOG(ERROR) << "Ignore invalid zero state update";
LOG(ERROR) << "Ignore invalid zero state update from " << source;
return;
}
if (!state_.zero_state_id.is_valid()) {
LOG(INFO) << "Init zerostate: " << zero_state_id.to_str();
LOG(INFO) << "Init zerostate from " << source << ": " << zero_state_id.to_str();
state_.zero_state_id = std::move(zero_state_id);
return;
}
@ -225,8 +251,9 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
return;
}
on_fatal_error(td::Status::Error(PSLICE() << "Masterchain zerostate mismatch: expected: "
<< state_.zero_state_id.to_str() << ", found " << zero_state_id.to_str()));
on_fatal_error(TonlibError::ValidateZeroState(PSLICE() << "Masterchain zerostate mismatch: expected: "
<< state_.zero_state_id.to_str() << ", found "
<< zero_state_id.to_str() << " from " << source));
}
bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
@ -256,6 +283,9 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) {
state_.last_key_block_id = mc_key_block_id;
LOG(INFO) << "Update masterchain key block id: " << state_.last_key_block_id.to_str();
//LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " "
//<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " "
//<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice());
return true;
}
return false;
@ -268,6 +298,7 @@ void LastBlock::update_utime(td::int64 utime) {
}
void LastBlock::on_sync_ok() {
VLOG(last_block) << "sync: ok " << state_;
for (auto& promise : promises_) {
auto state = state_;
promise.set_value(std::move(state));
@ -275,12 +306,14 @@ void LastBlock::on_sync_ok() {
promises_.clear();
}
void LastBlock::on_sync_error(td::Status status) {
VLOG(last_block) << "sync: error " << status;
for (auto& promise : promises_) {
promise.set_error(status.clone());
}
promises_.clear();
}
void LastBlock::on_fatal_error(td::Status status) {
VLOG(last_block) << "sync: fatal error " << status;
fatal_error_ = std::move(status);
on_sync_error(fatal_error_.clone());
}

View file

@ -22,6 +22,8 @@
#include "tonlib/Config.h"
#include "tonlib/ExtClient.h"
#include "td/utils/tl_helpers.h"
namespace block {
struct BlockProofChain;
}
@ -89,25 +91,44 @@ struct LastBlockState {
ton::BlockIdExt last_key_block_id;
ton::BlockIdExt last_block_id;
td::int64 utime{0};
ton::BlockIdExt init_block_id;
static constexpr td::int32 magic = 0xa7f171a4;
enum Version { None = 0, Magic, InitBlock, Next };
static constexpr td::int32 version = Version::Next - 1;
template <class StorerT>
void store(StorerT &storer) const {
using td::store;
using tonlib::store;
store(magic, storer);
store(version, storer);
store(zero_state_id, storer);
store(last_key_block_id, storer);
store(last_block_id, storer);
store(utime, storer);
store(init_block_id, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
using td::parse;
using tonlib::parse;
td::int32 version = 0;
if (parser.can_prefetch_int() && parser.prefetch_int_unsafe() == magic) {
td::int32 magic;
parse(magic, parser);
parse(version, parser);
}
parse(zero_state_id, parser);
parse(last_key_block_id, parser);
parse(last_block_id, parser);
parse(utime, parser);
if (version >= InitBlock) {
parse(init_block_id, parser);
}
}
};
@ -132,20 +153,36 @@ class LastBlock : public td::actor::Actor {
td::Status fatal_error_;
enum class QueryState { Empty, Active, Done };
QueryState get_mc_info_state_{QueryState::Empty};
QueryState get_last_block_state_{QueryState::Empty};
QueryState check_init_block_state_{QueryState::Empty};
QueryState get_mc_info_state_{QueryState::Empty}; // just to check zero state
QueryState check_init_block_state_{QueryState::Empty}; // init_block <---> last_key_block (from older to newer)
QueryState get_last_block_state_{QueryState::Empty}; // last_key_block_id --> ?
// stats
td::Timer total_sync_;
td::Timer validate_;
td::uint32 queries_;
struct Stats {
td::Timer total_sync_;
td::Timer validate_;
td::uint32 queries_;
void start() {
total_sync_ = td::Timer();
validate_ = td::Timer(true);
queries_ = 0;
}
friend td::StringBuilder &operator<<(td::StringBuilder &sb, const Stats &stats) {
return sb << " net queries: " << stats.queries_ << "\n"
<< " total: " << stats.total_sync_ << " validation: " << stats.validate_;
}
};
Stats check_init_block_stats_;
Stats get_last_block_stats_;
std::vector<td::Promise<LastBlockState>> promises_;
void do_check_init_block(ton::BlockIdExt from);
void do_check_init_block(ton::BlockIdExt from, ton::BlockIdExt to);
void on_init_block_proof(
ton::BlockIdExt from,
ton::BlockIdExt from, ton::BlockIdExt to,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
void on_masterchain_info(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info);
void do_get_last_block();
@ -155,7 +192,11 @@ class LastBlock : public td::actor::Actor {
ton::BlockIdExt from,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
void update_zero_state(ton::ZeroStateIdExt zero_state_id);
td::Result<std::unique_ptr<block::BlockProofChain>> process_block_proof(
ton::BlockIdExt from, ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof> block_proof);
void update_state(block::BlockProofChain &chain);
void update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice source);
bool update_mc_last_block(ton::BlockIdExt mc_block_id);
bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id);

View file

@ -18,6 +18,8 @@
*/
#include "LastBlockStorage.h"
#include "tonlib/utils.h"
#include "td/utils/as.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
@ -49,6 +51,7 @@ td::Result<LastBlockState> LastBlockStorage::get_state(td::Slice name) {
}
void LastBlockStorage::save_state(td::Slice name, LastBlockState state) {
VLOG(last_block) << "Save to cache: " << state;
auto x = td::serialize(state);
std::string y(x.size() + 8, 0);
td::MutableSlice(y).substr(8).copy_from(x);

View file

@ -17,6 +17,7 @@
Copyright 2017-2019 Telegram Systems LLP
*/
#include "Logging.h"
#include "utils.h"
#include "auto/tl/tonlib_api.h"
@ -36,11 +37,9 @@ static td::FileLog file_log;
static td::TsLog ts_log(&file_log);
static td::NullLog null_log;
td::int32 VERBOSITY_NAME(abc) = VERBOSITY_NAME(DEBUG);
td::int32 VERBOSITY_NAME(bcd) = VERBOSITY_NAME(DEBUG);
#define ADD_TAG(tag) \
{ #tag, &VERBOSITY_NAME(tag) }
static const std::map<td::Slice, int *> log_tags{ADD_TAG(abc), ADD_TAG(bcd)};
static const std::map<td::Slice, int *> log_tags{ADD_TAG(tonlib_query), ADD_TAG(last_block)};
#undef ADD_TAG
td::Status Logging::set_current_stream(tonlib_api::object_ptr<tonlib_api::LogStream> stream) {

View file

@ -22,18 +22,18 @@
#include "td/utils/base64.h"
namespace tonlib {
const block::StdAddress& TestGiver::address() {
const block::StdAddress& TestGiver::address() noexcept {
static block::StdAddress res =
block::StdAddress::parse("kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny").move_as_ok();
return res;
}
vm::CellHash TestGiver::get_init_code_hash() {
vm::CellHash TestGiver::get_init_code_hash() noexcept {
return vm::CellHash::from_slice(td::base64_decode("wDkZp0yR4xo+9+BnuAPfGVjBzK6FPzqdv2DwRq3z3KE=").move_as_ok());
}
td::Ref<vm::Cell> TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,
const block::StdAddress& dest_address) {
const block::StdAddress& dest_address) noexcept {
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb;

View file

@ -23,9 +23,9 @@ namespace tonlib {
class TestGiver {
public:
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static const block::StdAddress& address();
static vm::CellHash get_init_code_hash();
static const block::StdAddress& address() noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,
const block::StdAddress& dest_address);
const block::StdAddress& dest_address) noexcept;
};
} // namespace tonlib

View file

@ -24,13 +24,13 @@
#include "td/utils/base64.h"
namespace tonlib {
td::Ref<vm::Cell> TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) {
td::Ref<vm::Cell> TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept {
auto code = get_init_code();
auto data = get_init_data(public_key);
return GenericAccount::get_init_state(std::move(code), std::move(data));
}
td::Ref<vm::Cell> TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) {
td::Ref<vm::Cell> TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept {
std::string seq_no(4, 0);
auto signature =
private_key.sign(vm::CellBuilder().store_bytes(seq_no).finalize()->get_hash().as_slice()).move_as_ok();
@ -39,7 +39,7 @@ td::Ref<vm::Cell> TestWallet::get_init_message(const td::Ed25519::PrivateKey& pr
td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address) {
const block::StdAddress& dest_address) noexcept {
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb;
@ -48,18 +48,22 @@ td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey&
.append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok())
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
td::int32 send_mode = 3;
if (gramms == -1) {
gramms = 0;
send_mode += 128;
}
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, message, 35 * 8).ensure();
auto message_inner = cb.finalize();
td::int8 send_mode = 3;
auto message_outer =
vm::CellBuilder().store_long(seqno, 32).store_long(send_mode, 8).store_ref(message_inner).finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Ref<vm::Cell> TestWallet::get_init_code() {
td::Ref<vm::Cell> TestWallet::get_init_code() noexcept {
static auto res = [] {
auto serialized_code = td::base64_decode(
"te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/"
@ -70,11 +74,11 @@ td::Ref<vm::Cell> TestWallet::get_init_code() {
return res;
}
vm::CellHash TestWallet::get_init_code_hash() {
vm::CellHash TestWallet::get_init_code_hash() noexcept {
return get_init_code()->get_hash();
}
td::Ref<vm::Cell> TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) {
td::Ref<vm::Cell> TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept {
return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize();
}
} // namespace tonlib

View file

@ -27,14 +27,14 @@ namespace tonlib {
class TestWallet {
public:
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key);
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key);
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key) noexcept;
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept;
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address);
const block::StdAddress& dest_address) noexcept;
static td::Ref<vm::Cell> get_init_code();
static vm::CellHash get_init_code_hash();
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key);
static td::Ref<vm::Cell> get_init_code() noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key) noexcept;
};
} // namespace tonlib

File diff suppressed because it is too large Load diff

View file

@ -47,13 +47,13 @@ class TonlibClient : public td::actor::Actor {
private:
enum class State { Uninited, Running, Closed } state_ = State::Uninited;
td::unique_ptr<TonlibCallback> callback_;
// Config
Config config_;
td::uint32 config_generation_{0};
std::string blockchain_name_;
bool ignore_cache_{false};
bool use_callbacks_for_network_{false};
td::actor::ActorId<ExtClientOutbound> ext_client_outbound_;
// KeyStorage
std::shared_ptr<KeyValue> kv_;
@ -62,6 +62,7 @@ class TonlibClient : public td::actor::Actor {
// network
td::actor::ActorOwn<ton::adnl::AdnlExtClient> raw_client_;
td::actor::ActorId<ExtClientOutbound> ext_client_outbound_;
td::actor::ActorOwn<LastBlock> raw_last_block_;
ExtClient client_;
@ -158,6 +159,7 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(const tonlib_api::exportKey& request,
td::Promise<object_ptr<tonlib_api::exportedKey>>&& promise);
td::Status do_request(const tonlib_api::deleteKey& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::deleteAllKeys& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::importKey& request, td::Promise<object_ptr<tonlib_api::key>>&& promise);
td::Status do_request(const tonlib_api::exportPemKey& request,

157
tonlib/tonlib/TonlibError.h Normal file
View file

@ -0,0 +1,157 @@
/*
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 "td/utils/Status.h"
#include "common/errorcode.h"
// NEED_MNEMONIC_PASSWORD
// KEY_UNKNOWN
// KEY_DECRYPT
// INVALID_MNEMONIC
// INVALID_BAG_OF_CELLS
// INVALID_PUBLIC_KEY
// INVALID_ACCOUNT_ADDRESS
// INVALID_CONFIG
// INVALID_PEM_KEY
// MESSAGE_TOO_LONG
// EMPTY_FIELD
// INVALID_FIELD
// DANGEROUS_TRANSACTION
// ACCOUNT_NOT_INITED
// ACCOUNT_TYPE_UNKNOWN
// ACCOUNT_TYPE_UNEXPECTED
// VALIDATE_ACCOUNT_STATE
// VALIDATE_TRANSACTION
// VALIDATE_ZERO_STATE
// VALIDATE_BLOCK_PROOF
// NO_LITE_SERVERS
// LITE_SERVER_NETWORK
// CANCELLED
// NOT_ENOUGH_FUNDS
// LITE_SERVER
// INTERNAL
namespace tonlib {
struct TonlibError {
static td::Status NeedMnemonicPassword() {
return td::Status::Error(400, "NEED_MNEMONIC_PASSWORD");
}
static td::Status InvalidMnemonic() {
return td::Status::Error(400, "INVALID_MNEMONIC: Invalid mnemonic words or password (invalid checksum)");
}
static td::Status InvalidBagOfCells(td::Slice comment) {
return td::Status::Error(400, PSLICE() << "INVALID_BAG_OF_CELLS: " << comment);
}
static td::Status InvalidPublicKey() {
return td::Status::Error(400, "INVALID_PUBLIC_KEY");
}
static td::Status InvalidAccountAddress() {
return td::Status::Error(400, "INVALID_ACCOUNT_ADDRESS");
}
static td::Status InvalidConfig(td::Slice reason) {
return td::Status::Error(400, PSLICE() << "INVALID_CONFIG: " << reason);
}
static td::Status InvalidPemKey() {
return td::Status::Error(400, "INVALID_PEM_KEY");
}
static td::Status MessageTooLong() {
return td::Status::Error(400, "MESSAGE_TOO_LONG");
}
static td::Status EmptyField(td::Slice field_name) {
return td::Status::Error(400, PSLICE() << "EMPTY_FIELD: Field " << field_name << " must not be emtpy");
}
static td::Status InvalidField(td::Slice field_name, td::Slice reason) {
return td::Status::Error(400, PSLICE() << "INVALID_FIELD: Field " << field_name << " has invalid value " << reason);
}
static td::Status DangerousTransaction(td::Slice reason) {
return td::Status::Error(400, PSLICE() << "DANGEROUS_TRANSACTION: " << reason);
}
static td::Status AccountNotInited() {
return td::Status::Error(400, "ACCOUNT_NOT_INITED");
}
static td::Status AccountTypeUnknown() {
return td::Status::Error(400, "ACCOUNT_TYPE_UNKNOWN");
}
static td::Status AccountTypeUnexpected(td::Slice expected) {
return td::Status::Error(400, PSLICE() << "ACCOUNT_TYPE_UNEXPECTED: not a " << expected);
}
static td::Status Internal() {
return td::Status::Error(500, "INTERNAL");
}
static td::Status Internal(td::Slice message) {
return td::Status::Error(500, PSLICE() << "INTERNAL: " << message);
}
static td::Status KeyUnknown() {
return td::Status::Error(500, "KEY_UNKNOWN");
}
static td::Status KeyDecrypt() {
return td::Status::Error(500, "KEY_DECRYPT");
}
static td::Status ValidateAccountState() {
return td::Status::Error(500, "VALIDATE_ACCOUNT_STATE");
}
static td::Status ValidateTransactions() {
return td::Status::Error(500, "VALIDATE_TRANSACTION");
}
static td::Status ValidateZeroState(td::Slice message) {
return td::Status::Error(500, PSLICE() << "VALIDATE_ZERO_STATE: " << message);
}
static td::Status ValidateBlockProof() {
return td::Status::Error(500, "VALIDATE_BLOCK_PROOF");
}
static td::Status NoLiteServers() {
return td::Status::Error(500, "NO_LITE_SERVERS");
}
static td::Status LiteServerNetwork() {
return td::Status::Error(500, "LITE_SERVER_NETWORK");
}
static td::Status Cancelled() {
return td::Status::Error(500, "CANCELLED");
}
static td::Status NotEnoughFunds() {
return td::Status::Error(500, "NOT_ENOUGH_FUNDS");
}
static td::Status LiteServer(td::int32 code, td::Slice message) {
auto f = [&](td::Slice code_description) { return LiteServer(code, code_description, message); };
switch (ton::ErrorCode(code)) {
case ton::ErrorCode::cancelled:
return f("CANCELLED");
case ton::ErrorCode::failure:
return f("FAILURE");
case ton::ErrorCode::error:
return f("ERROR");
case ton::ErrorCode::warning:
return f("WARNING");
case ton::ErrorCode::protoviolation:
return f("PROTOVIOLATION");
case ton::ErrorCode::timeout:
return f("TIMEOUT");
case ton::ErrorCode::notready:
return f("NOTREADY");
}
return f("UNKNOWN");
}
static td::Status LiteServer(td::int32 code, td::Slice code_description, td::Slice message) {
return td::Status::Error(500, PSLICE() << "LITE_SERVER_" << code_description << ": " << message);
}
};
} // namespace tonlib

View file

@ -27,13 +27,13 @@
#include <limits>
namespace tonlib {
td::Ref<vm::Cell> Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) {
td::Ref<vm::Cell> Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept {
auto code = get_init_code();
auto data = get_init_data(public_key);
return GenericAccount::get_init_state(std::move(code), std::move(data));
}
td::Ref<vm::Cell> Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) {
td::Ref<vm::Cell> Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept {
td::uint32 seqno = 0;
td::uint32 valid_until = std::numeric_limits<td::uint32>::max();
auto signature =
@ -45,7 +45,7 @@ td::Ref<vm::Cell> Wallet::get_init_message(const td::Ed25519::PrivateKey& privat
td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::uint32 valid_until, td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address) {
const block::StdAddress& dest_address) noexcept {
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb;
@ -54,11 +54,15 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
.append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok())
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
td::int32 send_mode = 3;
if (gramms == -1) {
gramms = 0;
send_mode += 128;
}
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, message, 35 * 8).ensure();
auto message_inner = cb.finalize();
td::int8 send_mode = 3;
auto message_outer = vm::CellBuilder()
.store_long(seqno, 32)
.store_long(valid_until, 32)
@ -70,7 +74,7 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Ref<vm::Cell> Wallet::get_init_code() {
td::Ref<vm::Cell> Wallet::get_init_code() noexcept {
static auto res = [] {
auto serialized_code = td::base64_decode(
"te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/"
@ -81,11 +85,11 @@ td::Ref<vm::Cell> Wallet::get_init_code() {
return res;
}
vm::CellHash Wallet::get_init_code_hash() {
vm::CellHash Wallet::get_init_code_hash() noexcept {
return get_init_code()->get_hash();
}
td::Ref<vm::Cell> Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) {
td::Ref<vm::Cell> Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept {
return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize();
}
} // namespace tonlib

View file

@ -27,14 +27,14 @@ namespace tonlib {
class Wallet {
public:
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key);
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key);
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key) noexcept;
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept;
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::uint32 valid_until, td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address);
const block::StdAddress& dest_address) noexcept;
static td::Ref<vm::Cell> get_init_code();
static vm::CellHash get_init_code_hash();
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key);
static td::Ref<vm::Cell> get_init_code() noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key) noexcept;
};
} // namespace tonlib

View file

@ -25,7 +25,6 @@
namespace tonlib {
td::Result<DecryptedKey> EncryptedKey::decrypt(td::Slice local_password, bool check_public_key) {
LOG(ERROR) << "decrypt";
if (secret.size() != 32) {
return td::Status::Error("Failed to decrypt key: invalid secret size");
}

View file

@ -192,6 +192,7 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out() << "keys - show all stored keys\n";
td::TerminalIO::out() << "unpackaddress <address> - validate and parse address\n";
td::TerminalIO::out() << "importkey - import key\n";
td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n";
td::TerminalIO::out() << "exportkey [<key_id>] - export key\n";
td::TerminalIO::out() << "setconfig <path> [<name>] [<use_callback>] [<force>] - set lite server config\n";
td::TerminalIO::out() << "getstate <key_id> - get state of simple wallet with requested key\n";
@ -213,6 +214,8 @@ class TonlibCli : public td::actor::Actor {
try_stop();
} else if (cmd == "keys") {
dump_keys();
} else if (cmd == "deletekeys") {
delete_all_keys();
} else if (cmd == "exportkey") {
export_key(parser.read_word());
} else if (cmd == "importkey") {
@ -415,6 +418,27 @@ class TonlibCli : public td::actor::Actor {
dump_key(i);
}
}
void delete_all_keys() {
static td::Slice password = td::Slice("I have written down mnemonic words");
td::TerminalIO::out() << "You are going to delete ALL PRIVATE KEYS. To confirm enter `" << password << "`\n";
cont_ = [this](td::Slice entered) {
if (password == entered) {
this->do_delete_all_keys();
} else {
td::TerminalIO::out() << "Your keys left intact\n";
}
};
}
void do_delete_all_keys() {
send_query(tonlib_api::make_object<tonlib_api::deleteAllKeys>(), [](auto r_res) {
if (r_res.is_error()) {
td::TerminalIO::out() << "Something went wrong: " << r_res.error() << "\n";
return;
}
td::TerminalIO::out() << "All your keys have been deleted\n";
});
}
std::string key_db_path() {
return options_.key_dir + TD_DIR_SLASH + "key_db";

View file

@ -20,6 +20,10 @@
#include "td/utils/misc.h"
#include "vm/cellslice.h"
namespace tonlib {
int VERBOSITY_NAME(tonlib_query) = VERBOSITY_NAME(INFO);
int VERBOSITY_NAME(last_block) = VERBOSITY_NAME(INFO);
int VERBOSITY_NAME(lite_server) = VERBOSITY_NAME(INFO);
td::Result<td::Ref<vm::CellSlice>> binary_bitstring_to_cellslice(td::Slice literal) {
unsigned char buff[128];
if (!begins_with(literal, "b{") || !ends_with(literal, "}")) {

View file

@ -21,6 +21,21 @@
#include "ton/ton-types.h"
#include "block/block.h"
#include "block/block-parse.h"
namespace tonlib {
template <class F>
auto try_f(F&& f) noexcept -> decltype(f()) {
try {
return f();
} catch (vm::VmError error) {
return td::Status::Error(PSLICE() << "Got a vm exception: " << error.get_msg());
}
}
#define TRY_VM(f) try_f([&] { return f; })
extern int VERBOSITY_NAME(tonlib_query);
extern int VERBOSITY_NAME(last_block);
extern int VERBOSITY_NAME(lite_server);
td::Result<td::Ref<vm::CellSlice>> binary_bitstring_to_cellslice(td::Slice literal);
} // namespace tonlib

View file

@ -473,6 +473,21 @@ struct BlockHandleImpl : public BlockHandleInterface {
}
}
void unsafe_clear_applied() override {
if (is_applied()) {
lock();
flags_ &= ~Flags::dbf_applied;
unlock();
}
}
void unsafe_clear_next() override {
if (inited_next_left() || inited_next_right()) {
lock();
flags_ &= ~(Flags::dbf_inited_next_left | Flags::dbf_inited_next_right);
unlock();
}
}
td::BufferSlice serialize() const override;
BlockHandleImpl(BlockIdExt id)
: id_(id), flags_(id_.is_masterchain() ? static_cast<td::uint32>(dbf_masterchain) : 0) {

View file

@ -229,6 +229,73 @@ tl_object_ptr<ton_api::db_blockdb_lru> BlockDb::DbEntry::release() {
return create_tl_object<ton_api::db_blockdb_lru>(create_tl_block_id(block_id), prev, next);
}
void BlockDb::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) {
std::map<ShardIdFull, BlockSeqno> max_seqno;
max_seqno.emplace(ShardIdFull{masterchainId}, state->get_seqno() + 1);
auto shards = state->get_shards();
auto it = KeyHash::zero();
kv_->begin_transaction().ensure();
while (true) {
auto R = get_block_lru(it);
R.ensure();
auto v = R.move_as_ok();
it = v.next;
R = get_block_lru(it);
R.ensure();
v = R.move_as_ok();
if (v.is_empty()) {
break;
}
auto s = v.block_id.shard_full();
if (!max_seqno.count(s)) {
bool found = false;
for (auto &shard : shards) {
if (shard_intersects(shard->shard(), s)) {
found = true;
max_seqno.emplace(s, shard->top_block_id().seqno() + 1);
break;
}
}
if (!found) {
max_seqno.emplace(s, 0);
}
}
bool to_delete = v.block_id.seqno() >= max_seqno[s];
if (to_delete) {
auto key_hash = get_block_value_key(v.block_id);
auto B = get_block_value(key_hash);
B.ensure();
auto handleR = create_block_handle(B.move_as_ok());
handleR.ensure();
auto handle = handleR.move_as_ok();
handle->unsafe_clear_applied();
handle->unsafe_clear_next();
if (handle->need_flush()) {
set_block_value(key_hash, handle->serialize());
}
} else if (v.block_id.seqno() + 1 == max_seqno[s]) {
auto key_hash = get_block_value_key(v.block_id);
auto B = get_block_value(key_hash);
B.ensure();
auto handleR = create_block_handle(B.move_as_ok());
handleR.ensure();
auto handle = handleR.move_as_ok();
handle->unsafe_clear_next();
if (handle->need_flush()) {
set_block_value(key_hash, handle->serialize());
}
}
}
kv_->commit_transaction().ensure();
}
} // namespace validator
} // namespace ton

View file

@ -41,6 +41,8 @@ class BlockDb : public td::actor::Actor {
void gc();
void skip_gc();
void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise);
BlockDb(td::actor::ActorId<RootDb> root_db, std::string db_path);
private:

View file

@ -138,7 +138,7 @@ void FileDb::load_file(RefId ref_id, td::Promise<td::BufferSlice> promise) {
}
});
td::actor::create_actor<db::ReadFile>("readfile", get_file_name(ref_id, false), 0, -1, std::move(P)).release();
td::actor::create_actor<db::ReadFile>("readfile", get_file_name(ref_id, false), 0, -1, 0, std::move(P)).release();
}
void FileDb::load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size, td::Promise<td::BufferSlice> promise) {
@ -159,7 +159,7 @@ void FileDb::load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size,
}
});
td::actor::create_actor<db::ReadFile>("readfile", get_file_name(ref_id, false), offset, max_size, std::move(P))
td::actor::create_actor<db::ReadFile>("readfile", get_file_name(ref_id, false), offset, max_size, 0, std::move(P))
.release();
}

View file

@ -81,25 +81,32 @@ class WriteFile : public td::actor::Actor {
class ReadFile : public td::actor::Actor {
public:
enum Flags : td::uint32 { f_disable_log = 1 };
void start_up() override {
auto S = td::read_file(file_name_, max_length_, offset_);
if (S.is_ok()) {
promise_.set_result(S.move_as_ok());
} else {
// TODO check error code
LOG(ERROR) << "missing file " << file_name_;
if (flags_ & Flags::f_disable_log) {
LOG(DEBUG) << "missing file " << file_name_;
} else {
LOG(ERROR) << "missing file " << file_name_;
}
promise_.set_error(td::Status::Error(ErrorCode::notready, "file does not exist"));
}
stop();
}
ReadFile(std::string file_name, td::int64 offset, td::int64 max_length, td::Promise<td::BufferSlice> promise)
: file_name_(file_name), offset_(offset), max_length_(max_length), promise_(std::move(promise)) {
ReadFile(std::string file_name, td::int64 offset, td::int64 max_length, td::uint32 flags,
td::Promise<td::BufferSlice> promise)
: file_name_(file_name), offset_(offset), max_length_(max_length), flags_(flags), promise_(std::move(promise)) {
}
private:
std::string file_name_;
td::int64 offset_;
td::int64 max_length_;
td::uint32 flags_;
td::Promise<td::BufferSlice> promise_;
};

View file

@ -216,6 +216,95 @@ void LtDb::start_up() {
kv_ = std::make_shared<td::RocksDb>(td::RocksDb::open(db_path_).move_as_ok());
}
void LtDb::truncate_workchain(ShardIdFull shard, td::Ref<MasterchainState> state) {
auto key = get_desc_key(shard);
std::string value;
auto R = kv_->get(key, value);
R.ensure();
CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok);
auto F = fetch_tl_object<ton_api::db_lt_desc_value>(td::BufferSlice{value}, true);
F.ensure();
auto f = F.move_as_ok();
auto shards = state->get_shards();
BlockSeqno seqno = 0;
if (shard.is_masterchain()) {
seqno = state->get_seqno();
} else {
for (auto s : shards) {
if (shard_intersects(s->shard(), shard)) {
seqno = s->top_block_id().seqno();
break;
}
}
}
while (f->last_idx_ > f->first_idx_) {
auto db_key = get_el_key(shard, f->last_idx_ - 1);
R = kv_->get(db_key, value);
R.ensure();
CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok);
auto E = fetch_tl_object<ton_api::db_lt_el_value>(td::BufferSlice{value}, true);
E.ensure();
auto e = E.move_as_ok();
bool to_delete = static_cast<td::uint32>(e->id_->seqno_) > seqno;
if (!to_delete) {
break;
} else {
f->last_idx_--;
kv_->erase(db_key).ensure();
}
}
if (f->first_idx_ == f->last_idx_) {
f->last_ts_ = 0;
f->last_lt_ = 0;
f->last_seqno_ = 0;
}
kv_->set(key, serialize_tl_object(f, true)).ensure();
}
void LtDb::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) {
auto status_key = create_serialize_tl_object<ton_api::db_lt_status_key>();
td::Result<td::KeyValue::GetStatus> R;
td::uint32 total_shards = 0;
{
std::string value;
R = kv_->get(status_key.as_slice(), value);
R.ensure();
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
promise.set_value(td::Unit());
return;
}
auto F = fetch_tl_object<ton_api::db_lt_status_value>(value, true);
F.ensure();
auto f = F.move_as_ok();
total_shards = f->total_shards_;
if (total_shards == 0) {
promise.set_value(td::Unit());
return;
}
}
kv_->begin_transaction().ensure();
for (td::uint32 idx = 0; idx < total_shards; idx++) {
auto shard_key = create_serialize_tl_object<ton_api::db_lt_shard_key>(idx);
std::string value;
R = kv_->get(shard_key.as_slice(), value);
R.ensure();
CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok);
auto F = fetch_tl_object<ton_api::db_lt_shard_value>(value, true);
F.ensure();
auto f = F.move_as_ok();
truncate_workchain(ShardIdFull{f->workchain_, static_cast<td::uint64>(f->shard_)}, state);
}
kv_->commit_transaction().ensure();
promise.set_value(td::Unit());
}
} // namespace validator
} // namespace ton

View file

@ -20,6 +20,7 @@
#include "td/actor/actor.h"
#include "td/db/KeyValueAsync.h"
#include "validator/interfaces/db.h"
#include "ton/ton-types.h"
@ -42,6 +43,9 @@ class LtDb : public td::actor::Actor {
void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<BlockIdExt> promise);
void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise<BlockIdExt> promise);
void truncate_workchain(ShardIdFull shard, td::Ref<MasterchainState> state);
void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise);
void start_up() override;
LtDb(td::actor::ActorId<RootDb> root_db, std::string db_path) : root_db_(root_db), db_path_(std::move(db_path)) {

View file

@ -25,6 +25,7 @@
#include "td/utils/overloaded.h"
#include "common/checksum.h"
#include "validator/stats-merger.h"
#include "td/actor/MultiPromise.h"
namespace ton {
@ -413,6 +414,14 @@ void RootDb::get_async_serializer_state(td::Promise<AsyncSerializerState> promis
td::actor::send_closure(state_db_, &StateDb::get_async_serializer_state, std::move(promise));
}
void RootDb::update_hardforks(std::vector<BlockIdExt> blocks, td::Promise<td::Unit> promise) {
td::actor::send_closure(state_db_, &StateDb::update_hardforks, std::move(blocks), std::move(promise));
}
void RootDb::get_hardforks(td::Promise<std::vector<BlockIdExt>> promise) {
td::actor::send_closure(state_db_, &StateDb::get_hardforks, std::move(promise));
}
void RootDb::start_up() {
cell_db_ = td::actor::create_actor<CellDb>("celldb", actor_id(this), root_path_ + "/celldb/");
block_db_ = td::actor::create_actor<BlockDb>("blockdb", actor_id(this), root_path_ + "/blockdb/");
@ -481,6 +490,15 @@ void RootDb::prepare_stats(td::Promise<std::vector<std::pair<std::string, std::s
td::actor::send_closure(archive_db_, &FileDb::prepare_stats, merger.make_promise("archivedb."));
}
void RootDb::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) {
td::MultiPromise mp;
auto ig = mp.init_guard();
ig.add_promise(std::move(promise));
td::actor::send_closure(lt_db_, &LtDb::truncate, state, ig.get_promise());
td::actor::send_closure(block_db_, &BlockDb::truncate, state, ig.get_promise());
}
} // namespace validator
} // namespace ton

View file

@ -104,6 +104,9 @@ class RootDb : public Db {
void update_async_serializer_state(AsyncSerializerState state, td::Promise<td::Unit> promise) override;
void get_async_serializer_state(td::Promise<AsyncSerializerState> promise) override;
void update_hardforks(std::vector<BlockIdExt> blocks, td::Promise<td::Unit> promise) override;
void get_hardforks(td::Promise<std::vector<BlockIdExt>> promise) override;
void archive(BlockIdExt block_id, td::Promise<td::Unit> promise) override;
void allow_state_gc(BlockIdExt block_id, td::Promise<bool> promise);
@ -112,6 +115,8 @@ class RootDb : public Db {
void prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise) override;
void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) override;
private:
td::actor::ActorId<ValidatorManager> validator_manager_;

View file

@ -179,6 +179,44 @@ void StateDb::get_async_serializer_state(td::Promise<AsyncSerializerState> promi
static_cast<UnixTime>(obj->last_ts_)});
}
void StateDb::update_hardforks(std::vector<BlockIdExt> blocks, td::Promise<td::Unit> promise) {
auto key = create_hash_tl_object<ton_api::db_state_key_hardforks>();
std::vector<tl_object_ptr<ton_api::tonNode_blockIdExt>> vec;
for (auto &e : blocks) {
vec.push_back(create_tl_block_id(e));
}
kv_->begin_transaction().ensure();
kv_->set(key.as_slice(), create_serialize_tl_object<ton_api::db_state_hardforks>(std::move(vec))).ensure();
kv_->commit_transaction();
promise.set_value(td::Unit());
}
void StateDb::get_hardforks(td::Promise<std::vector<BlockIdExt>> promise) {
auto key = create_hash_tl_object<ton_api::db_state_key_hardforks>();
std::string value;
auto R = kv_->get(key.as_slice(), value);
R.ensure();
if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
promise.set_value(std::vector<BlockIdExt>{});
return;
}
auto F = fetch_tl_object<ton_api::db_state_hardforks>(value, true);
F.ensure();
auto f = F.move_as_ok();
std::vector<BlockIdExt> vec;
for (auto &e : f->blocks_) {
vec.push_back(create_block_id(e));
}
promise.set_value(std::move(vec));
}
StateDb::StateDb(td::actor::ActorId<RootDb> root_db, std::string db_path) : root_db_(root_db), db_path_(db_path) {
}

View file

@ -47,6 +47,9 @@ class StateDb : public td::actor::Actor {
void update_async_serializer_state(AsyncSerializerState state, td::Promise<td::Unit> promise);
void get_async_serializer_state(td::Promise<AsyncSerializerState> promise);
void update_hardforks(std::vector<BlockIdExt> blocks, td::Promise<td::Unit> promise);
void get_hardforks(td::Promise<std::vector<BlockIdExt>> promise);
StateDb(td::actor::ActorId<RootDb> root_db, std::string path);
void start_up() override;

View file

@ -25,7 +25,9 @@ namespace validator {
void StaticFilesDb::load_file(FileHash file_hash, td::Promise<td::BufferSlice> promise) {
auto path = path_ + "/" + file_hash.to_hex();
td::actor::create_actor<db::ReadFile>("read file", path, 0, -1, std::move(promise)).release();
td::actor::create_actor<db::ReadFile>("read file", path, 0, -1, db::ReadFile::Flags::f_disable_log,
std::move(promise))
.release();
}
} // namespace validator

View file

@ -56,6 +56,20 @@ void WaitBlockData::start_up() {
alarm_timestamp() = timeout_;
CHECK(handle_);
if (!handle_->id().is_masterchain()) {
start();
} else {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<bool> R) {
R.ensure();
auto value = R.move_as_ok();
td::actor::send_closure(SelfId, &WaitBlockData::set_is_hardfork, value);
});
td::actor::send_closure(manager_, &ValidatorManager::check_is_hardfork, handle_->id(), std::move(P));
}
}
void WaitBlockData::set_is_hardfork(bool value) {
is_hardfork_ = value;
start();
}
@ -76,6 +90,18 @@ void WaitBlockData::start() {
});
td::actor::send_closure(manager_, &ValidatorManager::get_block_data_from_db, handle_, std::move(P));
} else if (try_read_static_file_.is_in_past() && (is_hardfork_ || !handle_->id().is_masterchain())) {
try_read_static_file_ = td::Timestamp::in(30.0);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
if (R.is_error()) {
td::actor::send_closure(SelfId, &WaitBlockData::start);
} else {
td::actor::send_closure(SelfId, &WaitBlockData::got_static_file, R.move_as_ok());
}
});
td::actor::send_closure(manager_, &ValidatorManager::try_get_static_file, handle_->id().file_hash, std::move(P));
} else {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<ReceivedBlock> R) {
if (R.is_error()) {
@ -149,6 +175,29 @@ void WaitBlockData::force_read_from_db() {
td::actor::send_closure(manager_, &ValidatorManager::get_block_data_from_db, handle_, std::move(P));
}
void WaitBlockData::got_static_file(td::BufferSlice data) {
CHECK(td::sha256_bits256(data.as_slice()) == handle_->id().file_hash);
auto R = create_block(handle_->id(), std::move(data));
if (R.is_error()) {
LOG(ERROR) << "bad static file block: " << R.move_as_error();
start();
return;
}
data_ = R.move_as_ok();
CHECK(is_hardfork_ || !handle_->id().is_masterchain());
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
if (R.is_error()) {
td::actor::send_closure(SelfId, &WaitBlockData::abort_query, R.move_as_error_prefix("bad static file block: "));
} else {
td::actor::send_closure(SelfId, &WaitBlockData::finish_query);
}
});
run_hardfork_accept_block_query(handle_->id(), data_, manager_, std::move(P));
}
} // namespace validator
} // namespace ton

View file

@ -51,12 +51,14 @@ class WaitBlockData : public td::actor::Actor {
void force_read_from_db();
void start_up() override;
void got_block_handle(BlockHandle handle);
void set_is_hardfork(bool value);
void start();
void got_block_data_from_db(td::Ref<BlockData> data);
void got_block_data_from_net(ReceivedBlock data);
void failed_to_get_block_data_from_net(td::Status reason);
void got_static_file(td::BufferSlice data);
private:
BlockHandle handle_;
@ -69,6 +71,8 @@ class WaitBlockData : public td::actor::Actor {
td::Ref<BlockData> data_;
bool reading_from_db_ = false;
bool is_hardfork_ = false;
td::Timestamp try_read_static_file_ = td::Timestamp::now();
//td::PerfWarningTimer perf_timer_{"waitdata", 1.0};
};

View file

@ -52,6 +52,8 @@ void run_accept_block_query(BlockIdExt id, td::Ref<BlockData> data, std::vector<
void run_fake_accept_block_query(BlockIdExt id, td::Ref<BlockData> data, std::vector<BlockIdExt> prev,
td::Ref<ValidatorSet> validator_set, td::actor::ActorId<ValidatorManager> manager,
td::Promise<td::Unit> promise);
void run_hardfork_accept_block_query(BlockIdExt id, td::Ref<BlockData> data,
td::actor::ActorId<ValidatorManager> manager, td::Promise<td::Unit> promise);
void run_apply_block_query(BlockIdExt id, td::Ref<BlockData> block, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, td::Promise<td::Unit> promise);
void run_check_proof_query(BlockIdExt id, td::Ref<Proof> proof, td::actor::ActorId<ValidatorManager> manager,

View file

@ -1466,26 +1466,10 @@ bool Collator::fetch_config_params() {
if (cell.is_null()) {
return fatal_error("cannot fetch current gas prices and limits from masterchain configuration");
}
auto f = [self = this](const auto& r, td::uint64 spec_limit) {
self->compute_phase_cfg_.gas_limit = r.gas_limit;
self->compute_phase_cfg_.special_gas_limit = spec_limit;
self->compute_phase_cfg_.gas_credit = r.gas_credit;
self->compute_phase_cfg_.gas_price = r.gas_price;
self->storage_phase_cfg_.freeze_due_limit = td::RefInt256{true, r.freeze_due_limit};
self->storage_phase_cfg_.delete_due_limit = td::RefInt256{true, r.delete_due_limit};
};
block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
if (tlb::unpack_cell(cell, rec)) {
f(rec, rec.special_gas_limit);
} else {
block::gen::GasLimitsPrices::Record_gas_prices rec0;
if (tlb::unpack_cell(std::move(cell), rec0)) {
f(rec0, rec0.gas_limit);
} else {
return fatal_error("cannot unpack current gas prices and limits from masterchain configuration");
}
if (!compute_phase_cfg_.parse_GasLimitsPrices(std::move(cell), storage_phase_cfg_.freeze_due_limit,
storage_phase_cfg_.delete_due_limit)) {
return fatal_error("cannot unpack current gas prices and limits from masterchain configuration");
}
compute_phase_cfg_.compute_threshold();
compute_phase_cfg_.block_rand_seed = rand_seed_;
compute_phase_cfg_.libraries = std::make_unique<vm::Dictionary>(config_->get_libraries_root(), 256);
compute_phase_cfg_.global_config = config_->get_root_cell();

View file

@ -33,6 +33,7 @@
#include "top-shard-descr.hpp"
#include "ton/ton-io.hpp"
#include "liteserver.hpp"
#include "validator/fabric.h"
namespace ton {
@ -131,6 +132,11 @@ void run_fake_accept_block_query(BlockIdExt id, td::Ref<BlockData> data, std::ve
.release();
}
void run_hardfork_accept_block_query(BlockIdExt id, td::Ref<BlockData> data,
td::actor::ActorId<ValidatorManager> manager, td::Promise<td::Unit> promise) {
promise.set_error(td::Status::Error(ErrorCode::error, "not implemented"));
}
void run_apply_block_query(BlockIdExt id, td::Ref<BlockData> block, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, td::Promise<td::Unit> promise) {
td::actor::create_actor<ApplyBlock>(PSTRING() << "apply " << id, id, std::move(block), manager, timeout,

View file

@ -192,7 +192,7 @@ void LiteQuery::perform_getMasterchainInfo(int mode) {
}
td::actor::send_closure_later(
manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block,
[ Self = actor_id(this), mode ](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res) {
[Self = actor_id(this), mode](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
@ -230,7 +230,7 @@ void LiteQuery::perform_getBlock(BlockIdExt blkid) {
return;
}
td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
[ Self = actor_id(this), blkid ](td::Result<Ref<ton::validator::BlockData>> res) {
[Self = actor_id(this), blkid](td::Result<Ref<ton::validator::BlockData>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
@ -256,7 +256,7 @@ void LiteQuery::perform_getBlockHeader(BlockIdExt blkid, int mode) {
return;
}
td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
[ Self = actor_id(this), blkid, mode ](td::Result<Ref<ton::validator::BlockData>> res) {
[Self = actor_id(this), blkid, mode](td::Result<Ref<ton::validator::BlockData>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
@ -371,7 +371,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) {
}
if (blkid.id.seqno) {
td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
[ Self = actor_id(this), blkid ](td::Result<Ref<ton::validator::ShardState>> res) {
[Self = actor_id(this), blkid](td::Result<Ref<ton::validator::ShardState>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
@ -381,7 +381,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) {
});
} else {
td::actor::send_closure_later(manager_, &ValidatorManager::get_zero_state, blkid,
[ Self = actor_id(this), blkid ](td::Result<td::BufferSlice> res) {
[Self = actor_id(this), blkid](td::Result<td::BufferSlice> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
@ -440,7 +440,7 @@ bool LiteQuery::request_mc_block_data(BlockIdExt blkid) {
++pending_;
td::actor::send_closure_later(
manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
[ Self = actor_id(this), blkid ](td::Result<Ref<BlockData>> res) {
[Self = actor_id(this), blkid](td::Result<Ref<BlockData>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query,
res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : "));
@ -466,7 +466,7 @@ bool LiteQuery::request_mc_proof(BlockIdExt blkid, int mode) {
++pending_;
td::actor::send_closure_later(
manager_, &ValidatorManager::get_block_proof_from_db_short, blkid,
[ Self = actor_id(this), blkid, mode ](td::Result<Ref<Proof>> res) {
[Self = actor_id(this), blkid, mode](td::Result<Ref<Proof>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query,
res.move_as_error_prefix("cannot load proof for "s + blkid.to_str() + " : "));
@ -488,7 +488,7 @@ bool LiteQuery::request_mc_block_state(BlockIdExt blkid) {
++pending_;
td::actor::send_closure_later(
manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
[ Self = actor_id(this), blkid ](td::Result<Ref<ShardState>> res) {
[Self = actor_id(this), blkid](td::Result<Ref<ShardState>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query,
res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : "));
@ -519,7 +519,7 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) {
++pending_;
td::actor::send_closure_later(
manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
[ Self = actor_id(this), blkid ](td::Result<Ref<ShardState>> res) {
[Self = actor_id(this), blkid](td::Result<Ref<ShardState>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query,
res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : "));
@ -541,7 +541,7 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) {
++pending_;
td::actor::send_closure_later(
manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
[ Self = actor_id(this), blkid ](td::Result<Ref<BlockData>> res) {
[Self = actor_id(this), blkid](td::Result<Ref<BlockData>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query,
res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : "));
@ -563,7 +563,7 @@ bool LiteQuery::request_proof_link(BlockIdExt blkid) {
++pending_;
td::actor::send_closure_later(
manager_, &ValidatorManager::get_block_proof_link_from_db_short, blkid,
[ Self = actor_id(this), blkid ](td::Result<Ref<ProofLink>> res) {
[Self = actor_id(this), blkid](td::Result<Ref<ProofLink>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query,
res.move_as_error_prefix("cannot load proof link for "s + blkid.to_str() + " : "));
@ -588,7 +588,7 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) {
++pending_;
td::actor::send_closure_later(
manager_, &ValidatorManager::get_zero_state, blkid,
[ Self = actor_id(this), blkid ](td::Result<td::BufferSlice> res) {
[Self = actor_id(this), blkid](td::Result<td::BufferSlice> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query,
res.move_as_error_prefix("cannot load zerostate of "s + blkid.to_str() + " : "));
@ -632,7 +632,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain,
LOG(INFO) << "sending a get_top_masterchain_state_block query to manager";
td::actor::send_closure_later(
manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block,
[Self = actor_id(this)](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res)->void {
[Self = actor_id(this)](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res) -> void {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
@ -1067,7 +1067,7 @@ void LiteQuery::continue_getTransactions(unsigned remaining, bool exact) {
<< " " << trans_lt_;
td::actor::send_closure_later(
manager_, &ValidatorManager::get_block_by_lt_from_db, ton::extract_addr_prefix(acc_workchain_, acc_addr_),
trans_lt_, [ Self = actor_id(this), remaining, manager = manager_ ](td::Result<BlockIdExt> res) {
trans_lt_, [Self = actor_id(this), remaining, manager = manager_](td::Result<BlockIdExt> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), ton::BlockIdExt{});
} else {
@ -1141,7 +1141,7 @@ void LiteQuery::perform_getShardInfo(BlockIdExt blkid, ShardIdFull shard, bool e
void LiteQuery::perform_getConfigParams(BlockIdExt blkid, int mode, std::vector<int> param_list) {
LOG(INFO) << "started a getConfigParams(" << blkid.to_str() << ", " << mode << ", <list of " << param_list.size()
<< " parameters>) liteserver query";
set_continuation([ this, mode, param_list = std::move(param_list) ]() mutable {
set_continuation([this, mode, param_list = std::move(param_list)]() mutable {
continue_getConfigParams(mode, std::move(param_list));
});
request_mc_block_data_state(blkid);
@ -1294,7 +1294,7 @@ void LiteQuery::perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, Uni
LOG(INFO) << "performing a lookupBlock(" << blkid.to_str() << ", " << mode << ", " << lt << ", " << utime
<< ") query";
auto P = td::PromiseCreator::lambda(
[ Self = actor_id(this), manager = manager_, mode = (mode >> 4) ](td::Result<BlockIdExt> res) {
[Self = actor_id(this), manager = manager_, mode = (mode >> 4)](td::Result<BlockIdExt> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
@ -1445,10 +1445,21 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to,
fatal_error("destination block "s + to.to_str() + " is not a valid masterchain block id");
return;
}
if (!(mode & 1)) {
if (mode & 1) {
base_blk_id_ = (from.seqno() > to.seqno()) ? from : to;
td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, base_blk_id_,
[Self = actor_id(this), from, to, mode](td::Result<Ref<ShardState>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
td::actor::send_closure_later(Self, &LiteQuery::continue_getBlockProof, from, to,
mode, Ref<MasterchainStateQ>(res.move_as_ok()));
}
});
} else if (mode & 2) {
td::actor::send_closure_later(
manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block,
[ Self = actor_id(this), from, mode ](td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res)->void {
[Self = actor_id(this), from, mode](td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
@ -1458,14 +1469,13 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to,
}
});
} else {
base_blk_id_ = (from.seqno() > to.seqno()) ? from : to;
td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, base_blk_id_,
[ Self = actor_id(this), from, to, mode ](td::Result<Ref<ShardState>> res) {
td::actor::send_closure_later(manager_, &ton::validator::ValidatorManager::get_shard_client_state, false,
[Self = actor_id(this), from, mode](td::Result<BlockIdExt> res) {
if (res.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
} else {
td::actor::send_closure_later(Self, &LiteQuery::continue_getBlockProof, from, to,
mode, Ref<MasterchainStateQ>(res.move_as_ok()));
td::actor::send_closure_later(Self, &LiteQuery::perform_getBlockProof, from,
res.move_as_ok(), mode | 1);
}
});
}

View file

@ -734,26 +734,10 @@ bool ValidateQuery::fetch_config_params() {
if (cell.is_null()) {
return fatal_error("cannot fetch current gas prices and limits from masterchain configuration");
}
auto f = [self = this](const auto& r, td::uint64 spec_limit) {
self->compute_phase_cfg_.gas_limit = r.gas_limit;
self->compute_phase_cfg_.special_gas_limit = spec_limit;
self->compute_phase_cfg_.gas_credit = r.gas_credit;
self->compute_phase_cfg_.gas_price = r.gas_price;
self->storage_phase_cfg_.freeze_due_limit = td::RefInt256{true, r.freeze_due_limit};
self->storage_phase_cfg_.delete_due_limit = td::RefInt256{true, r.delete_due_limit};
};
block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
if (tlb::unpack_cell(cell, rec)) {
f(rec, rec.special_gas_limit);
} else {
block::gen::GasLimitsPrices::Record_gas_prices rec0;
if (tlb::unpack_cell(std::move(cell), rec0)) {
f(rec0, rec0.gas_limit);
} else {
return fatal_error("cannot unpack current gas prices and limits from masterchain configuration");
}
if (!compute_phase_cfg_.parse_GasLimitsPrices(std::move(cell), storage_phase_cfg_.freeze_due_limit,
storage_phase_cfg_.delete_due_limit)) {
return fatal_error("cannot unpack current gas prices and limits from masterchain configuration");
}
compute_phase_cfg_.compute_threshold();
compute_phase_cfg_.block_rand_seed = rand_seed_;
compute_phase_cfg_.libraries = std::make_unique<vm::Dictionary>(config_->get_libraries_root(), 256);
compute_phase_cfg_.global_config = config_->get_root_cell();
@ -5227,7 +5211,7 @@ bool ValidateQuery::check_block_create_stats() {
auto key = td::Bits256::zero();
auto old_val = ps_.block_create_stats_->lookup(key);
auto new_val = ns_.block_create_stats_->lookup(key);
if (new_val.is_null()) {
if (new_val.is_null() && (!created_by_.is_zero() || block_create_total_)) {
return reject_query(
"new masterchain state does not contain a BlockCreator entry with zero key with total statistics");
}

View file

@ -93,6 +93,9 @@ struct BlockHandleInterface {
virtual void set_archived() = 0;
virtual void set_applied() = 0;
virtual void unsafe_clear_applied() = 0;
virtual void unsafe_clear_next() = 0;
virtual td::BufferSlice serialize() const = 0;
virtual ~BlockHandleInterface() = default;

View file

@ -92,9 +92,14 @@ class Db : public td::actor::Actor {
virtual void update_async_serializer_state(AsyncSerializerState state, td::Promise<td::Unit> promise) = 0;
virtual void get_async_serializer_state(td::Promise<AsyncSerializerState> promise) = 0;
virtual void update_hardforks(std::vector<BlockIdExt> blocks, td::Promise<td::Unit> promise) = 0;
virtual void get_hardforks(td::Promise<std::vector<BlockIdExt>> promise) = 0;
virtual void archive(BlockIdExt block_id, td::Promise<td::Unit> promise) = 0;
virtual void prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise) = 0;
virtual void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) = 0;
};
} // namespace validator

View file

@ -128,7 +128,7 @@ class ValidatorManager : public ValidatorManagerInterface {
virtual void send_block_broadcast(BlockBroadcast broadcast) = 0;
virtual void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise<td::Unit> promise) = 0;
virtual void get_shard_client_state(td::Promise<BlockIdExt> promise) = 0;
virtual void get_shard_client_state(bool from_db, td::Promise<BlockIdExt> promise) = 0;
virtual void subscribe_to_shard(ShardIdFull shard) = 0;
virtual void update_async_serializer_state(AsyncSerializerState state, td::Promise<td::Unit> promise) = 0;
@ -153,6 +153,12 @@ class ValidatorManager : public ValidatorManagerInterface {
virtual void update_last_known_key_block(BlockHandle handle, bool send_request) = 0;
virtual void update_gc_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) = 0;
virtual void update_shard_client_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) = 0;
virtual void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) = 0;
virtual void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise<td::Unit> promise) = 0;
static bool is_persistent_state(UnixTime ts, UnixTime prev_ts) {
return ts / 1024 != prev_ts / 1024;
}

View file

@ -897,7 +897,7 @@ void ValidatorManagerImpl::update_shard_client_state(BlockIdExt masterchain_bloc
td::actor::send_closure(db_, &Db::update_shard_client_state, masterchain_block_id, std::move(promise));
}
void ValidatorManagerImpl::get_shard_client_state(td::Promise<BlockIdExt> promise) {
void ValidatorManagerImpl::get_shard_client_state(bool from_db, td::Promise<BlockIdExt> promise) {
td::actor::send_closure(db_, &Db::get_shard_client_state, std::move(promise));
}

View file

@ -241,7 +241,7 @@ class ValidatorManagerImpl : public ValidatorManager {
}
void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise<td::Unit> promise) override;
void get_shard_client_state(td::Promise<BlockIdExt> promise) override;
void get_shard_client_state(bool from_db, td::Promise<BlockIdExt> promise) override;
void subscribe_to_shard(ShardIdFull shard) override {
}
@ -329,11 +329,20 @@ class ValidatorManagerImpl : public ValidatorManager {
}
void update_last_known_key_block(BlockHandle handle, bool send_request) override {
}
void update_shard_client_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) override {
}
void prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise) override {
UNREACHABLE();
}
void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) override {
UNREACHABLE();
}
void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise<td::Unit> promise) override {
UNREACHABLE();
}
private:
PublicKeyHash local_id_;

View file

@ -25,6 +25,7 @@
#include "adnl/utils.hpp"
#include "validator/downloaders/download-state.hpp"
#include "common/delay.h"
#include "td/actor/MultiPromise.h"
namespace ton {
@ -33,7 +34,16 @@ namespace validator {
void ValidatorManagerMasterchainReiniter::start_up() {
CHECK(block_id_.is_masterchain());
CHECK(block_id_.id.shard == shardIdAll);
CHECK(block_id_.seqno() >= opts_->get_last_fork_masterchain_seqno());
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::written_hardforks);
});
td::actor::send_closure(db_, &Db::update_hardforks, opts_->get_hardforks(), std::move(P));
}
void ValidatorManagerMasterchainReiniter::written_hardforks() {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::got_masterchain_handle, R.move_as_ok());
@ -268,7 +278,7 @@ void ValidatorManagerMasterchainStarter::start_up() {
}
void ValidatorManagerMasterchainStarter::failed_to_get_init_block_id() {
td::actor::create_actor<ValidatorManagerMasterchainReiniter>("reiniter", opts_, manager_, std::move(promise_))
td::actor::create_actor<ValidatorManagerMasterchainReiniter>("reiniter", opts_, manager_, db_, std::move(promise_))
.release();
stop();
}
@ -351,7 +361,7 @@ void ValidatorManagerMasterchainStarter::got_gc_block_state(td::Ref<MasterchainS
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_shard_block_id, R.move_as_ok());
});
td::actor::send_closure(manager_, &ValidatorManager::get_shard_client_state, std::move(P));
td::actor::send_closure(manager_, &ValidatorManager::get_shard_client_state, true, std::move(P));
return;
}
@ -375,15 +385,169 @@ void ValidatorManagerMasterchainStarter::got_key_block_handle(BlockHandle handle
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_shard_block_id, R.move_as_ok());
});
td::actor::send_closure(manager_, &ValidatorManager::get_shard_client_state, std::move(P));
td::actor::send_closure(manager_, &ValidatorManager::get_shard_client_state, true, std::move(P));
}
void ValidatorManagerMasterchainStarter::got_shard_block_id(BlockIdExt block_id) {
client_ = td::actor::create_actor<ShardClient>("shardclient", opts_, manager_);
client_block_id_ = block_id;
finish();
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<std::vector<BlockIdExt>> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_hardforks, R.move_as_ok());
});
td::actor::send_closure(db_, &Db::get_hardforks, std::move(P));
}
void ValidatorManagerMasterchainStarter::got_hardforks(std::vector<BlockIdExt> vec) {
auto h = opts_->get_hardforks();
if (h.size() < vec.size()) {
LOG(FATAL) << "cannot start: number of hardforks decreased";
return;
}
if (h.size() == vec.size()) {
if (h.size() > 0) {
if (*h.rbegin() != *vec.rbegin()) {
LOG(FATAL) << "cannot start: hardforks list changed";
return;
}
}
finish();
return;
}
if (h.size() > vec.size() + 1) {
LOG(FATAL) << "cannot start: number of hardforks increase is too big";
return;
}
auto b = *h.rbegin();
if (b.seqno() > handle_->id().seqno()) {
truncated();
return;
}
if (b.seqno() <= gc_handle_->id().seqno()) {
LOG(FATAL) << "cannot start: new hardfork is on too old block (already gc'd)";
return;
}
BlockIdExt id;
if (state_->get_old_mc_block_id(b.seqno() - 1, id)) {
got_truncate_block_id(id);
return;
}
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockIdExt> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_id, R.move_as_ok());
});
td::actor::send_closure(db_, &Db::get_block_by_seqno, AccountIdPrefixFull{masterchainId, 0}, b.seqno() - 1,
std::move(P));
}
void ValidatorManagerMasterchainStarter::got_truncate_block_id(BlockIdExt block_id) {
block_id_ = block_id;
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_handle, R.move_as_ok());
});
td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, block_id_, false, std::move(P));
}
void ValidatorManagerMasterchainStarter::got_truncate_block_handle(BlockHandle handle) {
handle_ = std::move(handle);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Ref<ShardState>> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_state,
td::Ref<MasterchainState>{R.move_as_ok()});
});
td::actor::send_closure(db_, &Db::get_block_state, handle_, std::move(P));
}
void ValidatorManagerMasterchainStarter::got_truncate_state(td::Ref<MasterchainState> state) {
state_ = std::move(state);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::truncated_db);
});
td::actor::send_closure(manager_, &ValidatorManager::truncate, state_, std::move(P));
}
void ValidatorManagerMasterchainStarter::truncated_db() {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::truncated);
});
auto key = state_->last_key_block_id();
td::MultiPromise mp;
auto ig = mp.init_guard();
ig.add_promise(std::move(P));
td::actor::send_closure(db_, &Db::update_init_masterchain_block, block_id_, ig.get_promise());
if (client_block_id_.seqno() > block_id_.seqno()) {
client_block_id_ = block_id_;
td::actor::send_closure(db_, &Db::update_shard_client_state, client_block_id_, ig.get_promise());
}
if (last_key_block_handle_->id().seqno() > key.seqno()) {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this),
promise = ig.get_promise()](td::Result<BlockHandle> R) mutable {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_prev_key_block_handle, R.move_as_ok());
promise.set_value(td::Unit());
});
td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, key, false, std::move(P));
}
{
auto P = td::PromiseCreator::lambda(
[b = block_id_, key, db = db_, promise = ig.get_promise()](td::Result<AsyncSerializerState> R) mutable {
if (R.is_error()) {
promise.set_value(td::Unit());
return;
}
auto s = R.move_as_ok();
if (s.last_block_id.seqno() <= b.seqno()) {
promise.set_value(td::Unit());
return;
}
s.last_block_id = b;
if (s.last_written_block_id.seqno() > b.seqno()) {
s.last_written_block_id = key;
s.last_written_block_ts = 0; // may lead to extra state snapshot on disk. Does not seem like a problem
}
td::actor::send_closure(db, &Db::update_async_serializer_state, s, std::move(promise));
});
td::actor::send_closure(db_, &Db::get_async_serializer_state, std::move(P));
}
}
void ValidatorManagerMasterchainStarter::got_prev_key_block_handle(BlockHandle handle) {
last_key_block_handle_ = std::move(handle);
}
void ValidatorManagerMasterchainStarter::truncated() {
handle_->set_next(*opts_->get_hardforks().rbegin());
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::written_next);
});
handle_->flush(manager_, handle_, std::move(P));
}
void ValidatorManagerMasterchainStarter::written_next() {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
R.ensure();
td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::finish);
});
td::actor::send_closure(db_, &Db::update_hardforks, opts_->get_hardforks(), std::move(P));
}
void ValidatorManagerMasterchainStarter::finish() {
client_ = td::actor::create_actor<ShardClient>("shardclient", opts_, manager_);
promise_.set_value(
ValidatorManagerInitResult{handle_, state_, std::move(client_), gc_handle_, gc_state_, last_key_block_handle_});
stop();

View file

@ -34,12 +34,13 @@ namespace validator {
class ValidatorManagerMasterchainReiniter : public td::actor::Actor {
public:
ValidatorManagerMasterchainReiniter(td::Ref<ValidatorManagerOptions> opts,
td::actor::ActorId<ValidatorManager> manager,
td::actor::ActorId<ValidatorManager> manager, td::actor::ActorId<Db> db,
td::Promise<ValidatorManagerInitResult> promise)
: opts_(std::move(opts)), manager_(manager), promise_(std::move(promise)) {
: opts_(std::move(opts)), manager_(manager), db_(db), promise_(std::move(promise)) {
block_id_ = opts_->init_block_id();
}
void start_up() override;
void written_hardforks();
void got_masterchain_handle(BlockHandle handle);
void download_proof_link();
void downloaded_proof_link(td::BufferSlice data);
@ -70,6 +71,7 @@ class ValidatorManagerMasterchainReiniter : public td::actor::Actor {
std::vector<ShardIdFull> shards_;
td::actor::ActorId<ValidatorManager> manager_;
td::actor::ActorId<Db> db_;
td::Promise<ValidatorManagerInitResult> promise_;
@ -95,6 +97,14 @@ class ValidatorManagerMasterchainStarter : public td::actor::Actor {
void got_gc_block_state(td::Ref<MasterchainState> state);
void got_key_block_handle(BlockHandle handle);
void got_shard_block_id(BlockIdExt block_id);
void got_hardforks(std::vector<BlockIdExt> hardforks);
void got_truncate_block_id(BlockIdExt block_id);
void got_truncate_block_handle(BlockHandle handle);
void got_truncate_state(td::Ref<MasterchainState> state);
void truncated_db();
void got_prev_key_block_handle(BlockHandle handle);
void truncated();
void written_next();
void finish();
private:
@ -112,6 +122,7 @@ class ValidatorManagerMasterchainStarter : public td::actor::Actor {
td::Promise<ValidatorManagerInitResult> promise_;
BlockIdExt client_block_id_;
td::actor::ActorOwn<ShardClient> client_;
};

View file

@ -52,29 +52,52 @@ void ValidatorManagerImpl::validate_block_is_next_proof(BlockIdExt prev_block_id
td::Status::Error(ErrorCode::protoviolation, "validate_block_is_next_proof() can only work for masterchain"));
return;
}
if (prev_block_id.seqno() + 1 != next_block_id.seqno()) {
VLOG(VALIDATOR_NOTICE) << "prev=" << prev_block_id << " next=" << next_block_id;
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "validate_block_is_next_proof(): bad seqno"));
return;
}
CHECK(last_masterchain_state_.not_null());
auto pp = create_proof(next_block_id, std::move(proof));
if (pp.is_error()) {
promise.set_error(pp.move_as_error_prefix("failed to create proof: "));
return;
}
auto P =
td::PromiseCreator::lambda([promise = std::move(promise), id = prev_block_id](td::Result<BlockHandle> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
return;
}
auto handle = R.move_as_ok();
CHECK(!handle->merge_before());
if (handle->one_prev(true) != id) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "prev block mismatch"));
return;
}
promise.set_value(td::Unit());
});
if (last_masterchain_seqno_ == prev_block_id.seqno()) {
CHECK(last_masterchain_block_id_ == prev_block_id);
run_check_proof_query(next_block_id, pp.move_as_ok(), actor_id(this), td::Timestamp::in(2.0), std::move(P),
opts_->is_hardfork(next_block_id));
auto P = td::PromiseCreator::lambda(
[promise = std::move(promise), id = prev_block_id](td::Result<BlockHandle> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
return;
}
auto handle = R.move_as_ok();
CHECK(!handle->merge_before());
if (handle->one_prev(true) != id) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "prev block mismatch"));
return;
}
promise.set_value(td::Unit());
});
run_check_proof_query(next_block_id, pp.move_as_ok(), actor_id(this), td::Timestamp::in(2.0), std::move(P),
last_masterchain_state_, opts_->is_hardfork(next_block_id));
} else {
auto P =
td::PromiseCreator::lambda([promise = std::move(promise), next_block_id](td::Result<BlockHandle> R) mutable {
R.ensure();
auto handle = R.move_as_ok();
CHECK(handle->inited_next_left());
if (handle->one_next(true) == next_block_id) {
promise.set_value(td::Unit());
} else {
promise.set_error(td::Status::Error("next block id mismatch"));
}
});
get_block_handle(prev_block_id, false, std::move(P));
}
}
void ValidatorManagerImpl::validate_block_proof(BlockIdExt block_id, td::BufferSlice proof,
@ -468,7 +491,27 @@ void ValidatorManagerImpl::run_ext_query(td::BufferSlice data, td::Promise<td::B
promise.set_value(std::move(data));
});
run_liteserver_query(std::move(data), actor_id(this), lite_server_cache_.get(), std::move(P));
auto E = fetch_tl_prefix<lite_api::liteServer_waitMasterchainSeqno>(data, true);
if (E.is_error()) {
run_liteserver_query(std::move(data), actor_id(this), lite_server_cache_.get(), std::move(P));
} else {
auto e = E.move_as_ok();
if (static_cast<BlockSeqno>(e->seqno_) <= min_confirmed_masterchain_seqno_) {
run_liteserver_query(std::move(data), actor_id(this), lite_server_cache_.get(), std::move(P));
} else {
auto t = e->timeout_ms_ < 10000 ? e->timeout_ms_ * 0.001 : 10.0;
auto Q =
td::PromiseCreator::lambda([data = std::move(data), SelfId = actor_id(this), cache = lite_server_cache_.get(),
promise = std::move(P)](td::Result<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
return;
}
run_liteserver_query(std::move(data), SelfId, cache, std::move(promise));
});
wait_shard_client_state(e->seqno_, td::Timestamp::in(t), std::move(Q));
}
}
}
void ValidatorManagerImpl::wait_block_state(BlockHandle handle, td::uint32 priority, td::Timestamp timeout,
@ -1387,7 +1430,8 @@ void ValidatorManagerImpl::new_masterchain_block() {
}
void ValidatorManagerImpl::update_shards() {
if (last_masterchain_state_->rotated_all_shards() || last_masterchain_seqno_ == 0) {
if ((last_masterchain_state_->rotated_all_shards() || last_masterchain_seqno_ == 0) &&
opts_->get_last_fork_masterchain_seqno() <= last_masterchain_seqno_) {
allow_validate_ = true;
}
auto exp_vec = last_masterchain_state_->get_shards();
@ -1468,7 +1512,7 @@ void ValidatorManagerImpl::update_shards() {
auto val_set = last_masterchain_state_->get_validator_set(shard);
auto x = val_set->export_vector();
auto validator_id = get_validator(val_set);
auto validator_id = get_validator(shard, val_set);
if (!validator_id.is_zero()) {
auto val_group_id = get_validator_set_id(shard, val_set, opts_hash);
@ -1499,7 +1543,7 @@ void ValidatorManagerImpl::update_shards() {
for (auto &shard : future_shards) {
auto val_set = last_masterchain_state_->get_next_validator_set(shard);
auto validator_id = get_validator(val_set);
auto validator_id = get_validator(shard, val_set);
if (!validator_id.is_zero()) {
auto val_group_id = get_validator_set_id(shard, val_set, opts_hash);
auto it = next_validator_groups_.find(val_group_id);
@ -1548,7 +1592,7 @@ void ValidatorManagerImpl::update_shards() {
});
td::actor::send_closure(db_, &Db::update_destroyed_validator_sessions, gc_list_, std::move(P));
}
}
} // namespace validator
void ValidatorManagerImpl::written_destroyed_validator_sessions(std::vector<td::actor::ActorId<ValidatorGroup>> list) {
for (auto &v : list) {
@ -1609,7 +1653,7 @@ td::actor::ActorOwn<ValidatorGroup> ValidatorManagerImpl::create_validator_group
if (check_gc_list_.count(session_id) == 1) {
return td::actor::ActorOwn<ValidatorGroup>{};
} else {
auto validator_id = get_validator(validator_set);
auto validator_id = get_validator(shard, validator_set);
CHECK(!validator_id.is_zero());
auto G = td::actor::create_actor<ValidatorGroup>("validatorgroup", shard, validator_id, session_id, validator_set,
opts, keyring_, adnl_, rldp_, overlays_, db_root_, actor_id(this),
@ -1827,10 +1871,26 @@ void ValidatorManagerImpl::advance_gc(BlockHandle handle, td::Ref<MasterchainSta
try_advance_gc_masterchain_block();
}
void ValidatorManagerImpl::update_shard_client_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) {
auto seqno = handle->id().seqno();
shard_client_update(seqno);
promise.set_value(td::Unit());
}
void ValidatorManagerImpl::shard_client_update(BlockSeqno seqno) {
if (min_confirmed_masterchain_seqno_ < seqno) {
min_confirmed_masterchain_seqno_ = seqno;
}
while (shard_client_waiters_.size() > 0) {
auto it = shard_client_waiters_.begin();
if (it->first > seqno) {
break;
}
for (auto &y : it->second.waiting_) {
y.promise.set_value(td::Unit());
}
shard_client_waiters_.erase(it);
}
}
void ValidatorManagerImpl::state_serializer_update(BlockSeqno seqno) {
@ -1870,6 +1930,9 @@ void ValidatorManagerImpl::alarm() {
for (auto &w : wait_state_) {
w.second.check_timers();
}
for (auto &w : shard_client_waiters_) {
w.second.check_timers();
}
}
alarm_timestamp().relax(check_waiters_at_);
if (check_shard_clients_.is_in_past()) {
@ -1904,8 +1967,12 @@ void ValidatorManagerImpl::update_shard_client_state(BlockIdExt masterchain_bloc
td::actor::send_closure(db_, &Db::update_shard_client_state, masterchain_block_id, std::move(promise));
}
void ValidatorManagerImpl::get_shard_client_state(td::Promise<BlockIdExt> promise) {
td::actor::send_closure(db_, &Db::get_shard_client_state, std::move(promise));
void ValidatorManagerImpl::get_shard_client_state(bool from_db, td::Promise<BlockIdExt> promise) {
if (!shard_client_.empty() && !from_db) {
td::actor::send_closure(shard_client_, &ShardClient::get_processed_masterchain_block_id, std::move(promise));
} else {
td::actor::send_closure(db_, &Db::get_shard_client_state, std::move(promise));
}
}
void ValidatorManagerImpl::subscribe_to_shard(ShardIdFull shard) {
@ -1928,7 +1995,10 @@ bool ValidatorManagerImpl::is_validator() {
return temp_keys_.size() > 0 || permanent_keys_.size() > 0;
}
PublicKeyHash ValidatorManagerImpl::get_validator(td::Ref<ValidatorSet> val_set) {
PublicKeyHash ValidatorManagerImpl::get_validator(ShardIdFull shard, td::Ref<ValidatorSet> val_set) {
if (!opts_->need_validate(shard)) {
return PublicKeyHash::zero();
}
for (auto &key : temp_keys_) {
if (val_set->is_validator(key.bits256_value())) {
return key;
@ -2017,6 +2087,28 @@ void ValidatorManagerImpl::prepare_stats(td::Promise<std::vector<std::pair<std::
td::actor::send_closure(db_, &Db::prepare_stats, merger.make_promise("db."));
}
void ValidatorManagerImpl::truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) {
td::actor::send_closure(db_, &Db::truncate, std::move(state), std::move(promise));
}
void ValidatorManagerImpl::wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout,
td::Promise<td::Unit> promise) {
if (seqno <= min_confirmed_masterchain_seqno_) {
promise.set_value(td::Unit());
return;
}
if (timeout.is_in_past()) {
promise.set_error(td::Status::Error(ErrorCode::timeout, "timeout"));
return;
}
if (seqno > min_confirmed_masterchain_seqno_ + 100) {
promise.set_error(td::Status::Error(ErrorCode::notready, "too big masterchain block seqno"));
return;
}
shard_client_waiters_[seqno].waiting_.emplace_back(timeout, 0, std::move(promise));
}
td::actor::ActorOwn<ValidatorManagerInterface> ValidatorManagerFactory::create(
td::Ref<ValidatorManagerOptions> opts, std::string db_root, td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<rldp::Rldp> rldp,

View file

@ -143,6 +143,8 @@ class ValidatorManagerImpl : public ValidatorManager {
std::vector<Waiter<ResType>> waiting_;
td::actor::ActorId<ActorT> actor_;
WaitList() = default;
std::pair<td::Timestamp, td::uint32> get_timeout() const {
td::Timestamp t = td::Timestamp::now();
td::uint32 prio = 0;
@ -256,6 +258,7 @@ class ValidatorManagerImpl : public ValidatorManager {
void advance_gc(BlockHandle handle, td::Ref<MasterchainState> state);
void try_advance_gc_masterchain_block();
void update_gc_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) override;
void update_shard_client_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) override;
public:
void install_callback(std::unique_ptr<Callback> new_callback, td::Promise<td::Unit> promise) override {
@ -414,7 +417,7 @@ class ValidatorManagerImpl : public ValidatorManager {
void send_block_broadcast(BlockBroadcast broadcast) override;
void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise<td::Unit> promise) override;
void get_shard_client_state(td::Promise<BlockIdExt> promise) override;
void get_shard_client_state(bool from_db, td::Promise<BlockIdExt> promise) override;
void subscribe_to_shard(ShardIdFull shard) override;
void update_async_serializer_state(AsyncSerializerState state, td::Promise<td::Unit> promise) override;
@ -448,7 +451,7 @@ class ValidatorManagerImpl : public ValidatorManager {
void read_gc_list(std::vector<ValidatorSessionId> list);
bool is_validator();
PublicKeyHash get_validator(td::Ref<ValidatorSet> val_set);
PublicKeyHash get_validator(ShardIdFull shard, td::Ref<ValidatorSet> val_set);
ValidatorManagerImpl(td::Ref<ValidatorManagerOptions> opts, std::string db_root,
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
@ -502,6 +505,10 @@ class ValidatorManagerImpl : public ValidatorManager {
void prepare_stats(td::Promise<std::vector<std::pair<std::string, std::string>>> promise) override;
void truncate(td::Ref<MasterchainState> state, td::Promise<td::Unit> promise) override;
void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise<td::Unit> promise) override;
private:
td::Timestamp resend_shard_blocks_at_;
td::Timestamp check_waiters_at_;
@ -558,6 +565,9 @@ class ValidatorManagerImpl : public ValidatorManager {
double block_ttl() const {
return opts_->block_ttl();
}
private:
std::map<BlockSeqno, WaitList<td::actor::Actor, td::Unit>> shard_client_waiters_;
};
} // namespace validator

View file

@ -36,7 +36,7 @@ void ShardClient::start_up() {
R.ensure();
td::actor::send_closure(SelfId, &ShardClient::got_state_from_db, R.move_as_ok());
});
td::actor::send_closure(manager_, &ValidatorManager::get_shard_client_state, std::move(P));
td::actor::send_closure(manager_, &ValidatorManager::get_shard_client_state, true, std::move(P));
}
void ShardClient::got_state_from_db(BlockIdExt state) {
@ -94,6 +94,8 @@ void ShardClient::saved_to_db() {
}
CHECK(masterchain_block_handle_);
td::actor::send_closure(manager_, &ValidatorManager::update_shard_client_block_handle, masterchain_block_handle_,
[](td::Unit) {});
if (masterchain_block_handle_->inited_next_left()) {
new_masterchain_block_id(masterchain_block_handle_->one_next(true));
} else {
@ -198,6 +200,14 @@ void ShardClient::get_processed_masterchain_block(td::Promise<BlockSeqno> promis
promise.set_result(seqno);
}
void ShardClient::get_processed_masterchain_block_id(td::Promise<BlockIdExt> promise) {
if (masterchain_block_handle_) {
promise.set_result(masterchain_block_handle_->id());
} else {
promise.set_error(td::Status::Error(ErrorCode::notready, "shard client not started"));
}
}
void ShardClient::build_shard_overlays() {
auto v = masterchain_state_->get_shards();

View file

@ -88,6 +88,7 @@ class ShardClient : public td::actor::Actor {
void new_masterchain_block_notification(BlockHandle handle, td::Ref<MasterchainState> state);
void get_processed_masterchain_block(td::Promise<BlockSeqno> promise);
void get_processed_masterchain_block_id(td::Promise<BlockIdExt> promise);
};
} // namespace validator

View file

@ -25,7 +25,7 @@ namespace ton {
namespace validator {
td::Ref<ValidatorManagerOptions> ValidatorManagerOptions::create(
BlockIdExt zero_block_id, BlockIdExt init_block_id, std::function<bool(ShardIdFull)> check_shard,
BlockIdExt zero_block_id, BlockIdExt init_block_id, std::function<bool(ShardIdFull, ShardCheckMode)> check_shard,
bool allow_blockchain_init, td::ClocksBase::Duration sync_blocks_before, td::ClocksBase::Duration block_ttl,
td::ClocksBase::Duration state_ttl, td::ClocksBase::Duration archive_ttl, td::ClocksBase::Duration key_proof_ttl,
bool initial_sync_disabled) {

View file

@ -33,7 +33,10 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions {
return init_block_id_;
}
bool need_monitor(ShardIdFull shard) const override {
return check_shard_(shard);
return check_shard_(shard, ShardCheckMode::m_monitor);
}
bool need_validate(ShardIdFull shard) const override {
return check_shard_(shard, ShardCheckMode::m_validate);
}
bool allow_blockchain_init() const override {
return allow_blockchain_init_;
@ -82,6 +85,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions {
td::uint32 get_last_fork_masterchain_seqno() const override {
return hardforks_.size() ? hardforks_.rbegin()->seqno() : 0;
}
std::vector<BlockIdExt> get_hardforks() const override {
return hardforks_;
}
td::uint32 get_filedb_depth() const override {
return db_depth_;
}
@ -92,7 +98,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions {
void set_init_block_id(BlockIdExt block_id) override {
init_block_id_ = block_id;
}
void set_shard_check_function(std::function<bool(ShardIdFull)> check_shard) override {
void set_shard_check_function(std::function<bool(ShardIdFull, ShardCheckMode)> check_shard) override {
check_shard_ = std::move(check_shard);
}
void set_allow_blockchain_init(bool value) override {
@ -129,7 +135,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions {
}
ValidatorManagerOptionsImpl(BlockIdExt zero_block_id, BlockIdExt init_block_id,
std::function<bool(ShardIdFull)> check_shard, bool allow_blockchain_init,
std::function<bool(ShardIdFull, ShardCheckMode)> check_shard, bool allow_blockchain_init,
td::ClocksBase::Duration sync_blocks_before, td::ClocksBase::Duration block_ttl,
td::ClocksBase::Duration state_ttl, td::ClocksBase::Duration archive_ttl,
td::ClocksBase::Duration key_proof_ttl, bool initial_sync_disabled)
@ -148,7 +154,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions {
private:
BlockIdExt zero_block_id_;
BlockIdExt init_block_id_;
std::function<bool(ShardIdFull)> check_shard_;
std::function<bool(ShardIdFull, ShardCheckMode)> check_shard_;
bool allow_blockchain_init_;
td::ClocksBase::Duration sync_blocks_before_;
td::ClocksBase::Duration block_ttl_;

View file

@ -46,9 +46,12 @@ class DownloadToken {
struct ValidatorManagerOptions : public td::CntObject {
public:
enum class ShardCheckMode { m_monitor, m_validate };
virtual BlockIdExt zero_block_id() const = 0;
virtual BlockIdExt init_block_id() const = 0;
virtual bool need_monitor(ShardIdFull shard) const = 0;
virtual bool need_validate(ShardIdFull shard) const = 0;
virtual bool allow_blockchain_init() const = 0;
virtual td::ClocksBase::Duration sync_blocks_before() const = 0;
virtual td::ClocksBase::Duration block_ttl() const = 0;
@ -60,6 +63,7 @@ struct ValidatorManagerOptions : public td::CntObject {
virtual td::uint32 get_vertical_seqno(BlockSeqno seqno) const = 0;
virtual td::uint32 get_maximal_vertical_seqno() const = 0;
virtual td::uint32 get_last_fork_masterchain_seqno() const = 0;
virtual std::vector<BlockIdExt> get_hardforks() const = 0;
virtual td::uint32 get_filedb_depth() const = 0;
virtual td::uint32 key_block_utime_step() const {
return 86400;
@ -67,7 +71,7 @@ struct ValidatorManagerOptions : public td::CntObject {
virtual void set_zero_block_id(BlockIdExt block_id) = 0;
virtual void set_init_block_id(BlockIdExt block_id) = 0;
virtual void set_shard_check_function(std::function<bool(ShardIdFull)> check_shard) = 0;
virtual void set_shard_check_function(std::function<bool(ShardIdFull, ShardCheckMode)> check_shard) = 0;
virtual void set_allow_blockchain_init(bool value) = 0;
virtual void set_sync_blocks_before(td::ClocksBase::Duration value) = 0;
virtual void set_block_ttl(td::ClocksBase::Duration value) = 0;
@ -80,7 +84,7 @@ struct ValidatorManagerOptions : public td::CntObject {
static td::Ref<ValidatorManagerOptions> create(
BlockIdExt zero_block_id, BlockIdExt init_block_id,
std::function<bool(ShardIdFull)> check_shard = [](ShardIdFull) { return true; },
std::function<bool(ShardIdFull, ShardCheckMode)> check_shard = [](ShardIdFull, ShardCheckMode) { return true; },
bool allow_blockchain_init = false, td::ClocksBase::Duration sync_blocks_before = 300,
td::ClocksBase::Duration block_ttl = 86400 * 7, td::ClocksBase::Duration state_ttl = 3600,
td::ClocksBase::Duration archive_ttl = 86400 * 365, td::ClocksBase::Duration key_proof_ttl = 86400 * 3650,