diff --git a/crypto/block/block-parse.cpp b/crypto/block/block-parse.cpp index 89fc3d6f..c410e2cb 100644 --- a/crypto/block/block-parse.cpp +++ b/crypto/block/block-parse.cpp @@ -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>{}.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{}.skip(cs) // storage_ph:(Maybe TrStoragePhase) && t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase && Maybe>{}.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{}.skip(cs) // storage_ph:(Maybe TrStoragePhase) && Maybe{}.skip(cs) // credit_ph:(Maybe TrCreditPhase) && Maybe{}.skip(cs) // compute_ph:TrComputePhase && Maybe>{}.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>{}.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{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) && t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase && Maybe>{}.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{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) && Maybe{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase) && Maybe{}.validate_skip(cs, weak) // compute_ph:TrComputePhase && Maybe>{}.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 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{}.validate_skip(cs, weak); // description:^TransactionDescr } +bool Transaction::get_storage_fees(Ref cell, td::RefInt256& storage_fees) const { + Ref tdescr; + return get_descr(std::move(cell), tdescr) && t_TransactionDescr.get_storage_fees(std::move(tdescr), storage_fees); +} + +bool Transaction::get_descr(Ref cell, Ref& 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& 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 && diff --git a/crypto/block/block-parse.h b/crypto/block/block-parse.h index 381dc149..acbe9dd4 100644 --- a/crypto/block/block-parse.h +++ b/crypto/block/block-parse.h @@ -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 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 cell, Ref& tdescr) const; + bool get_descr(vm::CellSlice& cs, Ref& tdescr) const; + bool get_storage_fees(Ref cell, td::RefInt256& storage_fees) const; }; extern const Transaction t_Transaction; diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index b9db5fe6..51082947 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -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; diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 11f118d4..24fc731e 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -672,9 +672,56 @@ bool Transaction::prepare_credit_phase() { return true; } +bool ComputePhaseConfig::parse_GasLimitsPrices(Ref 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 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 stack = prepare_vm_stack(cp); if (stack.is_null()) { diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 45e824c1..015dc3f3 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -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 get_lib_root() const { return libraries ? libraries->get_root_cell() : Ref{}; } + bool parse_GasLimitsPrices(Ref cs, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit); + bool parse_GasLimitsPrices(Ref 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 diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 0b97ece9..87c269b7 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -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{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{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{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)); diff --git a/crypto/func/asmops.cpp b/crypto/func/asmops.cpp index 6afdac84..afc16471 100644 --- a/crypto/func/asmops.cpp +++ b/crypto/func/asmops.cpp @@ -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(); diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 2393e9c6..5010a256 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -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& res, std::vector& 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& res, std::vector& 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); diff --git a/crypto/func/func.h b/crypto/func/func.h index f4b66380..8fc881c8 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -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)); } diff --git a/crypto/smartcont/CreateState.fif b/crypto/smartcont/CreateState.fif index b16df7ca..d701ecdc 100644 --- a/crypto/smartcont/CreateState.fif +++ b/crypto/smartcont/CreateState.fif @@ -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 +// 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 { CellSlice::fetch_ref() { } } +bool CellSlice::prefetch_maybe_ref(Ref& 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& 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)); } diff --git a/crypto/vm/cells/CellSlice.h b/crypto/vm/cells/CellSlice.h index 5d7f906b..433344f9 100644 --- a/crypto/vm/cells/CellSlice.h +++ b/crypto/vm/cells/CellSlice.h @@ -185,6 +185,8 @@ class CellSlice : public td::CntObject { bool prefetch_ref_to(Ref& ref, unsigned offset = 0) const { return (ref = prefetch_ref(offset)).not_null(); } + bool fetch_maybe_ref(Ref& ref); + bool prefetch_maybe_ref(Ref& ref) const; td::BitSlice fetch_bits(unsigned bits); td::BitSlice prefetch_bits(unsigned bits) const; td::Ref fetch_subslice(unsigned bits, unsigned refs = 0); diff --git a/doc/FullNode-HOWTO b/doc/FullNode-HOWTO index 75432ffb..66840c95 100644 --- a/doc/FullNode-HOWTO +++ b/doc/FullNode-HOWTO @@ -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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/Validator-HOWTO b/doc/Validator-HOWTO index 72efc978..689bc6b5 100644 --- a/doc/Validator-HOWTO +++ b/doc/Validator-HOWTO @@ -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. diff --git a/tdactor/td/actor/common.h b/tdactor/td/actor/common.h index 039f25b2..3a29b473 100644 --- a/tdactor/td/actor/common.h +++ b/tdactor/td/actor/common.h @@ -364,7 +364,7 @@ inline void register_actor_info_ptr(core::ActorInfoPtr actor_info_ptr) { } template -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()); diff --git a/tdactor/td/actor/core/ActorExecutor.cpp b/tdactor/td/actor/core/ActorExecutor.cpp index fcca341f..7554a05d 100644 --- a/tdactor/td/actor/core/ActorExecutor.cpp +++ b/tdactor/td/actor/core/ActorExecutor.cpp @@ -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_)) { diff --git a/tdactor/td/actor/core/ActorExecutor.h b/tdactor/td/actor/core/ActorExecutor.h index d11c77b2..53fc1e11 100644 --- a/tdactor/td/actor/core/ActorExecutor.h +++ b/tdactor/td/actor/core/ActorExecutor.h @@ -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); diff --git a/tdutils/td/utils/Status.h b/tdutils/td/utils/Status.h index f96ca827..1759c0ab 100644 --- a/tdutils/td/utils/Status.h +++ b/tdutils/td/utils/Status.h @@ -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_; diff --git a/tdutils/td/utils/tl_helpers.h b/tdutils/td/utils/tl_helpers.h index 30cf0955..df89ed1a 100644 --- a/tdutils/td/utils/tl_helpers.h +++ b/tdutils/td/utils/tl_helpers.h @@ -32,10 +32,10 @@ #include #include -#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) \ diff --git a/tdutils/td/utils/tl_parsers.h b/tdutils/td/utils/tl_parsers.h index 734afdb7..e0fc4194 100644 --- a/tdutils/td/utils/tl_parsers.h +++ b/tdutils/td/utils/tl_parsers.h @@ -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)); diff --git a/tl/generate/scheme/lite_api.tl b/tl/generate/scheme/lite_api.tl index dc85227c..9604624a 100644 --- a/tl/generate/scheme/lite_api.tl +++ b/tl/generate/scheme/lite_api.tl @@ -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 diff --git a/tl/generate/scheme/lite_api.tlo b/tl/generate/scheme/lite_api.tlo index 495668b8..9b01c0d8 100644 Binary files a/tl/generate/scheme/lite_api.tlo and b/tl/generate/scheme/lite_api.tlo differ diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index 65593139..942ec47e 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -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; diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index 75381456..6727841e 100644 Binary files a/tl/generate/scheme/ton_api.tlo and b/tl/generate/scheme/ton_api.tlo differ diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 3729d3f3..deae3fa9 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -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.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.Transaction; raw.transactions transactions:vector 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; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index a88c1fd1..180f3521 100644 Binary files a/tl/generate/scheme/tonlib_api.tlo and b/tl/generate/scheme/tonlib_api.tlo differ diff --git a/tl/generate/tl_writer_td.cpp b/tl/generate/tl_writer_td.cpp index f697942b..dc4a98f7 100644 --- a/tl/generate/tl_writer_td.cpp +++ b/tl/generate/tl_writer_td.cpp @@ -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 } diff --git a/tonlib/CMakeLists.txt b/tonlib/CMakeLists.txt index a46f3d3c..42b909ca 100644 --- a/tonlib/CMakeLists.txt +++ b/tonlib/CMakeLists.txt @@ -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() diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index d6f6d92c..a11b8957 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -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(std::move(addr))).move_as_ok(); ASSERT_EQ("Ef9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX", addr_str->account_address_); + auto addr_str2 = sync_send(client, make_object( + make_object(-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( make_object(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(); diff --git a/tonlib/tonlib/ExtClient.cpp b/tonlib/tonlib/ExtClient.cpp index 0ec942ed..442d6503 100644 --- a/tonlib/tonlib/ExtClient.cpp +++ b/tonlib/tonlib/ExtClient.cpp @@ -30,7 +30,7 @@ void ExtClient::with_last_block(td::Promise 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 promise); template - void send_query(QueryT query, td::Promise promise) { + void send_query(QueryT query, td::Promise 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(std::move(raw_query)), true); - send_raw_query(std::move(liteserver_query), [promise = std::move(promise)](td::Result R) mutable { - promise.set_result([&]() -> td::Result { - TRY_RESULT(data, std::move(R)); - auto r_error = ton::fetch_tl_object(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(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 R) mutable { + auto res = [&]() -> td::Result { + TRY_RESULT_PREFIX(data, std::move(R), TonlibError::LiteServerNetwork()); + auto r_error = ton::fetch_tl_object(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(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: diff --git a/tonlib/tonlib/ExtClientOutbound.cpp b/tonlib/tonlib/ExtClientOutbound.cpp index a20a8c59..f1746af5 100644 --- a/tonlib/tonlib/ExtClientOutbound.cpp +++ b/tonlib/tonlib/ExtClientOutbound.cpp @@ -18,6 +18,7 @@ Copyright 2017-2019 Telegram Systems LLP */ #include "ExtClientOutbound.h" +#include "TonlibError.h" #include namespace tonlib { @@ -40,7 +41,7 @@ class ExtClientOutboundImp : public ExtClientOutbound { void on_query_result(td::int64 id, td::Result r_data, td::Promise 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(); } diff --git a/tonlib/tonlib/GenericAccount.cpp b/tonlib/tonlib/GenericAccount.cpp index 08ea33bd..9f44ede2 100644 --- a/tonlib/tonlib/GenericAccount.cpp +++ b/tonlib/tonlib/GenericAccount.cpp @@ -20,18 +20,19 @@ #include "tonlib/utils.h" #include "block/block-auto.h" namespace tonlib { -td::Ref GenericAccount::get_init_state(td::Ref code, td::Ref data) { +td::Ref GenericAccount::get_init_state(td::Ref code, td::Ref 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& init_state) { +block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, + const td::Ref& init_state) noexcept { return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/); } td::Ref GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref new_state, - td::Ref body) { + td::Ref body) noexcept { block::gen::Message::Record message; /*info*/ { block::gen::CommonMsgInfo::Record_ext_in_msg_info info; diff --git a/tonlib/tonlib/GenericAccount.h b/tonlib/tonlib/GenericAccount.h index 253274f0..4a36d78a 100644 --- a/tonlib/tonlib/GenericAccount.h +++ b/tonlib/tonlib/GenericAccount.h @@ -22,9 +22,9 @@ namespace tonlib { class GenericAccount { public: - static td::Ref get_init_state(td::Ref code, td::Ref data); - static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref& init_state); + static td::Ref get_init_state(td::Ref code, td::Ref data) noexcept; + static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref& init_state) noexcept; static td::Ref create_ext_message(const block::StdAddress& address, td::Ref new_state, - td::Ref body); + td::Ref body) noexcept; }; } // namespace tonlib diff --git a/tonlib/tonlib/KeyStorage.cpp b/tonlib/tonlib/KeyStorage.cpp index 8703a3ef..fd660777 100644 --- a/tonlib/tonlib/KeyStorage.cpp +++ b/tonlib/tonlib/KeyStorage.cpp @@ -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::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 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::export_key(InputKey input_key) { @@ -93,24 +98,43 @@ td::Result 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 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::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::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::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::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::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); } diff --git a/tonlib/tonlib/KeyStorage.h b/tonlib/tonlib/KeyStorage.h index fbde2a32..1e370828 100644 --- a/tonlib/tonlib/KeyStorage.h +++ b/tonlib/tonlib/KeyStorage.h @@ -60,6 +60,7 @@ class KeyStorage { td::Result 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 import_key(td::Slice local_password, td::Slice mnemonic_password, ExportedKey exported_key); td::Result import_pem_key(td::Slice local_password, td::Slice key_password, ExportedPemKey exported_key); diff --git a/tonlib/tonlib/KeyValue.cpp b/tonlib/tonlib/KeyValue.cpp index fa2f8ae3..d71744f0 100644 --- a/tonlib/tonlib/KeyValue.cpp +++ b/tonlib/tonlib/KeyValue.cpp @@ -42,6 +42,22 @@ class KeyValueDir : public KeyValue { return td::unlink(key.str()); } + void foreach_key(std::function 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 f) override { + for (auto &it : map_) { + f(it.first); + } + } private: class Cmp : public std::less<> { diff --git a/tonlib/tonlib/KeyValue.h b/tonlib/tonlib/KeyValue.h index 6ccd929f..7d2020ac 100644 --- a/tonlib/tonlib/KeyValue.h +++ b/tonlib/tonlib/KeyValue.h @@ -3,6 +3,8 @@ #include "td/utils/Slice.h" #include "td/utils/Status.h" +#include + 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 get(td::Slice key) = 0; + virtual void foreach_key(std::function f) = 0; static td::Result> create_dir(td::CSlice dir); static td::Result> create_inmemory(); diff --git a/tonlib/tonlib/LastBlock.cpp b/tonlib/tonlib/LastBlock.cpp index 7c4cda5d..fd15fed8 100644 --- a/tonlib/tonlib/LastBlock.cpp +++ b/tonlib/tonlib/LastBlock.cpp @@ -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) : 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 promise) { @@ -42,9 +49,13 @@ void LastBlock::get_last_block(td::Promise 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> LastBlock::process_block_proof( ton::BlockIdExt from, td::Result> 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> LastBlock::process_block_proof( + ton::BlockIdExt from, ton::ton_api::object_ptr 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> 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> 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> 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()); } diff --git a/tonlib/tonlib/LastBlock.h b/tonlib/tonlib/LastBlock.h index 2add3bcf..bb2a62ed 100644 --- a/tonlib/tonlib/LastBlock.h +++ b/tonlib/tonlib/LastBlock.h @@ -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 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 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> 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> r_block_proof); void on_masterchain_info(td::Result> r_info); void do_get_last_block(); @@ -155,7 +192,11 @@ class LastBlock : public td::actor::Actor { ton::BlockIdExt from, td::Result> r_block_proof); - void update_zero_state(ton::ZeroStateIdExt zero_state_id); + td::Result> process_block_proof( + ton::BlockIdExt from, ton::ton_api::object_ptr 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); diff --git a/tonlib/tonlib/LastBlockStorage.cpp b/tonlib/tonlib/LastBlockStorage.cpp index f25632c0..dff4265d 100644 --- a/tonlib/tonlib/LastBlockStorage.cpp +++ b/tonlib/tonlib/LastBlockStorage.cpp @@ -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 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); diff --git a/tonlib/tonlib/Logging.cpp b/tonlib/tonlib/Logging.cpp index 043fe019..31dc275c 100644 --- a/tonlib/tonlib/Logging.cpp +++ b/tonlib/tonlib/Logging.cpp @@ -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 log_tags{ADD_TAG(abc), ADD_TAG(bcd)}; +static const std::map log_tags{ADD_TAG(tonlib_query), ADD_TAG(last_block)}; #undef ADD_TAG td::Status Logging::set_current_stream(tonlib_api::object_ptr stream) { diff --git a/tonlib/tonlib/TestGiver.cpp b/tonlib/tonlib/TestGiver.cpp index 17c36a55..b906193d 100644 --- a/tonlib/tonlib/TestGiver.cpp +++ b/tonlib/tonlib/TestGiver.cpp @@ -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 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; diff --git a/tonlib/tonlib/TestGiver.h b/tonlib/tonlib/TestGiver.h index a5d8b79c..f8b62599 100644 --- a/tonlib/tonlib/TestGiver.h +++ b/tonlib/tonlib/TestGiver.h @@ -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 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 diff --git a/tonlib/tonlib/TestWallet.cpp b/tonlib/tonlib/TestWallet.cpp index dadb1748..8bdf78c9 100644 --- a/tonlib/tonlib/TestWallet.cpp +++ b/tonlib/tonlib/TestWallet.cpp @@ -24,13 +24,13 @@ #include "td/utils/base64.h" namespace tonlib { -td::Ref TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) { +td::Ref 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 TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) { +td::Ref 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 TestWallet::get_init_message(const td::Ed25519::PrivateKey& pr td::Ref 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 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 TestWallet::get_init_code() { +td::Ref TestWallet::get_init_code() noexcept { static auto res = [] { auto serialized_code = td::base64_decode( "te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/" @@ -70,11 +74,11 @@ td::Ref 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 TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) { +td::Ref 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 diff --git a/tonlib/tonlib/TestWallet.h b/tonlib/tonlib/TestWallet.h index 28c97ce1..ef726b55 100644 --- a/tonlib/tonlib/TestWallet.h +++ b/tonlib/tonlib/TestWallet.h @@ -27,14 +27,14 @@ namespace tonlib { class TestWallet { public: static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key); - static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key); + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; + static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; static td::Ref 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 get_init_code(); - static vm::CellHash get_init_code_hash(); - static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key); + static td::Ref get_init_code() noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; }; } // namespace tonlib diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 7a7e1d8d..7989e18e 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -29,6 +29,8 @@ #include "tonlib/utils.h" #include "tonlib/keys/Mnemonic.h" +#include "tonlib/TonlibError.h" + #include "auto/tl/tonlib_api.hpp" #include "block/block-auto.h" #include "block/check-proof.h" @@ -45,17 +47,6 @@ namespace tonlib { -template -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; }) - tonlib_api::object_ptr status_to_tonlib_api(const td::Status& status) { return tonlib_api::make_object(status.code(), status.message().str()); } @@ -73,6 +64,7 @@ struct RawAccountState { td::int64 balance = -1; td::Ref code; td::Ref data; + std::string frozen_hash; block::AccountState::Info info; }; @@ -96,8 +88,12 @@ td::Result to_balance(td::Ref balance_ref) { class GetTransactionHistory : public td::actor::Actor { public: GetTransactionHistory(ExtClientRef ext_client_ref, block::StdAddress address, ton::LogicalTime lt, ton::Bits256 hash, - td::Promise promise) - : address_(std::move(address)), lt_(std::move(lt)), hash_(std::move(hash)), promise_(std::move(promise)) { + td::actor::ActorShared<> parent, td::Promise promise) + : address_(std::move(address)) + , lt_(std::move(lt)) + , hash_(std::move(hash)) + , parent_(std::move(parent)) + , promise_(std::move(promise)) { client_.set_client(ext_client_ref); } @@ -107,6 +103,7 @@ class GetTransactionHistory : public td::actor::Actor { ton::Bits256 hash_; ExtClient client_; td::int32 count_{10}; + td::actor::ActorShared<> parent_; td::Promise promise_; void check(td::Status status) { @@ -117,7 +114,31 @@ class GetTransactionHistory : public td::actor::Actor { } } - td::Status do_with_transactions(std::vector blkids, td::BufferSlice transactions) { + void with_transactions( + td::Result> r_transactions) { + check(do_with_transactions(std::move(r_transactions))); + stop(); + } + + td::Status do_with_transactions( + td::Result> r_transactions) { + TRY_RESULT(transactions, std::move(r_transactions)); + TRY_RESULT_PREFIX(info, TRY_VM(do_with_transactions(std::move(transactions))), TonlibError::ValidateTransactions()); + promise_.set_value(std::move(info)); + return td::Status::OK(); + } + + td::Result do_with_transactions( + ton::lite_api::object_ptr transactions) { + std::vector blkids; + for (auto& id : transactions->ids_) { + blkids.push_back(ton::create_block_id(std::move(id))); + } + return do_with_transactions(std::move(blkids), std::move(transactions->transactions_)); + } + + td::Result do_with_transactions(std::vector blkids, + td::BufferSlice transactions) { LOG(INFO) << "got up to " << count_ << " transactions for " << address_ << " from last transaction " << lt_ << ":" << hash_.to_hex(); block::TransactionList list; @@ -130,24 +151,7 @@ class GetTransactionHistory : public td::actor::Actor { LOG(WARNING) << "obtained " << info.transactions.size() << " transaction, but only " << count_ << " have been requested"; } - promise_.set_value(std::move(info)); - return td::Status::OK(); - } - - td::Status do_with_transactions( - td::Result> r_transactions) { - TRY_RESULT(transactions, std::move(r_transactions)); - std::vector blkids; - for (auto& id : transactions->ids_) { - blkids.push_back(ton::create_block_id(std::move(id))); - } - return do_with_transactions(std::move(blkids), std::move(transactions->transactions_)); - } - - void with_transactions( - td::Result> r_transactions) { - check(TRY_VM(do_with_transactions(std::move(r_transactions)))); - stop(); + return info; } void start_up() override { @@ -166,25 +170,35 @@ class GetTransactionHistory : public td::actor::Actor { class GetRawAccountState : public td::actor::Actor { public: - GetRawAccountState(ExtClientRef ext_client_ref, block::StdAddress address, td::Promise&& promise) - : address_(std::move(address)), promise_(std::move(promise)) { + GetRawAccountState(ExtClientRef ext_client_ref, block::StdAddress address, td::actor::ActorShared<> parent, + td::Promise&& promise) + : address_(std::move(address)), promise_(std::move(promise)), parent_(std::move(parent)) { client_.set_client(ext_client_ref); } private: block::StdAddress address_; td::Promise promise_; + td::actor::ActorShared<> parent_; ExtClient client_; LastBlockState last_block_; void with_account_state(td::Result> r_account_state) { - promise_.set_result(TRY_VM(do_with_account_state(std::move(r_account_state)))); + check(do_with_account_state(std::move(r_account_state))); + } + + td::Status do_with_account_state( + td::Result> r_raw_account_state) { + TRY_RESULT(raw_account_state, std::move(r_raw_account_state)); + TRY_RESULT_PREFIX(state, TRY_VM(do_with_account_state(std::move(raw_account_state))), + TonlibError::ValidateAccountState()); + promise_.set_value(std::move(state)); stop(); + return td::Status::OK(); } td::Result do_with_account_state( - td::Result> r_account_state) { - TRY_RESULT(raw_account_state, std::move(r_account_state)); + ton::tl_object_ptr raw_account_state) { auto account_state = create_account_state(std::move(raw_account_state)); TRY_RESULT(info, account_state.validate(last_block_.last_block_id, address_)); auto serialized_state = account_state.state.clone(); @@ -192,10 +206,12 @@ class GetRawAccountState : public td::actor::Actor { res.info = std::move(info); LOG_IF(ERROR, res.info.gen_utime > last_block_.utime) << res.info.gen_utime << " " << last_block_.utime; auto cell = res.info.root; + std::ostringstream outp; + block::gen::t_Account.print_ref(outp, cell); + LOG(ERROR) << outp.str(); if (cell.is_null()) { return res; } - //block::gen::t_Account.print_ref(std::cerr, cell); block::gen::Account::Record_account account; if (!tlb::unpack_cell(cell, account)) { return td::Status::Error("Failed to unpack Account"); @@ -210,7 +226,14 @@ class GetRawAccountState : public td::actor::Actor { if (state_tag < 0) { return td::Status::Error("Failed to parse AccountState tag"); } - // TODO: handle frozen account + if (state_tag == block::gen::AccountState::account_frozen) { + block::gen::AccountState::Record_account_frozen state; + if (!tlb::csr_unpack(storage.state, state)) { + return td::Status::Error("Failed to parse AccountState"); + } + res.frozen_hash = state.state_hash.as_slice().str(); + return res; + } if (state_tag != block::gen::AccountState::account_active) { return res; } @@ -228,19 +251,24 @@ class GetRawAccountState : public td::actor::Actor { return res; } - void start_up() override { - client_.with_last_block([self = this](td::Result r_last_block) { - if (r_last_block.is_error()) { - return self->check(r_last_block.move_as_error()); - } - self->last_block_ = r_last_block.move_as_ok(); + void with_last_block(td::Result r_last_block) { + check(do_with_last_block(std::move(r_last_block))); + } - self->client_.send_query( - ton::lite_api::liteServer_getAccountState(ton::create_tl_lite_block_id(self->last_block_.last_block_id), - ton::create_tl_object( - self->address_.workchain, self->address_.addr)), - [self](auto r_state) { self->with_account_state(std::move(r_state)); }); - }); + td::Status do_with_last_block(td::Result r_last_block) { + TRY_RESULT_ASSIGN(last_block_, std::move(r_last_block)); + client_.send_query( + ton::lite_api::liteServer_getAccountState( + ton::create_tl_lite_block_id(last_block_.last_block_id), + ton::create_tl_object(address_.workchain, address_.addr)), + [self = this](auto r_state) { self->with_account_state(std::move(r_state)); }, + last_block_.last_block_id.id.seqno); + return td::Status::OK(); + } + + void start_up() override { + client_.with_last_block( + [self = this](td::Result r_last_block) { self->with_last_block(std::move(r_last_block)); }); } void check(td::Status status) { @@ -354,18 +382,19 @@ void TonlibClient::init_last_block() { raw_last_block_ = td::actor::create_actor("LastBlock", get_client_ref(), std::move(state), config_, td::make_unique(td::actor::actor_shared(this), config_generation_)); - client_.set_client(get_client_ref()); } void TonlibClient::on_result(td::uint64 id, tonlib_api::object_ptr response) { + VLOG(tonlib_query) << "Tonlib answer query " << td::tag("id", id) << " " << to_string(response); if (response->get_id() == tonlib_api::error::ID) { callback_->on_error(id, tonlib_api::move_object_as(response)); return; } callback_->on_result(id, std::move(response)); } + void TonlibClient::request(td::uint64 id, tonlib_api::object_ptr function) { - LOG(ERROR) << to_string(function); + VLOG(tonlib_query) << "Tonlib got query " << td::tag("id", id) << " " << to_string(function); if (function == nullptr) { LOG(ERROR) << "Receive empty static request"; return on_result(id, tonlib_api::make_object(400, "Request is empty")); @@ -408,6 +437,7 @@ void TonlibClient::close() { } tonlib_api::object_ptr TonlibClient::static_request( tonlib_api::object_ptr function) { + VLOG(tonlib_query) << "Tonlib got static query " << to_string(function); if (function == nullptr) { LOG(ERROR) << "Receive empty static request"; return tonlib_api::make_object(400, "Request is empty"); @@ -415,6 +445,7 @@ tonlib_api::object_ptr TonlibClient::static_request( tonlib_api::object_ptr response; downcast_call(*function, [&response](auto& request) { response = TonlibClient::do_static_request(request); }); + VLOG(tonlib_query) << " answer static query " << to_string(function); return response; } @@ -459,24 +490,35 @@ tonlib_api::object_ptr TonlibClient::do_static_request(const runner.run_all(); return tonlib_api::make_object(); } + +td::Result get_public_key(td::Slice public_key) { + TRY_RESULT_PREFIX(address, block::PublicKey::parse(public_key), TonlibError::InvalidPublicKey()); + return address; +} + td::Result get_account_address(const tonlib_api::raw_initialAccountState& raw_state) { - TRY_RESULT(code, vm::std_boc_deserialize(raw_state.code_)); - TRY_RESULT(data, vm::std_boc_deserialize(raw_state.data_)); + TRY_RESULT_PREFIX(code, vm::std_boc_deserialize(raw_state.code_), TonlibError::InvalidBagOfCells("raw_state.code")); + TRY_RESULT_PREFIX(data, vm::std_boc_deserialize(raw_state.data_), TonlibError::InvalidBagOfCells("raw_state.data")); return GenericAccount::get_address(0 /*zerochain*/, GenericAccount::get_init_state(std::move(code), std::move(data))); } td::Result get_account_address(const tonlib_api::testWallet_initialAccountState& test_wallet_state) { - TRY_RESULT(key_bytes, block::PublicKey::parse(test_wallet_state.public_key_)); + TRY_RESULT(key_bytes, get_public_key(test_wallet_state.public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); return GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)); } td::Result get_account_address(const tonlib_api::wallet_initialAccountState& test_wallet_state) { - TRY_RESULT(key_bytes, block::PublicKey::parse(test_wallet_state.public_key_)); + TRY_RESULT(key_bytes, get_public_key(test_wallet_state.public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); return GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)); } +td::Result get_account_address(td::Slice account_address) { + TRY_RESULT_PREFIX(address, block::StdAddress::parse(account_address), TonlibError::InvalidAccountAddress()); + return address; +} + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::raw_getAccountAddress& request) { auto r_account_address = get_account_address(*request.initital_account_state_); @@ -485,6 +527,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( } return tonlib_api::make_object(r_account_address.ok().rserialize(true)); } + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::testWallet_getAccountAddress& request) { auto r_account_address = get_account_address(*request.initital_account_state_); @@ -493,6 +536,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( } return tonlib_api::make_object(r_account_address.ok().rserialize(true)); } + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::wallet_getAccountAddress& request) { auto r_account_address = get_account_address(*request.initital_account_state_); @@ -501,6 +545,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( } return tonlib_api::make_object(r_account_address.ok().rserialize(true)); } + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::testGiver_getAccountAddress& request) { return tonlib_api::make_object(TestGiver::address().rserialize(true)); @@ -508,7 +553,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::unpackAccountAddress& request) { - auto r_account_address = block::StdAddress::parse(request.account_address_); + auto r_account_address = get_account_address(request.account_address_); if (r_account_address.is_error()) { return status_to_tonlib_api(r_account_address.move_as_error()); } @@ -521,10 +566,10 @@ tonlib_api::object_ptr TonlibClient::do_static_request( tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::packAccountAddress& request) { if (!request.account_address_) { - return status_to_tonlib_api(td::Status::Error(400, "Field account_address must not be empty")); + return status_to_tonlib_api(TonlibError::EmptyField("account_address")); } if (request.account_address_->addr_.size() != 32) { - return status_to_tonlib_api(td::Status::Error(400, "Field account_address.addr must not be exactly 32 bytes")); + return status_to_tonlib_api(TonlibError::InvalidField("account_address.addr", "must be 32 bytes long")); } block::StdAddress addr; addr.workchain = request.account_address_->workchain_id_; @@ -545,10 +590,10 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, return td::Status::Error(400, "Tonlib is already inited"); } if (!request.options_) { - return td::Status::Error(400, "Field options must not be empty"); + return TonlibError::EmptyField("options"); } if (!request.options_->keystore_type_) { - return td::Status::Error(400, "Field options.keystore_type must not be empty"); + return TonlibError::EmptyField("options.keystore_type"); } td::Result> r_kv; @@ -571,15 +616,14 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, } td::Status TonlibClient::set_config(object_ptr config) { - if (!config) { - return td::Status::Error(400, "config is empty"); - } + CHECK(config); if (config->config_.empty()) { - return td::Status::Error(400, "config is empty"); + return TonlibError::InvalidConfig("config is empty"); } - TRY_RESULT(new_config, Config::parse(std::move(config->config_))); + TRY_RESULT_PREFIX(new_config, Config::parse(std::move(config->config_)), + TonlibError::InvalidConfig("can't parse config")); if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) { - return td::Status::Error("No lite clients in config"); + return TonlibError::InvalidConfig("no lite clients"); } config_ = std::move(new_config); config_generation_++; @@ -592,6 +636,7 @@ td::Status TonlibClient::set_config(object_ptr config) { ignore_cache_ = config->ignore_cache_; init_ext_client(); init_last_block(); + client_.set_client(get_client_ref()); return td::Status::OK(); } @@ -605,6 +650,9 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request, td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise) { + if (!request.config_) { + return TonlibError::EmptyField("config"); + } TRY_STATUS(set_config(std::move(request.config_))); promise.set_value(tonlib_api::make_object()); return td::Status::OK(); @@ -677,6 +725,9 @@ td::Result> to_raw_message_or_th TRY_RESULT(balance, to_balance(msg_info.value)); TRY_RESULT(src, to_std_address(msg_info.src)); TRY_RESULT(dest, to_std_address(msg_info.dest)); + TRY_RESULT(fwd_fee, to_balance(msg_info.fwd_fee)); + TRY_RESULT(ihr_fee, to_balance(msg_info.ihr_fee)); + auto created_lt = static_cast(msg_info.created_lt); td::Ref body; if (message.body->prefetch_long(1) == 0) { body = std::move(message.body); @@ -684,6 +735,7 @@ td::Result> to_raw_message_or_th } else { body = vm::load_cell_slice_ref(message.body->prefetch_ref()); } + auto body_hash = vm::CellBuilder().append_cellslice(*body).finalize()->get_hash().as_slice().str(); std::string body_message; if (body->size() >= 32 && body->prefetch_long(32) == 0) { body.write().fetch_long(32); @@ -693,7 +745,8 @@ td::Result> to_raw_message_or_th } } - return tonlib_api::make_object(std::move(src), std::move(dest), balance, + return tonlib_api::make_object(std::move(src), std::move(dest), balance, fwd_fee, + ihr_fee, created_lt, std::move(body_hash), std::move(body_message)); } case block::gen::CommonMsgInfo::ext_in_msg_info: { @@ -702,7 +755,16 @@ td::Result> to_raw_message_or_th return td::Status::Error("Failed to unpack CommonMsgInfo::ext_in_msg_info"); } TRY_RESULT(dest, to_std_address(msg_info.dest)); - return tonlib_api::make_object("", std::move(dest), 0, ""); + td::Ref body; + if (message.body->prefetch_long(1) == 0) { + body = std::move(message.body); + body.write().advance(1); + } else { + body = vm::load_cell_slice_ref(message.body->prefetch_ref()); + } + auto body_hash = vm::CellBuilder().append_cellslice(*body).finalize()->get_hash().as_slice().str(); + return tonlib_api::make_object("", std::move(dest), 0, 0, 0, 0, std::move(body_hash), + ""); } case block::gen::CommonMsgInfo::ext_out_msg_info: { block::gen::CommonMsgInfo::Record_ext_out_msg_info msg_info; @@ -710,7 +772,7 @@ td::Result> to_raw_message_or_th return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info"); } TRY_RESULT(src, to_std_address(msg_info.src)); - return tonlib_api::make_object(std::move(src), "", 0, ""); + return tonlib_api::make_object(std::move(src), "", 0, 0, 0, 0, "", ""); } } @@ -728,6 +790,7 @@ td::Result> to_raw_transacti tonlib_api::object_ptr in_msg; std::vector> out_msgs; td::int64 fees = 0; + td::int64 storage_fee = 0; if (info.transaction.not_null()) { TRY_RESULT(copy_data, vm::std_boc_serialize(info.transaction)); data = copy_data.as_slice().str(); @@ -736,8 +799,7 @@ td::Result> to_raw_transacti return td::Status::Error("Failed to unpack Transaction"); } - TRY_RESULT(copy_fees, to_balance(trans.total_fees)); - fees = copy_fees; + TRY_RESULT_ASSIGN(fees, to_balance(trans.total_fees)); std::ostringstream outp; block::gen::t_Transaction.print_ref(outp, info.transaction); @@ -757,15 +819,22 @@ td::Result> to_raw_transacti vm::Dictionary dict{trans.r1.out_msgs, 15}; for (int x = 0; x < trans.outmsg_cnt && x < 100; x++) { TRY_RESULT(out_msg, to_raw_message(dict.lookup_ref(td::BitArray<15>{x}))); + fees += out_msg->fwd_fee_; + fees += out_msg->ihr_fee_; out_msgs.push_back(std::move(out_msg)); } } + td::RefInt256 storage_fees; + if (!block::tlb::t_TransactionDescr.get_storage_fees(trans.description, storage_fees)) { + return td::Status::Error("Failed to fetch storage fee from transaction"); + } + storage_fee = storage_fees->to_long(); } return tonlib_api::make_object( info.now, data, tonlib_api::make_object(info.prev_trans_lt, info.prev_trans_hash.as_slice().str()), - fees, std::move(in_msg), std::move(out_msgs)); + fees, storage_fee, fees - storage_fee, std::move(in_msg), std::move(out_msgs)); } td::Result> to_raw_transaction(block::Transaction::Info&& info) { @@ -791,8 +860,11 @@ td::Result> to_raw_transact td::Result> to_testWallet_accountState( RawAccountState&& raw_state) { - if (raw_state.data.is_null()) { - return td::Status::Error(400, "Not a TestWallet"); + if (raw_state.code.is_null()) { + return TonlibError::AccountNotInited(); + } + if (raw_state.code->prefetch_ref()->get_hash() != TestWallet::get_init_code_hash()) { + return TonlibError::AccountTypeUnexpected("TestWallet"); } auto ref = raw_state.data->prefetch_ref(); auto cs = vm::load_cell_slice(std::move(ref)); @@ -806,8 +878,11 @@ td::Result> to_testW td::Result> to_wallet_accountState( RawAccountState&& raw_state) { - if (raw_state.data.is_null()) { - return td::Status::Error(400, "Not a Wallet"); + if (raw_state.code.is_null()) { + return TonlibError::AccountNotInited(); + } + if (raw_state.code->prefetch_ref()->get_hash() != Wallet::get_init_code_hash()) { + return TonlibError::AccountTypeUnexpected("Wallet"); } auto ref = raw_state.data->prefetch_ref(); auto cs = vm::load_cell_slice(std::move(ref)); @@ -821,8 +896,11 @@ td::Result> to_wallet_ac td::Result> to_testGiver_accountState( RawAccountState&& raw_state) { - if (raw_state.data.is_null()) { - return td::Status::Error(400, "Not a TestGiver"); + if (raw_state.code.is_null()) { + return TonlibError::AccountNotInited(); + } + if (raw_state.code->prefetch_ref()->get_hash() != TestGiver::get_init_code_hash()) { + return TonlibError::AccountTypeUnexpected("TestGiver"); } auto ref = raw_state.data->prefetch_ref(); auto cs = vm::load_cell_slice(std::move(ref)); @@ -865,21 +943,18 @@ td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request, td::Promise>&& promise) { td::Ref init_state; if (!request.initial_account_state_.empty()) { - TRY_RESULT(new_init_state, vm::std_boc_deserialize(request.initial_account_state_)); + TRY_RESULT_PREFIX(new_init_state, vm::std_boc_deserialize(request.initial_account_state_), + TonlibError::InvalidBagOfCells("initial_account_state")); init_state = std::move(new_init_state); } - TRY_RESULT(data, vm::std_boc_deserialize(request.data_)); - TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); + TRY_RESULT_PREFIX(data, vm::std_boc_deserialize(request.data_), TonlibError::InvalidBagOfCells("data")); + TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); auto message = GenericAccount::create_ext_message(account_address, std::move(init_state), std::move(data)); client_.send_query(ton::lite_api::liteServer_sendMessage(vm::std_boc_serialize(message).move_as_ok()), [promise = std::move(promise)](auto r_info) mutable { - if (r_info.is_error()) { - promise.set_error(r_info.move_as_error()); - } else { - auto info = r_info.move_as_ok(); - LOG(ERROR) << "info: " << to_string(info); - promise.set_value(tonlib_api::make_object()); - } + TRY_RESULT_PROMISE(promise, info, std::move(r_info)); + LOG(ERROR) << "info: " << to_string(info); + promise.set_value(tonlib_api::make_object()); }); return td::Status::OK(); } @@ -887,30 +962,28 @@ td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request, td::Status TonlibClient::do_request(tonlib_api::raw_getAccountState& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); - td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_raw_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_raw_accountState(std::move(state))); + }); return td::Status::OK(); } td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } if (!request.from_transaction_id_) { - return td::Status::Error(400, "Field from_transaction_id must not be empty"); + return TonlibError::EmptyField("from_transaction_id"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); auto lt = request.from_transaction_id_->lt_; auto hash_str = request.from_transaction_id_->hash_; if (hash_str.size() != 32) { @@ -919,24 +992,22 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request, td::Bits256 hash; hash.as_slice().copy_from(hash_str); - td::actor::create_actor( - "GetTransactionHistory", client_.get_client(), account_address, lt, hash, + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetTransactionHistory", client_.get_client(), account_address, lt, hash, actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_info) mutable { - if (r_info.is_error()) { - return promise.set_error(r_info.move_as_error()); - } - promise.set_result(to_raw_transactions(r_info.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, info, std::move(r_info)); + promise.set_result(to_raw_transactions(std::move(info))); + }); return td::Status::OK(); } td::Result from_tonlib(tonlib_api::inputKey& input_key) { if (!input_key.key_) { - return td::Status::Error(400, "Field key must not be empty"); + return TonlibError::EmptyField("key"); } - TRY_RESULT(key_bytes, block::PublicKey::parse(input_key.key_->public_key_)); + TRY_RESULT(key_bytes, get_public_key(input_key.key_->public_key_)); return KeyStorage::InputKey{{td::SecureString(key_bytes.key), std::move(input_key.key_->secret_)}, std::move(input_key.local_password_)}; } @@ -962,15 +1033,15 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_init& request, td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& request, td::Promise>&& promise) { if (!request.destination_) { - return td::Status::Error(400, "Field destination must not be empty"); + return TonlibError::EmptyField("destination"); } if (!request.private_key_) { - return td::Status::Error(400, "Field private_key must not be empty"); + return TonlibError::EmptyField("private_key"); } if (request.message_.size() > TestWallet::max_message_size) { - return td::Status::Error(400, "Message is too long"); + return TonlibError::MessageTooLong(); } - TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); + TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); account_address.bounceable = false; TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto address = GenericAccount::get_address( @@ -979,43 +1050,37 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); std::string init_state; if (request.seqno_ == 0) { - TRY_RESULT(public_key, private_key.get_public_key()); + TRY_RESULT_PREFIX(public_key, private_key.get_public_key(), TonlibError::Internal()); init_state = vm::std_boc_serialize(TestWallet::get_init_state(public_key)).move_as_ok().as_slice().str(); } + auto message = + TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_, request.message_, account_address); + auto message_hash = message->get_hash().as_slice().str(); td::Promise> new_promise = - [promise = std::move(promise)](td::Result> res) mutable { - if (res.is_error()) { - promise.set_error(res.move_as_error()); - } else { - promise.set_value(tonlib_api::make_object(0)); - } + [promise = std::move(promise), + message_hash = std::move(message_hash)](td::Result> res) mutable { + TRY_RESULT_PROMISE(promise, ok, std::move(res)); + promise.set_value(tonlib_api::make_object(0, std::move(message_hash))); }; - return do_request( - tonlib_api::raw_sendMessage( - tonlib_api::make_object(address.rserialize(true)), std::move(init_state), - vm::std_boc_serialize(TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_, - request.message_, account_address)) - .move_as_ok() - .as_slice() - .str()), - std::move(new_promise)); + return do_request(tonlib_api::raw_sendMessage( + tonlib_api::make_object(address.rserialize(true)), + std::move(init_state), vm::std_boc_serialize(std::move(message)).move_as_ok().as_slice().str()), + std::move(new_promise)); } td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); - td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_testWallet_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_testWallet_accountState(std::move(state))); + }); return td::Status::OK(); } @@ -1023,7 +1088,7 @@ td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& requ td::Status TonlibClient::do_request(const tonlib_api::wallet_init& request, td::Promise>&& promise) { if (!request.private_key_) { - return td::Status::Error(400, "Field private_key must not be empty"); + return TonlibError::EmptyField("private_key"); } TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto init_state = Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())); @@ -1040,16 +1105,17 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_init& request, td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, td::Promise>&& promise) { if (!request.destination_) { - return td::Status::Error(400, "Field destination must not be empty"); + return TonlibError::EmptyField("destination"); } if (!request.private_key_) { - return td::Status::Error(400, "Field private_key must not be empty"); + return TonlibError::EmptyField("private_key"); } if (request.message_.size() > Wallet::max_message_size) { - return td::Status::Error(400, "Message is too long"); + return TonlibError::MessageTooLong(); } - TRY_RESULT(valid_until, td::narrow_cast_safe(request.valid_until_)); - TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); + TRY_RESULT_PREFIX(valid_until, td::narrow_cast_safe(request.valid_until_), + TonlibError::InvalidField("valid_until", "overflow")); + TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); account_address.bounceable = false; TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto address = GenericAccount::get_address( @@ -1058,43 +1124,37 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); std::string init_state; if (request.seqno_ == 0) { - TRY_RESULT(public_key, private_key.get_public_key()); + TRY_RESULT_PREFIX(public_key, private_key.get_public_key(), TonlibError::Internal()); init_state = vm::std_boc_serialize(Wallet::get_init_state(public_key)).move_as_ok().as_slice().str(); } + auto message = Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_, + request.message_, account_address); + auto message_hash = message->get_hash().as_slice().str(); td::Promise> new_promise = - [promise = std::move(promise), valid_until](td::Result> res) mutable { - if (res.is_error()) { - promise.set_error(res.move_as_error()); - } else { - promise.set_value(tonlib_api::make_object(valid_until)); - } + [promise = std::move(promise), valid_until, + message_hash = std::move(message_hash)](td::Result> res) mutable { + TRY_RESULT_PROMISE(promise, ok, std::move(res)); + promise.set_value(tonlib_api::make_object(valid_until, std::move(message_hash))); }; - return do_request( - tonlib_api::raw_sendMessage( - tonlib_api::make_object(address.rserialize(true)), std::move(init_state), - vm::std_boc_serialize(Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_, - request.message_, account_address)) - .move_as_ok() - .as_slice() - .str()), - std::move(new_promise)); + return do_request(tonlib_api::raw_sendMessage( + tonlib_api::make_object(address.rserialize(true)), + std::move(init_state), vm::std_boc_serialize(std::move(message)).move_as_ok().as_slice().str()), + std::move(new_promise)); } td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); - td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_wallet_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_wallet_accountState(std::move(state))); + }); return td::Status::OK(); } @@ -1102,60 +1162,52 @@ td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request, td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& request, td::Promise>&& promise) { if (!request.destination_) { - return td::Status::Error(400, "Field destination must not be empty"); + return TonlibError::EmptyField("destination"); } if (request.message_.size() > TestGiver::max_message_size) { - return td::Status::Error(400, "Message is too long"); + return TonlibError::MessageTooLong(); } - TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); + TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); account_address.bounceable = false; + auto message = TestGiver::make_a_gift_message(request.seqno_, request.amount_, request.message_, account_address); + auto message_hash = message->get_hash().as_slice().str(); td::Promise> new_promise = - [promise = std::move(promise)](td::Result> res) mutable { - if (res.is_error()) { - promise.set_error(res.move_as_error()); - } else { - promise.set_value(tonlib_api::make_object(0)); - } + [promise = std::move(promise), + message_hash = std::move(message_hash)](td::Result> res) mutable { + TRY_RESULT_PROMISE(promise, ok, std::move(res)); + promise.set_value(tonlib_api::make_object(0, std::move(message_hash))); }; return do_request(tonlib_api::raw_sendMessage( tonlib_api::make_object(TestGiver::address().rserialize(true)), "", - vm::std_boc_serialize(TestGiver::make_a_gift_message(request.seqno_, request.amount_, - request.message_, account_address)) - .move_as_ok() - .as_slice() - .str()), + vm::std_boc_serialize(std::move(message)).move_as_ok().as_slice().str()), std::move(new_promise)); } td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountState& request, td::Promise>&& promise) { - td::actor::create_actor( - "GetAccountState", client_.get_client(), TestGiver::address(), + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), TestGiver::address(), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_testGiver_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_testGiver_accountState(std::move(state))); + }); return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::generic_getAccountState& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); - td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_generic_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_generic_accountState(std::move(state))); + }); return td::Status::OK(); } @@ -1197,7 +1249,6 @@ class GenericSendGrams : public TonlibQueryActor { void check(td::Status status) { if (status.is_error()) { - LOG(ERROR) << status; promise_.set_error(std::move(status)); return stop(); } @@ -1208,17 +1259,16 @@ class GenericSendGrams : public TonlibQueryActor { } td::Status do_start_up() { - alarm_timestamp() = td::Timestamp::in(15); if (!send_grams_.destination_) { - return td::Status::Error(400, "Field destination must not be empty"); + return TonlibError::EmptyField("destination"); } - TRY_RESULT(destination_address, block::StdAddress::parse(send_grams_.destination_->account_address_)); + TRY_RESULT(destination_address, get_account_address(send_grams_.destination_->account_address_)); is_destination_bounce_ = destination_address.bounceable; if (!send_grams_.source_) { - return td::Status::Error(400, "Field source must not be empty"); + return TonlibError::EmptyField("destination"); } - TRY_RESULT(source_address, block::StdAddress::parse(send_grams_.source_->account_address_)); + TRY_RESULT(source_address, get_account_address(send_grams_.source_->account_address_)); source_address_ = std::move(source_address); send_query(tonlib_api::generic_getAccountState( @@ -1257,19 +1307,19 @@ class GenericSendGrams : public TonlibQueryActor { source_state_ = std::move(state); if (source_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && send_grams_.private_key_ && send_grams_.private_key_->key_) { - TRY_RESULT(key_bytes, block::PublicKey::parse(send_grams_.private_key_->key_->public_key_)); + TRY_RESULT(key_bytes, get_public_key(send_grams_.private_key_->key_->public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); if (GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)).addr == source_address_.addr) { auto state = ton::move_tl_object_as(source_state_); source_state_ = tonlib_api::make_object( - tonlib_api::make_object(-1, 0, nullptr, + tonlib_api::make_object(state->account_state_->balance_, 0, nullptr, state->account_state_->sync_utime_)); } else if (GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)).addr == source_address_.addr) { auto state = ton::move_tl_object_as(source_state_); source_state_ = tonlib_api::make_object( - tonlib_api::make_object(-1, 0, nullptr, + tonlib_api::make_object(state->account_state_->balance_, 0, nullptr, state->account_state_->sync_utime_)); } } @@ -1285,54 +1335,68 @@ class GenericSendGrams : public TonlibQueryActor { destination_state_ = std::move(state); if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounce_ && !send_grams_.allow_send_to_uninited_) { - return td::Status::Error(400, "DANGEROUS_TRANSACTION: Transfer to uninited wallet"); + return TonlibError::DangerousTransaction("Transfer to uninited wallet"); } return do_loop(); } - void alarm() override { - check(td::Status::Error("Timeout")); - } td::Status do_loop() { if (!source_state_ || !destination_state_) { return td::Status::OK(); } - downcast_call(*source_state_, - td::overloaded( - [&](tonlib_api::generic_accountStateTestGiver& test_giver_state) { - send_query(tonlib_api::testGiver_sendGrams( - std::move(send_grams_.destination_), test_giver_state.account_state_->seqno_, - send_grams_.amount_, std::move(send_grams_.message_)), - std::move(promise_)); - stop(); - }, - [&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) { - send_query(tonlib_api::testWallet_sendGrams( - std::move(send_grams_.private_key_), std::move(send_grams_.destination_), - test_wallet_state.account_state_->seqno_, send_grams_.amount_, - std::move(send_grams_.message_)), - std::move(promise_)); - stop(); - }, - [&](tonlib_api::generic_accountStateWallet& test_wallet_state) { - send_query(tonlib_api::wallet_sendGrams( - std::move(send_grams_.private_key_), std::move(send_grams_.destination_), - test_wallet_state.account_state_->seqno_, - send_grams_.timeout_ == 0 - ? 60 + test_wallet_state.account_state_->sync_utime_ - : send_grams_.timeout_ + test_wallet_state.account_state_->sync_utime_, - send_grams_.amount_, std::move(send_grams_.message_)), - std::move(promise_)); - stop(); - }, - [&](tonlib_api::generic_accountStateUninited&) { - promise_.set_error(td::Status::Error(400, "Account is not inited")); - stop(); - }, - [&](tonlib_api::generic_accountStateRaw&) { - promise_.set_error(td::Status::Error(400, "Unknown account type")); - stop(); - })); + downcast_call( + *source_state_, + td::overloaded( + [&](tonlib_api::generic_accountStateTestGiver& test_giver_state) { + auto amount = send_grams_.amount_; + send_query(tonlib_api::testGiver_sendGrams(std::move(send_grams_.destination_), + test_giver_state.account_state_->seqno_, amount, + std::move(send_grams_.message_)), + std::move(promise_)); + stop(); + }, + [&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) { + auto amount = send_grams_.amount_; + auto balance = test_wallet_state.account_state_->balance_; + if (false && amount == balance) { + amount = -1; + } else if (amount >= balance) { + promise_.set_error(TonlibError::NotEnoughFunds()); + return stop(); + } + send_query(tonlib_api::testWallet_sendGrams( + std::move(send_grams_.private_key_), std::move(send_grams_.destination_), + test_wallet_state.account_state_->seqno_, amount, std::move(send_grams_.message_)), + std::move(promise_)); + stop(); + }, + [&](tonlib_api::generic_accountStateWallet& wallet_state) { + auto amount = send_grams_.amount_; + auto balance = wallet_state.account_state_->balance_; + if (false && amount == balance) { + amount = -1; + } else if (amount >= balance) { + promise_.set_error(TonlibError::NotEnoughFunds()); + return stop(); + } + send_query( + tonlib_api::wallet_sendGrams(std::move(send_grams_.private_key_), std::move(send_grams_.destination_), + wallet_state.account_state_->seqno_, + send_grams_.timeout_ == 0 + ? 60 + wallet_state.account_state_->sync_utime_ + : send_grams_.timeout_ + wallet_state.account_state_->sync_utime_, + amount, std::move(send_grams_.message_)), + std::move(promise_)); + stop(); + }, + [&](tonlib_api::generic_accountStateUninited&) { + promise_.set_error(TonlibError::AccountNotInited()); + stop(); + }, + [&](tonlib_api::generic_accountStateRaw&) { + promise_.set_error(TonlibError::AccountTypeUnknown()); + stop(); + })); return td::Status::OK(); } }; @@ -1340,7 +1404,7 @@ class GenericSendGrams : public TonlibQueryActor { td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request, td::Promise>&& promise) { if (request.timeout_ < 0 || request.timeout_ > 300) { - return td::Status::Error(400, "Invalid timeout: must be between 0 and 300"); + return TonlibError::InvalidField("timeout", "must be between 0 and 300"); } auto id = actor_id_++; actors_[id] = td::actor::create_actor("GenericSendGrams", actor_shared(this, id), @@ -1348,11 +1412,19 @@ td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request, return td::Status::OK(); } +td::Result public_key_from_bytes(td::Slice bytes) { + TRY_RESULT_PREFIX(key_bytes, block::PublicKey::from_bytes(bytes), TonlibError::Internal()); + return key_bytes; +} + td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request, td::Promise>&& promise) { - TRY_RESULT(key, key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_), - std::move(request.random_extra_seed_))); - TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); + TRY_RESULT_PREFIX( + key, + key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_), + std::move(request.random_extra_seed_)), + TonlibError::Internal()); + TRY_RESULT(key_bytes, public_key_from_bytes(key.public_key.as_slice())); promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1360,7 +1432,7 @@ td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request, td::Status TonlibClient::do_request(const tonlib_api::exportKey& request, td::Promise>&& promise) { if (!request.input_key_) { - return td::Status::Error(400, "Field input_key must not be empty"); + return TonlibError::EmptyField("input_key"); } TRY_RESULT(input_key, from_tonlib(*request.input_key_)); TRY_RESULT(exported_key, key_storage_.export_key(std::move(input_key))); @@ -1371,13 +1443,20 @@ td::Status TonlibClient::do_request(const tonlib_api::exportKey& request, td::Status TonlibClient::do_request(const tonlib_api::deleteKey& request, td::Promise>&& promise) { if (!request.key_) { - return td::Status::Error(400, "Field key must not be empty"); + return TonlibError::EmptyField("key"); } - TRY_RESULT(key_bytes, block::PublicKey::parse(request.key_->public_key_)); + TRY_RESULT(key_bytes, get_public_key(request.key_->public_key_)); KeyStorage::Key key; key.public_key = td::SecureString(key_bytes.key); key.secret = std::move(request.key_->secret_); - TRY_STATUS(key_storage_.delete_key(key)); + TRY_STATUS_PREFIX(key_storage_.delete_key(key), TonlibError::KeyUnknown()); + promise.set_value(tonlib_api::make_object()); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(const tonlib_api::deleteAllKeys& request, + td::Promise>&& promise) { + TRY_STATUS_PREFIX(key_storage_.delete_all_keys(), TonlibError::Internal()); promise.set_value(tonlib_api::make_object()); return td::Status::OK(); } @@ -1385,11 +1464,11 @@ td::Status TonlibClient::do_request(const tonlib_api::deleteKey& request, td::Status TonlibClient::do_request(const tonlib_api::importKey& request, td::Promise>&& promise) { if (!request.exported_key_) { - return td::Status::Error(400, "Field exported_key must not be empty"); + return TonlibError::EmptyField("exported_key"); } TRY_RESULT(key, key_storage_.import_key(std::move(request.local_password_), std::move(request.mnemonic_password_), KeyStorage::ExportedKey{std::move(request.exported_key_->word_list_)})); - TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); + TRY_RESULT(key_bytes, public_key_from_bytes(key.public_key.as_slice())); promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1397,13 +1476,13 @@ td::Status TonlibClient::do_request(const tonlib_api::importKey& request, td::Status TonlibClient::do_request(const tonlib_api::exportPemKey& request, td::Promise>&& promise) { if (!request.input_key_) { - return td::Status::Error(400, "Field input_key must not be empty"); + return TonlibError::EmptyField("input_key"); } if (!request.input_key_->key_) { - return td::Status::Error(400, "Field key must not be empty"); + return TonlibError::EmptyField("key"); } - TRY_RESULT(key_bytes, block::PublicKey::parse(request.input_key_->key_->public_key_)); + TRY_RESULT(key_bytes, get_public_key(request.input_key_->key_->public_key_)); KeyStorage::InputKey input_key{{td::SecureString(key_bytes.key), std::move(request.input_key_->key_->secret_)}, std::move(request.input_key_->local_password_)}; TRY_RESULT(exported_pem_key, key_storage_.export_pem_key(std::move(input_key), std::move(request.key_password_))); @@ -1414,11 +1493,11 @@ td::Status TonlibClient::do_request(const tonlib_api::exportPemKey& request, td::Status TonlibClient::do_request(const tonlib_api::importPemKey& request, td::Promise>&& promise) { if (!request.exported_key_) { - return td::Status::Error(400, "Field exported_key must not be empty"); + return TonlibError::EmptyField("exported_key"); } TRY_RESULT(key, key_storage_.import_pem_key(std::move(request.local_password_), std::move(request.key_password_), KeyStorage::ExportedPemKey{std::move(request.exported_key_->pem_)})); - TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); + TRY_RESULT(key_bytes, public_key_from_bytes(key.public_key.as_slice())); promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1426,7 +1505,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importPemKey& request, td::Status TonlibClient::do_request(const tonlib_api::exportEncryptedKey& request, td::Promise>&& promise) { if (!request.input_key_) { - return td::Status::Error(400, "Field input_key must not be empty"); + return TonlibError::EmptyField("input_key"); } TRY_RESULT(input_key, from_tonlib(*request.input_key_)); TRY_RESULT(exported_key, key_storage_.export_encrypted_key(std::move(input_key), request.key_password_)); @@ -1437,12 +1516,12 @@ td::Status TonlibClient::do_request(const tonlib_api::exportEncryptedKey& reques td::Status TonlibClient::do_request(const tonlib_api::importEncryptedKey& request, td::Promise>&& promise) { if (!request.exported_encrypted_key_) { - return td::Status::Error(400, "Field exported_encrypted_key must not be empty"); + return TonlibError::EmptyField("exported_encrypted_key"); } TRY_RESULT(key, key_storage_.import_encrypted_key( std::move(request.local_password_), std::move(request.key_password_), KeyStorage::ExportedEncryptedKey{std::move(request.exported_encrypted_key_->data_)})); - TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); + TRY_RESULT(key_bytes, public_key_from_bytes(key.public_key.as_slice())); promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1450,10 +1529,10 @@ td::Status TonlibClient::do_request(const tonlib_api::importEncryptedKey& reques td::Status TonlibClient::do_request(const tonlib_api::changeLocalPassword& request, td::Promise>&& promise) { if (!request.input_key_) { - return td::Status::Error(400, "Field input_key must not be empty"); + return TonlibError::EmptyField("input_key"); } if (!request.input_key_->key_) { - return td::Status::Error(400, "Field key must not be empty"); + return TonlibError::EmptyField("key"); } TRY_RESULT(input_key, from_tonlib(*request.input_key_)); TRY_RESULT(key, key_storage_.change_local_password(std::move(input_key), std::move(request.new_local_password_))); @@ -1477,7 +1556,8 @@ td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryResult& r td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryError& request, td::Promise>&& promise) { send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_, - td::Status::Error(request.error_->code_, request.error_->message_), + td::Status::Error(request.error_->code_, request.error_->message_) + .move_as_error_prefix(TonlibError::LiteServerNetwork()), [promise = std::move(promise)](td::Result res) mutable { if (res.is_ok()) { promise.set_value(tonlib_api::make_object()); diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 90063551..649df52d 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -47,13 +47,13 @@ class TonlibClient : public td::actor::Actor { private: enum class State { Uninited, Running, Closed } state_ = State::Uninited; td::unique_ptr 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 ext_client_outbound_; // KeyStorage std::shared_ptr kv_; @@ -62,6 +62,7 @@ class TonlibClient : public td::actor::Actor { // network td::actor::ActorOwn raw_client_; + td::actor::ActorId ext_client_outbound_; td::actor::ActorOwn 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>&& promise); td::Status do_request(const tonlib_api::deleteKey& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::deleteAllKeys& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::importKey& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::exportPemKey& request, diff --git a/tonlib/tonlib/TonlibError.h b/tonlib/tonlib/TonlibError.h new file mode 100644 index 00000000..e0b4d0b1 --- /dev/null +++ b/tonlib/tonlib/TonlibError.h @@ -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 . + + 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 diff --git a/tonlib/tonlib/Wallet.cpp b/tonlib/tonlib/Wallet.cpp index dbd12d25..b4682823 100644 --- a/tonlib/tonlib/Wallet.cpp +++ b/tonlib/tonlib/Wallet.cpp @@ -27,13 +27,13 @@ #include namespace tonlib { -td::Ref Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) { +td::Ref 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 Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) { +td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept { td::uint32 seqno = 0; td::uint32 valid_until = std::numeric_limits::max(); auto signature = @@ -45,7 +45,7 @@ td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& privat td::Ref 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 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 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 Wallet::get_init_code() { +td::Ref Wallet::get_init_code() noexcept { static auto res = [] { auto serialized_code = td::base64_decode( "te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/" @@ -81,11 +85,11 @@ td::Ref 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 Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) { +td::Ref 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 diff --git a/tonlib/tonlib/Wallet.h b/tonlib/tonlib/Wallet.h index e4ee5aec..dd114cce 100644 --- a/tonlib/tonlib/Wallet.h +++ b/tonlib/tonlib/Wallet.h @@ -27,14 +27,14 @@ namespace tonlib { class Wallet { public: static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key); - static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key); + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; + static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; static td::Ref 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 get_init_code(); - static vm::CellHash get_init_code_hash(); - static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key); + static td::Ref get_init_code() noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; }; } // namespace tonlib diff --git a/tonlib/tonlib/keys/EncryptedKey.cpp b/tonlib/tonlib/keys/EncryptedKey.cpp index 075e89f4..14367791 100644 --- a/tonlib/tonlib/keys/EncryptedKey.cpp +++ b/tonlib/tonlib/keys/EncryptedKey.cpp @@ -25,7 +25,6 @@ namespace tonlib { td::Result 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"); } diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 298027a0..a783d2aa 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -192,6 +192,7 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "keys - show all stored keys\n"; td::TerminalIO::out() << "unpackaddress
- 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 [] - export key\n"; td::TerminalIO::out() << "setconfig [] [] [] - set lite server config\n"; td::TerminalIO::out() << "getstate - 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(), [](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"; diff --git a/tonlib/tonlib/utils.cpp b/tonlib/tonlib/utils.cpp index b577faf3..5cef21f5 100644 --- a/tonlib/tonlib/utils.cpp +++ b/tonlib/tonlib/utils.cpp @@ -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> binary_bitstring_to_cellslice(td::Slice literal) { unsigned char buff[128]; if (!begins_with(literal, "b{") || !ends_with(literal, "}")) { diff --git a/tonlib/tonlib/utils.h b/tonlib/tonlib/utils.h index 924e86d6..840bc10d 100644 --- a/tonlib/tonlib/utils.h +++ b/tonlib/tonlib/utils.h @@ -21,6 +21,21 @@ #include "ton/ton-types.h" #include "block/block.h" #include "block/block-parse.h" + namespace tonlib { +template +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> binary_bitstring_to_cellslice(td::Slice literal); } // namespace tonlib diff --git a/validator/block-handle.hpp b/validator/block-handle.hpp index 204d4054..42eddccb 100644 --- a/validator/block-handle.hpp +++ b/validator/block-handle.hpp @@ -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(dbf_masterchain) : 0) { diff --git a/validator/db/blockdb.cpp b/validator/db/blockdb.cpp index ac9c6883..1e1c0cf0 100644 --- a/validator/db/blockdb.cpp +++ b/validator/db/blockdb.cpp @@ -229,6 +229,73 @@ tl_object_ptr BlockDb::DbEntry::release() { return create_tl_object(create_tl_block_id(block_id), prev, next); } +void BlockDb::truncate(td::Ref state, td::Promise promise) { + std::map 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 diff --git a/validator/db/blockdb.hpp b/validator/db/blockdb.hpp index 949b8e32..e9e26c8c 100644 --- a/validator/db/blockdb.hpp +++ b/validator/db/blockdb.hpp @@ -41,6 +41,8 @@ class BlockDb : public td::actor::Actor { void gc(); void skip_gc(); + void truncate(td::Ref state, td::Promise promise); + BlockDb(td::actor::ActorId root_db, std::string db_path); private: diff --git a/validator/db/filedb.cpp b/validator/db/filedb.cpp index eea33b5c..4532641e 100644 --- a/validator/db/filedb.cpp +++ b/validator/db/filedb.cpp @@ -138,7 +138,7 @@ void FileDb::load_file(RefId ref_id, td::Promise promise) { } }); - td::actor::create_actor("readfile", get_file_name(ref_id, false), 0, -1, std::move(P)).release(); + td::actor::create_actor("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 promise) { @@ -159,7 +159,7 @@ void FileDb::load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size, } }); - td::actor::create_actor("readfile", get_file_name(ref_id, false), offset, max_size, std::move(P)) + td::actor::create_actor("readfile", get_file_name(ref_id, false), offset, max_size, 0, std::move(P)) .release(); } diff --git a/validator/db/files-async.hpp b/validator/db/files-async.hpp index 9d1b7cae..4f420776 100644 --- a/validator/db/files-async.hpp +++ b/validator/db/files-async.hpp @@ -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 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 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 promise_; }; diff --git a/validator/db/ltdb.cpp b/validator/db/ltdb.cpp index e0f85436..eea2aa63 100644 --- a/validator/db/ltdb.cpp +++ b/validator/db/ltdb.cpp @@ -216,6 +216,95 @@ void LtDb::start_up() { kv_ = std::make_shared(td::RocksDb::open(db_path_).move_as_ok()); } +void LtDb::truncate_workchain(ShardIdFull shard, td::Ref 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(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(td::BufferSlice{value}, true); + E.ensure(); + auto e = E.move_as_ok(); + + bool to_delete = static_cast(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 state, td::Promise promise) { + auto status_key = create_serialize_tl_object(); + td::Result 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(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(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(value, true); + F.ensure(); + auto f = F.move_as_ok(); + + truncate_workchain(ShardIdFull{f->workchain_, static_cast(f->shard_)}, state); + } + kv_->commit_transaction().ensure(); + promise.set_value(td::Unit()); +} + } // namespace validator } // namespace ton diff --git a/validator/db/ltdb.hpp b/validator/db/ltdb.hpp index 804c02c8..4c7651ec 100644 --- a/validator/db/ltdb.hpp +++ b/validator/db/ltdb.hpp @@ -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 promise); void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); + void truncate_workchain(ShardIdFull shard, td::Ref state); + void truncate(td::Ref state, td::Promise promise); + void start_up() override; LtDb(td::actor::ActorId root_db, std::string db_path) : root_db_(root_db), db_path_(std::move(db_path)) { diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index fb59dfc4..2a21d0c2 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -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 promis td::actor::send_closure(state_db_, &StateDb::get_async_serializer_state, std::move(promise)); } +void RootDb::update_hardforks(std::vector blocks, td::Promise promise) { + td::actor::send_closure(state_db_, &StateDb::update_hardforks, std::move(blocks), std::move(promise)); +} + +void RootDb::get_hardforks(td::Promise> promise) { + td::actor::send_closure(state_db_, &StateDb::get_hardforks, std::move(promise)); +} + void RootDb::start_up() { cell_db_ = td::actor::create_actor("celldb", actor_id(this), root_path_ + "/celldb/"); block_db_ = td::actor::create_actor("blockdb", actor_id(this), root_path_ + "/blockdb/"); @@ -481,6 +490,15 @@ void RootDb::prepare_stats(td::Promise state, td::Promise 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 diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index d03b590d..a584ac97 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -104,6 +104,9 @@ class RootDb : public Db { void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) override; void get_async_serializer_state(td::Promise promise) override; + void update_hardforks(std::vector blocks, td::Promise promise) override; + void get_hardforks(td::Promise> promise) override; + void archive(BlockIdExt block_id, td::Promise promise) override; void allow_state_gc(BlockIdExt block_id, td::Promise promise); @@ -112,6 +115,8 @@ class RootDb : public Db { void prepare_stats(td::Promise>> promise) override; + void truncate(td::Ref state, td::Promise promise) override; + private: td::actor::ActorId validator_manager_; diff --git a/validator/db/statedb.cpp b/validator/db/statedb.cpp index 0b0b3e86..ed78626e 100644 --- a/validator/db/statedb.cpp +++ b/validator/db/statedb.cpp @@ -179,6 +179,44 @@ void StateDb::get_async_serializer_state(td::Promise promi static_cast(obj->last_ts_)}); } +void StateDb::update_hardforks(std::vector blocks, td::Promise promise) { + auto key = create_hash_tl_object(); + + std::vector> 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(std::move(vec))).ensure(); + kv_->commit_transaction(); + + promise.set_value(td::Unit()); +} + +void StateDb::get_hardforks(td::Promise> promise) { + auto key = create_hash_tl_object(); + + 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{}); + return; + } + auto F = fetch_tl_object(value, true); + F.ensure(); + auto f = F.move_as_ok(); + + std::vector vec; + for (auto &e : f->blocks_) { + vec.push_back(create_block_id(e)); + } + + promise.set_value(std::move(vec)); +} + StateDb::StateDb(td::actor::ActorId root_db, std::string db_path) : root_db_(root_db), db_path_(db_path) { } diff --git a/validator/db/statedb.hpp b/validator/db/statedb.hpp index 0ef819a2..af5fde4f 100644 --- a/validator/db/statedb.hpp +++ b/validator/db/statedb.hpp @@ -47,6 +47,9 @@ class StateDb : public td::actor::Actor { void update_async_serializer_state(AsyncSerializerState state, td::Promise promise); void get_async_serializer_state(td::Promise promise); + void update_hardforks(std::vector blocks, td::Promise promise); + void get_hardforks(td::Promise> promise); + StateDb(td::actor::ActorId root_db, std::string path); void start_up() override; diff --git a/validator/db/staticfilesdb.cpp b/validator/db/staticfilesdb.cpp index d4037c66..e6b22319 100644 --- a/validator/db/staticfilesdb.cpp +++ b/validator/db/staticfilesdb.cpp @@ -25,7 +25,9 @@ namespace validator { void StaticFilesDb::load_file(FileHash file_hash, td::Promise promise) { auto path = path_ + "/" + file_hash.to_hex(); - td::actor::create_actor("read file", path, 0, -1, std::move(promise)).release(); + td::actor::create_actor("read file", path, 0, -1, db::ReadFile::Flags::f_disable_log, + std::move(promise)) + .release(); } } // namespace validator diff --git a/validator/downloaders/wait-block-data.cpp b/validator/downloaders/wait-block-data.cpp index b5d1aa84..def64ea2 100644 --- a/validator/downloaders/wait-block-data.cpp +++ b/validator/downloaders/wait-block-data.cpp @@ -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 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 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 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 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 diff --git a/validator/downloaders/wait-block-data.hpp b/validator/downloaders/wait-block-data.hpp index aa051273..88b342b2 100644 --- a/validator/downloaders/wait-block-data.hpp +++ b/validator/downloaders/wait-block-data.hpp @@ -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 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 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}; }; diff --git a/validator/fabric.h b/validator/fabric.h index e7709f8d..54ad09a9 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -52,6 +52,8 @@ void run_accept_block_query(BlockIdExt id, td::Ref data, std::vector< void run_fake_accept_block_query(BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::actor::ActorId manager, td::Promise promise); +void run_hardfork_accept_block_query(BlockIdExt id, td::Ref data, + td::actor::ActorId manager, td::Promise promise); void run_apply_block_query(BlockIdExt id, td::Ref block, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise); void run_check_proof_query(BlockIdExt id, td::Ref proof, td::actor::ActorId manager, diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index f6739bfb..16bc8731 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -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(config_->get_libraries_root(), 256); compute_phase_cfg_.global_config = config_->get_root_cell(); diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index a037e122..a4671a8b 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -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 data, std::ve .release(); } +void run_hardfork_accept_block_query(BlockIdExt id, td::Ref data, + td::actor::ActorId manager, td::Promise promise) { + promise.set_error(td::Status::Error(ErrorCode::error, "not implemented")); +} + void run_apply_block_query(BlockIdExt id, td::Ref block, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise) { td::actor::create_actor(PSTRING() << "apply " << id, id, std::move(block), manager, timeout, diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 2a01c99c..2cf588bd 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -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, BlockIdExt>> res) { + [Self = actor_id(this), mode](td::Result, 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> res) { + [Self = actor_id(this), blkid](td::Result> 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> res) { + [Self = actor_id(this), blkid, mode](td::Result> 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> res) { + [Self = actor_id(this), blkid](td::Result> 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 res) { + [Self = actor_id(this), blkid](td::Result 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> res) { + [Self = actor_id(this), blkid](td::Result> 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> res) { + [Self = actor_id(this), blkid, mode](td::Result> 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> res) { + [Self = actor_id(this), blkid](td::Result> 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> res) { + [Self = actor_id(this), blkid](td::Result> 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> res) { + [Self = actor_id(this), blkid](td::Result> 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> res) { + [Self = actor_id(this), blkid](td::Result> 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 res) { + [Self = actor_id(this), blkid](td::Result 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, BlockIdExt>> res)->void { + [Self = actor_id(this)](td::Result, 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 res) { + trans_lt_, [Self = actor_id(this), remaining, manager = manager_](td::Result 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 param_list) { LOG(INFO) << "started a getConfigParams(" << blkid.to_str() << ", " << mode << ", ) 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 res) { + [Self = actor_id(this), manager = manager_, mode = (mode >> 4)](td::Result 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> 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(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, BlockIdExt>> res)->void { + [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 { @@ -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> res) { + td::actor::send_closure_later(manager_, &ton::validator::ValidatorManager::get_shard_client_state, false, + [Self = actor_id(this), from, mode](td::Result 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(res.move_as_ok())); + td::actor::send_closure_later(Self, &LiteQuery::perform_getBlockProof, from, + res.move_as_ok(), mode | 1); } }); } diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 3b574cdb..19892874 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -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(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"); } diff --git a/validator/interfaces/block-handle.h b/validator/interfaces/block-handle.h index 23aaf3c0..1f6ad877 100644 --- a/validator/interfaces/block-handle.h +++ b/validator/interfaces/block-handle.h @@ -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; diff --git a/validator/interfaces/db.h b/validator/interfaces/db.h index 92b28e12..846371a1 100644 --- a/validator/interfaces/db.h +++ b/validator/interfaces/db.h @@ -92,9 +92,14 @@ class Db : public td::actor::Actor { virtual void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) = 0; virtual void get_async_serializer_state(td::Promise promise) = 0; + virtual void update_hardforks(std::vector blocks, td::Promise promise) = 0; + virtual void get_hardforks(td::Promise> promise) = 0; + virtual void archive(BlockIdExt block_id, td::Promise promise) = 0; virtual void prepare_stats(td::Promise>> promise) = 0; + + virtual void truncate(td::Ref state, td::Promise promise) = 0; }; } // namespace validator diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index aaf40f49..4328ce4f 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -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 promise) = 0; - virtual void get_shard_client_state(td::Promise promise) = 0; + virtual void get_shard_client_state(bool from_db, td::Promise promise) = 0; virtual void subscribe_to_shard(ShardIdFull shard) = 0; virtual void update_async_serializer_state(AsyncSerializerState state, td::Promise 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 promise) = 0; + virtual void update_shard_client_block_handle(BlockHandle handle, td::Promise promise) = 0; + + virtual void truncate(td::Ref state, td::Promise promise) = 0; + + virtual void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise) = 0; + static bool is_persistent_state(UnixTime ts, UnixTime prev_ts) { return ts / 1024 != prev_ts / 1024; } diff --git a/validator/manager-disk.cpp b/validator/manager-disk.cpp index d521ebc4..9798bb17 100644 --- a/validator/manager-disk.cpp +++ b/validator/manager-disk.cpp @@ -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 promise) { +void ValidatorManagerImpl::get_shard_client_state(bool from_db, td::Promise promise) { td::actor::send_closure(db_, &Db::get_shard_client_state, std::move(promise)); } diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index c5fa38de..6da9110e 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -241,7 +241,7 @@ class ValidatorManagerImpl : public ValidatorManager { } void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override; - void get_shard_client_state(td::Promise promise) override; + void get_shard_client_state(bool from_db, td::Promise 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 promise) override { + } void prepare_stats(td::Promise>> promise) override { UNREACHABLE(); } + void truncate(td::Ref state, td::Promise promise) override { + UNREACHABLE(); + } + void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise) override { + UNREACHABLE(); + } + private: PublicKeyHash local_id_; diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index 8fd1473b..3a8fe1fd 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -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 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 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("reiniter", opts_, manager_, std::move(promise_)) + td::actor::create_actor("reiniter", opts_, manager_, db_, std::move(promise_)) .release(); stop(); } @@ -351,7 +361,7 @@ void ValidatorManagerMasterchainStarter::got_gc_block_state(td::Ref("shardclient", opts_, manager_); + client_block_id_ = block_id; finish(); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> 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 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 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 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> R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_state, + td::Ref{R.move_as_ok()}); + }); + td::actor::send_closure(db_, &Db::get_block_state, handle_, std::move(P)); +} + +void ValidatorManagerMasterchainStarter::got_truncate_state(td::Ref state) { + state_ = std::move(state); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result 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 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 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 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 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 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", opts_, manager_); promise_.set_value( ValidatorManagerInitResult{handle_, state_, std::move(client_), gc_handle_, gc_state_, last_key_block_handle_}); stop(); diff --git a/validator/manager-init.hpp b/validator/manager-init.hpp index 30041347..e8c2ad4b 100644 --- a/validator/manager-init.hpp +++ b/validator/manager-init.hpp @@ -34,12 +34,13 @@ namespace validator { class ValidatorManagerMasterchainReiniter : public td::actor::Actor { public: ValidatorManagerMasterchainReiniter(td::Ref opts, - td::actor::ActorId manager, + td::actor::ActorId manager, td::actor::ActorId db, td::Promise 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 shards_; td::actor::ActorId manager_; + td::actor::ActorId db_; td::Promise promise_; @@ -95,6 +97,14 @@ class ValidatorManagerMasterchainStarter : public td::actor::Actor { void got_gc_block_state(td::Ref state); void got_key_block_handle(BlockHandle handle); void got_shard_block_id(BlockIdExt block_id); + void got_hardforks(std::vector hardforks); + void got_truncate_block_id(BlockIdExt block_id); + void got_truncate_block_handle(BlockHandle handle); + void got_truncate_state(td::Ref 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 promise_; + BlockIdExt client_block_id_; td::actor::ActorOwn client_; }; diff --git a/validator/manager.cpp b/validator/manager.cpp index 2d5b1175..30467363 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -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 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 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 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(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(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 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> list) { for (auto &v : list) { @@ -1609,7 +1653,7 @@ td::actor::ActorOwn ValidatorManagerImpl::create_validator_group if (check_gc_list_.count(session_id) == 1) { return td::actor::ActorOwn{}; } 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", 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 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 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 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 val_set) { +PublicKeyHash ValidatorManagerImpl::get_validator(ShardIdFull shard, td::Ref 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 state, td::Promise 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 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 ValidatorManagerFactory::create( td::Ref opts, std::string db_root, td::actor::ActorId keyring, td::actor::ActorId adnl, td::actor::ActorId rldp, diff --git a/validator/manager.hpp b/validator/manager.hpp index 082432fa..6ce036ca 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -143,6 +143,8 @@ class ValidatorManagerImpl : public ValidatorManager { std::vector> waiting_; td::actor::ActorId actor_; + WaitList() = default; + std::pair 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 state); void try_advance_gc_masterchain_block(); void update_gc_block_handle(BlockHandle handle, td::Promise promise) override; + void update_shard_client_block_handle(BlockHandle handle, td::Promise promise) override; public: void install_callback(std::unique_ptr new_callback, td::Promise 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 promise) override; - void get_shard_client_state(td::Promise promise) override; + void get_shard_client_state(bool from_db, td::Promise promise) override; void subscribe_to_shard(ShardIdFull shard) override; void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) override; @@ -448,7 +451,7 @@ class ValidatorManagerImpl : public ValidatorManager { void read_gc_list(std::vector list); bool is_validator(); - PublicKeyHash get_validator(td::Ref val_set); + PublicKeyHash get_validator(ShardIdFull shard, td::Ref val_set); ValidatorManagerImpl(td::Ref opts, std::string db_root, td::actor::ActorId keyring, td::actor::ActorId adnl, @@ -502,6 +505,10 @@ class ValidatorManagerImpl : public ValidatorManager { void prepare_stats(td::Promise>> promise) override; + void truncate(td::Ref state, td::Promise promise) override; + + void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise 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> shard_client_waiters_; }; } // namespace validator diff --git a/validator/shard-client.cpp b/validator/shard-client.cpp index 44973e44..2f05bdac 100644 --- a/validator/shard-client.cpp +++ b/validator/shard-client.cpp @@ -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 promis promise.set_result(seqno); } +void ShardClient::get_processed_masterchain_block_id(td::Promise 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(); diff --git a/validator/shard-client.hpp b/validator/shard-client.hpp index e00f4477..2025041f 100644 --- a/validator/shard-client.hpp +++ b/validator/shard-client.hpp @@ -88,6 +88,7 @@ class ShardClient : public td::actor::Actor { void new_masterchain_block_notification(BlockHandle handle, td::Ref state); void get_processed_masterchain_block(td::Promise promise); + void get_processed_masterchain_block_id(td::Promise promise); }; } // namespace validator diff --git a/validator/validator-options.cpp b/validator/validator-options.cpp index 856bdd73..8dcc6a80 100644 --- a/validator/validator-options.cpp +++ b/validator/validator-options.cpp @@ -25,7 +25,7 @@ namespace ton { namespace validator { td::Ref ValidatorManagerOptions::create( - BlockIdExt zero_block_id, BlockIdExt init_block_id, std::function check_shard, + BlockIdExt zero_block_id, BlockIdExt init_block_id, std::function 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) { diff --git a/validator/validator-options.hpp b/validator/validator-options.hpp index 21bf79cb..315027e9 100644 --- a/validator/validator-options.hpp +++ b/validator/validator-options.hpp @@ -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 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 check_shard) override { + void set_shard_check_function(std::function 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 check_shard, bool allow_blockchain_init, + std::function 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 check_shard_; + std::function check_shard_; bool allow_blockchain_init_; td::ClocksBase::Duration sync_blocks_before_; td::ClocksBase::Duration block_ttl_; diff --git a/validator/validator.h b/validator/validator.h index 2e80c135..1387fc95 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -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 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 check_shard) = 0; + virtual void set_shard_check_function(std::function 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 create( BlockIdExt zero_block_id, BlockIdExt init_block_id, - std::function check_shard = [](ShardIdFull) { return true; }, + std::function 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,