1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-13 03:32:22 +00:00

Add CollatorNode and make validators request blocks from it

This commit is contained in:
SpyCheese 2022-07-20 15:39:50 +03:00
parent 996c23e506
commit 53270a00e6
25 changed files with 577 additions and 33 deletions

View file

@ -739,6 +739,10 @@ _ OracleBridgeParams = ConfigParam 71; // Ethereum bridge
_ OracleBridgeParams = ConfigParam 72; // Binance Smart Chain bridge
_ OracleBridgeParams = ConfigParam 73; // Polygon bridge
// Set of collators: each collator is (workchain:int32 shard:int64 adnl_id:int256)
colator_set#a0 collators:(HashmapE 352 Unit) = CollatorSet;
_ CollatorSet = ConfigParam 81;
//
// PROOFS
//

View file

@ -253,6 +253,10 @@ class PublicKey {
td::BufferSlice export_as_slice() const;
static td::Result<PublicKey> import(td::Slice s);
bool is_ed25519() const {
return pub_key_.get_offset() == pub_key_.offset<pubkeys::Ed25519>();
}
pubkeys::Ed25519 ed25519_value() const {
CHECK(pub_key_.get_offset() == pub_key_.offset<pubkeys::Ed25519>());
return pub_key_.get<pubkeys::Ed25519>();

View file

@ -572,6 +572,7 @@ engine.dht id:int256 = engine.Dht;
engine.validatorTempKey key:int256 expire_at:int = engine.ValidatorTempKey;
engine.validatorAdnlAddress id:int256 expire_at:int = engine.ValidatorAdnlAddress;
engine.validator id:int256 temp_keys:(vector engine.validatorTempKey) adnl_addrs:(vector engine.validatorAdnlAddress) election_date:int expire_at:int = engine.Validator;
engine.collator adnl_id:int256 workchain:int shard:long = engine.Validator;
engine.liteServer id:int256 port:int = engine.LiteServer;
engine.controlProcess id:int256 permissions:int = engine.ControlProcess;
engine.controlInterface id:int256 port:int allowed:(vector engine.controlProcess) = engine.ControlInterface;
@ -582,7 +583,7 @@ engine.validator.fullNodeMaster port:int adnl:int256 = engine.validator.FullNode
engine.validator.fullNodeSlave ip:int port:int adnl:PublicKey = engine.validator.FullNodeSlave;
engine.validator.config out_port:int addrs:(vector engine.Addr) adnl:(vector engine.adnl)
dht:(vector engine.dht)
validators:(vector engine.validator) fullnode:int256 fullnodeslaves:(vector engine.validator.fullNodeSlave)
validators:(vector engine.Validator) fullnode:int256 fullnodeslaves:(vector engine.validator.fullNodeSlave)
fullnodemasters:(vector engine.validator.fullNodeMaster)
liteservers:(vector engine.liteServer) control:(vector engine.controlInterface)
gc:engine.gc = engine.validator.Config;
@ -690,6 +691,8 @@ engine.validator.generateBlockCandidate block_id:tonNode.BlockId = db.Candidate;
engine.validator.getRequiredBlockCandidates = engine.validator.RequiredBlockCandidates;
engine.validator.importBlockCandidate block:db.candidate = engine.validator.Success;
engine.validator.addCollator adnl_id:int256 workchain:int shard:long = engine.validator.Success;
---types---
storage.pong = storage.Pong;
@ -741,3 +744,10 @@ validatorSession.statsRound timestamp:long producers:(vector validatorSession.st
validatorSession.stats id:tonNode.blockId timestamp:long self:int256 creator:int256 total_validators:int total_weight:long
signatures:int signatures_weight:long approve_signatures:int approve_signatures_weight:long
first_round:int rounds:(vector validatorSession.statsRound) = validatorSession.Stats;
collatorNode.generateBlockSuccess candidate:db.Candidate = collatorNode.GenerateBlockResult;
collatorNode.generateBlockError code:int message:string = collatorNode.GenerateBlockResult;
---functions---
collatorNode.generateBlock workchain:int shard:long min_mc_id:tonNode.blockIdExt prev_blocks:(vector tonNode.blockIdExt)
creator:int256 = collatorNode.GenerateBlockResult;

Binary file not shown.

View file

@ -397,6 +397,9 @@ struct Ed25519_PublicKey {
bool operator==(const Ed25519_PublicKey& other) const {
return _pubkey == other._pubkey;
}
bool operator!=(const Ed25519_PublicKey& other) const {
return _pubkey != other._pubkey;
}
bool clear() {
_pubkey.set_zero();
return true;
@ -479,4 +482,9 @@ struct ValidatorSessionConfig {
static const td::uint32 BLOCK_HASH_COVERS_DATA_FROM_VERSION = 2;
};
struct CollatorNodeDescr {
ShardIdFull shard;
NodeIdShort adnl_id;
};
} // namespace ton

View file

@ -1091,3 +1091,24 @@ td::Status ImportBlockCandidateQuery::receive(td::BufferSlice data) {
td::TerminalIO::out() << "successfully imported a block candidate\n";
return td::Status::OK();
}
td::Status AddCollatorQuery::run() {
TRY_RESULT_ASSIGN(adnl_id_, tokenizer_.get_token<ton::PublicKeyHash>());
TRY_RESULT_ASSIGN(wc_, tokenizer_.get_token<td::int32>());
TRY_RESULT_ASSIGN(shard_, tokenizer_.get_token<td::int64>());
return td::Status::OK();
}
td::Status AddCollatorQuery::send() {
auto b = ton::create_serialize_tl_object<ton::ton_api::engine_validator_addCollator>(
adnl_id_.tl(), wc_, shard_);
td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise());
return td::Status::OK();
}
td::Status AddCollatorQuery::receive(td::BufferSlice data) {
TRY_RESULT_PREFIX(f, ton::fetch_tl_object<ton::ton_api::engine_validator_success>(data.as_slice(), true),
"received incorrect answer: ");
td::TerminalIO::out() << "successfully added collator\n";
return td::Status::OK();
}

View file

@ -1163,3 +1163,27 @@ class ImportBlockCandidateQuery : public Query {
private:
std::string file_;
};
class AddCollatorQuery : public Query {
public:
AddCollatorQuery(td::actor::ActorId<ValidatorEngineConsole> console, Tokenizer tokenizer)
: Query(console, std::move(tokenizer)) {
}
td::Status run() override;
td::Status send() override;
td::Status receive(td::BufferSlice data) override;
static std::string get_name() {
return "addcollator";
}
static std::string get_help() {
return "addcollator <adnl_id> <workchain> <shard>\tadd collator with given adnl_id and shard";
}
std::string name() const override {
return get_name();
}
private:
ton::PublicKeyHash adnl_id_;
td::int32 wc_;
td::int64 shard_;
};

View file

@ -144,6 +144,7 @@ void ValidatorEngineConsole::run() {
add_query_runner(std::make_unique<QueryRunnerImpl<GenerateBlockCandidateQuery>>());
add_query_runner(std::make_unique<QueryRunnerImpl<GetRequiredBlockCandidatesQuery>>());
add_query_runner(std::make_unique<QueryRunnerImpl<ImportBlockCandidateQuery>>());
add_query_runner(std::make_unique<QueryRunnerImpl<AddCollatorQuery>>());
}
bool ValidatorEngineConsole::envelope_send_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise) {

View file

@ -125,15 +125,24 @@ Config::Config(ton::ton_api::engine_validator_config &config) {
for (auto &dht : config.dht_) {
config_add_dht_node(ton::PublicKeyHash{dht->id_}).ensure();
}
for (auto &val : config.validators_) {
auto key = ton::PublicKeyHash{val->id_};
config_add_validator_permanent_key(key, val->election_date_, val->expire_at_).ensure();
for (auto &temp : val->temp_keys_) {
for (auto &v : config.validators_) {
ton::ton_api::downcast_call(
*v, td::overloaded(
[&](ton::ton_api::engine_validator &val) {
auto key = ton::PublicKeyHash{val.id_};
config_add_validator_permanent_key(key, val.election_date_, val.expire_at_).ensure();
for (auto &temp : val.temp_keys_) {
config_add_validator_temp_key(key, ton::PublicKeyHash{temp->key_}, temp->expire_at_).ensure();
}
for (auto &adnl : val->adnl_addrs_) {
for (auto &adnl : val.adnl_addrs_) {
config_add_validator_adnl_id(key, ton::PublicKeyHash{adnl->id_}, adnl->expire_at_).ensure();
}
},
[&](ton::ton_api::engine_collator &col) {
auto key = ton::PublicKeyHash{col.adnl_id_};
ton::ShardIdFull shard(col.workchain_, col.shard_);
config_add_collator(key, shard).ensure();
}));
}
config_add_full_node_adnl_id(ton::PublicKeyHash{config.fullnode_}).ensure();
@ -192,7 +201,7 @@ ton::tl_object_ptr<ton::ton_api::engine_validator_config> Config::tl() const {
dht_vec.push_back(ton::create_tl_object<ton::ton_api::engine_dht>(x.tl()));
}
std::vector<ton::tl_object_ptr<ton::ton_api::engine_validator>> val_vec;
std::vector<ton::tl_object_ptr<ton::ton_api::engine_Validator>> val_vec;
for (auto &val : validators) {
std::vector<ton::tl_object_ptr<ton::ton_api::engine_validatorTempKey>> temp_vec;
for (auto &t : val.second.temp_keys) {
@ -205,6 +214,10 @@ ton::tl_object_ptr<ton::ton_api::engine_validator_config> Config::tl() const {
val_vec.push_back(ton::create_tl_object<ton::ton_api::engine_validator>(
val.first.tl(), std::move(temp_vec), std::move(adnl_val_vec), val.second.election_date, val.second.expire_at));
}
for (auto &col : collators) {
val_vec.push_back(ton::create_tl_object<ton::ton_api::engine_collator>(
col.adnl_id.tl(), col.shard.workchain, col.shard.shard));
}
std::vector<ton::tl_object_ptr<ton::ton_api::engine_validator_fullNodeSlave>> full_node_slaves_vec;
for (auto &x : full_node_slaves) {
@ -383,6 +396,18 @@ td::Result<bool> Config::config_add_validator_adnl_id(ton::PublicKeyHash perm_ke
}
}
td::Result<bool> Config::config_add_collator(ton::PublicKeyHash addr, ton::ShardIdFull shard) {
if (!shard.is_valid_ext()) {
return td::Status::Error(PSTRING() << "invalid shard: " << shard.to_str());
}
Collator c{addr, shard};
if (std::find(collators.begin(), collators.end(), c) != collators.end()) {
return false;
}
collators.push_back(c);
return true;
}
td::Result<bool> Config::config_add_full_node_adnl_id(ton::PublicKeyHash id) {
if (full_node == id) {
return false;
@ -1836,6 +1861,19 @@ void ValidatorEngine::start_lite_server() {
}
void ValidatorEngine::started_lite_server() {
start_collator();
}
void ValidatorEngine::start_collator() {
for (auto &c : config_.collators) {
td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_collator,
ton::adnl::AdnlNodeIdShort(c.adnl_id), c.shard);
}
started_collator();
}
void ValidatorEngine::started_collator() {
start_control_interface();
}
@ -3395,6 +3433,47 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_importBlo
});
}
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_addCollator &query,
td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm,
td::Promise<td::BufferSlice> promise) {
if (!(perm & ValidatorEnginePermissions::vep_modify)) {
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized")));
return;
}
if (!started_) {
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started")));
return;
}
auto id = ton::PublicKeyHash{query.adnl_id_};
auto shard = ton::ShardIdFull(query.workchain_, query.shard_);
if (!shard.is_valid_ext()) {
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "invalid shard")));
return;
}
auto R = config_.config_add_collator(id, shard);
if (R.is_error()) {
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized")));
return;
}
if (!R.move_as_ok()) {
promise.set_value(ton::serialize_tl_object(ton::create_tl_object<ton::ton_api::engine_validator_success>(), true));
return;
}
if (!validator_manager_.empty()) {
td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::add_collator,
ton::adnl::AdnlNodeIdShort(id), shard);
}
write_config([promise = std::move(promise)](td::Result<td::Unit> R) mutable {
if (R.is_error()) {
promise.set_value(create_control_query_error(R.move_as_error()));
} else {
promise.set_value(ton::serialize_tl_object(ton::create_tl_object<ton::ton_api::engine_validator_success>(), true));
}
});
}
void ValidatorEngine::process_control_query(td::uint16 port, ton::adnl::AdnlNodeIdShort src,
ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) {

View file

@ -66,6 +66,14 @@ struct Config {
ton::UnixTime election_date;
ton::UnixTime expire_at;
};
struct Collator {
ton::PublicKeyHash adnl_id;
ton::ShardIdFull shard;
bool operator==(const Collator& b) const {
return adnl_id == b.adnl_id && shard == b.shard;
}
};
struct Control {
ton::PublicKeyHash key;
std::map<ton::PublicKeyHash, td::uint32> clients;
@ -81,6 +89,7 @@ struct Config {
std::map<ton::PublicKeyHash, AdnlCategory> adnl_ids;
std::set<ton::PublicKeyHash> dht_ids;
std::map<ton::PublicKeyHash, Validator> validators;
std::vector<Collator> collators;
ton::PublicKeyHash full_node = ton::PublicKeyHash::zero();
std::vector<FullNodeSlave> full_node_slaves;
std::map<td::int32, ton::PublicKeyHash> full_node_masters;
@ -104,6 +113,7 @@ struct Config {
ton::UnixTime expire_at);
td::Result<bool> config_add_validator_adnl_id(ton::PublicKeyHash perm_key, ton::PublicKeyHash adnl_id,
ton::UnixTime expire_at);
td::Result<bool> config_add_collator(ton::PublicKeyHash addr, ton::ShardIdFull shard);
td::Result<bool> config_add_full_node_adnl_id(ton::PublicKeyHash id);
td::Result<bool> config_add_full_node_slave(td::IPAddress addr, ton::PublicKey id);
td::Result<bool> config_add_full_node_master(td::int32 port, ton::PublicKeyHash id);
@ -293,6 +303,8 @@ class ValidatorEngine : public td::actor::Actor {
void add_lite_server(ton::PublicKeyHash id, td::uint16 port);
void start_lite_server();
void started_lite_server();
void start_collator();
void started_collator();
void add_control_interface(ton::PublicKeyHash id, td::uint16 port);
void add_control_process(ton::PublicKeyHash id, td::uint16 port, ton::PublicKeyHash pub, td::int32 permissions);
@ -419,6 +431,8 @@ class ValidatorEngine : public td::actor::Actor {
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
void run_control_query(ton::ton_api::engine_validator_importBlockCandidate &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
void run_control_query(ton::ton_api::engine_validator_addCollator &query, td::BufferSlice data,
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
template <class T>
void run_control_query(T &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm,
td::Promise<td::BufferSlice> promise) {

View file

@ -53,6 +53,7 @@ set(VALIDATOR_HEADERS
import-db-slice.hpp
collator-node.hpp
manager-disk.h
manager-disk.hpp
manager-init.h
@ -68,6 +69,7 @@ set(VALIDATOR_HEADERS
set(VALIDATOR_SOURCE
apply-block.cpp
block-handle.cpp
collator-node.cpp
get-next-key-blocks.cpp
import-db-slice.cpp
shard-client.cpp

145
validator/collator-node.cpp Normal file
View file

@ -0,0 +1,145 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "collator-node.hpp"
#include "ton/ton-tl.hpp"
#include "fabric.h"
namespace ton {
namespace validator {
CollatorNode::CollatorNode(adnl::AdnlNodeIdShort local_id, td::actor::ActorId<ValidatorManager> manager,
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<rldp::Rldp> rldp)
: local_id_(local_id), manager_(std::move(manager)), adnl_(std::move(adnl)), rldp_(std::move(rldp)) {
}
void CollatorNode::start_up() {
class Cb : public adnl::Adnl::Callback {
public:
Cb(td::actor::ActorId<CollatorNode> id) : id_(std::move(id)) {
}
void receive_message(adnl::AdnlNodeIdShort src, adnl::AdnlNodeIdShort dst, td::BufferSlice data) override {
}
void receive_query(adnl::AdnlNodeIdShort src, adnl::AdnlNodeIdShort dst, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) override {
td::actor::send_closure(id_, &CollatorNode::receive_query, src, std::move(data), std::move(promise));
}
private:
td::actor::ActorId<CollatorNode> id_;
};
td::actor::send_closure(adnl_, &adnl::Adnl::subscribe, local_id_,
adnl::Adnl::int_to_bytestring(ton_api::collatorNode_generateBlock::ID),
std::make_unique<Cb>(actor_id(this)));
td::actor::send_closure(rldp_, &rldp::Rldp::add_id, adnl::AdnlNodeIdShort(local_id_));
}
void CollatorNode::tear_down() {
td::actor::send_closure(adnl_, &adnl::Adnl::unsubscribe, local_id_,
adnl::Adnl::int_to_bytestring(ton_api::collatorNode_generateBlock::ID));
}
void CollatorNode::add_shard(ShardIdFull shard) {
LOG(INFO) << "Collator node: local_id=" << local_id_ << " , shard=" << shard.to_str();
shards_.push_back(shard);
}
static td::BufferSlice serialize_error(td::Status error) {
return create_serialize_tl_object<ton_api::collatorNode_generateBlockError>(error.code(), error.message().c_str());
}
void CollatorNode::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data,
td::Promise<td::BufferSlice> promise) {
auto status = [&]() -> td::Status {
TRY_RESULT(f, fetch_tl_object<ton_api::collatorNode_generateBlock>(std::move(data), true));
ShardIdFull shard(f->workchain_, f->shard_);
if (!shard.is_valid_ext()) {
return td::Status::Error(PSTRING() << "invalid shard " << shard.to_str());
}
bool found = false;
for (ShardIdFull our_shard : shards_) {
if (shard_is_ancestor(shard, our_shard)) {
found = true;
break;
}
}
if (!found) {
return td::Status::Error(PSTRING() << "this node doesn't collate shard " << shard.to_str());
}
if (f->prev_blocks_.size() != 1 && f->prev_blocks_.size() != 2) {
return td::Status::Error(PSTRING() << "invalid size of prev_blocks: " << f->prev_blocks_.size());
}
std::vector<BlockIdExt> prev_blocks;
for (const auto& b : f->prev_blocks_) {
prev_blocks.push_back(create_block_id(b));
}
BlockIdExt min_mc_id = create_block_id(f->min_mc_id_);
Ed25519_PublicKey creator(f->creator_);
LOG(INFO) << "Query from " << src << ": shard=" << shard.to_str() << ", min_mc_id=" << min_mc_id.to_str();
auto P = td::PromiseCreator::lambda([=, SelfId = actor_id(this), prev_blocks = std::move(prev_blocks),
promise = std::move(promise)](td::Result<td::Ref<ShardState>> R) mutable {
if (R.is_error()) {
LOG(WARNING) << "Query from " << src << ", error: " << R.error();
promise.set_result(serialize_error(R.move_as_error_prefix("failed to get masterchain state: ")));
} else {
td::Ref<MasterchainState> state(R.move_as_ok());
if (state.is_null()) {
LOG(WARNING) << "Query from " << src << ", error: failed to get masterchain state";
promise.set_result(serialize_error(R.move_as_error_prefix("failed to get masterchain state: ")));
return;
}
td::actor::send_closure(SelfId, &CollatorNode::receive_query_cont, src, shard, std::move(state),
std::move(prev_blocks), creator, std::move(promise));
}
});
td::actor::send_closure(manager_, &ValidatorManager::wait_block_state_short, min_mc_id, 1, td::Timestamp::in(5.0),
std::move(P));
return td::Status::OK();
}();
if (status.is_error()) {
LOG(WARNING) << "Query from " << src << ", error: " << status;
promise.set_result(serialize_error(std::move(status)));
}
}
void CollatorNode::receive_query_cont(adnl::AdnlNodeIdShort src, ShardIdFull shard,
td::Ref<MasterchainState> min_mc_state, std::vector<BlockIdExt> prev_blocks,
Ed25519_PublicKey creator, td::Promise<td::BufferSlice> promise) {
auto P = td::PromiseCreator::lambda([promise = std::move(promise), src](td::Result<BlockCandidate> R) mutable {
if (R.is_error()) {
LOG(WARNING) << "Query from " << src << ", error: " << R.error();
promise.set_result(serialize_error(R.move_as_error()));
} else {
LOG(INFO) << "Query from " << src << ", success";
auto block = R.move_as_ok();
auto result = create_serialize_tl_object<ton_api::collatorNode_generateBlockSuccess>(
create_tl_object<ton_api::db_candidate>(PublicKey{pubkeys::Ed25519{block.pubkey.as_bits256()}}.tl(),
create_tl_block_id(block.id), std::move(block.data),
std::move(block.collated_data)));
promise.set_result(std::move(result));
}
});
run_collate_query(shard, min_mc_state->get_block_id(), std::move(prev_blocks), creator,
min_mc_state->get_validator_set(shard), manager_, td::Timestamp::in(10.0), std::move(P));
}
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,51 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "interfaces/validator-manager.h"
#include "rldp/rldp.h"
namespace ton {
namespace validator {
class ValidatorManager;
class CollatorNode : public td::actor::Actor {
public:
CollatorNode(adnl::AdnlNodeIdShort local_id, td::actor::ActorId<ValidatorManager> manager,
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<rldp::Rldp> rldp);
void start_up() override;
void tear_down() override;
void add_shard(ShardIdFull shard);
private:
void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice data, td::Promise<td::BufferSlice> promise);
void receive_query_cont(adnl::AdnlNodeIdShort src, ShardIdFull shard, td::Ref<MasterchainState> min_mc_state,
std::vector<BlockIdExt> prev_blocks, Ed25519_PublicKey creator,
td::Promise<td::BufferSlice> promise);
adnl::AdnlNodeIdShort local_id_;
td::actor::ActorId<ValidatorManager> manager_;
td::actor::ActorId<adnl::Adnl> adnl_;
td::actor::ActorId<rldp::Rldp> rldp_;
std::vector<ShardIdFull> shards_;
};
} // namespace validator
} // namespace ton

View file

@ -364,7 +364,8 @@ td::Status MasterchainStateQ::mc_init() {
td::Status MasterchainStateQ::mc_reinit() {
auto res = block::ConfigInfo::extract_config(
root_cell(), block::ConfigInfo::needStateRoot | block::ConfigInfo::needValidatorSet |
block::ConfigInfo::needShardHashes | block::ConfigInfo::needPrevBlocks);
block::ConfigInfo::needShardHashes | block::ConfigInfo::needPrevBlocks |
block::ConfigInfo::needWorkchainInfo);
cur_validators_.reset();
next_validators_.reset();
if (res.is_error()) {
@ -510,6 +511,27 @@ bool MasterchainStateQ::check_old_mc_block_id(const ton::BlockIdExt& blkid, bool
return config_ && config_->check_old_mc_block_id(blkid, strict);
}
std::vector<CollatorNodeDescr> MasterchainStateQ::get_collator_set() const {
block::gen::CollatorSet::Record rec;
auto cell = config_->get_config_param(81);
if (cell.is_null() || !tlb::unpack_cell(std::move(cell), rec)) {
return {};
}
vm::Dictionary dict{rec.collators->prefetch_ref(), 32 + 64 + 256};
std::vector<CollatorNodeDescr> collators;
dict.check_for_each([&](Ref<vm::CellSlice>, td::ConstBitPtr key, int n) {
CHECK(n == 32 + 64 + 256);
auto workchain = (td::int32)key.get_int(32);
key.advance(32);
td::uint64 shard = key.get_uint(64);
key.advance(64);
td::Bits256 adnl_id(key);
collators.push_back({ShardIdFull(workchain, shard), adnl_id});
return true;
});
return collators;
}
td::uint32 MasterchainStateQ::min_split_depth(WorkchainId workchain_id) const {
if (!config_) {
return 0;

View file

@ -147,6 +147,10 @@ class MasterchainStateQ : public MasterchainState, public ShardStateQ {
return td::make_ref<ConfigHolderQ>(config_);
}
}
block::WorkchainSet get_workchain_list() const override {
return config_ ? config_->get_workchain_list() : block::WorkchainSet();
}
std::vector<CollatorNodeDescr> get_collator_set() const override;
private:
ZeroStateIdExt zerostate_id_;

View file

@ -448,7 +448,6 @@ bool ValidateQuery::init_parse() {
return reject_query("after_merge value mismatch in block header");
}
rand_seed_ = extra.rand_seed;
created_by_ = extra.created_by;
if (created_by_ != extra.created_by) {
return reject_query("block candidate "s + id_.to_str() + " has creator " + created_by_.to_hex() +
" but the block header contains different value " + extra.created_by.to_hex());

View file

@ -81,6 +81,8 @@ class MasterchainState : virtual public ShardState {
ton::LogicalTime* end_lt = nullptr) const = 0;
virtual bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const = 0;
virtual td::Result<td::Ref<ConfigHolder>> get_config_holder() const = 0;
virtual block::WorkchainSet get_workchain_list() const = 0;
virtual std::vector<CollatorNodeDescr> get_collator_set() const = 0;
virtual td::Status prepare() {
return td::Status::OK();
}

View file

@ -384,6 +384,10 @@ class ValidatorManagerImpl : public ValidatorManager {
UNREACHABLE();
}
void add_collator(adnl::AdnlNodeIdShort id, ShardIdFull shard) override {
UNREACHABLE();
}
private:
PublicKeyHash local_id_;

View file

@ -443,6 +443,9 @@ class ValidatorManagerImpl : public ValidatorManager {
UNREACHABLE();
}
void add_collator(adnl::AdnlNodeIdShort id, ShardIdFull shard) override {
UNREACHABLE();
}
private:
td::Ref<ValidatorManagerOptions> opts_;

View file

@ -2025,7 +2025,8 @@ td::actor::ActorOwn<ValidatorGroup> ValidatorManagerImpl::create_validator_group
auto validator_id = get_validator(shard, validator_set);
CHECK(!validator_id.is_zero());
auto G = td::actor::create_actor<ValidatorGroup>(
"validatorgroup", shard, validator_id, session_id, validator_set, opts, keyring_, adnl_, rldp_, overlays_,
"validatorgroup", shard, validator_id, session_id, validator_set, last_masterchain_state_->get_collator_set(),
opts, keyring_, adnl_, rldp_, overlays_,
db_root_, actor_id(this), init_session,
opts_->check_unsafe_resync_allowed(validator_set->get_catchain_seqno()), true);
return G;
@ -2692,6 +2693,15 @@ void ValidatorManagerImpl::cleanup_old_pending_candidates(BlockId block_id, td::
}
}
void ValidatorManagerImpl::add_collator(adnl::AdnlNodeIdShort id, ShardIdFull shard) {
auto it = collator_nodes_.find(id);
if (it == collator_nodes_.end()) {
auto actor = td::actor::create_actor<CollatorNode>("collatornode", id, actor_id(this), adnl_, rldp_);
it = collator_nodes_.emplace(id, std::move(actor)).first;
}
td::actor::send_closure(it->second, &CollatorNode::add_shard, shard);
}
td::actor::ActorOwn<ValidatorManagerInterface> ValidatorManagerFactory::create(
td::Ref<ValidatorManagerOptions> opts, std::string db_root, td::actor::ActorId<keyring::Keyring> keyring,
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<rldp::Rldp> rldp,

View file

@ -28,6 +28,7 @@
#include "state-serializer.hpp"
#include "rldp/rldp.h"
#include "token-manager.h"
#include "collator-node.hpp"
#include <map>
#include <set>
@ -539,6 +540,8 @@ class ValidatorManagerImpl : public ValidatorManager {
void import_block_candidate(BlockCandidate candidate, td::Promise<td::Unit> promise) override;
void wait_block_candidate(BlockId block_id, td::Timestamp timeout, td::Promise<BlockCandidate> promise) override;
void add_collator(adnl::AdnlNodeIdShort id, ShardIdFull shard) override;
private:
td::Timestamp resend_shard_blocks_at_;
td::Timestamp check_waiters_at_;
@ -606,6 +609,8 @@ class ValidatorManagerImpl : public ValidatorManager {
std::map<BlockId, std::vector<std::pair<td::Promise<BlockCandidate>, td::Timestamp>>> pending_block_candidates_;
void cleanup_old_pending_candidates(BlockId block_id, td::Timestamp now);
std::map<adnl::AdnlNodeIdShort, td::actor::ActorOwn<CollatorNode>> collator_nodes_;
};
} // namespace validator

View file

@ -248,9 +248,11 @@ void ShardClient::get_processed_masterchain_block_id(td::Promise<BlockIdExt> pro
void ShardClient::build_shard_overlays() {
auto v = masterchain_state_->get_shards();
std::set<WorkchainId> workchains;
for (auto &x : v) {
auto shard = x->shard();
workchains.insert(shard.workchain);
if (opts_->need_monitor(shard)) {
auto d = masterchain_state_->soft_min_split_depth(shard.workchain);
auto l = shard_prefix_length(shard.shard);
@ -264,6 +266,20 @@ void ShardClient::build_shard_overlays() {
}
}
}
for (const auto &wpair : masterchain_state_->get_workchain_list()) {
ton::WorkchainId wc = wpair.first;
const block::WorkchainInfo *winfo = wpair.second.get();
if (workchains.count(wc) == 0 && winfo->active && winfo->enabled_since <= masterchain_state_->get_unix_time()) {
auto shard = ShardIdFull(wc);
if (opts_->need_monitor(shard) && created_overlays_.count(shard) == 0) {
td::actor::send_closure(manager_, &ValidatorManager::subscribe_to_shard, shard);
BlockIdExt block_id(shard.workchain, shard.shard, 0, winfo->zerostate_root_hash, winfo->zerostate_file_hash);
td::actor::send_closure_later(manager_, &ValidatorManager::wait_block_state_short, block_id, 0,
td::Timestamp::in(5.0), [](td::Result<td::Ref<ShardState>>) {});
}
}
}
}
void ShardClient::force_update_shard_client(BlockHandle handle, td::Promise<td::Unit> promise) {

View file

@ -22,6 +22,7 @@
#include "td/utils/overloaded.h"
#include "common/delay.h"
#include "ton/ton-tl.hpp"
#include "td/utils/Random.h"
namespace ton {
@ -36,19 +37,7 @@ void ValidatorGroup::generate_block_candidate(td::uint32 round_id, td::Promise<B
return;
}
if (lite_mode_) {
auto P = td::PromiseCreator::lambda(
[promise = std::move(promise),
pubkey = Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}](td::Result<BlockCandidate> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error());
} else {
BlockCandidate candidate = R.move_as_ok();
candidate.pubkey = pubkey;
promise.set_result(std::move(candidate));
}
});
td::actor::send_closure(manager_, &ValidatorManager::wait_block_candidate, create_next_block_id_simple(),
td::Timestamp::in(15.0), std::move(P));
send_collate_query(round_id, td::Timestamp::in(10.0), std::move(promise));
return;
}
run_collate_query(shard_, min_masterchain_block_id_, prev_block_ids_,
@ -65,6 +54,17 @@ void ValidatorGroup::validate_block_candidate(td::uint32 round_id, BlockCandidat
promise.set_error(td::Status::Error(ErrorCode::notready, "too old"));
return;
}
if (approved_candidates_cache_round_ != round_id) {
approved_candidates_cache_round_ = round_id;
approved_candidates_cache_.clear();
}
CacheKey cache_key = block_to_cache_key(block);
auto it = approved_candidates_cache_.find(cache_key);
if (it != approved_candidates_cache_.end()) {
promise.set_result(it->second);
}
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), round_id, block = block.clone(),
promise = std::move(promise)](td::Result<ValidateCandidateResult> R) mutable {
if (R.is_error()) {
@ -80,10 +80,15 @@ void ValidatorGroup::validate_block_candidate(td::uint32 round_id, BlockCandidat
td::Timestamp::in(0.1));
} else {
auto v = R.move_as_ok();
v.visit(td::overloaded([&](UnixTime ts) { promise.set_result(ts); },
v.visit(td::overloaded(
[&](UnixTime ts) {
td::actor::send_closure(SelfId, &ValidatorGroup::update_approve_cache, round_id, block_to_cache_key(block),
ts);
promise.set_result(ts);
},
[&](CandidateReject reject) {
promise.set_error(td::Status::Error(ErrorCode::protoviolation,
PSTRING() << "bad candidate: " << reject.reason));
promise.set_error(
td::Status::Error(ErrorCode::protoviolation, PSTRING() << "bad candidate: " << reject.reason));
}));
}
});
@ -98,6 +103,14 @@ void ValidatorGroup::validate_block_candidate(td::uint32 round_id, BlockCandidat
td::Timestamp::in(10.0), std::move(P), lite_mode_ ? ValidateMode::lite : 0);
}
void ValidatorGroup::update_approve_cache(td::uint32 round_id, CacheKey key, UnixTime value) {
if (approved_candidates_cache_round_ != round_id) {
approved_candidates_cache_round_ = round_id;
approved_candidates_cache_.clear();
}
approved_candidates_cache_[key] = value;
}
void ValidatorGroup::accept_block_candidate(td::uint32 round_id, PublicKeyHash src, td::BufferSlice block_data,
RootHash root_hash, FileHash file_hash,
std::vector<BlockSignature> signatures,
@ -339,6 +352,91 @@ void ValidatorGroup::get_session_info(
td::actor::send_closure(session_, &validatorsession::ValidatorSession::get_session_info, std::move(P));
}
void ValidatorGroup::send_collate_query(td::uint32 round_id, td::Timestamp timeout,
td::Promise<BlockCandidate> promise) {
adnl::AdnlNodeIdShort collator = adnl::AdnlNodeIdShort::zero();
// TODO: some other way for storing and choosing collators for real network
int cnt = 0;
for (const CollatorNodeDescr& c : collator_set_) {
if (shard_is_ancestor(shard_, c.shard) && td::Random::fast(0, cnt) == 0) {
collator = adnl::AdnlNodeIdShort(c.adnl_id);
++cnt;
}
}
if (collator.is_zero()) {
promise.set_error(td::Status::Error(PSTRING() << "no collator for shard " << shard_.to_str()));
return;
}
std::vector<tl_object_ptr<ton_api::tonNode_blockIdExt>> prev_blocks;
for (const BlockIdExt &p : prev_block_ids_) {
prev_blocks.push_back(create_tl_block_id(p));
}
td::BufferSlice query = create_serialize_tl_object<ton_api::collatorNode_generateBlock>(
shard_.workchain, shard_.shard, create_tl_block_id(min_masterchain_block_id_), std::move(prev_blocks),
local_id_full_.ed25519_value().raw());
auto P = td::PromiseCreator::lambda(
[SelfId = actor_id(this), round_id, promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error_prefix("rldp query failed: "));
return;
}
td::actor::send_closure(SelfId, &ValidatorGroup::receive_collate_query_response, round_id, R.move_as_ok(),
std::move(promise));
});
LOG(INFO) << "collate query for " << shard_.to_str() << ": send query to " << collator;
size_t max_answer_size = config_.max_block_size + config_.max_collated_data_size + 256;
td::actor::send_closure(rldp_, &rldp::Rldp::send_query_ex, adnl::AdnlNodeIdShort(local_id_), collator, "collatequery",
std::move(P), timeout, std::move(query), max_answer_size);
}
void ValidatorGroup::receive_collate_query_response(td::uint32 round_id, td::BufferSlice data,
td::Promise<BlockCandidate> promise) {
if (round_id < last_known_round_id_) {
promise.set_error(td::Status::Error("too old"));
return;
}
TRY_RESULT_PROMISE(promise, f, fetch_tl_object<ton_api::collatorNode_GenerateBlockResult>(data, true));
tl_object_ptr<ton_api::db_candidate> b;
ton_api::downcast_call(*f, td::overloaded(
[&](ton_api::collatorNode_generateBlockError &r) {
td::Status error = td::Status::Error(r.code_, r.message_);
promise.set_error(error.move_as_error_prefix("collate query: "));
},
[&](ton_api::collatorNode_generateBlockSuccess &r) { b = std::move(r.candidate_); }));
if (!b) {
return;
}
auto key = PublicKey{b->source_};
if (!key.is_ed25519()) {
promise.set_error(td::Status::Error("collate query: block candidate source mismatch"));
}
auto e_key = Ed25519_PublicKey{key.ed25519_value().raw()};
if (e_key != Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}) {
promise.set_error(td::Status::Error("collate query: block candidate source mismatch"));
return;
}
auto block_id = ton::create_block_id(b->id_);
if (block_id.shard_full() != shard_) {
promise.set_error(td::Status::Error("collate query: shard mismatch"));
return;
}
auto collated_data_hash = td::sha256_bits256(b->collated_data_);
BlockCandidate candidate(e_key, block_id, collated_data_hash, std::move(b->data_), std::move(b->collated_data_));
auto P = td::PromiseCreator::lambda(
[candidate = candidate.clone(), promise = std::move(promise)](td::Result<UnixTime> R) mutable {
if (R.is_error()) {
promise.set_error(R.move_as_error_prefix("validate received block error: "));
return;
}
promise.set_result(std::move(candidate));
});
validate_block_candidate(round_id, std::move(candidate), std::move(P));
}
} // namespace validator
} // namespace ton

View file

@ -55,12 +55,14 @@ class ValidatorGroup : public td::actor::Actor {
init_ = false;
create_session();
}
td::actor::send_closure(rldp_, &rldp::Rldp::add_id, adnl::AdnlNodeIdShort(local_id_));
}
void get_session_info(td::Promise<tl_object_ptr<ton_api::engine_validator_validatorSessionInfo>> promise);
ValidatorGroup(ShardIdFull shard, PublicKeyHash local_id, ValidatorSessionId session_id,
td::Ref<ValidatorSet> validator_set, validatorsession::ValidatorSessionOptions config,
td::Ref<ValidatorSet> validator_set, std::vector<CollatorNodeDescr> collator_set,
validatorsession::ValidatorSessionOptions config,
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
td::actor::ActorId<rldp::Rldp> rldp, td::actor::ActorId<overlay::Overlays> overlays,
std::string db_root, td::actor::ActorId<ValidatorManager> validator_manager, bool create_session,
@ -69,6 +71,7 @@ class ValidatorGroup : public td::actor::Actor {
, local_id_(std::move(local_id))
, session_id_(session_id)
, validator_set_(std::move(validator_set))
, collator_set_(std::move(collator_set))
, config_(std::move(config))
, keyring_(keyring)
, adnl_(adnl)
@ -83,6 +86,8 @@ class ValidatorGroup : public td::actor::Actor {
private:
std::unique_ptr<validatorsession::ValidatorSession::Callback> make_validator_session_callback();
void send_collate_query(td::uint32 round_id, td::Timestamp timeout, td::Promise<BlockCandidate> promise);
void receive_collate_query_response(td::uint32 round_id, td::BufferSlice data, td::Promise<BlockCandidate> promise);
struct PostponedAccept {
RootHash root_hash;
@ -105,6 +110,7 @@ class ValidatorGroup : public td::actor::Actor {
BlockIdExt min_masterchain_block_id_;
td::Ref<ValidatorSet> validator_set_;
std::vector<CollatorNodeDescr> collator_set_;
validatorsession::ValidatorSessionOptions config_;
td::actor::ActorId<keyring::Keyring> keyring_;
@ -120,6 +126,16 @@ class ValidatorGroup : public td::actor::Actor {
bool allow_unsafe_self_blocks_resync_;
bool lite_mode_ = false;
td::uint32 last_known_round_id_ = 0;
typedef std::tuple<td::Bits256, BlockIdExt, FileHash, FileHash> CacheKey;
std::map<CacheKey, UnixTime> approved_candidates_cache_;
td::uint32 approved_candidates_cache_round_ = 0;
void update_approve_cache(td::uint32 round_id, CacheKey key, UnixTime value);
static CacheKey block_to_cache_key(const BlockCandidate& block) {
return std::make_tuple(block.pubkey.as_bits256(), block.id, sha256_bits256(block.data), block.collated_file_hash);
}
};
} // namespace validator

View file

@ -220,6 +220,8 @@ class ValidatorManagerInterface : public td::actor::Actor {
virtual void generate_block_candidate(BlockId block_id, td::Promise<BlockCandidate> promise) = 0;
virtual void get_required_block_candidates(td::Promise<std::vector<BlockId>> promise) = 0;
virtual void import_block_candidate(BlockCandidate candidate, td::Promise<td::Unit> promise) = 0;
virtual void add_collator(adnl::AdnlNodeIdShort id, ShardIdFull shard) = 0;
};
} // namespace validator