diff --git a/crypto/smartcont/config-code.fc b/crypto/smartcont/config-code.fc index 3a897d4c..c7058f50 100644 --- a/crypto/smartcont/config-code.fc +++ b/crypto/smartcont/config-code.fc @@ -26,7 +26,7 @@ (int, int) check_validator_set(cell vset) { var cs = vset.begin_parse(); - throw_if(9, (cs~load_uint(8) - 0x11) & -2); ;; validators#11 or validators_ext#12 + throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only int utime_since = cs~load_uint(32); int utime_until = cs~load_uint(32); int total = cs~load_uint(16); @@ -103,7 +103,7 @@ .end_cell(), 0); } -() after_code_upgrade(slice param, cell old_code) impure method_id(1666) { +() after_code_upgrade(slice param, cont old_code) impure method_id(1666) { } _ perform_action(cfg_dict, public_key, action, cs) { @@ -119,7 +119,7 @@ _ perform_action(cfg_dict, public_key, action, cs) { var new_code = cs~load_ref(); set_code(new_code); var old_code = get_c3(); - set_c3(new_code); + set_c3(new_code.begin_parse().bless()); after_code_upgrade(cs, old_code); throw(0); return (cfg_dict, public_key); @@ -231,7 +231,7 @@ cell register_vote(vote_dict, action, cs, idx, weight) { if (ds.slice_bits() >= 40) { var tag = ds~load_uint(8); var since = ds.preload_uint(32); - if ((tag == 0x11) & (since >= now())) { + if ((since <= now()) & (tag == 0x12)) { ;; next validator set becomes active! var cur_vset = cfg_dict~idict_set_get_ref(kl, 34, next_vset); ;; next_vset -> cur_vset cfg_dict~idict_set_get_ref(kl, 32, cur_vset); ;; cur_vset -> prev_vset @@ -241,3 +241,7 @@ cell register_vote(vote_dict, action, cs, idx, weight) { } set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell()); } + +int seqno() impure method_id { + return get_data().begin_parse().preload_uint(32); +} diff --git a/crypto/smartcont/elector-code.fc b/crypto/smartcont/elector-code.fc index 263aa463..37257d1b 100644 --- a/crypto/smartcont/elector-code.fc +++ b/crypto/smartcont/elector-code.fc @@ -338,7 +338,7 @@ int upgrade_code(s_addr, cs, query_id) { var code = cs~load_ref(); set_code(code); ifnot(cs.slice_empty?()) { - set_c3(code); + set_c3(code.begin_parse().bless()); ;; run_method3(1666, s_addr, cs, query_id); after_code_upgrade(s_addr, cs, query_id); throw(0); diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index 196ca5de..4277445f 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -215,10 +215,11 @@ now dup orig_vset_valid_for + 0 config.validators! * */ "auto/config-code.fif" include // code in separate source file - // data empty_cell // libraries GR$10 // balance diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 0798a0a1..ba941670 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -35,8 +35,10 @@ int check_data_signature(slice data, slice signature, int public_key) asm "CHKSI cell get_data() asm "c4 PUSH"; () set_data(cell c) impure asm "c4 POP"; -cell get_c3() impure asm "c3 PUSH"; -() set_c3(cell c) impure asm "c3 POP"; +cont get_c3() impure asm "c3 PUSH"; +() set_c3(cont c) impure asm "c3 POP"; +cont bless(slice s) impure asm "BLESS"; + () accept_message() impure asm "ACCEPT"; () commit() impure asm "COMMIT"; diff --git a/crypto/smartcont/update-config-smc.fif b/crypto/smartcont/update-config-smc.fif index 33e5fbce..a954ff14 100755 --- a/crypto/smartcont/update-config-smc.fif +++ b/crypto/smartcont/update-config-smc.fif @@ -9,14 +9,14 @@ $# dup 2 < swap 3 > or ' usage if "config-master" constant file-base -0 constant seqno +0 constant qseqno -1 constant idx true constant bounce "auto/config-code.fif" constant config-source 100 constant interval // valid for 100 seconds $1 =: file-base -$2 parse-int =: seqno +$2 parse-int =: qseqno def? $3 { @' $3 } { "config-query" } cond constant savefile file-base +".addr" load-address @@ -30,7 +30,7 @@ config-source include dup + dup ."signing message: " [-B ] [-C ] []" cr +{ ."usage: " @' $0 type ." [-n] [-B ] [-C ] []" cr ."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt } : usage "" =: comment // comment for simple transfers +true =: allow-bounce +def? $5 { @' $5 "-n" $= { false =: allow-bounce [forget] $5 + def? $6 { @' $6 =: $5 [forget] $6 } if + def? $7 { @' $7 =: $6 [forget] $7 } if + @' $# 1- =: $# + } if +} if + def? $6 { @' $5 dup "-B" $= swap "-C" $= tuck or { @' $6 swap { =: comment } { =: body-boc-file } cond [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond @@ -17,7 +25,7 @@ $# dup 4 < swap 5 > or ' usage if true constant bounce $1 =: file-base -$2 bounce parse-load-address =: bounce 2=: dest_addr +$2 bounce parse-load-address allow-bounce and =: bounce 2=: dest_addr $3 parse-int =: seqno $4 $>GR =: amount def? $5 { @' $5 } { "wallet-query" } cond constant savefile diff --git a/crypto/vm/contops.cpp b/crypto/vm/contops.cpp index 0039439e..6fcda8de 100644 --- a/crypto/vm/contops.cpp +++ b/crypto/vm/contops.cpp @@ -626,9 +626,23 @@ inline void throw_rangechk(bool ok) { } } // namespace +int exec_bless_pop_c3(VmState* st) { + Stack& stack = st->get_stack(); + VM_LOG(st) << "execute CTOSBLESSPOPc3"; + stack.check_underflow(1); + throw_typechk(st->set_c(3, Ref{true, vm::load_cell_slice_ref(stack.pop_cell()), st->get_cp()})); + return 0; +} + int exec_pop_ctr(VmState* st, unsigned args) { unsigned idx = args & 15; VM_LOG(st) << "execute POP c" << idx; + /* + if (idx == 3 && st->get_stack().depth() > 0 && st->get_stack().tos().is(StackEntry::t_cell)) { + // temp hack: accept cell argument for POP c3 and do auto-BLESSing + return exec_bless_pop_c3(st); + } + */ throw_typechk(st->set(idx, st->get_stack().pop_chk())); return 0; } diff --git a/doc/FullNode-HOWTO b/doc/FullNode-HOWTO index 66840c95..2778b02f 100644 --- a/doc/FullNode-HOWTO +++ b/doc/FullNode-HOWTO @@ -5,7 +5,7 @@ Note that you need a machine with a public IP address and a high-bandwidth netwo 0. Downloading and compiling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The complete TON Blockchain Library and Validator software is downloaded and compiled similarly to the Lite Client. This process is outlined in the corresponding README file. The most important difference is that you have to download the complete sources from public GitHub repository https://github.com/ton-blockchain/ton (e.g., by running `git clone https://github.com/ton-blockchain/ton`) instead of downloading the smaller Lite Client source archive. You should also build all goals defined in CMakeLists.txt (e.g. by running `cmake ` and `make` in your build directory), not only those specifically related to the Lite Client (which is also included in the larger distribution; you don't have to download and build it separately). We strongly recommend building a "release" or a "release with debug information" version of the TON Blockchain Library and especially of the Validator/Full Node by passing `-DCMAKE_BUILD_TYPE=Release` or `-DCMAKE_BUILD_TYPE=RelWithDebInfo` as an extra argument to `cmake` during its first run (if you forgot to do this, you can later delete file `CMakeCache.txt` from your build directory and re-run `cmake` with the appropriate options). +The complete TON Blockchain Library and Validator software is downloaded and compiled similarly to the Lite Client. This process is outlined in the corresponding README file. The most important difference is that you have to download the complete sources from public GitHub repository https://github.com/ton-blockchain/ton (e.g., by running `git clone https://github.com/ton-blockchain/ton` and `git submodule update` afterwards) instead of downloading the smaller Lite Client source archive. You should also build all goals defined in CMakeLists.txt (e.g. by running `cmake ` and `make` in your build directory), not only those specifically related to the Lite Client (which is also included in the larger distribution; you don't have to download and build it separately). We strongly recommend building a "release" or a "release with debug information" version of the TON Blockchain Library and especially of the Validator/Full Node by passing `-DCMAKE_BUILD_TYPE=Release` or `-DCMAKE_BUILD_TYPE=RelWithDebInfo` as an extra argument to `cmake` during its first run (if you forgot to do this, you can later delete file `CMakeCache.txt` from your build directory and re-run `cmake` with the appropriate options). 1. Full Node binaries ~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/Validator-HOWTO b/doc/Validator-HOWTO index 689bc6b5..9545582d 100644 --- a/doc/Validator-HOWTO +++ b/doc/Validator-HOWTO @@ -10,7 +10,7 @@ The basic instructions are the same as for a TON Blockchain Full Node, as explai 1. Controlling smart contract of a validator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -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. +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,001 Grams in the production network, and at least 10,001 test Grams in the test network. The actual value (in nanograms) can be found as the value of `min_stake` in configuration parameter #17 (available by typing `getconfig 17` into the Lite Client), plus one Gram. 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. diff --git a/lite-client-docs/README b/lite-client-docs/README index 7379d91e..a47456e2 100644 --- a/lite-client-docs/README +++ b/lite-client-docs/README @@ -8,18 +8,19 @@ The software is likely to compile and work properly on most Linux systems. It sh BASIC COMPILATION AND INSTALLATION INSTRUCTIONS -1) Download and unpack the newest version of this archive, available at +1) Download the newest version of the TON blockchain sources, available at GitHub repository https://github.com/ton-blockchain/ton/ : -https://test.ton.org/download +git clone https://github.com/ton-blockchain/ton.git +git submodule update The TON Blockchain Test Network is updated quite often, so we cannot guarantee that older versions of the Lite Client will always work. Backward compatibility is not enforced at this development stage. 2) Install the newest versions of make, cmake (version 3.0.2 or later), OpenSSL (including C header files), and g++ or clang (or another C++14-compatible compiler as appropriate for your operating system). We strongly recommend installing OpenSSL version 1.1.1 or later for better performance, especially if you intend to run a Full Node or a Validator as well. -3) Suppose that you have unpacked this archive to directory ~/lite-client, where ~ is your home directory, and that you have created an empty directory ~/liteclient-build. Then run the following in a terminal on a Linux system: +3) Suppose that you have fetched the source tree to directory ~/ton, where ~ is your home directory, and that you have created an empty directory ~/liteclient-build. Then run the following in a terminal on a Linux system: cd ~/liteclient-build -cmake ~/lite-client +cmake ~/ton cmake --build . --target lite-client You might also build some extra utilities useful for smart-contract development: diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 1d3b8ea9..7eef7bbc 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -250,8 +250,7 @@ void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td void ArchiveManager::get_file(BlockHandle handle, FileReference ref_id, td::Promise promise) { if (handle->moved_to_archive()) { - auto f = get_file_desc(handle->id().shard_full(), PackageId{handle->masterchain_ref_block(), false, false}, 0, 0, 0, - false); + auto f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), 0, 0, 0, false); if (f) { td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(ref_id), std::move(promise)); return; diff --git a/validator/downloaders/download-state.cpp b/validator/downloaders/download-state.cpp index 9e690fff..de5eec08 100644 --- a/validator/downloaders/download-state.cpp +++ b/validator/downloaders/download-state.cpp @@ -193,6 +193,9 @@ void DownloadShardState::written_shard_state(td::Ref state) { handle_->set_logical_time(state_->get_logical_time()); handle_->set_applied(); handle_->set_split(state_->before_split()); + if (!block_id_.is_masterchain()) { + handle_->set_masterchain_ref_block(masterchain_block_id_.seqno()); + } auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle = handle_](td::Result R) { CHECK(handle->handle_moved_to_archive()); diff --git a/validator/full-node.cpp b/validator/full-node.cpp index b55b1008..5531df2c 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -99,7 +99,6 @@ void FullNodeImpl::initial_read_complete(BlockHandle top_handle) { } void FullNodeImpl::add_shard(ShardIdFull shard) { - LOG(WARNING) << "add shard " << shard; while (true) { if (shards_.count(shard) == 0) { shards_.emplace(shard, FullNodeShard::create(shard, local_id_, adnl_id_, zero_state_file_hash_, keyring_, adnl_, @@ -239,6 +238,7 @@ void FullNodeImpl::download_archive(BlockSeqno masterchain_seqno, std::string tm } td::actor::ActorId FullNodeImpl::get_shard(ShardIdFull shard) { + add_shard(ShardIdFull{shard.workchain, shardIdAll}); while (shards_.count(shard) == 0) { if (shard.shard == shardIdAll) { return td::actor::ActorId{}; diff --git a/validator/import-db-slice.cpp b/validator/import-db-slice.cpp index 46fcc0ae..4e404b7e 100644 --- a/validator/import-db-slice.cpp +++ b/validator/import-db-slice.cpp @@ -5,6 +5,8 @@ #include "td/actor/MultiPromise.h" #include "common/checksum.h" #include "td/utils/port/path.h" +#include "ton/ton-io.hpp" +#include "downloaders/download-state.hpp" namespace ton { @@ -94,79 +96,85 @@ void ArchiveImporter::check_masterchain_block(BlockSeqno seqno) { checked_all_masterchain_blocks(seqno - 1); return; } - if (seqno < state_->get_block_id().seqno()) { - if (!state_->check_old_mc_block_id(it->second)) { - abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); - return; - } - check_masterchain_block(seqno + 1); - } else if (seqno == state_->get_block_id().seqno()) { - if (state_->get_block_id() != it->second) { - abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); - return; - } - check_masterchain_block(seqno + 1); - } else { - if (seqno != state_->get_block_id().seqno() + 1) { - abort_query(td::Status::Error(ErrorCode::protoviolation, "hole in masterchain seqno")); - return; - } - auto it2 = blocks_.find(it->second); - CHECK(it2 != blocks_.end()); - - auto R1 = package_->read(it2->second[0]); - if (R1.is_error()) { - abort_query(R1.move_as_error()); - return; - } - - auto proofR = create_proof(it->second, std::move(R1.move_as_ok().second)); - if (proofR.is_error()) { - abort_query(proofR.move_as_error()); - return; - } - - auto R2 = package_->read(it2->second[1]); - if (R2.is_error()) { - abort_query(R2.move_as_error()); - return; - } - - if (sha256_bits256(R2.ok().second.as_slice()) != it->second.file_hash) { - abort_query(td::Status::Error(ErrorCode::protoviolation, "bad block file hash")); - return; - } - auto dataR = create_block(it->second, std::move(R2.move_as_ok().second)); - if (dataR.is_error()) { - abort_query(dataR.move_as_error()); - return; - } - - auto proof = proofR.move_as_ok(); - auto data = dataR.move_as_ok(); - - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id = state_->get_block_id(), - data](td::Result R) mutable { - if (R.is_error()) { - td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); + while (seqno <= state_->get_block_id().seqno()) { + if (seqno < state_->get_block_id().seqno()) { + if (!state_->check_old_mc_block_id(it->second)) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); return; } - auto handle = R.move_as_ok(); - CHECK(!handle->merge_before()); - if (handle->one_prev(true) != id) { - td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, - td::Status::Error(ErrorCode::protoviolation, "prev block mismatch")); + } else { + if (state_->get_block_id() != it->second) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); return; } - td::actor::send_closure(SelfId, &ArchiveImporter::checked_masterchain_proof, std::move(handle), std::move(data)); - }); - - run_check_proof_query(it->second, std::move(proof), manager_, td::Timestamp::in(2.0), std::move(P), state_, - opts_->is_hardfork(it->second)); + } + seqno++; + it = masterchain_blocks_.find(seqno); + if (it == masterchain_blocks_.end()) { + checked_all_masterchain_blocks(seqno - 1); + return; + } } + if (seqno != state_->get_block_id().seqno() + 1) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "hole in masterchain seqno")); + return; + } + auto it2 = blocks_.find(it->second); + CHECK(it2 != blocks_.end()); + + auto R1 = package_->read(it2->second[0]); + if (R1.is_error()) { + abort_query(R1.move_as_error()); + return; + } + + auto proofR = create_proof(it->second, std::move(R1.move_as_ok().second)); + if (proofR.is_error()) { + abort_query(proofR.move_as_error()); + return; + } + + auto R2 = package_->read(it2->second[1]); + if (R2.is_error()) { + abort_query(R2.move_as_error()); + return; + } + + if (sha256_bits256(R2.ok().second.as_slice()) != it->second.file_hash) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "bad block file hash")); + return; + } + auto dataR = create_block(it->second, std::move(R2.move_as_ok().second)); + if (dataR.is_error()) { + abort_query(dataR.move_as_error()); + return; + } + + auto proof = proofR.move_as_ok(); + auto data = dataR.move_as_ok(); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id = state_->get_block_id(), + data](td::Result R) mutable { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); + return; + } + auto handle = R.move_as_ok(); + CHECK(!handle->merge_before()); + if (handle->one_prev(true) != id) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, + td::Status::Error(ErrorCode::protoviolation, "prev block mismatch")); + return; + } + td::actor::send_closure(SelfId, &ArchiveImporter::checked_masterchain_proof, std::move(handle), std::move(data)); + }); + + run_check_proof_query(it->second, std::move(proof), manager_, td::Timestamp::in(2.0), std::move(P), state_, + opts_->is_hardfork(it->second)); } void ArchiveImporter::checked_masterchain_proof(BlockHandle handle, td::Ref data) { + CHECK(data.not_null()); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &ArchiveImporter::applied_masterchain_block, std::move(handle)); @@ -189,17 +197,16 @@ void ArchiveImporter::got_new_materchain_state(td::Ref state) } void ArchiveImporter::checked_all_masterchain_blocks(BlockSeqno seqno) { - max_shard_client_seqno_ = seqno; check_next_shard_client_seqno(shard_client_seqno_ + 1); } void ArchiveImporter::check_next_shard_client_seqno(BlockSeqno seqno) { - if (seqno > max_shard_client_seqno_) { + if (seqno > state_->get_seqno()) { finish_query(); return; } - if (seqno == max_shard_client_seqno_) { + if (seqno == state_->get_seqno()) { got_masterchain_state(state_); } else { BlockIdExt b; @@ -217,14 +224,13 @@ void ArchiveImporter::check_next_shard_client_seqno(BlockSeqno seqno) { void ArchiveImporter::got_masterchain_state(td::Ref state) { auto s = state->get_shards(); - auto P = td::PromiseCreator::lambda( - [SelfId = actor_id(this), seqno = state->get_block_id().seqno()](td::Result R) { - if (R.is_error()) { - td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); - } else { - td::actor::send_closure(SelfId, &ArchiveImporter::check_next_shard_client_seqno, seqno + 1); - } - }); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), seqno = state->get_seqno()](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveImporter::checked_shard_client_seqno, seqno); + } + }); td::MultiPromise mp; auto ig = mp.init_guard(); @@ -235,6 +241,12 @@ void ArchiveImporter::got_masterchain_state(td::Ref state) { } } +void ArchiveImporter::checked_shard_client_seqno(BlockSeqno seqno) { + CHECK(shard_client_seqno_ + 1 == seqno); + shard_client_seqno_++; + check_next_shard_client_seqno(seqno + 1); +} + void ArchiveImporter::apply_shard_block(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) { auto P = td::PromiseCreator::lambda( @@ -253,9 +265,18 @@ void ArchiveImporter::apply_shard_block_cont1(BlockHandle handle, BlockIdExt mas return; } + if (handle->id().seqno() == 0) { + auto P = td::PromiseCreator::lambda( + [promise = std::move(promise)](td::Result> R) mutable { promise.set_value(td::Unit()); }); + td::actor::create_actor("downloadstate", handle->id(), masterchain_block_id, 2, manager_, + td::Timestamp::in(3600), std::move(P)) + .release(); + return; + } + auto it = blocks_.find(handle->id()); if (it == blocks_.end()) { - promise.set_error(td::Status::Error(ErrorCode::notready, "no proof for shard block")); + promise.set_error(td::Status::Error(ErrorCode::notready, PSTRING() << "no proof for shard block " << handle->id())); return; } TRY_RESULT_PROMISE(promise, data, package_->read(it->second[0])); @@ -280,21 +301,21 @@ void ArchiveImporter::apply_shard_block_cont2(BlockHandle handle, BlockIdExt mas } CHECK(handle->id().seqno() > 0); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle, masterchain_block_id, + promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveImporter::apply_shard_block_cont3, std::move(handle), + masterchain_block_id, std::move(promise)); + } + }); if (!handle->merge_before() && handle->one_prev(true).shard_full() == handle->id().shard_full()) { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle, masterchain_block_id, - promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - td::actor::send_closure(SelfId, &ArchiveImporter::apply_shard_block_cont3, std::move(handle), - masterchain_block_id, std::move(promise)); - } - }); apply_shard_block(handle->one_prev(true), masterchain_block_id, std::move(P)); } else { td::MultiPromise mp; auto ig = mp.init_guard(); - ig.add_promise(std::move(promise)); + ig.add_promise(std::move(P)); check_shard_block_applied(handle->one_prev(true), ig.get_promise()); if (handle->merge_before()) { check_shard_block_applied(handle->one_prev(false), ig.get_promise()); @@ -335,15 +356,12 @@ void ArchiveImporter::check_shard_block_applied(BlockIdExt block_id, td::Promise } void ArchiveImporter::abort_query(td::Status error) { - if (promise_) { - promise_.set_error(std::move(error)); - td::unlink(path_).ensure(); - } - stop(); + LOG(INFO) << error; + finish_query(); } void ArchiveImporter::finish_query() { if (promise_) { - promise_.set_value(std::vector(state_->get_block_id().seqno(), max_shard_client_seqno_)); + promise_.set_value(std::vector{state_->get_seqno(), shard_client_seqno_}); td::unlink(path_).ensure(); } stop(); diff --git a/validator/import-db-slice.hpp b/validator/import-db-slice.hpp index 5b7b1f1d..b7f383d8 100644 --- a/validator/import-db-slice.hpp +++ b/validator/import-db-slice.hpp @@ -25,6 +25,7 @@ class ArchiveImporter : public td::actor::Actor { void checked_all_masterchain_blocks(BlockSeqno seqno); void check_next_shard_client_seqno(BlockSeqno seqno); + void checked_shard_client_seqno(BlockSeqno seqno); void got_masterchain_state(td::Ref state); void apply_shard_block(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise); void apply_shard_block_cont1(BlockHandle handle, BlockIdExt masterchain_block_id, td::Promise promise); @@ -36,7 +37,6 @@ class ArchiveImporter : public td::actor::Actor { std::string path_; td::Ref state_; BlockSeqno shard_client_seqno_; - BlockSeqno max_shard_client_seqno_; td::Ref opts_; diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index d2a7b861..80366875 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -414,7 +414,8 @@ void ValidatorManagerMasterchainStarter::got_key_block_handle(BlockHandle handle void ValidatorManagerMasterchainStarter::got_shard_block_id(BlockIdExt block_id) { client_block_id_ = block_id; - finish(); + start_shard_client(); + return; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { R.ensure(); @@ -436,7 +437,7 @@ void ValidatorManagerMasterchainStarter::got_hardforks(std::vector v return; } } - finish(); + start_shard_client(); return; } if (h.size() > vec.size() + 1) { @@ -565,13 +566,20 @@ void ValidatorManagerMasterchainStarter::truncated() { 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(SelfId, &ValidatorManagerMasterchainStarter::start_shard_client); }); td::actor::send_closure(db_, &Db::update_hardforks, opts_->get_hardforks(), std::move(P)); } +void ValidatorManagerMasterchainStarter::start_shard_client() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::finish); + }); + client_ = td::actor::create_actor("shardclient", opts_, manager_, 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 e8c2ad4b..49e7562c 100644 --- a/validator/manager-init.hpp +++ b/validator/manager-init.hpp @@ -105,6 +105,7 @@ class ValidatorManagerMasterchainStarter : public td::actor::Actor { void got_prev_key_block_handle(BlockHandle handle); void truncated(); void written_next(); + void start_shard_client(); void finish(); private: diff --git a/validator/manager.cpp b/validator/manager.cpp index 4a627196..17bf449a 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1414,7 +1414,6 @@ void ValidatorManagerImpl::started(ValidatorManagerInitResult R) { gc_masterchain_state_ = std::move(R.gc_state); shard_client_ = std::move(R.clients); - td::actor::send_closure(shard_client_, &ShardClient::start); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { @@ -1429,8 +1428,6 @@ void ValidatorManagerImpl::started(ValidatorManagerInitResult R) { }); td::actor::send_closure(db_, &Db::get_destroyed_validator_sessions, std::move(P)); - - send_peek_key_block_request(); } void ValidatorManagerImpl::read_gc_list(std::vector list) { @@ -1443,6 +1440,104 @@ void ValidatorManagerImpl::read_gc_list(std::vector list) { serializer_ = td::actor::create_actor("serializer", last_key_block_handle_->id(), opts_, actor_id(this)); + if (!out_of_sync()) { + completed_prestart_sync(); + } else { + prestart_sync(); + } +} + +bool ValidatorManagerImpl::out_of_sync() { + if (last_masterchain_block_handle_->unix_time() + 600 > td::Clocks::system()) { + return false; + } + + if (validator_groups_.size() > 0 && last_known_key_block_handle_->id().seqno() <= last_masterchain_seqno_) { + return false; + } + + return true; +} + +void ValidatorManagerImpl::prestart_sync() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + }); + td::actor::send_closure(db_, &Db::set_async_mode, false, std::move(P)); +} + +void ValidatorManagerImpl::download_next_archive() { + if (!out_of_sync()) { + finish_prestart_sync(); + return; + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + LOG(INFO) << "failed to download archive slice: " << R.error(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + } else { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok()); + } + }); + callback_->download_archive(shard_client_handle_->id().seqno() + 1, db_root_ + "/tmp/", td::Timestamp::in(3600.0), + std::move(P)); +} + +void ValidatorManagerImpl::downloaded_archive_slice(std::string name) { + LOG(INFO) << "downloaded archive slice: " << name; + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + LOG(INFO) << "failed to check downloaded archive slice: " << R.error(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + } else { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.move_as_ok()); + } + }); + + td::actor::create_actor("archiveimport", name, last_masterchain_state_, + shard_client_handle_->id().seqno(), opts_, actor_id(this), std::move(P)) + .release(); +} + +void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) { + CHECK(seqno.size() == 2); + LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1]; + CHECK(seqno[0] <= last_masterchain_seqno_); + + BlockIdExt b; + CHECK(last_masterchain_state_->get_old_mc_block_id(seqno[1], b)); + + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), db = db_.get(), client = shard_client_.get()](td::Result R) { + R.ensure(); + auto handle = R.move_as_ok(); + auto P = td::PromiseCreator::lambda([SelfId, client, handle](td::Result> R) mutable { + auto P = td::PromiseCreator::lambda([SelfId](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + }); + td::actor::send_closure(client, &ShardClient::force_update_shard_client_ex, std::move(handle), + td::Ref{R.move_as_ok()}, std::move(P)); + }); + td::actor::send_closure(db, &Db::get_block_state, std::move(handle), std::move(P)); + }); + get_block_handle(b, true, std::move(P)); +} + +void ValidatorManagerImpl::finish_prestart_sync() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::completed_prestart_sync); + }); + td::actor::send_closure(db_, &Db::set_async_mode, false, std::move(P)); +} + +void ValidatorManagerImpl::completed_prestart_sync() { + td::actor::send_closure(shard_client_, &ShardClient::start); + + send_peek_key_block_request(); + LOG(WARNING) << "initial read complete: " << last_masterchain_block_handle_->id() << " " << last_masterchain_block_id_; callback_->initial_read_complete(last_masterchain_block_handle_); @@ -1979,11 +2074,6 @@ void ValidatorManagerImpl::alarm() { } log_status_at_ = td::Timestamp::in(60.0); } - if (false && !downloading_archive_slice_ && shard_client_handle_ && - shard_client_handle_->unix_time() + 600 <= td::Clocks::system() && next_download_archive_slice_at_.is_in_past()) { - next_download_archive_slice_at_ = td::Timestamp::in(10.0); - try_download_archive_slice(); - } alarm_timestamp().relax(log_status_at_); if (resend_shard_blocks_at_ && resend_shard_blocks_at_.is_in_past()) { resend_shard_blocks_at_ = td::Timestamp::never(); @@ -2053,49 +2143,6 @@ void ValidatorManagerImpl::try_get_static_file(FileHash file_hash, td::Promise R) { - if (R.is_error()) { - LOG(INFO) << "failed to download archive slice: " << R.error(); - td::actor::send_closure(SelfId, &ValidatorManagerImpl::failed_to_download_archive_slice); - } else { - td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok()); - } - }); - callback_->download_archive(shard_client_handle_->id().seqno(), db_root_ + "/tmp/", td::Timestamp::in(3600.0), - std::move(P)); -} - -void ValidatorManagerImpl::failed_to_download_archive_slice() { - downloading_archive_slice_ = false; -} - -void ValidatorManagerImpl::downloaded_archive_slice(std::string name) { - LOG(INFO) << "downloaded archive slice: " << name; - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { - if (R.is_error()) { - LOG(INFO) << "failed to check downloaded archive slice: " << R.error(); - td::actor::send_closure(SelfId, &ValidatorManagerImpl::failed_to_download_archive_slice); - } else { - td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.move_as_ok()); - } - }); - - td::actor::create_actor("archiveimport", name, last_masterchain_state_, - shard_client_handle_->id().seqno(), opts_, actor_id(this), std::move(P)) - .release(); -} - -void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) { - CHECK(seqno.size() == 2); - LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1]; - downloading_archive_slice_ = false; - next_download_archive_slice_at_ = td::Timestamp::in(10.0); -} - void ValidatorManagerImpl::get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) { td::actor::send_closure(db_, &Db::get_archive_id, masterchain_seqno, std::move(promise)); } diff --git a/validator/manager.hpp b/validator/manager.hpp index 2177766e..b0c64c96 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -261,6 +261,14 @@ class ValidatorManagerImpl : public ValidatorManager { void update_gc_block_handle(BlockHandle handle, td::Promise promise) override; void update_shard_client_block_handle(BlockHandle handle, td::Promise promise) override; + bool out_of_sync(); + void prestart_sync(); + void download_next_archive(); + void downloaded_archive_slice(std::string name); + void checked_archive_slice(std::vector seqno); + void finish_prestart_sync(); + void completed_prestart_sync(); + public: void install_callback(std::unique_ptr new_callback, td::Promise promise) override { callback_ = std::move(new_callback); @@ -427,10 +435,6 @@ class ValidatorManagerImpl : public ValidatorManager { void get_async_serializer_state(td::Promise promise) override; void try_get_static_file(FileHash file_hash, td::Promise promise) override; - void try_download_archive_slice(); - void downloaded_archive_slice(std::string name); - void checked_archive_slice(std::vector seqno); - void failed_to_download_archive_slice(); void get_download_token(size_t download_size, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override { @@ -558,9 +562,6 @@ class ValidatorManagerImpl : public ValidatorManager { bool started_ = false; bool allow_validate_ = false; - bool downloading_archive_slice_ = false; - td::Timestamp next_download_archive_slice_at_ = td::Timestamp::now(); - private: double state_ttl() const { return opts_->state_ttl(); diff --git a/validator/shard-client.cpp b/validator/shard-client.cpp index a609dcf9..662ba172 100644 --- a/validator/shard-client.cpp +++ b/validator/shard-client.cpp @@ -40,11 +40,9 @@ void ShardClient::start_up() { } void ShardClient::start() { - if (!started_ && masterchain_state_.not_null()) { - started_ = true; - apply_all_shards(); - } else { + if (!started_) { started_ = true; + saved_to_db(); } } @@ -52,7 +50,31 @@ void ShardClient::got_state_from_db(BlockIdExt state) { CHECK(!init_mode_); CHECK(state.is_valid()); - new_masterchain_block_id(state); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ShardClient::got_init_handle_from_db, R.move_as_ok()); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, state, true, std::move(P)); +} + +void ShardClient::got_init_handle_from_db(BlockHandle handle) { + masterchain_block_handle_ = std::move(handle); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &ShardClient::got_init_state_from_db, td::Ref{R.move_as_ok()}); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_state_from_db, masterchain_block_handle_, + std::move(P)); +} + +void ShardClient::got_init_state_from_db(td::Ref state) { + masterchain_state_ = std::move(state); + build_shard_overlays(); + masterchain_state_.clear(); + + saved_to_db(); } void ShardClient::start_up_init_mode() { @@ -97,14 +119,19 @@ void ShardClient::applied_all_shards() { } void ShardClient::saved_to_db() { - if (init_mode_) { - promise_.set_value(td::Unit()); - init_mode_ = false; - } - CHECK(masterchain_block_handle_); td::actor::send_closure(manager_, &ValidatorManager::update_shard_client_block_handle, masterchain_block_handle_, [](td::Unit) {}); + if (promise_) { + promise_.set_value(td::Unit()); + } + if (init_mode_) { + init_mode_ = false; + } + + if (!started_) { + return; + } if (masterchain_block_handle_->inited_next_left()) { new_masterchain_block_id(masterchain_block_handle_->one_next(true)); } else { @@ -239,6 +266,40 @@ void ShardClient::build_shard_overlays() { } } +void ShardClient::force_update_shard_client(BlockHandle handle, td::Promise promise) { + CHECK(!init_mode_); + CHECK(!started_); + + if (masterchain_block_handle_->id().seqno() >= handle->id().seqno()) { + promise.set_value(td::Unit()); + return; + } + + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), handle, promise = std::move(promise)](td::Result> R) mutable { + R.ensure(); + td::actor::send_closure(SelfId, &ShardClient::force_update_shard_client_ex, std::move(handle), + td::Ref{R.move_as_ok()}, std::move(promise)); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_state_from_db, std::move(handle), std::move(P)); +} + +void ShardClient::force_update_shard_client_ex(BlockHandle handle, td::Ref state, + td::Promise promise) { + CHECK(!init_mode_); + CHECK(!started_); + + if (masterchain_block_handle_->id().seqno() >= handle->id().seqno()) { + promise.set_value(td::Unit()); + return; + } + masterchain_block_handle_ = std::move(handle); + masterchain_state_ = std::move(state); + promise_ = std::move(promise); + build_shard_overlays(); + applied_all_shards(); +} + } // namespace validator } // namespace ton diff --git a/validator/shard-client.hpp b/validator/shard-client.hpp index 9ad62da2..960f2514 100644 --- a/validator/shard-client.hpp +++ b/validator/shard-client.hpp @@ -55,8 +55,9 @@ class ShardClient : public td::actor::Actor { , promise_(std::move(promise)) { init_mode_ = true; } - ShardClient(td::Ref opts, td::actor::ActorId manager) - : opts_(std::move(opts)), manager_(manager) { + ShardClient(td::Ref opts, td::actor::ActorId manager, + td::Promise promise) + : opts_(std::move(opts)), manager_(manager), promise_(std::move(promise)) { } static constexpr td::uint32 shard_client_priority() { @@ -70,6 +71,8 @@ class ShardClient : public td::actor::Actor { void start_up_init_mode_finished(); void start(); void got_state_from_db(BlockIdExt masterchain_block_id); + void got_init_handle_from_db(BlockHandle handle); + void got_init_state_from_db(td::Ref state); void im_download_shard_state(BlockIdExt block_id, td::Promise promise); void im_downloaded_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise promise); @@ -91,6 +94,9 @@ class ShardClient : public td::actor::Actor { void get_processed_masterchain_block(td::Promise promise); void get_processed_masterchain_block_id(td::Promise promise); + + void force_update_shard_client(BlockHandle handle, td::Promise promise); + void force_update_shard_client_ex(BlockHandle handle, td::Ref state, td::Promise promise); }; } // namespace validator