mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +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…
	
	Add table
		Add a link
		
	
		Reference in a new issue