mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-12 11:12:16 +00:00
added support for config change proposals
- added some fift scripts for the config change proposal voting - added validator-engine-console support for the config change proposal voting - additional sanity checks in catchain - unsafe slow catchain resync method
This commit is contained in:
parent
a31f8d4424
commit
4dd5eea11f
35 changed files with 753 additions and 144 deletions
|
@ -60,7 +60,8 @@ class CatChainReceiverInterface : public td::actor::Actor {
|
|||
td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainSessionId unique_hash, std::string db_root);
|
||||
CatChainSessionId unique_hash, std::string db_root,
|
||||
bool allow_unsafe_self_blocks_resync);
|
||||
|
||||
virtual ~CatChainReceiverInterface() = default;
|
||||
};
|
||||
|
|
|
@ -53,6 +53,9 @@ class CatChainReceiverSource {
|
|||
virtual void block_received(CatChainBlockHeight height) = 0;
|
||||
virtual void block_delivered(CatChainBlockHeight height) = 0;
|
||||
|
||||
virtual bool has_unreceived() const = 0;
|
||||
virtual bool has_undelivered() const = 0;
|
||||
|
||||
virtual td::Status validate_dep_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) = 0;
|
||||
virtual void on_new_block(CatChainReceivedBlock *block) = 0;
|
||||
virtual void on_found_fork_proof(td::Slice fork) = 0;
|
||||
|
|
|
@ -78,6 +78,19 @@ class CatChainReceiverSourceImpl : public CatChainReceiverSource {
|
|||
CatChainBlockHeight received_height() const override {
|
||||
return received_height_;
|
||||
}
|
||||
bool has_unreceived() const override {
|
||||
if (blamed()) {
|
||||
return true;
|
||||
}
|
||||
if (!blocks_.size()) {
|
||||
return false;
|
||||
}
|
||||
CHECK(blocks_.rbegin()->second->get_height() >= received_height_);
|
||||
return blocks_.rbegin()->second->get_height() > received_height_;
|
||||
}
|
||||
bool has_undelivered() const override {
|
||||
return delivered_height_ < received_height_;
|
||||
}
|
||||
CatChainReceivedBlock *get_block(CatChainBlockHeight height) const override;
|
||||
|
||||
td::Status validate_dep_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) override;
|
||||
|
|
|
@ -83,6 +83,16 @@ void CatChainReceiverImpl::receive_block(adnl::AdnlNodeIdShort src, tl_object_pt
|
|||
return;
|
||||
}
|
||||
|
||||
if (block->src_ == static_cast<td::int32>(local_idx_)) {
|
||||
if (!allow_unsafe_self_blocks_resync_ || started_) {
|
||||
LOG(FATAL) << this << ": received unknown SELF block from " << src
|
||||
<< " (unsafe=" << allow_unsafe_self_blocks_resync_ << ")";
|
||||
} else {
|
||||
LOG(ERROR) << this << ": received unknown SELF block from " << src << ". UPDATING LOCAL DATABASE. UNSAFE";
|
||||
initial_sync_complete_at_ = td::Timestamp::in(300.0);
|
||||
}
|
||||
}
|
||||
|
||||
auto raw_data = serialize_tl_object(block, true, payload.as_slice());
|
||||
create_block(std::move(block), td::SharedSlice{payload.as_slice()});
|
||||
|
||||
|
@ -420,14 +430,16 @@ CatChainReceiverImpl::CatChainReceiverImpl(std::unique_ptr<Callback> callback, C
|
|||
td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainSessionId unique_hash, std::string db_root)
|
||||
CatChainSessionId unique_hash, std::string db_root,
|
||||
bool allow_unsafe_self_blocks_resync)
|
||||
: callback_(std::move(callback))
|
||||
, opts_(std::move(opts))
|
||||
, keyring_(keyring)
|
||||
, adnl_(adnl)
|
||||
, overlay_manager_(overlay_manager)
|
||||
, local_id_(local_id)
|
||||
, db_root_(db_root) {
|
||||
, db_root_(db_root)
|
||||
, allow_unsafe_self_blocks_resync_(allow_unsafe_self_blocks_resync) {
|
||||
std::vector<td::Bits256> short_ids;
|
||||
local_idx_ = static_cast<td::uint32>(ids.size());
|
||||
for (auto &id : ids) {
|
||||
|
@ -589,19 +601,20 @@ void CatChainReceiverImpl::read_db() {
|
|||
|
||||
next_rotate_ = td::Timestamp::in(60 + td::Random::fast(0, 60));
|
||||
next_sync_ = td::Timestamp::in(0.001 * td::Random::fast(0, 60));
|
||||
initial_sync_complete_at_ = td::Timestamp::in(allow_unsafe_self_blocks_resync_ ? 300.0 : 5.0);
|
||||
alarm_timestamp().relax(next_rotate_);
|
||||
alarm_timestamp().relax(next_sync_);
|
||||
|
||||
callback_->start();
|
||||
alarm_timestamp().relax(initial_sync_complete_at_);
|
||||
}
|
||||
|
||||
td::actor::ActorOwn<CatChainReceiverInterface> CatChainReceiverInterface::create(
|
||||
std::unique_ptr<Callback> callback, CatChainOptions opts, td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id, CatChainSessionId unique_hash, std::string db_root) {
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id, CatChainSessionId unique_hash, std::string db_root,
|
||||
bool allow_unsafe_self_blocks_resync) {
|
||||
auto A = td::actor::create_actor<CatChainReceiverImpl>("catchainreceiver", std::move(callback), std::move(opts),
|
||||
keyring, adnl, overlay_manager, std::move(ids), local_id,
|
||||
unique_hash, db_root);
|
||||
unique_hash, db_root, allow_unsafe_self_blocks_resync);
|
||||
return std::move(A);
|
||||
}
|
||||
|
||||
|
@ -906,6 +919,59 @@ void CatChainReceiverImpl::choose_neighbours() {
|
|||
neighbours_ = std::move(n);
|
||||
}
|
||||
|
||||
bool CatChainReceiverImpl::unsafe_start_up_check_completed() {
|
||||
auto S = get_source(local_idx_);
|
||||
CHECK(!S->blamed());
|
||||
if (S->has_unreceived() || S->has_undelivered()) {
|
||||
LOG(INFO) << "catchain: has_unreceived=" << S->has_unreceived() << " has_undelivered=" << S->has_undelivered();
|
||||
run_scheduler();
|
||||
initial_sync_complete_at_ = td::Timestamp::in(60.0);
|
||||
return false;
|
||||
}
|
||||
auto h = S->delivered_height();
|
||||
if (h == 0) {
|
||||
CHECK(last_sent_block_->get_height() == 0);
|
||||
CHECK(!unsafe_root_block_writing_);
|
||||
return true;
|
||||
}
|
||||
if (last_sent_block_->get_height() == h) {
|
||||
CHECK(!unsafe_root_block_writing_);
|
||||
return true;
|
||||
}
|
||||
if (unsafe_root_block_writing_) {
|
||||
initial_sync_complete_at_ = td::Timestamp::in(5.0);
|
||||
LOG(INFO) << "catchain: writing=true";
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe_root_block_writing_ = true;
|
||||
auto B = S->get_block(h);
|
||||
CHECK(B != nullptr);
|
||||
CHECK(B->delivered());
|
||||
CHECK(B->in_db());
|
||||
|
||||
auto id = B->get_hash();
|
||||
|
||||
td::BufferSlice raw_data{32};
|
||||
raw_data.as_slice().copy_from(as_slice(id));
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), block = B](td::Result<td::Unit> R) mutable {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::written_unsafe_root_block, block);
|
||||
});
|
||||
|
||||
db_.set(CatChainBlockHash::zero(), std::move(raw_data), std::move(P), 0);
|
||||
initial_sync_complete_at_ = td::Timestamp::in(5.0);
|
||||
LOG(INFO) << "catchain: need update root";
|
||||
return false;
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::written_unsafe_root_block(CatChainReceivedBlock *block) {
|
||||
CHECK(last_sent_block_->get_height() < block->get_height());
|
||||
last_sent_block_ = block;
|
||||
unsafe_root_block_writing_ = false;
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::alarm() {
|
||||
alarm_timestamp() = td::Timestamp::never();
|
||||
if (next_sync_ && next_sync_.is_in_past()) {
|
||||
|
@ -923,8 +989,22 @@ void CatChainReceiverImpl::alarm() {
|
|||
next_rotate_ = td::Timestamp::in(td::Random::fast(60.0, 120.0));
|
||||
choose_neighbours();
|
||||
}
|
||||
if (!started_ && read_db_ && initial_sync_complete_at_ && initial_sync_complete_at_.is_in_past()) {
|
||||
bool allow = false;
|
||||
if (allow_unsafe_self_blocks_resync_) {
|
||||
allow = unsafe_start_up_check_completed();
|
||||
} else {
|
||||
allow = true;
|
||||
}
|
||||
if (allow) {
|
||||
initial_sync_complete_at_ = td::Timestamp::never();
|
||||
started_ = true;
|
||||
callback_->start();
|
||||
}
|
||||
}
|
||||
alarm_timestamp().relax(next_rotate_);
|
||||
alarm_timestamp().relax(next_sync_);
|
||||
alarm_timestamp().relax(initial_sync_complete_at_);
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::send_fec_broadcast(td::BufferSlice data) {
|
||||
|
|
|
@ -134,6 +134,9 @@ class CatChainReceiverImpl : public CatChainReceiver {
|
|||
|
||||
void block_written_to_db(CatChainBlockHash hash);
|
||||
|
||||
bool unsafe_start_up_check_completed();
|
||||
void written_unsafe_root_block(CatChainReceivedBlock *block);
|
||||
|
||||
void destroy() override;
|
||||
|
||||
CatChainReceivedBlock *get_block(CatChainBlockHash hash) const;
|
||||
|
@ -141,7 +144,7 @@ class CatChainReceiverImpl : public CatChainReceiver {
|
|||
CatChainReceiverImpl(std::unique_ptr<Callback> callback, CatChainOptions opts,
|
||||
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays>, std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainBlockHash unique_hash, std::string db_root);
|
||||
CatChainBlockHash unique_hash, std::string db_root, bool allow_unsafe_self_blocks_resync);
|
||||
|
||||
private:
|
||||
std::unique_ptr<overlay::Overlays::Callback> make_callback() {
|
||||
|
@ -222,6 +225,10 @@ class CatChainReceiverImpl : public CatChainReceiver {
|
|||
DbType db_;
|
||||
|
||||
bool intentional_fork_ = false;
|
||||
td::Timestamp initial_sync_complete_at_{td::Timestamp::never()};
|
||||
bool allow_unsafe_self_blocks_resync_{false};
|
||||
bool unsafe_root_block_writing_{false};
|
||||
bool started_{false};
|
||||
|
||||
std::list<CatChainReceivedBlock *> to_run_;
|
||||
};
|
||||
|
|
|
@ -222,8 +222,9 @@ void CatChainImpl::on_receiver_started() {
|
|||
CatChainImpl::CatChainImpl(std::unique_ptr<Callback> callback, CatChainOptions opts,
|
||||
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager, std::vector<CatChainNode> ids,
|
||||
PublicKeyHash local_id, CatChainSessionId unique_hash, std::string db_root)
|
||||
: opts_(std::move(opts)), db_root_(db_root) {
|
||||
PublicKeyHash local_id, CatChainSessionId unique_hash, std::string db_root,
|
||||
bool allow_unsafe_self_blocks_resync)
|
||||
: opts_(std::move(opts)), db_root_(db_root), allow_unsafe_self_blocks_resync_(allow_unsafe_self_blocks_resync) {
|
||||
callback_ = std::move(callback);
|
||||
sources_.resize(ids.size());
|
||||
unique_hash_ = unique_hash;
|
||||
|
@ -280,9 +281,9 @@ void CatChainImpl::start_up() {
|
|||
|
||||
auto cb = std::make_unique<ChainCb>(actor_id(this));
|
||||
|
||||
receiver_ =
|
||||
CatChainReceiverInterface::create(std::move(cb), opts_, args_->keyring, args_->adnl, args_->overlay_manager,
|
||||
std::move(args_->ids), args_->local_id, args_->unique_hash, db_root_);
|
||||
receiver_ = CatChainReceiverInterface::create(std::move(cb), opts_, args_->keyring, args_->adnl,
|
||||
args_->overlay_manager, std::move(args_->ids), args_->local_id,
|
||||
args_->unique_hash, db_root_, allow_unsafe_self_blocks_resync_);
|
||||
args_ = nullptr;
|
||||
//alarm_timestamp() = td::Timestamp::in(opts_.idle_timeout);
|
||||
}
|
||||
|
@ -292,9 +293,11 @@ td::actor::ActorOwn<CatChain> CatChain::create(std::unique_ptr<Callback> callbac
|
|||
td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainSessionId unique_hash, std::string db_root) {
|
||||
CatChainSessionId unique_hash, std::string db_root,
|
||||
bool allow_unsafe_self_blocks_resync) {
|
||||
return td::actor::create_actor<CatChainImpl>("catchain", std::move(callback), std::move(opts), keyring, adnl,
|
||||
overlay_manager, std::move(ids), local_id, unique_hash, db_root);
|
||||
overlay_manager, std::move(ids), local_id, unique_hash, db_root,
|
||||
allow_unsafe_self_blocks_resync);
|
||||
}
|
||||
|
||||
CatChainBlock *CatChainImpl::get_block(CatChainBlockHash hash) const {
|
||||
|
|
|
@ -102,7 +102,8 @@ class CatChain : public td::actor::Actor {
|
|||
td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainSessionId unique_hash, std::string db_root);
|
||||
CatChainSessionId unique_hash, std::string db_root,
|
||||
bool allow_unsafe_self_blocks_resync);
|
||||
virtual ~CatChain() = default;
|
||||
};
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ class CatChainImpl : public CatChain {
|
|||
bool receiver_started_ = false;
|
||||
|
||||
std::string db_root_;
|
||||
bool allow_unsafe_self_blocks_resync_;
|
||||
|
||||
void send_process();
|
||||
void send_preprocess(CatChainBlock *block);
|
||||
|
@ -118,7 +119,7 @@ class CatChainImpl : public CatChain {
|
|||
CatChainImpl(std::unique_ptr<Callback> callback, CatChainOptions opts, td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id, CatChainSessionId unique_hash,
|
||||
std::string db_root);
|
||||
std::string db_root, bool allow_unsafe_self_blocks_resync);
|
||||
|
||||
void alarm() override;
|
||||
void start_up() override;
|
||||
|
|
|
@ -932,6 +932,32 @@ void interpret_concat_builders(vm::Stack& stack) {
|
|||
stack.push_builder(std::move(cb1));
|
||||
}
|
||||
|
||||
void interpret_cell_datasize(vm::Stack& stack, int mode) {
|
||||
auto bound = (mode & 4 ? stack.pop_int() : td::make_refint(1 << 22));
|
||||
Ref<vm::Cell> cell;
|
||||
Ref<vm::CellSlice> cs;
|
||||
if (mode & 2) {
|
||||
cs = stack.pop_cellslice();
|
||||
} else {
|
||||
cell = stack.pop_maybe_cell();
|
||||
}
|
||||
if (!bound->is_valid() || bound->sgn() < 0) {
|
||||
throw IntError{"finite non-negative integer expected"};
|
||||
}
|
||||
vm::VmStorageStat stat{bound->unsigned_fits_bits(63) ? bound->to_long() : (1ULL << 63) - 1};
|
||||
bool ok = (mode & 2 ? stat.add_storage(cs.write()) : stat.add_storage(std::move(cell)));
|
||||
if (ok) {
|
||||
stack.push_smallint(stat.cells);
|
||||
stack.push_smallint(stat.bits);
|
||||
stack.push_smallint(stat.refs);
|
||||
} else if (!(mode & 1)) {
|
||||
throw IntError{"scanned too many cells"};
|
||||
}
|
||||
if (mode & 1) {
|
||||
stack.push_bool(ok);
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_slice_bitrefs(vm::Stack& stack, int mode) {
|
||||
auto cs = stack.pop_cellslice();
|
||||
if (mode & 1) {
|
||||
|
@ -2755,6 +2781,8 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("sbits ", std::bind(interpret_slice_bitrefs, _1, 1));
|
||||
d.def_stack_word("srefs ", std::bind(interpret_slice_bitrefs, _1, 2));
|
||||
d.def_stack_word("sbitrefs ", std::bind(interpret_slice_bitrefs, _1, 3));
|
||||
d.def_stack_word("totalcsize ", std::bind(interpret_cell_datasize, _1, 0));
|
||||
d.def_stack_word("totalssize ", std::bind(interpret_cell_datasize, _1, 2));
|
||||
// boc manipulation
|
||||
d.def_stack_word("B>boc ", interpret_boc_deserialize);
|
||||
d.def_stack_word("boc>B ", interpret_boc_serialize);
|
||||
|
|
|
@ -23,18 +23,18 @@
|
|||
.end_cell());
|
||||
}
|
||||
|
||||
;; [min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price]
|
||||
;; (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price)
|
||||
_ parse_vote_config(cell c) inline {
|
||||
var cs = c.begin_parse();
|
||||
throw_unless(44, cs~load_uint(8) == 0x36);
|
||||
var res = [cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)];
|
||||
var res = (cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
|
||||
cs.end_parse();
|
||||
return res;
|
||||
}
|
||||
|
||||
;; cfg_vote_setup#91 normal_params:^ConfigProposalSetup critical_params:^ConfigProposalSetup = ConfigVotingSetup;
|
||||
_ get_vote_config(int critical?) inline_ref {
|
||||
var cs = config_param(11).begin_parse();
|
||||
_ get_vote_config_internal(int critical?, cell cparam11) inline_ref {
|
||||
var cs = cparam11.begin_parse();
|
||||
throw_unless(44, cs~load_uint(8) == 0x91);
|
||||
if (critical?) {
|
||||
cs~load_ref();
|
||||
|
@ -42,6 +42,10 @@ _ get_vote_config(int critical?) inline_ref {
|
|||
return parse_vote_config(cs.preload_ref());
|
||||
}
|
||||
|
||||
_ get_vote_config(int critical?) inline {
|
||||
return get_vote_config_internal(critical?, config_param(11));
|
||||
}
|
||||
|
||||
(int, int) check_validator_set(cell vset) {
|
||||
var cs = vset.begin_parse();
|
||||
throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only
|
||||
|
@ -123,7 +127,7 @@ _ perform_action(cfg_dict, public_key, action, cs) inline_ref {
|
|||
}
|
||||
}
|
||||
|
||||
(cell, int, slice) get_current_vset() inline_ref {
|
||||
(cell, int, cell) get_current_vset() inline_ref {
|
||||
var vset = config_param(34);
|
||||
var cs = begin_parse(vset);
|
||||
;; validators_ext#12 utime_since:uint32 utime_until:uint32
|
||||
|
@ -131,13 +135,13 @@ _ perform_action(cfg_dict, public_key, action, cs) inline_ref {
|
|||
;; total_weight:uint64
|
||||
throw_unless(40, cs~load_uint(8) == 0x12);
|
||||
cs~skip_bits(32 + 32 + 16 + 16);
|
||||
int total_weight = cs~load_uint(64);
|
||||
return (vset, total_weight, cs);
|
||||
var (total_weight, dict) = (cs~load_uint(64), cs~load_dict());
|
||||
cs.end_parse();
|
||||
return (vset, total_weight, dict);
|
||||
}
|
||||
|
||||
(slice, int) get_validator_descr(int idx) inline_ref {
|
||||
var (vset, total_weight, cs) = get_current_vset();
|
||||
var dict = begin_cell().store_slice(cs).end_cell();
|
||||
var (vset, total_weight, dict) = get_current_vset();
|
||||
var (value, _) = dict.udict_get?(16, idx);
|
||||
return (value, total_weight);
|
||||
}
|
||||
|
@ -227,7 +231,7 @@ _ perform_action(cfg_dict, public_key, action, cs) inline_ref {
|
|||
}
|
||||
|
||||
slice update_proposal_status(slice rest, int weight_remaining, int critical?) inline_ref {
|
||||
var [min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _] = get_vote_config(critical?);
|
||||
var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?);
|
||||
var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
|
||||
losses -= (weight_remaining >= 0);
|
||||
if (losses > max_losses) {
|
||||
|
@ -241,8 +245,8 @@ slice update_proposal_status(slice rest, int weight_remaining, int critical?) in
|
|||
}
|
||||
return begin_cell()
|
||||
.store_uint(rounds_remaining, 8)
|
||||
.store_uint(losses, 8)
|
||||
.store_uint(wins, 8)
|
||||
.store_uint(losses, 8)
|
||||
.end_cell().begin_parse();
|
||||
}
|
||||
|
||||
|
@ -261,7 +265,7 @@ builder begin_pack_proposal_status(int expires, cell proposal, int critical?, ce
|
|||
var (pstatus, found?) = vote_dict.udict_get?(256, phash);
|
||||
ifnot (found?) {
|
||||
;; config proposal not found
|
||||
return (vote_dict, null(), false);
|
||||
return (vote_dict, null(), -1);
|
||||
}
|
||||
var (cur_vset, total_weight, _) = get_current_vset();
|
||||
int cur_vset_id = cur_vset.cell_hash();
|
||||
|
@ -269,7 +273,7 @@ builder begin_pack_proposal_status(int expires, cell proposal, int critical?, ce
|
|||
if (expires <= now()) {
|
||||
;; config proposal expired, delete and report not found
|
||||
vote_dict~udict_delete?(256, phash);
|
||||
return (vote_dict, null(), false);
|
||||
return (vote_dict, null(), -1);
|
||||
}
|
||||
if (vset_id != cur_vset_id) {
|
||||
;; config proposal belongs to a previous validator set
|
||||
|
@ -281,12 +285,12 @@ builder begin_pack_proposal_status(int expires, cell proposal, int critical?, ce
|
|||
if (rest.null?()) {
|
||||
;; discard proposal (existed for too many rounds, or too many losses)
|
||||
vote_dict~udict_delete?(256, phash);
|
||||
return (vote_dict, null(), false);
|
||||
return (vote_dict, null(), -1);
|
||||
}
|
||||
var (_, found?) = voters.udict_get?(16, idx);
|
||||
if (found?) {
|
||||
;; already voted for this proposal, ignore vote
|
||||
return (vote_dict, null(), false);
|
||||
return (vote_dict, null(), -2);
|
||||
}
|
||||
;; register vote
|
||||
voters~udict_set_builder(16, idx, begin_cell().store_uint(now(), 32));
|
||||
|
@ -296,16 +300,16 @@ builder begin_pack_proposal_status(int expires, cell proposal, int critical?, ce
|
|||
;; not enough votes, or proposal already accepted in this round
|
||||
;; simply update weight_remaining
|
||||
vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id).store_slice(rest));
|
||||
return (vote_dict, null(), false);
|
||||
return (vote_dict, null(), 2);
|
||||
}
|
||||
;; proposal wins in this round
|
||||
var [min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _] = get_vote_config(critical?);
|
||||
var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?);
|
||||
var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8));
|
||||
wins += 1;
|
||||
if (wins >= min_wins) {
|
||||
;; proposal is accepted, remove and process
|
||||
vote_dict~udict_delete?(256, phash);
|
||||
return (vote_dict, proposal, critical?);
|
||||
return (vote_dict, proposal, 6 - critical?);
|
||||
}
|
||||
;; update proposal info
|
||||
vote_dict~udict_set_builder(256, phash,
|
||||
|
@ -313,7 +317,24 @@ builder begin_pack_proposal_status(int expires, cell proposal, int critical?, ce
|
|||
.store_uint(rounds_remaining, 8)
|
||||
.store_uint(wins, 8)
|
||||
.store_uint(losses, 8));
|
||||
return (vote_dict, null(), false);
|
||||
return (vote_dict, null(), 2);
|
||||
}
|
||||
|
||||
int proceed_register_vote(phash, idx, weight) impure inline_ref {
|
||||
var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
|
||||
(vote_dict, var accepted_proposal, var status) = register_vote(vote_dict, phash, idx, weight);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
ifnot (accepted_proposal.null?()) {
|
||||
var critical? = 6 - status;
|
||||
(cfg_dict, var param_id, var param_val) = accept_proposal(cfg_dict, accepted_proposal, critical?);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
if (param_id) {
|
||||
commit();
|
||||
(cfg_dict, public_key) = perform_proposed_action(cfg_dict, public_key, param_id, param_val);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
(slice, int) scan_proposal(int phash, slice pstatus) inline_ref {
|
||||
|
@ -394,7 +415,7 @@ int register_voting_proposal(slice cs, int msg_value) impure inline_ref {
|
|||
}
|
||||
;; obtain vote proposal configuration
|
||||
var vote_cfg = get_vote_config(critical?);
|
||||
var [min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price] = vote_cfg;
|
||||
var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) = vote_cfg;
|
||||
if (expire_at < min_store_sec) {
|
||||
return -0xc5787069; ;; expired
|
||||
}
|
||||
|
@ -486,6 +507,19 @@ int register_voting_proposal(slice cs, int msg_value) impure inline_ref {
|
|||
}
|
||||
return send_answer(s_addr, query_id, ans_tag, mode);
|
||||
}
|
||||
if (tag == 0x566f7465) {
|
||||
;; vote for a configuration proposal
|
||||
var signature = in_msg~load_bits(512);
|
||||
var msg_body = in_msg;
|
||||
var (sign_tag, idx, phash) = (in_msg~load_uint(32), in_msg~load_uint(16), in_msg~load_uint(256));
|
||||
in_msg.end_parse();
|
||||
throw_unless(37, sign_tag == 0x566f7445);
|
||||
var (vdescr, total_weight) = get_validator_descr(idx);
|
||||
var (val_pubkey, weight) = unpack_validator_descr(vdescr);
|
||||
throw_unless(34, check_data_signature(msg_body, signature, val_pubkey));
|
||||
int res = proceed_register_vote(phash, idx, weight);
|
||||
return send_confirmation(s_addr, query_id, res + 0xd6745240);
|
||||
}
|
||||
;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query)
|
||||
;; to bounce message back to sender
|
||||
throw_unless(37, (tag == 0) | (tag & (1 << 31)));
|
||||
|
@ -501,33 +535,24 @@ int register_voting_proposal(slice cs, int msg_value) impure inline_ref {
|
|||
throw_if(35, valid_until < now());
|
||||
throw_if(39, slice_depth(cs) > 128);
|
||||
var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
if (action == 0x566f7465) {
|
||||
;; vote for a configuration proposal
|
||||
var (idx, phash) = (cs~load_uint(16), cs~load_uint(256));
|
||||
cs.end_parse();
|
||||
var (vdescr, total_weight) = get_validator_descr(idx);
|
||||
var (val_pubkey, weight) = unpack_validator_descr(vdescr);
|
||||
throw_unless(34, check_data_signature(in_msg, signature, val_pubkey));
|
||||
accept_message();
|
||||
stored_seqno += 1;
|
||||
stored_seqno = (stored_seqno + 1) % (1 << 32);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
commit();
|
||||
(vote_dict, var accepted_proposal, var critical?) = register_vote(vote_dict, phash, idx, weight);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
ifnot (accepted_proposal.null?()) {
|
||||
(cfg_dict, var param_id, var param_val) = accept_proposal(cfg_dict, accepted_proposal, critical?);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
if (param_id) {
|
||||
commit();
|
||||
(cfg_dict, public_key) = perform_proposed_action(cfg_dict, public_key, param_id, param_val);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
}
|
||||
}
|
||||
proceed_register_vote(phash, idx, weight);
|
||||
return ();
|
||||
}
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
accept_message();
|
||||
stored_seqno += 1;
|
||||
stored_seqno = (stored_seqno + 1) % (1 << 32);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
commit();
|
||||
(cfg_dict, public_key) = perform_action(cfg_dict, public_key, action, cs);
|
||||
|
@ -603,3 +628,14 @@ _ list_proposals() method_id {
|
|||
} until (~ f);
|
||||
return list;
|
||||
}
|
||||
|
||||
_ proposal_storage_price(int critical?, int seconds, int bits, int refs) method_id {
|
||||
var cfg_dict = get_data().begin_parse().preload_ref();
|
||||
var cparam11 = cfg_dict.idict_get_ref(32, 11);
|
||||
var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) = get_vote_config_internal(critical?, cparam11);
|
||||
if (seconds < min_store_sec) {
|
||||
return -1;
|
||||
}
|
||||
seconds = min(seconds, max_store_sec);
|
||||
return (bit_price * (bits + 1024) + cell_price * (refs + 2)) * seconds;
|
||||
}
|
||||
|
|
|
@ -3,31 +3,44 @@
|
|||
"GetOpt.fif" include
|
||||
|
||||
"validator-to-sign.req" =: savefile
|
||||
false =: internal?
|
||||
-1 =: seqno
|
||||
-1 =: expire-at
|
||||
|
||||
{ ."usage: " @' $0 type ." <expire-at> <validator-idx> <proposal-hash> [<savefile>]" cr
|
||||
."Creates an unsigned request expiring at unixtime <expire-at> to vote for configuration proposal <proposal-hash> (decimal; prefix with '0x' if needed) on behalf of validator with zero-based index <validator-idx> in current validator set (as stored in configuration parameter 34)" cr
|
||||
{ ."usage: " @' $0 type ." (-i | <cfg-seqno> <expire-at>) <validator-idx> <proposal-hash> [<savefile>]" cr
|
||||
."Creates an unsigned request expiring at unixtime <expire-at> to vote for configuration proposal <proposal-hash> (decimal; prefix with '0x' if needed) on behalf of validator with zero-based index <validator-idx> in current validator set (as stored in configuration parameter 34)." cr
|
||||
."If -i is selected, prepares unsigned request to be incorporated into an internal message instead." cr
|
||||
."The result is saved into <savefile> (" savefile type ." by default) and output in hexadecimal form, to be signed later by the validator public key" cr 1 halt
|
||||
} : usage
|
||||
|
||||
$# dup 3 < swap 4 > or ' usage if
|
||||
4 :$1..n
|
||||
$# dup 3 < swap 5 > or ' usage if
|
||||
5 :$1..n
|
||||
{ $* @ dup null? ' dup ' uncons cond $* ! } : $next
|
||||
|
||||
$1 parse-int dup 30 1<< < { now + 1000 + 2000 /c 2000 * } if
|
||||
dup =: expire-at
|
||||
dup now <= abort"expiration time must be in the future"
|
||||
32 ufits not abort"invalid expiration time"
|
||||
$2 parse-int dup =: val-idx
|
||||
$1 "-i" $= { $next drop true =: internal? } if
|
||||
|
||||
internal? {
|
||||
$next parse-int =: seqno
|
||||
$next parse-int dup 30 1<< < { now + 1000 + 2000 /c 2000 * } if
|
||||
dup =: expire-at
|
||||
dup now <= abort"expiration time must be in the future"
|
||||
32 ufits not abort"invalid expiration time"
|
||||
} ifnot
|
||||
$# dup 2 < swap 3 > or ' usage if
|
||||
$1 parse-int dup =: val-idx
|
||||
16 ufits not abort"validator index out of range"
|
||||
$3 parse-int dup =: prop-hash
|
||||
$2 parse-int dup =: prop-hash
|
||||
256 ufits not abort"invalid proposal hash"
|
||||
$4 savefile replace-if-null =: savefile
|
||||
0 =: seqno
|
||||
$3 savefile replace-if-null =: savefile
|
||||
|
||||
."Creating a request expiring at " expire-at .
|
||||
."Creating a request " internal? { ."with seqno " seqno . ."expiring at " expire-at . } ifnot
|
||||
."to vote for configuration proposal 0x" prop-hash 64x.
|
||||
."on behalf of validator with index " val-idx . cr
|
||||
|
||||
B{566f7465} seqno 32 u>B B+ expire-at 32 u>B B+ val-idx 16 u>B B+ prop-hash 256 u>B B+
|
||||
internal? { B{566f7445} } {
|
||||
B{566f7465} seqno 32 u>B B+ expire-at 32 u>B B+
|
||||
} cond
|
||||
val-idx 16 u>B B+ prop-hash 256 u>B B+
|
||||
dup Bx. cr
|
||||
dup B>base64url type cr
|
||||
savefile tuck B>file ."Saved to file " type cr
|
||||
|
|
|
@ -3,49 +3,70 @@
|
|||
"GetOpt.fif" include
|
||||
|
||||
"vote-query.boc" =: savefile
|
||||
false =: internal?
|
||||
-1 =: seqno
|
||||
0 0 2=: config-addr
|
||||
-1 =: expire-at
|
||||
|
||||
{ ."usage: " @' $0 type ." <config-addr> <expire-at> <validator-idx> <proposal-hash> <validator-pubkey> <validator-signature> [<savefile>]" cr
|
||||
."Creates an external message addressed to the configuration smart contract <config-addr> containing a signed request expiring at unixtime <expire-at> to vote for configuration proposal <proposal-hash> (decimal; prefix with '0x' if needed) on behalf of validator with zero-based index <validator-idx> and (Base64) public key <validator-pubkey> in current validator set (as stored in configuration parameter 34)" cr
|
||||
{ ."usage: " @' $0 type ." (-i | <config-addr> <config-seqno> <expire-at>) <validator-idx> <proposal-hash> <validator-pubkey> <validator-signature> [<savefile>]" cr
|
||||
."Creates an external message addressed to the configuration smart contract <config-addr> and current sequence number <config-seqno> containing a signed request expiring at unixtime <expire-at> to vote for configuration proposal <proposal-hash> (decimal; prefix with '0x' if needed) on behalf of validator with zero-based index <validator-idx> and (Base64) public key <validator-pubkey> in current validator set (as stored in configuration parameter 34)" cr
|
||||
."<validator-signature> must be the base64 representation of Ed25519 signature of the previously generated unsigned request by means of <validator-pubkey>" cr
|
||||
."If -i is selected, instead generates the body of an internal message to be sent to the configuration smart contract from any smartcontract residing in the masterchain" cr
|
||||
."The result is saved into <savefile> (" savefile type ." by default), to be sent later by the lite-client" cr 1 halt
|
||||
} : usage
|
||||
|
||||
$# dup 6 < swap 7 > or ' usage if
|
||||
7 :$1..n
|
||||
$# dup 5 < swap 8 > or ' usage if
|
||||
8 :$1..n
|
||||
|
||||
$1 true parse-load-address drop over 1+ abort"configuration smart contract must be in masterchain"
|
||||
2=: config-addr
|
||||
$2 parse-int dup 30 1<< < { now + 1000 + 2000 /c 2000 * } if
|
||||
dup =: expire-at
|
||||
dup now <= abort"expiration time must be in the future"
|
||||
32 ufits not abort"invalid expiration time"
|
||||
$3 parse-int dup =: val-idx
|
||||
{ $* @ dup null? ' dup ' uncons cond $* ! } : $next
|
||||
$1 "-i" $= { $next drop true =: internal? "vote-msg-body.boc" =: savefile } if
|
||||
|
||||
internal? {
|
||||
$next true parse-load-address drop over 1+ abort"configuration smart contract must be in masterchain"
|
||||
2=: config-addr
|
||||
$next parse-int =: seqno
|
||||
$next parse-int dup 30 1<< < { now + 1000 + 2000 /c 2000 * } if
|
||||
dup =: expire-at
|
||||
dup now <= abort"expiration time must be in the future"
|
||||
32 ufits not abort"invalid expiration time"
|
||||
} ifnot
|
||||
$# dup 4 < swap 5 > or ' usage if
|
||||
$1 parse-int dup =: val-idx
|
||||
16 ufits not abort"validator index out of range"
|
||||
$4 parse-int dup =: prop-hash
|
||||
$2 parse-int dup =: prop-hash
|
||||
256 ufits not abort"invalid proposal hash"
|
||||
$5 base64>B dup Blen 36 <> abort"validator Ed25519 public key must be exactly 36 bytes long"
|
||||
$3 base64>B dup Blen 36 <> abort"validator Ed25519 public key must be exactly 36 bytes long"
|
||||
32 B>u@+ 0xC6B41348 <> abort"invalid Ed25519 public key: unknown magic number"
|
||||
=: pubkey
|
||||
$6 base64>B dup Blen 64 <> abort"validator Ed25519 signature must be exactly 64 bytes long"
|
||||
$4 base64>B dup Blen 64 <> abort"validator Ed25519 signature must be exactly 64 bytes long"
|
||||
=: signature
|
||||
$7 savefile replace-if-null =: savefile
|
||||
0 =: seqno
|
||||
$5 savefile replace-if-null =: savefile
|
||||
|
||||
."Creating an external message to configuration smart contract "
|
||||
config-addr 2dup 6 .Addr ." = " .addr cr
|
||||
."containing a signed request expiring at " expire-at .
|
||||
internal? {
|
||||
."Creating the body of an internal message to be sent to the configuration smart contract" cr }
|
||||
{ ."Creating an external message to configuration smart contract "
|
||||
config-addr 2dup 6 .Addr ." = " .addr cr
|
||||
} cond
|
||||
."containing a signed request " internal? { ."expiring at " expire-at . } ifnot
|
||||
."to vote for configuration proposal 0x" prop-hash 64x.
|
||||
."on behalf of validator with index " val-idx . "and public key" pubkey 64x. cr
|
||||
."on behalf of validator with index " val-idx . "and public key" pubkey Bx. cr
|
||||
|
||||
B{566f7465} seqno 32 u>B B+ expire-at 32 u>B B+ val-idx 16 u>B B+ prop-hash 256 u>B B+
|
||||
dup =: to_sign
|
||||
internal? { B{566f7445} } {
|
||||
B{566f7465} seqno 32 u>B B+ expire-at 32 u>B B+
|
||||
} cond
|
||||
val-idx 16 u>B B+ prop-hash 256 u>B B+ dup =: to_sign
|
||||
."String to sign is " Bx. cr
|
||||
|
||||
to_sign signature pubkey ed25519_chksign not abort"Ed25519 signature is invalid"
|
||||
."Provided a valid Ed25519 signature " signature Bx. ." with validator public key " pubkey Bx. cr
|
||||
|
||||
<b b{1000100} s, config-addr addr, 0 Gram, b{00} s,
|
||||
now 32 << prop-hash 32 1<< mod + =: query-id
|
||||
|
||||
<b internal? { x{566f7465} s, query-id 64 u, }
|
||||
{ b{1000100} s, config-addr addr, 0 Gram, b{00} s, } cond
|
||||
signature B, to_sign B, b>
|
||||
cr ."External message is " dup <s csr. cr
|
||||
cr
|
||||
internal? { ."Internal message body is " } { ."External message is " } cond
|
||||
dup <s csr. cr
|
||||
|
||||
2 boc+>B savefile tuck B>file ."Saved to file " type cr
|
||||
|
|
|
@ -41,7 +41,11 @@ boc-filename dup "null" $= {
|
|||
} cond
|
||||
=: param-value
|
||||
|
||||
{ 2drop true } : is-valid-config?
|
||||
{ dup -1000 = { drop <s ref@ <s 12 u@ 0xFF0 = } {
|
||||
dup -1001 = { drop <s ref@ <s 12 u@ 0xFF0 = } {
|
||||
2drop true
|
||||
} cond } cond
|
||||
} : is-valid-config?
|
||||
|
||||
dup param-idx is-valid-config? not abort"not a valid value for chosen configuration parameter"
|
||||
|
||||
|
@ -59,5 +63,11 @@ now 32 << param-idx + =: query-id
|
|||
|
||||
dup ."resulting internal message body: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
|
||||
param-value dup null? { drop } {
|
||||
totalcsize swap ."(a total of " . ."data bits, " . ."cell references -> "
|
||||
drop dup Blen . ."BoC data bytes)" cr
|
||||
} cond
|
||||
|
||||
savefile tuck B>file
|
||||
."(Saved to file " type .")" cr
|
||||
|
|
65
crypto/smartcont/create-config-upgrade-proposal.fif
Normal file
65
crypto/smartcont/create-config-upgrade-proposal.fif
Normal file
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/fift -s
|
||||
"Asm.fif" include
|
||||
"TonUtil.fif" include
|
||||
"GetOpt.fif" include
|
||||
|
||||
// only the next three lines differ in elector/configuration smart contract upgrades
|
||||
"configuration" =: name
|
||||
"auto/config-code.fif" =: src-file
|
||||
-1000 =: param-idx
|
||||
|
||||
86400 30 * =: expire-in
|
||||
true =: critical
|
||||
-1 =: old-hash
|
||||
|
||||
{ show-options-help 1 halt } : usage
|
||||
{ name $+ } : +name
|
||||
|
||||
begin-options
|
||||
" [-s <fift-asm-code>] [-x <expire-in>] [-H <old-hash>] [<savefile>]" +cr +tab
|
||||
+"Creates a new configuration proposal for upgrading the " +name +" smart contract code to <fift-asm-code> ('" src-file $+ +"' by default), "
|
||||
+"and saves it as an internal message body into <savefile>.boc ('config-msg-body.boc' by default)"
|
||||
disable-digit-options generic-help-setopt
|
||||
"x" "--expires-in" { parse-int =: expire-in } short-long-option-arg
|
||||
"Sets proposal expiration time in seconds (default " expire-in (.) $+ +")" option-help
|
||||
"s" "--source" { =: src-file } short-long-option-arg
|
||||
"Fift assembler source file for the new " +name +" smart contract code ('"
|
||||
src-file $+ +"' by default)" option-help
|
||||
"H" "--old-hash" { (hex-number) not abort"256-bit hex number expected as hash" =: old-hash }
|
||||
short-long-option-arg
|
||||
"Sets the required cell hash of existing parameter value (0 means no value)" option-help
|
||||
"h" "--help" { usage } short-long-option
|
||||
"Shows a help message" option-help
|
||||
parse-options
|
||||
|
||||
$# 1 > ' usage if
|
||||
1 :$1..n
|
||||
|
||||
$1 "config-msg-body.boc" replace-if-null =: savefile
|
||||
expire-in now + =: expire-at
|
||||
|
||||
."Assembling new " name type ." smart contract code from " src-file type cr
|
||||
src-file include
|
||||
dup <s 12 u@ 0xFF0 <> abort"not valid smart contract code"
|
||||
<b swap ref, b> =: param-value
|
||||
|
||||
critical { ."Critical" } { ."Non-critical" } cond
|
||||
." configuration proposal for configuration parameter " param-idx .
|
||||
."will expire at " expire-at . ."(in " expire-in . ."seconds)" cr
|
||||
|
||||
now 32 << param-idx + =: query-id
|
||||
."Query id is " query-id . cr
|
||||
|
||||
// create message body
|
||||
<b x{6e565052} s, query-id 64 u, expire-at 32 u,
|
||||
<b x{f3} s, param-idx 32 i, param-value dict,
|
||||
old-hash tuck 0>= tuck 1 i, -rot { 256 u, } { drop } cond b> ref,
|
||||
critical 1 i, b>
|
||||
|
||||
dup ."resulting internal message body: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
param-value totalcsize swap ."(a total of " . ."data bits, " . ."cell references -> "
|
||||
drop dup Blen . ."BoC data bytes)" cr
|
||||
|
||||
savefile tuck B>file
|
||||
."(Saved to file " type .")" cr
|
65
crypto/smartcont/create-elector-upgrade-proposal.fif
Normal file
65
crypto/smartcont/create-elector-upgrade-proposal.fif
Normal file
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/fift -s
|
||||
"Asm.fif" include
|
||||
"TonUtil.fif" include
|
||||
"GetOpt.fif" include
|
||||
|
||||
// only the next three lines differ in elector/configuration smart contract upgrades
|
||||
"elector" =: name
|
||||
"auto/elector-code.fif" =: src-file
|
||||
-1001 =: param-idx
|
||||
|
||||
86400 30 * =: expire-in
|
||||
true =: critical
|
||||
-1 =: old-hash
|
||||
|
||||
{ show-options-help 1 halt } : usage
|
||||
{ name $+ } : +name
|
||||
|
||||
begin-options
|
||||
" [-s <fift-asm-code>] [-x <expire-in>] [-H <old-hash>] [<savefile>]" +cr +tab
|
||||
+"Creates a new configuration proposal for upgrading the " +name +" smart contract code to <fift-asm-code> ('" src-file $+ +"' by default), "
|
||||
+"and saves it as an internal message body into <savefile>.boc ('config-msg-body.boc' by default)"
|
||||
disable-digit-options generic-help-setopt
|
||||
"x" "--expires-in" { parse-int =: expire-in } short-long-option-arg
|
||||
"Sets proposal expiration time in seconds (default " expire-in (.) $+ +")" option-help
|
||||
"s" "--source" { =: src-file } short-long-option-arg
|
||||
"Fift assembler source file for the new " +name +" smart contract code ('"
|
||||
src-file $+ +"' by default)" option-help
|
||||
"H" "--old-hash" { (hex-number) not abort"256-bit hex number expected as hash" =: old-hash }
|
||||
short-long-option-arg
|
||||
"Sets the required cell hash of existing parameter value (0 means no value)" option-help
|
||||
"h" "--help" { usage } short-long-option
|
||||
"Shows a help message" option-help
|
||||
parse-options
|
||||
|
||||
$# 1 > ' usage if
|
||||
1 :$1..n
|
||||
|
||||
$1 "config-msg-body.boc" replace-if-null =: savefile
|
||||
expire-in now + =: expire-at
|
||||
|
||||
."Assembling new " name type ." smart contract code from " src-file type cr
|
||||
src-file include
|
||||
dup <s 12 u@ 0xFF0 <> abort"not valid smart contract code"
|
||||
<b swap ref, b> =: param-value
|
||||
|
||||
critical { ."Critical" } { ."Non-critical" } cond
|
||||
." configuration proposal for configuration parameter " param-idx .
|
||||
."will expire at " expire-at . ."(in " expire-in . ."seconds)" cr
|
||||
|
||||
now 32 << param-idx + =: query-id
|
||||
."Query id is " query-id . cr
|
||||
|
||||
// create message body
|
||||
<b x{6e565052} s, query-id 64 u, expire-at 32 u,
|
||||
<b x{f3} s, param-idx 32 i, param-value dict,
|
||||
old-hash tuck 0>= tuck 1 i, -rot { 256 u, } { drop } cond b> ref,
|
||||
critical 1 i, b>
|
||||
|
||||
dup ."resulting internal message body: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
param-value totalcsize swap ."(a total of " . ."data bits, " . ."cell references -> "
|
||||
drop dup Blen . ."BoC data bytes)" cr
|
||||
|
||||
savefile tuck B>file
|
||||
."(Saved to file " type .")" cr
|
|
@ -1131,4 +1131,28 @@ void NewCellStorageStat::dfs(Ref<Cell> cell, bool need_stat, bool need_proof_sta
|
|||
}
|
||||
}
|
||||
|
||||
bool VmStorageStat::add_storage(Ref<Cell> cell) {
|
||||
if (cell.is_null() || !check_visited(cell)) {
|
||||
return true;
|
||||
}
|
||||
if (cells >= limit) {
|
||||
return false;
|
||||
}
|
||||
++cells;
|
||||
bool special;
|
||||
auto cs = load_cell_slice_special(std::move(cell), special);
|
||||
return cs.is_valid() && add_storage(std::move(cs));
|
||||
}
|
||||
|
||||
bool VmStorageStat::add_storage(const CellSlice& cs) {
|
||||
bits += cs.size();
|
||||
refs += cs.size_refs();
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
if (!add_storage(cs.prefetch_ref(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace vm
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "td/utils/Status.h"
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/HashMap.h"
|
||||
#include "td/utils/HashSet.h"
|
||||
|
||||
namespace vm {
|
||||
using td::Ref;
|
||||
|
@ -127,6 +128,21 @@ struct CellStorageStat {
|
|||
bool add_used_storage(Ref<vm::Cell> cell, bool kill_dup = true, unsigned skip_count_root = 0);
|
||||
};
|
||||
|
||||
struct VmStorageStat {
|
||||
td::uint64 cells{0}, bits{0}, refs{0}, limit;
|
||||
td::HashSet<CellHash> visited;
|
||||
VmStorageStat(td::uint64 _limit) : limit(_limit) {
|
||||
}
|
||||
bool add_storage(Ref<Cell> cell);
|
||||
bool add_storage(const CellSlice& cs);
|
||||
bool check_visited(const CellHash& cell_hash) {
|
||||
return visited.insert(cell_hash).second;
|
||||
}
|
||||
bool check_visited(const Ref<Cell>& cell) {
|
||||
return check_visited(cell->get_hash());
|
||||
}
|
||||
};
|
||||
|
||||
struct CellSerializationInfo {
|
||||
bool special;
|
||||
Cell::LevelMask level_mask;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "vm/excno.hpp"
|
||||
#include "vm/vm.h"
|
||||
#include "vm/dict.h"
|
||||
#include "vm/boc.h"
|
||||
#include "Ed25519.h"
|
||||
|
||||
#include "openssl/digest.hpp"
|
||||
|
@ -399,45 +400,6 @@ void register_ton_crypto_ops(OpcodeTable& cp0) {
|
|||
.insert(OpcodeInstr::mksimple(0xf911, 16, "CHKSIGNS", std::bind(exec_ed25519_check_signature, _1, true)));
|
||||
}
|
||||
|
||||
struct VmStorageStat {
|
||||
td::uint64 cells{0}, bits{0}, refs{0}, limit;
|
||||
td::HashSet<CellHash> visited;
|
||||
VmStorageStat(td::uint64 _limit) : limit(_limit) {
|
||||
}
|
||||
bool add_storage(Ref<Cell> cell);
|
||||
bool add_storage(const CellSlice& cs);
|
||||
bool check_visited(const CellHash& cell_hash) {
|
||||
return visited.insert(cell_hash).second;
|
||||
}
|
||||
bool check_visited(const Ref<Cell>& cell) {
|
||||
return check_visited(cell->get_hash());
|
||||
}
|
||||
};
|
||||
|
||||
bool VmStorageStat::add_storage(Ref<Cell> cell) {
|
||||
if (cell.is_null() || !check_visited(cell)) {
|
||||
return true;
|
||||
}
|
||||
if (cells >= limit) {
|
||||
return false;
|
||||
}
|
||||
++cells;
|
||||
bool special;
|
||||
auto cs = load_cell_slice_special(std::move(cell), special);
|
||||
return cs.is_valid() && add_storage(std::move(cs));
|
||||
}
|
||||
|
||||
bool VmStorageStat::add_storage(const CellSlice& cs) {
|
||||
bits += cs.size();
|
||||
refs += cs.size_refs();
|
||||
for (unsigned i = 0; i < cs.size_refs(); i++) {
|
||||
if (!add_storage(cs.prefetch_ref(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int exec_compute_data_size(VmState* st, int mode) {
|
||||
VM_LOG(st) << (mode & 2 ? 'S' : 'C') << "DATASIZE" << (mode & 1 ? "Q" : "");
|
||||
Stack& stack = st->get_stack();
|
||||
|
|
|
@ -2156,6 +2156,8 @@ Typical values of $x$ are $x=0$ or $x=2$ for very small bags of cells (e.g., TON
|
|||
\item {\tt ten} ( -- $10$), pushes {\em Integer\/} constant 10.
|
||||
\item {\tt third} ($t$ -- $x$), returns the third component of a {\em Tuple}, cf.~\ptref{p:tuples}. Equivalent to {\tt 2 []}.
|
||||
\item {\tt times} ($e$ $n$ -- ), executes execution token ({\em WordDef\/}) $e$ exactly $n$ times, if $n\geq0$, cf.~\ptref{p:simple.loops}. If $n$ is negative, throws an exception.
|
||||
\item {\tt totalcsize} ($c$ -- $x$ $y$ $z$), recursively computes the total number of unique cells $x$, data bits $y$ and cell references $z$ in the tree of cells rooted in {\em Cell\/}~$c$.
|
||||
\item {\tt totalssize} ($s$ -- $x$ $y$ $z$), recursively computes the total number of unique cells $x$, data bits $y$ and cell references $z$ in the tree of cells rooted in {\em Slice\/}~$s$.
|
||||
\item {\tt triple} ($x$ $y$ $z$ -- $t$), creates new triple $t=(x,y,z)$, cf.~\ptref{p:tuples}. Equivalent to {\tt 3 tuple}.
|
||||
\item {\tt true} ( -- $-1$), pushes $-1$ into the stack, cf.~\ptref{p:bool}. Equivalent to {\tt -1}.
|
||||
\item {\tt tuck} ($x$ $y$ -- $y$ $x$ $y$), equivalent to {\tt swap over}, cf.~\ptref{p:stack.ops}.
|
||||
|
|
|
@ -129,8 +129,9 @@ class CatChainInst : public td::actor::Actor {
|
|||
for (auto &n : nodes_) {
|
||||
nodes.push_back(ton::catchain::CatChainNode{n.adnl_id, n.id_full});
|
||||
}
|
||||
catchain_ = ton::catchain::CatChain::create(make_callback(), opts, keyring_, adnl_, overlay_manager_,
|
||||
std::move(nodes), nodes_[idx_].id, unique_hash_, std::string(""));
|
||||
catchain_ =
|
||||
ton::catchain::CatChain::create(make_callback(), opts, keyring_, adnl_, overlay_manager_, std::move(nodes),
|
||||
nodes_[idx_].id, unique_hash_, std::string(""), false);
|
||||
}
|
||||
|
||||
CatChainInst(td::actor::ActorId<ton::keyring::Keyring> keyring, td::actor::ActorId<ton::adnl::Adnl> adnl,
|
||||
|
|
|
@ -598,6 +598,7 @@ engine.validator.success = engine.validator.Success;
|
|||
engine.validator.jsonConfig data:string = engine.validator.JsonConfig;
|
||||
|
||||
engine.validator.electionBid election_date:int perm_key:int256 adnl_addr:int256 to_send_payload:bytes = engine.validator.ElectionBid;
|
||||
engine.validator.proposalVote perm_key:int256 to_send:bytes = engine.validator.ProposalVote;
|
||||
|
||||
engine.validator.dhtServerStatus id:int256 status:int = engine.validator.DhtServerStatus;
|
||||
engine.validator.dhtServersStatus servers:(vector engine.validator.dhtServerStatus) = engine.validator.DhtServersStatus;
|
||||
|
@ -638,6 +639,7 @@ engine.validator.getConfig = engine.validator.JsonConfig;
|
|||
engine.validator.setVerbosity verbosity:int = engine.validator.Success;
|
||||
|
||||
engine.validator.createElectionBid election_date:int election_addr:string wallet:string = engine.validator.ElectionBid;
|
||||
engine.validator.createProposalVote vote:bytes = engine.validator.ProposalVote;
|
||||
|
||||
engine.validator.checkDhtServers id:int256 = engine.validator.DhtServersStatus;
|
||||
|
||||
|
|
Binary file not shown.
|
@ -657,6 +657,27 @@ td::Status CreateElectionBidQuery::receive(td::BufferSlice data) {
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status CreateProposalVoteQuery::run() {
|
||||
TRY_RESULT_ASSIGN(data_, tokenizer_.get_token<std::string>());
|
||||
TRY_RESULT_ASSIGN(fname_, tokenizer_.get_token<std::string>());
|
||||
TRY_STATUS(tokenizer_.check_endl());
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status CreateProposalVoteQuery::send() {
|
||||
auto b = ton::create_serialize_tl_object<ton::ton_api::engine_validator_createProposalVote>(td::BufferSlice(data_));
|
||||
td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise());
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status CreateProposalVoteQuery::receive(td::BufferSlice data) {
|
||||
TRY_RESULT_PREFIX(f, ton::fetch_tl_object<ton::ton_api::engine_validator_proposalVote>(data.as_slice(), true),
|
||||
"received incorrect answer: ");
|
||||
td::TerminalIO::out() << "success: permkey=" << f->perm_key_.to_hex() << "\n";
|
||||
TRY_STATUS(td::write_file(fname_, f->to_send_.as_slice()));
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status CheckDhtServersQuery::run() {
|
||||
TRY_RESULT_ASSIGN(id_, tokenizer_.get_token<ton::PublicKeyHash>());
|
||||
return td::Status::OK();
|
||||
|
|
|
@ -835,6 +835,29 @@ class CreateElectionBidQuery : public Query {
|
|||
std::string fname_;
|
||||
};
|
||||
|
||||
class CreateProposalVoteQuery : public Query {
|
||||
public:
|
||||
CreateProposalVoteQuery(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 "createproposalvote";
|
||||
}
|
||||
static std::string get_help() {
|
||||
return "createproposalvote <data> <fname>\tcreate proposal vote";
|
||||
}
|
||||
std::string name() const override {
|
||||
return get_name();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string data_;
|
||||
std::string fname_;
|
||||
};
|
||||
|
||||
class CheckDhtServersQuery : public Query {
|
||||
public:
|
||||
CheckDhtServersQuery(td::actor::ActorId<ValidatorEngineConsole> console, Tokenizer tokenizer)
|
||||
|
|
|
@ -130,6 +130,7 @@ void ValidatorEngineConsole::run() {
|
|||
add_query_runner(std::make_unique<QueryRunnerImpl<AddNetworkAddressQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<AddNetworkProxyAddressQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<CreateElectionBidQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<CreateProposalVoteQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<CheckDhtServersQuery>>());
|
||||
}
|
||||
|
||||
|
|
|
@ -841,6 +841,131 @@ class ValidatorElectionBidCreator : public td::actor::Actor {
|
|||
td::BufferSlice result_;
|
||||
};
|
||||
|
||||
class ValidatorProposalVoteCreator : public td::actor::Actor {
|
||||
public:
|
||||
ValidatorProposalVoteCreator(td::BufferSlice proposal, std::string dir, td::actor::ActorId<ValidatorEngine> engine,
|
||||
td::actor::ActorId<ton::keyring::Keyring> keyring, td::Promise<td::BufferSlice> promise)
|
||||
: proposal_(std::move(proposal))
|
||||
, dir_(std::move(dir))
|
||||
, engine_(engine)
|
||||
, keyring_(keyring)
|
||||
, promise_(std::move(promise)) {
|
||||
}
|
||||
|
||||
void start_up() override {
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<std::pair<ton::PublicKey, size_t>> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &ValidatorProposalVoteCreator::abort_query,
|
||||
R.move_as_error_prefix("failed to find self permanent key: "));
|
||||
} else {
|
||||
auto v = R.move_as_ok();
|
||||
td::actor::send_closure(SelfId, &ValidatorProposalVoteCreator::got_id, v.first, v.second);
|
||||
}
|
||||
});
|
||||
td::actor::send_closure(engine_, &ValidatorEngine::get_current_validator_perm_key, std::move(P));
|
||||
}
|
||||
|
||||
void got_id(ton::PublicKey pubkey, size_t idx) {
|
||||
pubkey_ = std::move(pubkey);
|
||||
idx_ = idx;
|
||||
auto codeR = td::read_file_str(dir_ + "/config-proposal-vote-req.fif");
|
||||
if (codeR.is_error()) {
|
||||
abort_query(codeR.move_as_error_prefix("fif not found (validator-elect-req.fif)"));
|
||||
return;
|
||||
}
|
||||
auto data = proposal_.as_slice().str();
|
||||
auto R = fift::mem_run_fift(codeR.move_as_ok(), {"config-proposal-vote-req.fif", "-i", td::to_string(idx_), data},
|
||||
dir_ + "/");
|
||||
if (R.is_error()) {
|
||||
abort_query(R.move_as_error_prefix("fift fail (cofig-proposal-vote-req.fif)"));
|
||||
return;
|
||||
}
|
||||
auto res = R.move_as_ok();
|
||||
auto to_signR = res.source_lookup.read_file("validator-to-sign.req");
|
||||
if (to_signR.is_error()) {
|
||||
abort_query(td::Status::Error(PSTRING() << "strange error: no to sign file. Output: " << res.output));
|
||||
return;
|
||||
}
|
||||
auto to_sign = td::BufferSlice{to_signR.move_as_ok().data};
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
td::actor::send_closure(SelfId, &ValidatorProposalVoteCreator::abort_query,
|
||||
R.move_as_error_prefix("sign fail: "));
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &ValidatorProposalVoteCreator::signed_vote, R.move_as_ok());
|
||||
}
|
||||
});
|
||||
|
||||
td::actor::send_closure(keyring_, &ton::keyring::Keyring::sign_message, pubkey_.compute_short_id(),
|
||||
std::move(to_sign), std::move(P));
|
||||
}
|
||||
|
||||
void signed_vote(td::BufferSlice signature) {
|
||||
signature_ = std::move(signature);
|
||||
|
||||
auto codeR = td::read_file_str(dir_ + "/config-proposal-vote-signed.fif");
|
||||
if (codeR.is_error()) {
|
||||
abort_query(codeR.move_as_error_prefix("fif not found (config-proposal-vote-signed.fif)"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto key = td::base64_encode(pubkey_.export_as_slice().as_slice());
|
||||
auto sig = td::base64_encode(signature_.as_slice());
|
||||
|
||||
auto data = proposal_.as_slice().str();
|
||||
auto R = fift::mem_run_fift(
|
||||
codeR.move_as_ok(), {"config-proposal-vote-signed.fif", "-i", td::to_string(idx_), data, key, sig}, dir_ + "/");
|
||||
if (R.is_error()) {
|
||||
abort_query(R.move_as_error_prefix("fift fail (config-proposal-vote-signed.fif)"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto res = R.move_as_ok();
|
||||
auto dataR = res.source_lookup.read_file("vote-msg-body.boc");
|
||||
if (dataR.is_error()) {
|
||||
abort_query(td::Status::Error("strage error: no result boc"));
|
||||
return;
|
||||
}
|
||||
|
||||
result_ = td::BufferSlice(dataR.move_as_ok().data);
|
||||
finish_query();
|
||||
}
|
||||
|
||||
void abort_query(td::Status error) {
|
||||
promise_.set_value(ValidatorEngine::create_control_query_error(std::move(error)));
|
||||
stop();
|
||||
}
|
||||
void finish_query() {
|
||||
promise_.set_value(ton::create_serialize_tl_object<ton::ton_api::engine_validator_proposalVote>(
|
||||
pubkey_.compute_short_id().bits256_value(), std::move(result_)));
|
||||
stop();
|
||||
}
|
||||
|
||||
private:
|
||||
td::BufferSlice proposal_;
|
||||
std::string dir_;
|
||||
|
||||
ton::PublicKey pubkey_;
|
||||
size_t idx_;
|
||||
|
||||
td::BufferSlice signature_;
|
||||
td::BufferSlice result_;
|
||||
td::actor::ActorId<ValidatorEngine> engine_;
|
||||
td::actor::ActorId<ton::keyring::Keyring> keyring_;
|
||||
|
||||
td::Promise<td::BufferSlice> promise_;
|
||||
|
||||
td::uint32 ttl_;
|
||||
AdnlCategory cat_ = 2;
|
||||
double frac = 2.7;
|
||||
|
||||
ton::PublicKeyHash perm_key_;
|
||||
ton::PublicKey perm_key_full_;
|
||||
ton::adnl::AdnlNodeIdShort adnl_addr_;
|
||||
ton::adnl::AdnlNodeIdFull adnl_key_full_;
|
||||
};
|
||||
|
||||
class CheckDhtServerStatusQuery : public td::actor::Actor {
|
||||
public:
|
||||
void start_up() override {
|
||||
|
@ -1064,6 +1189,9 @@ td::Status ValidatorEngine::load_global_config() {
|
|||
if (db_depth_ <= 32) {
|
||||
validator_options_.write().set_filedb_depth(db_depth_);
|
||||
}
|
||||
for (auto seq : unsafe_catchains_) {
|
||||
validator_options_.write().add_unsafe_resync_catchain(seq);
|
||||
}
|
||||
|
||||
std::vector<ton::BlockIdExt> h;
|
||||
for (auto &x : conf.validator_->hardforks_) {
|
||||
|
@ -2822,6 +2950,32 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_checkDhtS
|
|||
.release();
|
||||
}
|
||||
|
||||
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_createProposalVote &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 (keyring_.empty()) {
|
||||
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "keyring not started")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!started_) {
|
||||
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (fift_dir_.empty()) {
|
||||
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "no fift dir")));
|
||||
return;
|
||||
}
|
||||
|
||||
td::actor::create_actor<ValidatorProposalVoteCreator>("votecreate", std::move(query.vote_), fift_dir_, actor_id(this),
|
||||
keyring_.get(), std::move(promise))
|
||||
.release();
|
||||
}
|
||||
|
||||
void ValidatorEngine::process_control_query(td::uint16 port, ton::adnl::AdnlNodeIdShort src,
|
||||
ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
|
@ -2885,6 +3039,29 @@ void ValidatorEngine::run() {
|
|||
load_config(std::move(P));
|
||||
}
|
||||
|
||||
void ValidatorEngine::get_current_validator_perm_key(td::Promise<std::pair<ton::PublicKey, size_t>> promise) {
|
||||
if (state_.is_null()) {
|
||||
promise.set_error(td::Status::Error(ton::ErrorCode::notready, "not started"));
|
||||
return;
|
||||
}
|
||||
|
||||
auto val_set = state_->get_total_validator_set(0);
|
||||
CHECK(val_set.not_null());
|
||||
auto vec = val_set->export_vector();
|
||||
for (size_t idx = 0; idx < vec.size(); idx++) {
|
||||
auto &el = vec[idx];
|
||||
ton::PublicKey pub{ton::pubkeys::Ed25519{el.key.as_bits256()}};
|
||||
auto pubkey_hash = pub.compute_short_id();
|
||||
|
||||
auto it = config_.validators.find(pubkey_hash);
|
||||
if (it != config_.validators.end()) {
|
||||
promise.set_value(std::make_pair(pub, idx));
|
||||
return;
|
||||
}
|
||||
}
|
||||
promise.set_error(td::Status::Error(ton::ErrorCode::notready, "not a validator"));
|
||||
}
|
||||
|
||||
std::atomic<bool> need_stats_flag{false};
|
||||
void need_stats(int sig) {
|
||||
need_stats_flag.store(true);
|
||||
|
@ -3029,6 +3206,11 @@ int main(int argc, char *argv[]) {
|
|||
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_sync_ttl, v); });
|
||||
return td::Status::OK();
|
||||
});
|
||||
p.add_option('U', "unsafe-catchain-restore", "use SLOW and DANGEROUS catchain recover method", [&](td::Slice id) {
|
||||
TRY_RESULT(seq, td::to_integer_safe<ton::CatchainSeqno>(id));
|
||||
acts.push_back([&x, seq]() { td::actor::send_closure(x, &ValidatorEngine::add_unsafe_catchain, seq); });
|
||||
return td::Status::OK();
|
||||
});
|
||||
td::uint32 threads = 7;
|
||||
p.add_option('t', "threads", PSTRING() << "number of threads (default=" << threads << ")", [&](td::Slice fname) {
|
||||
td::int32 v;
|
||||
|
|
|
@ -200,11 +200,16 @@ class ValidatorEngine : public td::actor::Actor {
|
|||
bool started_keyring_ = false;
|
||||
bool started_ = false;
|
||||
|
||||
std::set<ton::CatchainSeqno> unsafe_catchains_;
|
||||
|
||||
public:
|
||||
static constexpr td::uint8 max_cat() {
|
||||
return 250;
|
||||
}
|
||||
|
||||
void add_unsafe_catchain(ton::CatchainSeqno seq) {
|
||||
unsafe_catchains_.insert(seq);
|
||||
}
|
||||
void set_local_config(std::string str);
|
||||
void set_global_config(std::string str);
|
||||
void set_fift_dir(std::string str) {
|
||||
|
@ -286,6 +291,8 @@ class ValidatorEngine : public td::actor::Actor {
|
|||
void alarm() override;
|
||||
void run();
|
||||
|
||||
void get_current_validator_perm_key(td::Promise<std::pair<ton::PublicKey, size_t>> promise);
|
||||
|
||||
void try_add_adnl_node(ton::PublicKeyHash pub, AdnlCategory cat, td::Promise<td::Unit> promise);
|
||||
void try_add_dht_node(ton::PublicKeyHash pub, td::Promise<td::Unit> promise);
|
||||
void try_add_validator_permanent_key(ton::PublicKeyHash key_hash, td::uint32 election_date, td::uint32 ttl,
|
||||
|
@ -376,6 +383,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_checkDhtServers &query, td::BufferSlice data,
|
||||
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
||||
void run_control_query(ton::ton_api::engine_validator_createProposalVote &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) {
|
||||
|
|
|
@ -814,14 +814,16 @@ ValidatorSessionImpl::ValidatorSessionImpl(catchain::CatChainSessionId session_i
|
|||
std::unique_ptr<Callback> callback,
|
||||
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<overlay::Overlays> overlays, std::string db_root,
|
||||
bool allow_unsafe_self_blocks_resync)
|
||||
: unique_hash_(session_id)
|
||||
, callback_(std::move(callback))
|
||||
, db_root_(db_root)
|
||||
, keyring_(keyring)
|
||||
, adnl_(adnl)
|
||||
, rldp_(rldp)
|
||||
, overlay_manager_(overlays) {
|
||||
, overlay_manager_(overlays)
|
||||
, allow_unsafe_self_blocks_resync_(allow_unsafe_self_blocks_resync) {
|
||||
description_ = ValidatorSessionDescription::create(std::move(opts), nodes, local_id);
|
||||
}
|
||||
|
||||
|
@ -834,7 +836,8 @@ void ValidatorSessionImpl::start() {
|
|||
catchain_ = catchain::CatChain::create(
|
||||
make_catchain_callback(),
|
||||
catchain::CatChainOptions{description().opts().catchain_idle_timeout, description().opts().catchain_max_deps},
|
||||
keyring_, adnl_, overlay_manager_, std::move(w), local_id(), unique_hash_, db_root_);
|
||||
keyring_, adnl_, overlay_manager_, std::move(w), local_id(), unique_hash_, db_root_,
|
||||
allow_unsafe_self_blocks_resync_);
|
||||
|
||||
check_all();
|
||||
}
|
||||
|
@ -864,10 +867,11 @@ td::actor::ActorOwn<ValidatorSession> ValidatorSession::create(
|
|||
catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id,
|
||||
std::vector<ValidatorSessionNode> nodes, std::unique_ptr<Callback> callback,
|
||||
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<rldp::Rldp> rldp, td::actor::ActorId<overlay::Overlays> overlays, std::string db_root,
|
||||
bool allow_unsafe_self_blocks_resync) {
|
||||
return td::actor::create_actor<ValidatorSessionImpl>("session", session_id, std::move(opts), local_id,
|
||||
std::move(nodes), std::move(callback), keyring, adnl, rldp,
|
||||
overlays, db_root);
|
||||
overlays, db_root, allow_unsafe_self_blocks_resync);
|
||||
}
|
||||
|
||||
td::Bits256 ValidatorSessionOptions::get_hash() const {
|
||||
|
|
|
@ -95,7 +95,8 @@ class ValidatorSession : public td::actor::Actor {
|
|||
catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id,
|
||||
std::vector<ValidatorSessionNode> nodes, std::unique_ptr<Callback> callback,
|
||||
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<rldp::Rldp> rldp, td::actor::ActorId<overlay::Overlays> overlays, std::string db_root,
|
||||
bool allow_unsafe_self_blocks_resync);
|
||||
virtual ~ValidatorSession() = default;
|
||||
};
|
||||
|
||||
|
|
|
@ -150,13 +150,14 @@ class ValidatorSessionImpl : public ValidatorSession {
|
|||
|
||||
bool started_ = false;
|
||||
bool catchain_started_ = false;
|
||||
bool allow_unsafe_self_blocks_resync_;
|
||||
|
||||
public:
|
||||
ValidatorSessionImpl(catchain::CatChainSessionId session_id, ValidatorSessionOptions opts, PublicKeyHash local_id,
|
||||
std::vector<ValidatorSessionNode> nodes, std::unique_ptr<Callback> callback,
|
||||
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);
|
||||
std::string db_root, bool allow_unsafe_self_blocks_resync);
|
||||
void start_up() override;
|
||||
void alarm() override;
|
||||
|
||||
|
|
|
@ -1868,9 +1868,10 @@ td::actor::ActorOwn<ValidatorGroup> ValidatorManagerImpl::create_validator_group
|
|||
} else {
|
||||
auto validator_id = get_validator(shard, validator_set);
|
||||
CHECK(!validator_id.is_zero());
|
||||
auto G = td::actor::create_actor<ValidatorGroup>("validatorgroup", shard, validator_id, session_id, validator_set,
|
||||
opts, keyring_, adnl_, rldp_, overlays_, db_root_, actor_id(this),
|
||||
init_session);
|
||||
auto G = td::actor::create_actor<ValidatorGroup>(
|
||||
"validatorgroup", shard, validator_id, session_id, validator_set, opts, keyring_, adnl_, rldp_, overlays_,
|
||||
db_root_, actor_id(this), init_session,
|
||||
opts_->check_unsafe_resync_allowed(validator_set->get_catchain_seqno()));
|
||||
return G;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -242,7 +242,7 @@ void ValidatorGroup::create_session() {
|
|||
|
||||
session_ = validatorsession::ValidatorSession::create(session_id_, config_, local_id_, std::move(vec),
|
||||
make_validator_session_callback(), keyring_, adnl_, rldp_,
|
||||
overlays_, db_root_);
|
||||
overlays_, db_root_, allow_unsafe_self_blocks_resync_);
|
||||
if (prev_block_ids_.size() > 0) {
|
||||
td::actor::send_closure(session_, &validatorsession::ValidatorSession::start);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,8 @@ class ValidatorGroup : public td::actor::Actor {
|
|||
td::Ref<ValidatorSet> validator_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)
|
||||
std::string db_root, td::actor::ActorId<ValidatorManager> validator_manager, bool create_session,
|
||||
bool allow_unsafe_self_blocks_resync)
|
||||
: shard_(shard)
|
||||
, local_id_(std::move(local_id))
|
||||
, session_id_(session_id)
|
||||
|
@ -71,7 +72,8 @@ class ValidatorGroup : public td::actor::Actor {
|
|||
, overlays_(overlays)
|
||||
, db_root_(std::move(db_root))
|
||||
, manager_(validator_manager)
|
||||
, init_(create_session) {
|
||||
, init_(create_session)
|
||||
, allow_unsafe_self_blocks_resync_(allow_unsafe_self_blocks_resync) {
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -98,6 +100,7 @@ class ValidatorGroup : public td::actor::Actor {
|
|||
td::actor::ActorOwn<validatorsession::ValidatorSession> session_;
|
||||
|
||||
bool init_ = false;
|
||||
bool allow_unsafe_self_blocks_resync_;
|
||||
td::uint32 last_known_round_id_ = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -91,6 +91,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions {
|
|||
td::uint32 get_filedb_depth() const override {
|
||||
return db_depth_;
|
||||
}
|
||||
bool check_unsafe_resync_allowed(CatchainSeqno seqno) const override {
|
||||
return unsafe_catchains_.count(seqno) > 0;
|
||||
}
|
||||
|
||||
void set_zero_block_id(BlockIdExt block_id) override {
|
||||
zero_block_id_ = block_id;
|
||||
|
@ -129,6 +132,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions {
|
|||
CHECK(value <= 32);
|
||||
db_depth_ = value;
|
||||
}
|
||||
void add_unsafe_resync_catchain(CatchainSeqno seqno) override {
|
||||
unsafe_catchains_.insert(seqno);
|
||||
}
|
||||
|
||||
ValidatorManagerOptionsImpl *make_copy() const override {
|
||||
return new ValidatorManagerOptionsImpl(*this);
|
||||
|
@ -164,6 +170,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions {
|
|||
bool initial_sync_disabled_;
|
||||
std::vector<BlockIdExt> hardforks_;
|
||||
td::uint32 db_depth_ = 2;
|
||||
std::set<CatchainSeqno> unsafe_catchains_;
|
||||
};
|
||||
|
||||
} // namespace validator
|
||||
|
|
|
@ -68,6 +68,7 @@ struct ValidatorManagerOptions : public td::CntObject {
|
|||
virtual td::uint32 key_block_utime_step() const {
|
||||
return 86400;
|
||||
}
|
||||
virtual bool check_unsafe_resync_allowed(CatchainSeqno seqno) const = 0;
|
||||
|
||||
virtual void set_zero_block_id(BlockIdExt block_id) = 0;
|
||||
virtual void set_init_block_id(BlockIdExt block_id) = 0;
|
||||
|
@ -81,6 +82,7 @@ struct ValidatorManagerOptions : public td::CntObject {
|
|||
virtual void set_initial_sync_disabled(bool value) = 0;
|
||||
virtual void set_hardforks(std::vector<BlockIdExt> hardforks) = 0;
|
||||
virtual void set_filedb_depth(td::uint32 value) = 0;
|
||||
virtual void add_unsafe_resync_catchain(CatchainSeqno seqno) = 0;
|
||||
|
||||
static td::Ref<ValidatorManagerOptions> create(
|
||||
BlockIdExt zero_block_id, BlockIdExt init_block_id,
|
||||
|
|
Loading…
Reference in a new issue