2021-02-27 12:34:41 +00:00
/*
2019-09-07 10:03:22 +00:00
This file is part of TON Blockchain source code .
TON Blockchain is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
TON Blockchain is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with TON Blockchain . If not , see < http : //www.gnu.org/licenses/>.
2021-02-27 12:34:41 +00:00
In addition , as a special exception , the copyright holders give permission
to link the code of portions of this program with the OpenSSL library .
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL . If you modify file ( s ) with this
exception , you may extend this exception to your version of the file ( s ) ,
but you are not obligated to do so . If you do not wish to do so , delete this
exception statement from your version . If you delete this exception statement
2019-09-07 10:03:22 +00:00
from all source files in the program , then also delete it here .
2020-02-02 12:53:37 +00:00
Copyright 2017 - 2020 Telegram Systems LLP
2019-09-07 10:03:22 +00:00
*/
# include "lite-client.h"
2019-09-18 17:46:32 +00:00
# include "lite-client-common.h"
2019-09-07 10:03:22 +00:00
# include "tl-utils/lite-utils.hpp"
# include "auto/tl/ton_api_json.h"
2019-09-14 14:14:55 +00:00
# include "auto/tl/lite_api.hpp"
2020-07-06 14:07:20 +00:00
# include "td/utils/OptionParser.h"
2019-09-07 10:03:22 +00:00
# include "td/utils/Time.h"
# include "td/utils/filesystem.h"
# include "td/utils/Random.h"
# include "td/utils/crypto.h"
# include "td/utils/port/signals.h"
# include "td/utils/port/FileFd.h"
# include "ton/lite-tl.hpp"
# include "block/block-db.h"
# include "block/block.h"
# include "block/block-parse.h"
# include "block/block-auto.h"
# include "block/mc-config.h"
# include "block/check-proof.h"
# include "vm/boc.h"
# include "vm/cellops.h"
# include "vm/cells/MerkleProof.h"
2020-02-06 17:56:46 +00:00
# include "vm/vm.h"
2019-09-07 10:03:22 +00:00
# include "vm/cp0.h"
2020-02-08 19:24:24 +00:00
# include "vm/memo.h"
2019-11-28 14:44:14 +00:00
# include "crypto/vm/utils.h"
2020-02-28 10:28:47 +00:00
# include "crypto/common/util.h"
2022-06-29 09:30:19 +00:00
# include "common/checksum.h"
2019-09-07 10:03:22 +00:00
# if TD_DARWIN || TD_LINUX
# include <unistd.h>
# endif
# include <iostream>
2021-03-25 22:26:49 +00:00
# include "git.h"
2019-09-07 10:03:22 +00:00
2019-09-14 14:14:55 +00:00
using namespace std : : literals : : string_literals ;
2019-09-07 10:03:22 +00:00
using td : : Ref ;
int verbosity ;
void TestNode : : run ( ) {
class Cb : public td : : TerminalIO : : Callback {
public :
void line_cb ( td : : BufferSlice line ) override {
td : : actor : : send_closure ( id_ , & TestNode : : parse_line , std : : move ( line ) ) ;
}
Cb ( td : : actor : : ActorId < TestNode > id ) : id_ ( id ) {
}
private :
td : : actor : : ActorId < TestNode > id_ ;
} ;
2020-07-10 10:46:16 +00:00
io_ = td : : TerminalIO : : create ( " > " , readline_enabled_ , ex_mode_ , std : : make_unique < Cb > ( actor_id ( this ) ) ) ;
2019-09-07 10:03:22 +00:00
td : : actor : : send_closure ( io_ , & td : : TerminalIO : : set_log_interface ) ;
2024-11-26 11:46:58 +00:00
std : : vector < liteclient : : LiteServerConfig > servers ;
if ( ! single_remote_public_key_ . empty ( ) ) { // Use single provided liteserver
servers . push_back (
liteclient : : LiteServerConfig { ton : : adnl : : AdnlNodeIdFull { single_remote_public_key_ } , single_remote_addr_ } ) ;
td : : TerminalIO : : out ( ) < < " using liteserver " < < single_remote_addr_ < < " \n " ;
} else {
2019-09-07 10:03:22 +00:00
auto G = td : : read_file ( global_config_ ) . move_as_ok ( ) ;
auto gc_j = td : : json_decode ( G . as_slice ( ) ) . move_as_ok ( ) ;
ton : : ton_api : : liteclient_config_global gc ;
ton : : ton_api : : from_json ( gc , gc_j . get_object ( ) ) . ensure ( ) ;
2024-11-26 11:46:58 +00:00
auto r_servers = liteclient : : LiteServerConfig : : parse_global_config ( gc ) ;
r_servers . ensure ( ) ;
servers = r_servers . move_as_ok ( ) ;
2019-09-14 14:14:55 +00:00
if ( gc . validator_ & & gc . validator_ - > zero_state_ ) {
zstate_id_ . workchain = gc . validator_ - > zero_state_ - > workchain_ ;
if ( zstate_id_ . workchain ! = ton : : workchainInvalid ) {
zstate_id_ . root_hash = gc . validator_ - > zero_state_ - > root_hash_ ;
zstate_id_ . file_hash = gc . validator_ - > zero_state_ - > file_hash_ ;
td : : TerminalIO : : out ( ) < < " zerostate set to " < < zstate_id_ . to_str ( ) < < " \n " ;
}
}
2024-11-26 11:46:58 +00:00
if ( single_liteserver_idx_ ! = - 1 ) { // Use single liteserver from config
CHECK ( single_liteserver_idx_ > = 0 & & ( size_t ) single_liteserver_idx_ < servers . size ( ) ) ;
td : : TerminalIO : : out ( ) < < " using liteserver # " < < single_liteserver_idx_ < < " with addr "
< < servers [ single_liteserver_idx_ ] . addr < < " \n " ;
servers = { servers [ single_liteserver_idx_ ] } ;
}
2019-09-07 10:03:22 +00:00
}
2024-11-26 11:46:58 +00:00
CHECK ( ! servers . empty ( ) ) ;
client_ = liteclient : : ExtClient : : create ( std : : move ( servers ) , nullptr ) ;
ready_ = true ;
2019-09-07 10:03:22 +00:00
2024-11-26 11:46:58 +00:00
run_init_queries ( ) ;
2019-09-07 10:03:22 +00:00
}
2020-04-27 12:01:46 +00:00
void TestNode : : got_result ( td : : Result < td : : BufferSlice > R , td : : Promise < td : : BufferSlice > promise ) {
if ( R . is_error ( ) ) {
auto err = R . move_as_error ( ) ;
LOG ( ERROR ) < < " failed query: " < < err ;
promise . set_error ( std : : move ( err ) ) ;
td : : actor : : send_closure_later ( actor_id ( this ) , & TestNode : : after_got_result , false ) ;
return ;
}
auto data = R . move_as_ok ( ) ;
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_error > ( data . clone ( ) , true ) ;
if ( F . is_ok ( ) ) {
auto f = F . move_as_ok ( ) ;
auto err = td : : Status : : Error ( f - > code_ , f - > message_ ) ;
LOG ( ERROR ) < < " liteserver error: " < < err ;
promise . set_error ( std : : move ( err ) ) ;
td : : actor : : send_closure_later ( actor_id ( this ) , & TestNode : : after_got_result , false ) ;
return ;
}
promise . set_result ( std : : move ( data ) ) ;
td : : actor : : send_closure_later ( actor_id ( this ) , & TestNode : : after_got_result , true ) ;
}
void TestNode : : after_got_result ( bool ok ) {
2019-09-07 10:03:22 +00:00
running_queries_ - - ;
2020-04-27 12:01:46 +00:00
if ( ex_mode_ & & ! ok ) {
LOG ( ERROR ) < < " fatal error executing command-line queries, skipping the rest " ;
std : : cout . flush ( ) ;
std : : cerr . flush ( ) ;
std : : _Exit ( 1 ) ;
}
2019-09-07 10:03:22 +00:00
if ( ! running_queries_ & & ex_queries_ . size ( ) > 0 ) {
auto data = std : : move ( ex_queries_ [ 0 ] ) ;
ex_queries_ . erase ( ex_queries_ . begin ( ) ) ;
parse_line ( std : : move ( data ) ) ;
}
if ( ex_mode_ & & ! running_queries_ & & ex_queries_ . size ( ) = = 0 ) {
2020-02-28 10:28:47 +00:00
std : : cout . flush ( ) ;
std : : cerr . flush ( ) ;
2019-09-07 10:03:22 +00:00
std : : _Exit ( 0 ) ;
}
}
bool TestNode : : envelope_send_query ( td : : BufferSlice query , td : : Promise < td : : BufferSlice > promise ) {
running_queries_ + + ;
if ( ! ready_ | | client_ . empty ( ) ) {
2020-04-27 12:01:46 +00:00
got_result ( td : : Status : : Error ( " failed to send query to server: not ready " ) , std : : move ( promise ) ) ;
2019-09-07 10:03:22 +00:00
return false ;
}
auto P = td : : PromiseCreator : : lambda (
2020-04-10 19:06:01 +00:00
[ SelfId = actor_id ( this ) , promise = std : : move ( promise ) ] ( td : : Result < td : : BufferSlice > R ) mutable {
2020-04-27 12:01:46 +00:00
td : : actor : : send_closure ( SelfId , & TestNode : : got_result , std : : move ( R ) , std : : move ( promise ) ) ;
2019-09-07 10:03:22 +00:00
} ) ;
td : : BufferSlice b =
ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_query > ( std : : move ( query ) ) , true ) ;
2024-11-26 11:46:58 +00:00
td : : actor : : send_closure ( client_ , & liteclient : : ExtClient : : send_query , " query " , std : : move ( b ) , td : : Timestamp : : in ( 10.0 ) ,
std : : move ( P ) ) ;
2019-09-07 10:03:22 +00:00
return true ;
}
2020-02-28 10:28:47 +00:00
td : : Promise < td : : Unit > TestNode : : trivial_promise ( ) {
return td : : PromiseCreator : : lambda ( [ Self = actor_id ( this ) ] ( td : : Result < td : : Unit > res ) {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " error: " < < res . move_as_error ( ) ;
}
} ) ;
}
2019-09-07 10:03:22 +00:00
bool TestNode : : register_blkid ( const ton : : BlockIdExt & blkid ) {
for ( const auto & id : known_blk_ids_ ) {
if ( id = = blkid ) {
return false ;
}
}
known_blk_ids_ . push_back ( blkid ) ;
return true ;
}
bool TestNode : : show_new_blkids ( bool all ) {
if ( all ) {
shown_blk_ids_ = 0 ;
}
int cnt = 0 ;
while ( shown_blk_ids_ < known_blk_ids_ . size ( ) ) {
td : : TerminalIO : : out ( ) < < " BLK# " < < shown_blk_ids_ + 1 < < " = " < < known_blk_ids_ [ shown_blk_ids_ ] . to_str ( )
< < std : : endl ;
+ + shown_blk_ids_ ;
+ + cnt ;
}
return cnt ;
}
bool TestNode : : complete_blkid ( ton : : BlockId partial_blkid , ton : : BlockIdExt & complete_blkid ) const {
auto n = known_blk_ids_ . size ( ) ;
while ( n ) {
- - n ;
if ( known_blk_ids_ [ n ] . id = = partial_blkid ) {
complete_blkid = known_blk_ids_ [ n ] ;
return true ;
}
}
if ( partial_blkid . is_masterchain ( ) & & partial_blkid . seqno = = ~ 0U ) {
complete_blkid . id = ton : : BlockId { ton : : masterchainId , ton : : shardIdAll , ~ 0U } ;
complete_blkid . root_hash . set_zero ( ) ;
complete_blkid . file_hash . set_zero ( ) ;
return true ;
}
return false ;
}
2020-03-31 13:11:56 +00:00
const tlb : : TypenameLookup & TestNode : : get_tlb_dict ( ) {
static tlb : : TypenameLookup tlb_dict = [ ] ( ) {
tlb : : TypenameLookup tlb_dict0 ;
tlb_dict0 . register_types ( block : : gen : : register_simple_types ) ;
return tlb_dict0 ;
} ( ) ;
return tlb_dict ;
}
bool TestNode : : list_cached_cells ( ) const {
for ( const auto & kv : cell_cache_ ) {
td : : TerminalIO : : out ( ) < < kv . first . to_hex ( ) < < std : : endl ;
}
return true ;
}
bool TestNode : : dump_cached_cell ( td : : Slice hash_pfx , td : : Slice type_name ) {
if ( hash_pfx . size ( ) > 64 ) {
return false ;
}
td : : Bits256 hv_min ;
int len = ( int ) hv_min . from_hex ( hash_pfx , true ) ;
if ( len < 0 | | len > 256 ) {
return set_error ( " cannot parse hex cell hash prefix " ) ;
}
( hv_min . bits ( ) + len ) . fill ( false , 256 - len ) ;
const tlb : : TLB * tptr = nullptr ;
block : : gen : : ConfigParam tpconf ( 0 ) ;
if ( type_name . size ( ) ) {
td : : int32 idx ;
if ( type_name . substr ( 0 , 11 ) = = " ConfigParam " & & convert_int32 ( type_name . substr ( 11 ) , idx ) & & idx > = 0 ) {
tpconf = block : : gen : : ConfigParam ( idx ) ;
tptr = & tpconf ;
} else {
tptr = get_tlb_dict ( ) . lookup ( type_name ) ;
}
if ( ! tptr ) {
return set_error ( " unknown TL-B type " ) ;
}
td : : TerminalIO : : out ( ) < < " dumping cells as values of TLB type " < < tptr - > get_type_name ( ) < < std : : endl ;
}
auto it = std : : lower_bound ( cell_cache_ . begin ( ) , cell_cache_ . end ( ) , hv_min ,
[ ] ( const auto & x , const auto & y ) { return x . first < y ; } ) ;
int cnt = 0 ;
for ( ; it ! = cell_cache_ . end ( ) & & it - > first . bits ( ) . equals ( hv_min . bits ( ) , len ) ; + + it ) {
std : : ostringstream os ;
os < < " C{ " < < it - > first . to_hex ( ) < < " } = " < < std : : endl ;
vm : : load_cell_slice ( it - > second ) . print_rec ( print_limit_ , os , 2 ) ;
if ( tptr ) {
tptr - > print_ref ( print_limit_ , os , it - > second , 2 ) ;
os < < std : : endl ;
}
td : : TerminalIO : : out ( ) < < os . str ( ) ;
+ + cnt ;
}
if ( ! cnt ) {
LOG ( ERROR ) < < " no known cells with specified hash prefix " ;
return false ;
}
return true ;
}
2019-09-07 10:03:22 +00:00
bool TestNode : : get_server_time ( ) {
auto b = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getTime > ( ) , true ) ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ & , Self = actor_id ( this ) ] ( td : : Result < td : : BufferSlice > res ) - > void {
2019-09-07 10:03:22 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot get server time " ;
return ;
} else {
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_currentTime > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getTime " ;
} else {
2024-11-26 11:46:58 +00:00
mc_server_time_ = F . move_as_ok ( ) - > now_ ;
mc_server_time_got_at_ = now ( ) ;
LOG ( INFO ) < < " server time is " < < mc_server_time_ < < " (delta " < < mc_server_time_ - mc_server_time_got_at_
< < " ) " ;
2019-09-07 10:03:22 +00:00
}
}
} ) ;
}
2019-09-16 08:06:04 +00:00
bool TestNode : : get_server_version ( int mode ) {
2019-09-07 10:03:22 +00:00
auto b = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getVersion > ( ) , true ) ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , mode ] ( td : : Result < td : : BufferSlice > res ) {
2019-09-16 08:06:04 +00:00
td : : actor : : send_closure_later ( Self , & TestNode : : got_server_version , std : : move ( res ) , mode ) ;
} ) ;
} ;
void TestNode : : got_server_version ( td : : Result < td : : BufferSlice > res , int mode ) {
2024-11-26 11:46:58 +00:00
mc_server_ok_ = false ;
2019-09-16 08:06:04 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot get server version and time (server too old?) " ;
} else {
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_version > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getVersion " ;
2019-09-07 10:03:22 +00:00
} else {
2019-09-16 08:06:04 +00:00
auto a = F . move_as_ok ( ) ;
2024-11-26 11:46:58 +00:00
set_mc_server_version ( a - > version_ , a - > capabilities_ ) ;
set_mc_server_time ( a - > now_ ) ;
2019-09-14 14:14:55 +00:00
}
2019-09-16 08:06:04 +00:00
}
2024-11-26 11:46:58 +00:00
if ( ! mc_server_ok_ ) {
2019-09-16 08:06:04 +00:00
LOG ( ERROR ) < < " server version is too old (at least " < < ( min_ls_version > > 8 ) < < " . " < < ( min_ls_version & 0xff )
< < " with capabilities " < < min_ls_capabilities < < " required), some queries are unavailable " ;
}
if ( mode & 0x100 ) {
get_server_mc_block_id ( ) ;
}
}
2024-11-26 11:46:58 +00:00
void TestNode : : set_mc_server_version ( td : : int32 version , td : : int64 capabilities ) {
if ( mc_server_version_ ! = version | | mc_server_capabilities_ ! = capabilities ) {
mc_server_version_ = version ;
mc_server_capabilities_ = capabilities ;
LOG ( WARNING ) < < " server version is " < < ( mc_server_version_ > > 8 ) < < " . " < < ( mc_server_version_ & 0xff )
< < " , capabilities " < < mc_server_capabilities_ ;
2019-09-16 08:06:04 +00:00
}
2024-11-26 11:46:58 +00:00
mc_server_ok_ = ( mc_server_version_ > = min_ls_version ) & & ! ( ~ mc_server_capabilities_ & min_ls_capabilities ) ;
2019-09-16 08:06:04 +00:00
}
2024-11-26 11:46:58 +00:00
void TestNode : : set_mc_server_time ( int server_utime ) {
mc_server_time_ = server_utime ;
mc_server_time_got_at_ = now ( ) ;
LOG ( INFO ) < < " server time is " < < mc_server_time_ < < " (delta " < < mc_server_time_ - mc_server_time_got_at_ < < " ) " ;
2019-09-07 10:03:22 +00:00
}
bool TestNode : : get_server_mc_block_id ( ) {
2024-11-26 11:46:58 +00:00
int mode = ( mc_server_capabilities_ & 2 ) ? 0 : - 1 ;
2019-09-16 08:06:04 +00:00
if ( mode < 0 ) {
auto b = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getMasterchainInfo > ( ) , true ) ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) ] ( td : : Result < td : : BufferSlice > res ) - > void {
2019-09-16 08:06:04 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot get masterchain info from server " ;
return ;
2019-09-07 10:03:22 +00:00
} else {
2019-09-16 08:06:04 +00:00
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_masterchainInfo > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getMasterchainInfo " ;
} else {
auto f = F . move_as_ok ( ) ;
auto blk_id = create_block_id ( f - > last_ ) ;
auto zstate_id = create_zero_state_id ( f - > init_ ) ;
LOG ( INFO ) < < " last masterchain block is " < < blk_id . to_str ( ) ;
td : : actor : : send_closure_later ( Self , & TestNode : : got_server_mc_block_id , blk_id , zstate_id , 0 ) ;
}
2019-09-07 10:03:22 +00:00
}
2019-09-16 08:06:04 +00:00
} ) ;
} else {
auto b =
ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getMasterchainInfoExt > ( mode ) , true ) ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , mode ] ( td : : Result < td : : BufferSlice > res ) - > void {
2019-09-16 08:06:04 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot get extended masterchain info from server " ;
return ;
} else {
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_masterchainInfoExt > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getMasterchainInfoExt " ;
} else {
auto f = F . move_as_ok ( ) ;
auto blk_id = create_block_id ( f - > last_ ) ;
auto zstate_id = create_zero_state_id ( f - > init_ ) ;
LOG ( INFO ) < < " last masterchain block is " < < blk_id . to_str ( ) ;
td : : actor : : send_closure_later ( Self , & TestNode : : got_server_mc_block_id_ext , blk_id , zstate_id , mode ,
f - > version_ , f - > capabilities_ , f - > last_utime_ , f - > now_ ) ;
}
}
} ) ;
}
2019-09-07 10:03:22 +00:00
}
2019-09-16 08:06:04 +00:00
void TestNode : : got_server_mc_block_id ( ton : : BlockIdExt blkid , ton : : ZeroStateIdExt zstateid , int created ) {
2019-09-07 10:03:22 +00:00
if ( ! zstate_id_ . is_valid ( ) ) {
zstate_id_ = zstateid ;
LOG ( INFO ) < < " zerostate id set to " < < zstate_id_ . to_str ( ) ;
} else if ( zstate_id_ ! = zstateid ) {
2019-09-14 14:14:55 +00:00
LOG ( FATAL ) < < " fatal: masterchain zero state id suddenly changed: expected " < < zstate_id_ . to_str ( ) < < " , found "
2019-09-07 10:03:22 +00:00
< < zstateid . to_str ( ) ;
2019-09-14 14:14:55 +00:00
_exit ( 3 ) ;
2019-09-07 10:03:22 +00:00
return ;
}
register_blkid ( blkid ) ;
2019-09-14 14:14:55 +00:00
register_blkid ( ton : : BlockIdExt { ton : : masterchainId , ton : : shardIdAll , 0 , zstateid . root_hash , zstateid . file_hash } ) ;
2019-09-07 10:03:22 +00:00
if ( ! mc_last_id_ . is_valid ( ) ) {
mc_last_id_ = blkid ;
request_block ( blkid ) ;
// request_state(blkid);
} else if ( mc_last_id_ . id . seqno < blkid . id . seqno ) {
mc_last_id_ = blkid ;
}
2019-09-16 08:06:04 +00:00
td : : TerminalIO : : out ( ) < < " latest masterchain block known to server is " < < blkid . to_str ( ) ;
if ( created > 0 ) {
2020-04-27 12:01:46 +00:00
td : : TerminalIO : : out ( ) < < " created at " < < created < < " ( " < < now ( ) - created < < " seconds ago) \n " ;
2019-09-16 08:06:04 +00:00
} else {
td : : TerminalIO : : out ( ) < < " \n " ;
}
2019-09-14 14:14:55 +00:00
show_new_blkids ( ) ;
2019-09-07 10:03:22 +00:00
}
2019-09-16 08:06:04 +00:00
void TestNode : : got_server_mc_block_id_ext ( ton : : BlockIdExt blkid , ton : : ZeroStateIdExt zstateid , int mode , int version ,
long long capabilities , int last_utime , int server_now ) {
2024-11-26 11:46:58 +00:00
set_mc_server_version ( version , capabilities ) ;
set_mc_server_time ( server_now ) ;
2019-09-16 08:06:04 +00:00
if ( last_utime > server_now ) {
LOG ( WARNING ) < < " server claims to have a masterchain block " < < blkid . to_str ( ) < < " created at " < < last_utime
< < " ( " < < last_utime - server_now < < " seconds in the future) " ;
} else if ( last_utime < server_now - 60 ) {
LOG ( WARNING ) < < " server appears to be out of sync: its newest masterchain block is " < < blkid . to_str ( )
< < " created at " < < last_utime < < " ( " < < server_now - last_utime
< < " seconds ago according to the server's clock) " ;
2024-11-26 11:46:58 +00:00
} else if ( last_utime < mc_server_time_got_at_ - 60 ) {
2019-09-16 08:06:04 +00:00
LOG ( WARNING ) < < " either the server is out of sync, or the local clock is set incorrectly: the newest masterchain "
" block known to server is "
2024-11-26 11:46:58 +00:00
< < blkid . to_str ( ) < < " created at " < < last_utime < < " ( " < < server_now - mc_server_time_got_at_
2019-09-16 08:06:04 +00:00
< < " seconds ago according to the local clock) " ;
}
got_server_mc_block_id ( blkid , zstateid , last_utime ) ;
}
2019-09-07 10:03:22 +00:00
bool TestNode : : request_block ( ton : : BlockIdExt blkid ) {
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getBlock > ( ton : : create_tl_lite_block_id ( blkid ) ) , true ) ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , blkid ] ( td : : Result < td : : BufferSlice > res ) - > void {
2019-09-07 10:03:22 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot obtain block " < < blkid . to_str ( ) < < " from server " ;
return ;
} else {
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_blockData > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getBlock " ;
} else {
auto f = F . move_as_ok ( ) ;
auto blk_id = ton : : create_block_id ( f - > id_ ) ;
LOG ( INFO ) < < " obtained block " < < blk_id . to_str ( ) < < " from server " ;
if ( blk_id ! = blkid ) {
LOG ( ERROR ) < < " block id mismatch: expected data for block " < < blkid . to_str ( ) < < " , obtained for "
< < blk_id . to_str ( ) ;
}
td : : actor : : send_closure_later ( Self , & TestNode : : got_mc_block , blk_id , std : : move ( f - > data_ ) ) ;
}
}
} ) ;
}
bool TestNode : : request_state ( ton : : BlockIdExt blkid ) {
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getState > ( ton : : create_tl_lite_block_id ( blkid ) ) , true ) ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , blkid ] ( td : : Result < td : : BufferSlice > res ) - > void {
2019-09-07 10:03:22 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot obtain state " < < blkid . to_str ( ) < < " from server " ;
return ;
} else {
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_blockState > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getState " ;
} else {
auto f = F . move_as_ok ( ) ;
auto blk_id = ton : : create_block_id ( f - > id_ ) ;
LOG ( INFO ) < < " obtained state " < < blk_id . to_str ( ) < < " from server " ;
if ( blk_id ! = blkid ) {
LOG ( ERROR ) < < " block id mismatch: expected state for block " < < blkid . to_str ( ) < < " , obtained for "
< < blk_id . to_str ( ) ;
}
td : : actor : : send_closure_later ( Self , & TestNode : : got_mc_state , blk_id , f - > root_hash_ , f - > file_hash_ ,
std : : move ( f - > data_ ) ) ;
}
}
} ) ;
}
void TestNode : : got_mc_block ( ton : : BlockIdExt blkid , td : : BufferSlice data ) {
LOG ( INFO ) < < " obtained " < < data . size ( ) < < " data bytes for block " < < blkid . to_str ( ) ;
ton : : FileHash fhash ;
td : : sha256 ( data . as_slice ( ) , fhash . as_slice ( ) ) ;
if ( fhash ! = blkid . file_hash ) {
LOG ( ERROR ) < < " file hash mismatch for block " < < blkid . to_str ( ) < < " : expected " < < blkid . file_hash . to_hex ( )
< < " , computed " < < fhash . to_hex ( ) ;
return ;
}
register_blkid ( blkid ) ;
last_block_id_ = blkid ;
last_block_data_ = data . clone ( ) ;
if ( ! db_root_ . empty ( ) ) {
auto res = save_db_file ( fhash , std : : move ( data ) ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " error saving block file: " < < res . to_string ( ) ;
}
}
show_new_blkids ( ) ;
}
void TestNode : : got_mc_state ( ton : : BlockIdExt blkid , ton : : RootHash root_hash , ton : : FileHash file_hash ,
td : : BufferSlice data ) {
LOG ( INFO ) < < " obtained " < < data . size ( ) < < " state bytes for block " < < blkid . to_str ( ) ;
ton : : FileHash fhash ;
td : : sha256 ( data . as_slice ( ) , fhash . as_slice ( ) ) ;
if ( fhash ! = file_hash ) {
LOG ( ERROR ) < < " file hash mismatch for state " < < blkid . to_str ( ) < < " : expected " < < file_hash . to_hex ( )
< < " , computed " < < fhash . to_hex ( ) ;
return ;
}
register_blkid ( blkid ) ;
last_state_id_ = blkid ;
last_state_data_ = data . clone ( ) ;
if ( ! db_root_ . empty ( ) ) {
auto res = save_db_file ( fhash , std : : move ( data ) ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " error saving state file: " < < res . to_string ( ) ;
}
}
show_new_blkids ( ) ;
}
td : : Status TestNode : : save_db_file ( ton : : FileHash file_hash , td : : BufferSlice data ) {
std : : string fname = block : : compute_db_filename ( db_root_ + ' / ' , file_hash ) ;
for ( int i = 0 ; i < 10 ; i + + ) {
std : : string tmp_fname = block : : compute_db_tmp_filename ( db_root_ + ' / ' , file_hash , i ) ;
auto res = block : : save_binary_file ( tmp_fname , data ) ;
if ( res . is_ok ( ) ) {
if ( std : : rename ( tmp_fname . c_str ( ) , fname . c_str ( ) ) < 0 ) {
int err = errno ;
LOG ( ERROR ) < < " cannot rename " < < tmp_fname < < " to " < < fname < < " : " < < std : : strerror ( err ) ;
return td : : Status : : Error ( std : : string { " cannot rename file: " } + std : : strerror ( err ) ) ;
} else {
LOG ( INFO ) < < data . size ( ) < < " bytes saved into file " < < fname ;
return td : : Status : : OK ( ) ;
}
} else if ( i = = 9 ) {
return res ;
}
}
return td : : Status : : Error ( " cannot save data file " ) ;
}
void TestNode : : run_init_queries ( ) {
2019-09-16 08:06:04 +00:00
get_server_version ( 0x100 ) ;
2019-09-07 10:03:22 +00:00
}
2019-11-15 14:02:37 +00:00
td : : Slice TestNode : : get_word ( char delim ) {
2019-09-07 10:03:22 +00:00
if ( delim = = ' ' | | ! delim ) {
skipspc ( ) ;
}
const char * ptr = parse_ptr_ ;
while ( ptr < parse_end_ & & * ptr ! = delim & & ( * ptr ! = ' \t ' | | delim ! = ' ' ) ) {
ptr + + ;
}
std : : swap ( ptr , parse_ptr_ ) ;
2019-11-15 14:02:37 +00:00
return td : : Slice { ptr , parse_ptr_ } ;
}
td : : Slice TestNode : : get_word_ext ( const char * delims , const char * specials ) {
if ( delims [ 0 ] = = ' ' ) {
skipspc ( ) ;
}
const char * ptr = parse_ptr_ ;
while ( ptr < parse_end_ & & ! strchr ( delims , * ptr ) ) {
if ( specials & & strchr ( specials , * ptr ) ) {
if ( ptr = = parse_ptr_ ) {
ptr + + ;
}
break ;
}
ptr + + ;
}
std : : swap ( ptr , parse_ptr_ ) ;
return td : : Slice { ptr , parse_ptr_ } ;
2019-09-07 10:03:22 +00:00
}
bool TestNode : : get_word_to ( std : : string & str , char delim ) {
2019-11-15 14:02:37 +00:00
str = get_word ( delim ) . str ( ) ;
return ! str . empty ( ) ;
}
bool TestNode : : get_word_to ( td : : Slice & str , char delim ) {
2019-09-07 10:03:22 +00:00
str = get_word ( delim ) ;
return ! str . empty ( ) ;
}
int TestNode : : skipspc ( ) {
int i = 0 ;
while ( parse_ptr_ < parse_end_ & & ( * parse_ptr_ = = ' ' | | * parse_ptr_ = = ' \t ' ) ) {
i + + ;
parse_ptr_ + + ;
}
return i ;
}
std : : string TestNode : : get_line_tail ( bool remove_spaces ) const {
const char * ptr = parse_ptr_ , * end = parse_end_ ;
if ( remove_spaces ) {
while ( ptr < end & & ( * ptr = = ' ' | | * ptr = = ' \t ' ) ) {
ptr + + ;
}
while ( ptr < end & & ( end [ - 1 ] = = ' ' | | end [ - 1 ] = = ' \t ' ) ) {
- - end ;
}
}
return std : : string { ptr , end } ;
}
bool TestNode : : eoln ( ) const {
return parse_ptr_ = = parse_end_ ;
}
bool TestNode : : seekeoln ( ) {
skipspc ( ) ;
return eoln ( ) ;
}
2020-02-28 10:28:47 +00:00
bool TestNode : : parse_account_addr ( ton : : WorkchainId & wc , ton : : StdSmcAddress & addr , bool allow_none ) {
auto word = get_word ( ) ;
if ( allow_none & & ( word = = " none " | | word = = " root " ) ) {
wc = ton : : workchainInvalid ;
return true ;
}
return block : : parse_std_account_addr ( word , wc , addr ) | | set_error ( " cannot parse account address " ) ;
2019-09-07 10:03:22 +00:00
}
2020-05-07 06:35:23 +00:00
bool TestNode : : parse_account_addr_ext ( ton : : WorkchainId & wc , ton : : StdSmcAddress & addr , int & addr_ext , bool allow_none ) {
addr_ext = 0 ;
auto word = get_word ( ) ;
if ( allow_none & & ( word = = " none " | | word = = " root " ) ) {
wc = ton : : workchainInvalid ;
return true ;
}
if ( word = = " config " | | word = = " elector " | | word = = " dnsroot " ) {
wc = ton : : masterchainId ;
addr . set_zero ( ) ;
addr_ext = 1 + ( word = = " elector " ) + ( word = = " dnsroot " ) * 2 ;
if ( addr_ext = = 1 & & config_addr_queried_ ) {
addr = config_addr_ ;
addr_ext = 0 ;
} else if ( addr_ext = = 2 & & elect_addr_queried_ ) {
addr = elect_addr_ ;
addr_ext = 0 ;
} else if ( addr_ext = = 3 & & dns_root_queried_ ) {
addr = dns_root_ ;
addr_ext = 0 ;
}
return true ;
}
return block : : parse_std_account_addr ( word , wc , addr ) | | set_error ( " cannot parse account address " ) ;
}
2019-11-15 14:02:37 +00:00
bool TestNode : : convert_uint64 ( td : : Slice word , td : : uint64 & val ) {
2019-09-07 10:03:22 +00:00
val = ~ 0ULL ;
if ( word . empty ( ) ) {
return false ;
}
2019-11-15 14:02:37 +00:00
const char * ptr = word . data ( ) ;
2019-09-07 10:03:22 +00:00
char * end = nullptr ;
val = std : : strtoull ( ptr , & end , 10 ) ;
if ( end = = ptr + word . size ( ) ) {
return true ;
} else {
val = ~ 0ULL ;
return false ;
}
}
2019-11-15 14:02:37 +00:00
bool TestNode : : convert_int64 ( td : : Slice word , td : : int64 & val ) {
2019-09-07 10:03:22 +00:00
val = ( ~ 0ULL < < 63 ) ;
if ( word . empty ( ) ) {
return false ;
}
2019-11-15 14:02:37 +00:00
const char * ptr = word . data ( ) ;
2019-09-07 10:03:22 +00:00
char * end = nullptr ;
val = std : : strtoll ( ptr , & end , 10 ) ;
if ( end = = ptr + word . size ( ) ) {
return true ;
} else {
val = ( ~ 0ULL < < 63 ) ;
return false ;
}
}
2019-11-15 14:02:37 +00:00
bool TestNode : : convert_uint32 ( td : : Slice word , td : : uint32 & val ) {
2019-09-07 10:03:22 +00:00
td : : uint64 tmp ;
if ( convert_uint64 ( word , tmp ) & & ( td : : uint32 ) tmp = = tmp ) {
val = ( td : : uint32 ) tmp ;
return true ;
} else {
return false ;
}
}
2019-11-15 14:02:37 +00:00
bool TestNode : : convert_int32 ( td : : Slice word , td : : int32 & val ) {
2019-09-07 10:03:22 +00:00
td : : int64 tmp ;
if ( convert_int64 ( word , tmp ) & & ( td : : int32 ) tmp = = tmp ) {
val = ( td : : int32 ) tmp ;
return true ;
} else {
return false ;
}
}
bool TestNode : : parse_lt ( ton : : LogicalTime & lt ) {
return convert_uint64 ( get_word ( ) , lt ) | | set_error ( " cannot parse logical time " ) ;
}
bool TestNode : : parse_uint32 ( td : : uint32 & val ) {
return convert_uint32 ( get_word ( ) , val ) | | set_error ( " cannot parse 32-bit unsigned integer " ) ;
}
2020-02-28 10:28:47 +00:00
bool TestNode : : parse_int32 ( td : : int32 & val ) {
return convert_int32 ( get_word ( ) , val ) | | set_error ( " cannot parse 32-bit integer " ) ;
}
bool TestNode : : parse_int16 ( int & val ) {
return ( convert_int32 ( get_word ( ) , val ) & & val = = ( td : : int16 ) val ) | | set_error ( " cannot parse 16-bit integer " ) ;
}
2019-09-07 10:03:22 +00:00
bool TestNode : : set_error ( td : : Status error ) {
if ( error . is_ok ( ) ) {
return true ;
}
LOG ( ERROR ) < < " error: " < < error . to_string ( ) ;
if ( error_ . is_ok ( ) ) {
error_ = std : : move ( error ) ;
}
return false ;
}
int TestNode : : parse_hex_digit ( int c ) {
if ( c > = ' 0 ' & & c < = ' 9 ' ) {
return c - ' 0 ' ;
}
c | = 0x20 ;
if ( c > = ' a ' & & c < = ' z ' ) {
return c - ' a ' + 10 ;
}
return - 1 ;
}
2019-11-15 14:02:37 +00:00
bool TestNode : : parse_hash ( td : : Slice str , ton : : Bits256 & hash ) {
return str . size ( ) = = 64 & & parse_hash ( str . data ( ) , hash ) ;
}
2019-09-07 10:03:22 +00:00
bool TestNode : : parse_hash ( const char * str , ton : : Bits256 & hash ) {
unsigned char * data = hash . data ( ) ;
for ( int i = 0 ; i < 32 ; i + + ) {
int a = parse_hex_digit ( str [ 2 * i ] ) ;
if ( a < 0 ) {
return false ;
}
int b = parse_hex_digit ( str [ 2 * i + 1 ] ) ;
if ( b < 0 ) {
return false ;
}
data [ i ] = ( unsigned char ) ( ( a < < 4 ) + b ) ;
}
return true ;
}
bool TestNode : : parse_block_id_ext ( std : : string blkid_str , ton : : BlockIdExt & blkid , bool allow_incomplete ) const {
if ( blkid_str . empty ( ) ) {
return false ;
}
auto fc = blkid_str [ 0 ] ;
if ( fc = = ' B ' | | fc = = ' # ' ) {
unsigned n = 0 ;
if ( sscanf ( blkid_str . c_str ( ) , fc = = ' B ' ? " BLK#%u " : " #%u " , & n ) ! = 1 | | ! n | | n > known_blk_ids_ . size ( ) ) {
return false ;
}
blkid = known_blk_ids_ . at ( n - 1 ) ;
return true ;
}
if ( blkid_str [ 0 ] ! = ' ( ' ) {
return false ;
}
auto pos = blkid_str . find ( ' ) ' ) ;
if ( pos = = std : : string : : npos | | pos > = 38 ) {
return false ;
}
char buffer [ 40 ] ;
std : : memcpy ( buffer , blkid_str . c_str ( ) , pos + 1 ) ;
buffer [ pos + 1 ] = 0 ;
unsigned long long shard ;
if ( sscanf ( buffer , " (%d,%016llx,%u) " , & blkid . id . workchain , & shard , & blkid . id . seqno ) ! = 3 ) {
return false ;
}
blkid . id . shard = shard ;
if ( ! blkid . id . is_valid_full ( ) ) {
return false ;
}
pos + + ;
if ( pos = = blkid_str . size ( ) ) {
blkid . root_hash . set_zero ( ) ;
blkid . file_hash . set_zero ( ) ;
return complete_blkid ( blkid . id , blkid ) | | allow_incomplete ;
}
return pos + 2 * 65 = = blkid_str . size ( ) & & blkid_str [ pos ] = = ' : ' & & blkid_str [ pos + 65 ] = = ' : ' & &
parse_hash ( blkid_str . c_str ( ) + pos + 1 , blkid . root_hash ) & &
parse_hash ( blkid_str . c_str ( ) + pos + 66 , blkid . file_hash ) & & blkid . is_valid_full ( ) ;
}
bool TestNode : : parse_block_id_ext ( ton : : BlockIdExt & blk , bool allow_incomplete ) {
2019-11-15 14:02:37 +00:00
return parse_block_id_ext ( get_word ( ) . str ( ) , blk , allow_incomplete ) | | set_error ( " cannot parse BlockIdExt " ) ;
2019-09-07 10:03:22 +00:00
}
bool TestNode : : parse_hash ( ton : : Bits256 & hash ) {
auto word = get_word ( ) ;
2019-11-15 14:02:37 +00:00
return parse_hash ( word , hash ) | | set_error ( " cannot parse hash " ) ;
2019-09-07 10:03:22 +00:00
}
2019-11-15 14:02:37 +00:00
bool TestNode : : convert_shard_id ( td : : Slice str , ton : : ShardIdFull & shard ) {
2019-09-07 10:03:22 +00:00
shard . workchain = ton : : workchainInvalid ;
shard . shard = 0 ;
auto pos = str . find ( ' : ' ) ;
if ( pos = = std : : string : : npos | | pos > 10 ) {
return false ;
}
if ( ! convert_int32 ( str . substr ( 0 , pos ) , shard . workchain ) ) {
return false ;
}
int t = 64 ;
while ( + + pos < str . size ( ) ) {
int z = parse_hex_digit ( str [ pos ] ) ;
if ( z < 0 ) {
if ( t = = 64 ) {
shard . shard = ton : : shardIdAll ;
}
return pos = = str . size ( ) - 1 & & str [ pos ] = = ' _ ' ;
}
t - = 4 ;
if ( t > = 0 ) {
shard . shard | = ( ( td : : uint64 ) z < < t ) ;
}
}
return true ;
}
bool TestNode : : parse_shard_id ( ton : : ShardIdFull & shard ) {
return convert_shard_id ( get_word ( ) , shard ) | | set_error ( " cannot parse full shard identifier or prefix " ) ;
}
bool TestNode : : set_error ( std : : string err_msg ) {
return set_error ( td : : Status : : Error ( - 1 , err_msg ) ) ;
}
void TestNode : : parse_line ( td : : BufferSlice data ) {
line_ = data . as_slice ( ) . str ( ) ;
parse_ptr_ = line_ . c_str ( ) ;
parse_end_ = parse_ptr_ + line_ . size ( ) ;
error_ = td : : Status : : OK ( ) ;
if ( seekeoln ( ) ) {
return ;
}
if ( ! do_parse_line ( ) | | error_ . is_error ( ) ) {
show_context ( ) ;
LOG ( ERROR ) < < ( error_ . is_ok ( ) ? " Syntax error " : error_ . to_string ( ) ) ;
error_ = td : : Status : : OK ( ) ;
}
show_new_blkids ( ) ;
}
void TestNode : : show_context ( ) const {
const char * ptr = line_ . c_str ( ) ;
CHECK ( parse_ptr_ > = ptr & & parse_ptr_ < = parse_end_ ) ;
auto out = td : : TerminalIO : : out ( ) ;
for ( ; ptr < parse_ptr_ ; ptr + + ) {
out < < ( char ) ( * ptr = = ' \t ' ? * ptr : ' ' ) ;
}
out < < " ^ " < < ' \n ' ;
}
bool TestNode : : show_help ( std : : string command ) {
td : : TerminalIO : : out ( )
< < " list of available commands: \n "
" time \t Get server time \n "
" remote-version \t Shows server time, version and capabilities \n "
" last \t Get last block and state info from server \n "
" sendfile <filename> \t Load a serialized message from <filename> and send it to server \n "
" status \t Show connection and local database status \n "
" getaccount <addr> [<block-id-ext>] \t Loads the most recent state of specified account; <addr> is in "
" [<workchain>:]<hex-or-base64-addr> format \n "
" saveaccount[code|data] <filename> <addr> [<block-id-ext>] \t Saves into specified file the most recent state "
" (StateInit) or just the code or data of specified account; <addr> is in "
" [<workchain>:]<hex-or-base64-addr> format \n "
2024-10-31 06:54:05 +00:00
" runmethod[full] <addr> [<block-id-ext>] <name> <params>... \t Runs GET method <name> of account "
2020-02-28 10:28:47 +00:00
" <addr> "
2019-09-07 10:03:22 +00:00
" with specified parameters \n "
2020-02-28 10:28:47 +00:00
" dnsresolve [<block-id-ext>] <domain> [<category>] \t Resolves a domain starting from root dns smart contract \n "
" dnsresolvestep <addr> [<block-id-ext>] <domain> [<category>] \t Resolves a subdomain using dns smart contract "
" <addr> \n "
2019-09-07 10:03:22 +00:00
" allshards [<block-id-ext>] \t Shows shard configuration from the most recent masterchain "
" state or from masterchain state corresponding to <block-id-ext> \n "
" getconfig [<param>...] \t Shows specified or all configuration parameters from the latest masterchain state \n "
" getconfigfrom <block-id-ext> [<param>...] \t Shows specified or all configuration parameters from the "
" masterchain state of <block-id-ext> \n "
2020-04-27 12:01:46 +00:00
" getkeyconfig <block-id-ext> [<param>...] \t Shows specified or all configuration parameters from the "
" previous key block with respect to <block-id-ext> \n "
2019-09-07 10:03:22 +00:00
" saveconfig <filename> [<block-id-ext>] \t Saves all configuration parameters into specified file \n "
" gethead <block-id-ext> \t Shows block header for <block-id-ext> \n "
" getblock <block-id-ext> \t Downloads block \n "
" dumpblock <block-id-ext> \t Downloads and dumps specified block \n "
" getstate <block-id-ext> \t Downloads state corresponding to specified block \n "
" dumpstate <block-id-ext> \t Downloads and dumps state corresponding to specified block \n "
" dumptrans <block-id-ext> <account-id> <trans-lt> \t Dumps one transaction of specified account \n "
" lasttrans[dump] <account-id> <trans-lt> <trans-hash> [<count>] \t Shows or dumps specified transaction and "
" several preceding "
" ones \n "
2024-06-27 13:12:23 +00:00
" listblocktrans[rev][meta] <block-id-ext> <count> [<start-account-id> <start-trans-lt>] \t Lists block "
" transactions, starting immediately after or before the specified one \n "
2019-09-14 14:14:55 +00:00
" blkproofchain[step] <from-block-id-ext> [<to-block-id-ext>] \t Downloads and checks proof of validity of the "
" second "
" indicated block (or the last known masterchain block) starting from given block \n "
2019-09-07 10:03:22 +00:00
" byseqno <workchain> <shard-prefix> <seqno> \t Looks up a block by workchain, shard and seqno, and shows its "
" header \n "
" bylt <workchain> <shard-prefix> <lt> \t Looks up a block by workchain, shard and logical time, and shows its "
" header \n "
" byutime <workchain> <shard-prefix> <utime> \t Looks up a block by workchain, shard and creation time, and "
" shows its header \n "
2019-11-28 14:44:14 +00:00
" creatorstats <block-id-ext> [<count> [<start-pubkey>]] \t Lists block creator statistics by validator public "
" key \n "
" recentcreatorstats <block-id-ext> <start-utime> [<count> [<start-pubkey>]] \t Lists block creator statistics "
" updated after <start-utime> by validator public "
" key \n "
2024-10-15 14:59:12 +00:00
" checkload[all|severe][-v2] <start-utime> <end-utime> [<savefile-prefix>] \t Checks whether all validators "
" worked properly during specified time "
2020-04-27 12:01:46 +00:00
" interval, and optionally saves proofs into <savefile-prefix>-<n>.boc \n "
" loadproofcheck <filename> \t Checks a validator misbehavior proof previously created by checkload \n "
" pastvalsets \t Lists known past validator set ids and their hashes \n "
" savecomplaints <election-id> <filename-pfx> \t Saves all complaints registered for specified validator set id "
" into files <filename-pfx><complaint-hash>.boc \n "
" complaintprice <expires-in> <complaint-boc> \t Computes the price (in nanograms) for creating a complaint \n "
2024-03-26 12:55:58 +00:00
" msgqueuesizes \t Shows current sizes of outbound message queues in all shards \n "
2024-09-13 17:47:30 +00:00
" dispatchqueueinfo <block-id> \t Shows list of account dispatch queue of a block \n "
" dispatchqueuemessages <block-id> <addr> [<after-lt>] \t Shows deferred messages from account <addr>, lt > "
" <after_lt> \n "
" dispatchqueuemessagesall <block-id> [<after-addr> [<after-lt>]] \t Shows messages from dispatch queue of a "
" block, starting after <after_addr>, <after-lt> \n "
2019-09-07 10:03:22 +00:00
" known \t Shows the list of all known block ids \n "
2020-03-31 13:11:56 +00:00
" knowncells \t Shows the list of hashes of all known (cached) cells \n "
" dumpcell <hex-hash-pfx> \n Dumps a cached cell by a prefix of its hash \n "
" dumpcellas <tlb-type> <hex-hash-pfx> \n Finds a cached cell by a prefix of its hash and prints it as a value "
" of <tlb-type> \n "
2019-09-07 10:03:22 +00:00
" privkey <filename> \t Loads a private key from file \n "
" help [<command>] \t This help \n "
" quit \t Exit \n " ;
return true ;
}
bool TestNode : : do_parse_line ( ) {
ton : : WorkchainId workchain = ton : : masterchainId ; // change to basechain later
2020-05-07 06:35:23 +00:00
int addr_ext = 0 ;
2024-09-13 17:47:30 +00:00
ton : : StdSmcAddress addr = ton : : StdSmcAddress : : zero ( ) ;
2019-09-07 10:03:22 +00:00
ton : : BlockIdExt blkid { } ;
2024-09-13 17:47:30 +00:00
ton : : LogicalTime lt = 0 ;
2019-09-07 10:03:22 +00:00
ton : : Bits256 hash { } ;
ton : : ShardIdFull shard { } ;
ton : : BlockSeqno seqno { } ;
ton : : UnixTime utime { } ;
unsigned count { } ;
2019-11-15 14:02:37 +00:00
std : : string word = get_word ( ) . str ( ) ;
2019-09-07 10:03:22 +00:00
skipspc ( ) ;
if ( word = = " time " ) {
return eoln ( ) & & get_server_time ( ) ;
} else if ( word = = " remote-version " ) {
return eoln ( ) & & get_server_version ( ) ;
} else if ( word = = " last " ) {
return eoln ( ) & & get_server_mc_block_id ( ) ;
} else if ( word = = " sendfile " ) {
return ! eoln ( ) & & set_error ( send_ext_msg_from_filename ( get_line_tail ( ) ) ) ;
2022-11-30 05:39:32 +00:00
} else if ( word = = " getaccount " | | word = = " getaccountprunned " ) {
bool prunned = word = = " getaccountprunned " ;
2020-05-07 06:35:23 +00:00
return parse_account_addr_ext ( workchain , addr , addr_ext ) & &
2022-11-30 05:39:32 +00:00
( seekeoln ( ) ? get_account_state ( workchain , addr , mc_last_id_ , addr_ext , " " , - 1 , prunned )
: parse_block_id_ext ( blkid ) & & seekeoln ( ) & &
get_account_state ( workchain , addr , blkid , addr_ext , " " , - 1 , prunned ) ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " saveaccount " | | word = = " saveaccountcode " | | word = = " saveaccountdata " ) {
std : : string filename ;
int mode = ( ( word . c_str ( ) [ 11 ] > > 1 ) & 3 ) ;
2020-05-07 06:35:23 +00:00
return get_word_to ( filename ) & & parse_account_addr_ext ( workchain , addr , addr_ext ) & &
( seekeoln ( ) ? get_account_state ( workchain , addr , mc_last_id_ , addr_ext , filename , mode )
: parse_block_id_ext ( blkid ) & & seekeoln ( ) & &
get_account_state ( workchain , addr , blkid , addr_ext , filename , mode ) ) ;
2020-02-28 10:28:47 +00:00
} else if ( word = = " runmethod " | | word = = " runmethodx " | | word = = " runmethodfull " ) {
2019-09-07 10:03:22 +00:00
std : : string method ;
2020-05-07 06:35:23 +00:00
return parse_account_addr_ext ( workchain , addr , addr_ext ) & & get_word_to ( method ) & &
2019-09-07 10:03:22 +00:00
( parse_block_id_ext ( method , blkid ) ? get_word_to ( method ) : ( blkid = mc_last_id_ ) . is_valid ( ) ) & &
2020-05-07 06:35:23 +00:00
parse_run_method ( workchain , addr , blkid , addr_ext , method , word . size ( ) < = 10 ) ;
2020-02-28 10:28:47 +00:00
} else if ( word = = " dnsresolve " | | word = = " dnsresolvestep " ) {
workchain = ton : : workchainInvalid ;
bool step = ( word . size ( ) > 10 ) ;
std : : string domain ;
2022-06-29 09:30:19 +00:00
std : : string cat_str ;
2020-02-28 10:28:47 +00:00
return ( ! step | | parse_account_addr ( workchain , addr ) ) & & get_word_to ( domain ) & &
( parse_block_id_ext ( domain , blkid ) ? get_word_to ( domain ) : ( blkid = mc_last_id_ ) . is_valid ( ) ) & &
2022-06-29 09:30:19 +00:00
( seekeoln ( ) | | get_word_to ( cat_str ) ) & & seekeoln ( ) & &
dns_resolve_start ( workchain , addr , blkid , domain ,
cat_str . empty ( ) ? td : : Bits256 : : zero ( ) : td : : sha256_bits256 ( td : : as_slice ( cat_str ) ) ,
step ? 3 : 0 ) ;
2020-04-08 19:49:28 +00:00
} else if ( word = = " allshards " | | word = = " allshardssave " ) {
std : : string filename ;
return ( word . size ( ) < = 9 | | get_word_to ( filename ) ) & &
( seekeoln ( ) ? get_all_shards ( filename )
: ( parse_block_id_ext ( blkid ) & & seekeoln ( ) & & get_all_shards ( filename , false , blkid ) ) ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " saveconfig " ) {
blkid = mc_last_id_ ;
std : : string filename ;
return get_word_to ( filename ) & & ( seekeoln ( ) | | parse_block_id_ext ( blkid ) ) & & seekeoln ( ) & &
2020-04-27 12:01:46 +00:00
parse_get_config_params ( blkid , - 1 , filename ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " getconfig " | | word = = " getconfigfrom " ) {
blkid = mc_last_id_ ;
2020-04-27 12:01:46 +00:00
return ( word = = " getconfig " | | parse_block_id_ext ( blkid ) ) & & parse_get_config_params ( blkid , 0 ) ;
} else if ( word = = " getkeyconfig " ) {
return parse_block_id_ext ( blkid ) & & parse_get_config_params ( blkid , 0x8000 ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " getblock " ) {
return parse_block_id_ext ( blkid ) & & seekeoln ( ) & & get_block ( blkid , false ) ;
} else if ( word = = " dumpblock " ) {
return parse_block_id_ext ( blkid ) & & seekeoln ( ) & & get_block ( blkid , true ) ;
} else if ( word = = " getstate " ) {
return parse_block_id_ext ( blkid ) & & seekeoln ( ) & & get_state ( blkid , false ) ;
} else if ( word = = " dumpstate " ) {
return parse_block_id_ext ( blkid ) & & seekeoln ( ) & & get_state ( blkid , true ) ;
} else if ( word = = " gethead " ) {
2020-04-27 12:01:46 +00:00
return parse_block_id_ext ( blkid ) & & seekeoln ( ) & & get_show_block_header ( blkid , 0xffff ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " dumptrans " ) {
return parse_block_id_ext ( blkid ) & & parse_account_addr ( workchain , addr ) & & parse_lt ( lt ) & & seekeoln ( ) & &
get_one_transaction ( blkid , workchain , addr , lt , true ) ;
} else if ( word = = " lasttrans " | | word = = " lasttransdump " ) {
2019-09-30 12:49:45 +00:00
count = 10 ;
return parse_account_addr ( workchain , addr ) & & parse_lt ( lt ) & & parse_hash ( hash ) & &
( seekeoln ( ) | | parse_uint32 ( count ) ) & & seekeoln ( ) & &
get_last_transactions ( workchain , addr , lt , hash , count , word = = " lasttransdump " ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " listblocktrans " | | word = = " listblocktransrev " ) {
lt = 0 ;
int mode = ( word = = " listblocktrans " ? 7 : 0x47 ) ;
return parse_block_id_ext ( blkid ) & & parse_uint32 ( count ) & &
( seekeoln ( ) | | ( parse_hash ( hash ) & & parse_lt ( lt ) & & ( mode | = 128 ) & & seekeoln ( ) ) ) & &
get_block_transactions ( blkid , mode , count , hash , lt ) ;
2024-06-27 13:12:23 +00:00
} else if ( word = = " listblocktransmeta " | | word = = " listblocktransrevmeta " ) {
lt = 0 ;
int mode = ( word = = " listblocktransmeta " ? 7 : 0x47 ) ;
mode | = 256 ;
return parse_block_id_ext ( blkid ) & & parse_uint32 ( count ) & &
( seekeoln ( ) | | ( parse_hash ( hash ) & & parse_lt ( lt ) & & ( mode | = 128 ) & & seekeoln ( ) ) ) & &
get_block_transactions ( blkid , mode , count , hash , lt ) ;
2019-09-14 14:14:55 +00:00
} else if ( word = = " blkproofchain " | | word = = " blkproofchainstep " ) {
ton : : BlockIdExt blkid2 { } ;
return parse_block_id_ext ( blkid ) & & ( seekeoln ( ) | | parse_block_id_ext ( blkid2 ) ) & & seekeoln ( ) & &
get_block_proof ( blkid , blkid2 , blkid2 . is_valid ( ) + ( word = = " blkproofchain " ) * 0x1000 ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " byseqno " ) {
2020-04-27 12:01:46 +00:00
return parse_shard_id ( shard ) & & parse_uint32 ( seqno ) & & seekeoln ( ) & & lookup_show_block ( shard , 1 , seqno ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " byutime " ) {
2020-04-27 12:01:46 +00:00
return parse_shard_id ( shard ) & & parse_uint32 ( utime ) & & seekeoln ( ) & & lookup_show_block ( shard , 4 , utime ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " bylt " ) {
2020-04-27 12:01:46 +00:00
return parse_shard_id ( shard ) & & parse_lt ( lt ) & & seekeoln ( ) & & lookup_show_block ( shard , 2 , lt ) ;
2019-11-28 14:44:14 +00:00
} else if ( word = = " creatorstats " | | word = = " recentcreatorstats " ) {
count = 1000 ;
int mode = ( word = = " recentcreatorstats " ? 4 : 0 ) ;
2020-04-27 12:01:46 +00:00
return parse_block_id_ext ( blkid ) & & ( ! mode | | parse_uint32 ( utime ) ) & &
( seekeoln ( ) ? ( mode | = 0x100 ) : parse_uint32 ( count ) ) & & ( seekeoln ( ) | | ( parse_hash ( hash ) & & ( mode | = 1 ) ) ) & &
seekeoln ( ) & & get_creator_stats ( blkid , mode , count , hash , utime ) ;
2024-10-15 14:59:12 +00:00
} else if ( word = = " checkload " | | word = = " checkloadall " | | word = = " checkloadsevere " | | word = = " checkload-v2 " | |
word = = " checkloadall-v2 " | | word = = " checkloadsevere-v2 " ) {
int time1 , time2 , mode = 0 ;
if ( word = = " checkloadsevere " | | word = = " checkloadsevere-v2 " ) {
mode | = 1 ;
}
if ( td : : ends_with ( word , " -v2 " ) ) {
mode | = 4 ;
}
2020-04-27 12:01:46 +00:00
std : : string file_pfx ;
return parse_int32 ( time1 ) & & parse_int32 ( time2 ) & & ( seekeoln ( ) | | ( ( mode | = 2 ) & & get_word_to ( file_pfx ) ) ) & &
seekeoln ( ) & & check_validator_load ( time1 , time2 , mode , file_pfx ) ;
} else if ( word = = " loadproofcheck " ) {
std : : string filename ;
return get_word_to ( filename ) & & seekeoln ( ) & & set_error ( check_validator_load_proof ( filename ) ) ;
} else if ( word = = " pastvalsets " ) {
return eoln ( ) & & get_past_validator_sets ( ) ;
} else if ( word = = " savecomplaints " ) {
td : : uint32 elect_id ;
std : : string file_pfx ;
return parse_uint32 ( elect_id ) & & get_word_to ( file_pfx ) & & seekeoln ( ) & & get_complaints ( elect_id , file_pfx ) ;
} else if ( word = = " complaintprice " ) {
td : : uint32 expire_in ;
std : : string filename ;
return parse_uint32 ( expire_in ) & & get_word_to ( filename ) & & seekeoln ( ) & &
set_error ( get_complaint_price ( expire_in , filename ) ) ;
2024-03-26 12:55:58 +00:00
} else if ( word = = " msgqueuesizes " ) {
return get_msg_queue_sizes ( ) ;
2024-09-13 17:47:30 +00:00
} else if ( word = = " dispatchqueueinfo " ) {
return parse_block_id_ext ( blkid ) & & seekeoln ( ) & & get_dispatch_queue_info ( blkid ) ;
} else if ( word = = " dispatchqueuemessages " | | word = = " dispatchqueuemessagesall " ) {
bool one_account = word = = " dispatchqueuemessages " ;
if ( ! parse_block_id_ext ( blkid ) ) {
return false ;
}
workchain = blkid . id . workchain ;
return ( ( ! one_account & & seekeoln ( ) ) | | parse_account_addr ( workchain , addr ) ) & & ( seekeoln ( ) | | parse_lt ( lt ) ) & &
seekeoln ( ) & & get_dispatch_queue_messages ( blkid , workchain , addr , lt , one_account ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " known " ) {
return eoln ( ) & & show_new_blkids ( true ) ;
2020-03-31 13:11:56 +00:00
} else if ( word = = " knowncells " ) {
return eoln ( ) & & list_cached_cells ( ) ;
} else if ( word = = " dumpcell " | | word = = " dumpcellas " ) {
td : : Slice chash ;
td : : Slice tname ;
return ( word = = " dumpcell " | | get_word_to ( tname ) ) & & get_word_to ( chash ) & & seekeoln ( ) & &
dump_cached_cell ( chash , tname ) ;
2019-09-07 10:03:22 +00:00
} else if ( word = = " quit " & & eoln ( ) ) {
LOG ( INFO ) < < " Exiting " ;
stop ( ) ;
// std::exit(0);
return true ;
} else if ( word = = " help " ) {
return show_help ( get_line_tail ( ) ) ;
} else {
td : : TerminalIO : : out ( ) < < " unknown command: " < < word < < " ; type `help` to get help " < < ' \n ' ;
return false ;
}
}
td : : Result < std : : pair < Ref < vm : : Cell > , std : : shared_ptr < vm : : StaticBagOfCellsDb > > > lazy_boc_deserialize (
td : : BufferSlice data ) {
vm : : StaticBagOfCellsDbLazy : : Options options ;
options . check_crc32c = true ;
2020-05-27 18:10:46 +00:00
TRY_RESULT ( boc , vm : : StaticBagOfCellsDbLazy : : create ( td : : BufferSliceBlobView : : create ( std : : move ( data ) ) , options ) ) ;
2019-09-07 10:03:22 +00:00
TRY_RESULT ( rc , boc - > get_root_count ( ) ) ;
if ( rc ! = 1 ) {
return td : : Status : : Error ( - 668 , " bag-of-cells is not standard (exactly one root cell expected) " ) ;
}
TRY_RESULT ( root , boc - > get_root_cell ( 0 ) ) ;
return std : : make_pair ( std : : move ( root ) , std : : move ( boc ) ) ;
}
td : : Status TestNode : : send_ext_msg_from_filename ( std : : string filename ) {
auto F = td : : read_file ( filename ) ;
if ( F . is_error ( ) ) {
auto err = F . move_as_error ( ) ;
LOG ( ERROR ) < < " failed to read file ` " < < filename < < " `: " < < err . to_string ( ) ;
return err ;
}
if ( ready_ & & ! client_ . empty ( ) ) {
LOG ( ERROR ) < < " sending query from file " < < filename ;
auto P = td : : PromiseCreator : : lambda ( [ ] ( td : : Result < td : : BufferSlice > R ) {
if ( R . is_error ( ) ) {
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_sendMsgStatus > ( R . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.sendMessage " ;
} else {
int status = F . move_as_ok ( ) - > status_ ;
LOG ( INFO ) < < " external message status is " < < status ;
}
} ) ;
auto b =
ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_sendMessage > ( F . move_as_ok ( ) ) , true ) ;
return envelope_send_query ( std : : move ( b ) , std : : move ( P ) ) ? td : : Status : : OK ( )
: td : : Status : : Error ( " cannot send query to server " ) ;
} else {
return td : : Status : : Error ( " server connection not ready " ) ;
}
}
bool TestNode : : get_account_state ( ton : : WorkchainId workchain , ton : : StdSmcAddress addr , ton : : BlockIdExt ref_blkid ,
2022-11-30 05:39:32 +00:00
int addr_ext , std : : string filename , int mode , bool prunned ) {
2019-09-07 10:03:22 +00:00
if ( ! ref_blkid . is_valid ( ) ) {
return set_error ( " must obtain last block information before making other queries " ) ;
}
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
return set_error ( " server connection not ready " ) ;
}
2020-05-07 06:35:23 +00:00
if ( addr_ext ) {
2022-11-30 05:39:32 +00:00
return get_special_smc_addr (
addr_ext , [ this , ref_blkid , filename , mode , prunned ] ( td : : Result < ton : : StdSmcAddress > res ) {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot resolve special smart contract address: " < < res . move_as_error ( ) ;
} else {
get_account_state ( ton : : masterchainId , res . move_as_ok ( ) , ref_blkid , 0 , filename , mode , prunned ) ;
}
} ) ;
2020-05-07 06:35:23 +00:00
}
2019-09-07 10:03:22 +00:00
auto a = ton : : create_tl_object < ton : : lite_api : : liteServer_accountId > ( workchain , addr ) ;
2022-11-30 05:39:32 +00:00
td : : BufferSlice b ;
if ( prunned ) {
b = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getAccountStatePrunned > (
ton : : create_tl_lite_block_id ( ref_blkid ) , std : : move ( a ) ) ,
true ) ;
} else {
b = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getAccountState > (
ton : : create_tl_lite_block_id ( ref_blkid ) , std : : move ( a ) ) ,
true ) ;
}
LOG ( INFO ) < < " requesting " < < ( prunned ? " prunned " : " " ) < < " account state for " < < workchain < < " : " < < addr . to_hex ( )
< < " with respect to " < < ref_blkid . to_str ( ) < < " with savefile ` " < < filename < < " ` and mode " < < mode ;
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , workchain , addr , ref_blkid , filename , mode ,
prunned ] ( td : : Result < td : : BufferSlice > R ) {
if ( R . is_error ( ) ) {
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_accountState > ( R . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getAccountState " ;
} else {
auto f = F . move_as_ok ( ) ;
td : : actor : : send_closure_later ( Self , & TestNode : : got_account_state , ref_blkid , ton : : create_block_id ( f - > id_ ) ,
ton : : create_block_id ( f - > shardblk_ ) , std : : move ( f - > shard_proof_ ) ,
std : : move ( f - > proof_ ) , std : : move ( f - > state_ ) , workchain , addr , filename , mode ,
prunned ) ;
}
} ) ;
2019-09-07 10:03:22 +00:00
}
2020-02-02 12:53:37 +00:00
td : : int64 TestNode : : compute_method_id ( std : : string method ) {
td : : int64 method_id ;
if ( ! convert_int64 ( method , method_id ) ) {
method_id = ( td : : crc16 ( td : : Slice { method } ) & 0xffff ) | 0x10000 ;
}
return method_id ;
}
2020-03-31 13:11:56 +00:00
bool TestNode : : cache_cell ( Ref < vm : : Cell > cell ) {
if ( cell . is_null ( ) ) {
return false ;
}
td : : Bits256 hash = cell - > get_hash ( ) . bits ( ) ;
LOG ( INFO ) < < " caching cell " < < hash . to_hex ( ) ;
auto res = cell_cache_ . emplace ( hash , std : : move ( cell ) ) ;
return res . second ;
}
2019-09-07 10:03:22 +00:00
bool TestNode : : parse_run_method ( ton : : WorkchainId workchain , ton : : StdSmcAddress addr , ton : : BlockIdExt ref_blkid ,
2020-05-07 06:35:23 +00:00
int addr_ext , std : : string method_name , bool ext_mode ) {
2019-11-28 14:44:14 +00:00
auto R = vm : : parse_stack_entries ( td : : Slice ( parse_ptr_ , parse_end_ ) ) ;
if ( R . is_error ( ) ) {
return set_error ( R . move_as_error ( ) . to_string ( ) ) ;
2019-09-07 10:03:22 +00:00
}
2019-11-28 14:44:14 +00:00
parse_ptr_ = parse_end_ ;
2020-05-07 06:35:23 +00:00
if ( addr_ext ) {
return get_special_smc_addr ( addr_ext , [ this , ref_blkid , method_name , ext_mode ,
args = R . move_as_ok ( ) ] ( td : : Result < ton : : StdSmcAddress > res ) mutable {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot resolve special smart contract address: " < < res . move_as_error ( ) ;
} else {
after_parse_run_method ( ton : : masterchainId , res . move_as_ok ( ) , ref_blkid , method_name , std : : move ( args ) , ext_mode ) ;
}
} ) ;
}
return after_parse_run_method ( workchain , addr , ref_blkid , method_name , R . move_as_ok ( ) , ext_mode ) ;
}
bool TestNode : : after_parse_run_method ( ton : : WorkchainId workchain , ton : : StdSmcAddress addr , ton : : BlockIdExt ref_blkid ,
std : : string method_name , std : : vector < vm : : StackEntry > params , bool ext_mode ) {
2020-03-31 13:11:56 +00:00
auto P = td : : PromiseCreator : : lambda ( [ this ] ( td : : Result < std : : vector < vm : : StackEntry > > R ) {
2020-02-28 10:28:47 +00:00
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < R . move_as_error ( ) ;
2020-03-31 13:11:56 +00:00
} else {
for ( const auto & v : R . move_as_ok ( ) ) {
v . for_each_scalar ( [ this ] ( const vm : : StackEntry & val ) {
if ( val . is_cell ( ) ) {
cache_cell ( val . as_cell ( ) ) ;
}
} ) ;
}
2020-02-28 10:28:47 +00:00
}
} ) ;
2024-02-16 13:30:43 +00:00
return start_run_method ( workchain , addr , ref_blkid , method_name , std : : move ( params ) , ext_mode ? 0x17 : 0 ,
2020-05-07 06:35:23 +00:00
std : : move ( P ) ) ;
2020-02-28 10:28:47 +00:00
}
bool TestNode : : start_run_method ( ton : : WorkchainId workchain , ton : : StdSmcAddress addr , ton : : BlockIdExt ref_blkid ,
std : : string method_name , std : : vector < vm : : StackEntry > params , int mode ,
td : : Promise < std : : vector < vm : : StackEntry > > promise ) {
2019-09-07 10:03:22 +00:00
if ( ! ref_blkid . is_valid ( ) ) {
return set_error ( " must obtain last block information before making other queries " ) ;
}
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
return set_error ( " server connection not ready " ) ;
}
auto a = ton : : create_tl_object < ton : : lite_api : : liteServer_accountId > ( workchain , addr ) ;
2020-02-02 12:53:37 +00:00
if ( ! mode ) {
auto b = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getAccountState > (
ton : : create_tl_lite_block_id ( ref_blkid ) , std : : move ( a ) ) ,
true ) ;
LOG ( INFO ) < < " requesting account state for " < < workchain < < " : " < < addr . to_hex ( ) < < " with respect to "
< < ref_blkid . to_str ( ) < < " to run method " < < method_name < < " with " < < params . size ( ) < < " parameters " ;
2020-04-10 19:06:01 +00:00
return envelope_send_query (
std : : move ( b ) , [ Self = actor_id ( this ) , workchain , addr , ref_blkid , method_name , params = std : : move ( params ) ,
promise = std : : move ( promise ) ] ( td : : Result < td : : BufferSlice > R ) mutable {
if ( R . is_error ( ) ) {
promise . set_error ( R . move_as_error ( ) ) ;
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_accountState > ( R . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getAccountState " ;
promise . set_error ( td : : Status : : Error ( " cannot parse answer to liteServer.getAccountState " ) ) ;
} else {
auto f = F . move_as_ok ( ) ;
td : : actor : : send_closure_later ( Self , & TestNode : : run_smc_method , 0 , ref_blkid , ton : : create_block_id ( f - > id_ ) ,
ton : : create_block_id ( f - > shardblk_ ) , std : : move ( f - > shard_proof_ ) ,
std : : move ( f - > proof_ ) , std : : move ( f - > state_ ) , workchain , addr , method_name ,
std : : move ( params ) , td : : BufferSlice ( ) , td : : BufferSlice ( ) , td : : BufferSlice ( ) ,
- 0x10000 , std : : move ( promise ) ) ;
}
} ) ;
2020-02-02 12:53:37 +00:00
} else {
td : : int64 method_id = compute_method_id ( method_name ) ;
2020-02-08 19:24:24 +00:00
// set serialization limits
vm : : FakeVmStateLimits fstate ( 1000 ) ; // limit recursive (de)serialization calls
vm : : VmStateInterface : : Guard guard ( & fstate ) ;
2020-02-02 12:53:37 +00:00
// serialize parameters
vm : : CellBuilder cb ;
Ref < vm : : Cell > cell ;
if ( ! ( vm : : Stack { params } . serialize ( cb ) & & cb . finalize_to ( cell ) ) ) {
return set_error ( " cannot serialize stack with get-method parameters " ) ;
}
auto stk = vm : : std_boc_serialize ( std : : move ( cell ) ) ;
if ( stk . is_error ( ) ) {
return set_error ( " cannot serialize stack with get-method parameters : " s + stk . move_as_error ( ) . to_string ( ) ) ;
}
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_runSmcMethod > ( mode , ton : : create_tl_lite_block_id ( ref_blkid ) ,
std : : move ( a ) , method_id , stk . move_as_ok ( ) ) ,
true ) ;
LOG ( INFO ) < < " requesting remote get-method execution for " < < workchain < < " : " < < addr . to_hex ( )
< < " with respect to " < < ref_blkid . to_str ( ) < < " to run method " < < method_name < < " with "
< < params . size ( ) < < " parameters " ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , workchain , addr , ref_blkid , method_name , mode ,
params = std : : move ( params ) ,
promise = std : : move ( promise ) ] ( td : : Result < td : : BufferSlice > R ) mutable {
2020-02-02 12:53:37 +00:00
if ( R . is_error ( ) ) {
2020-02-28 10:28:47 +00:00
promise . set_error ( R . move_as_error ( ) ) ;
2020-02-02 12:53:37 +00:00
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_runMethodResult > ( R . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.runSmcMethod " ;
2020-02-28 10:28:47 +00:00
promise . set_error ( td : : Status : : Error ( " cannot parse answer to liteServer.runSmcMethod " ) ) ;
2020-02-02 12:53:37 +00:00
} else {
auto f = F . move_as_ok ( ) ;
td : : actor : : send_closure_later ( Self , & TestNode : : run_smc_method , mode , ref_blkid , ton : : create_block_id ( f - > id_ ) ,
ton : : create_block_id ( f - > shardblk_ ) , std : : move ( f - > shard_proof_ ) ,
std : : move ( f - > proof_ ) , std : : move ( f - > state_proof_ ) , workchain , addr , method_name ,
std : : move ( params ) , std : : move ( f - > init_c7_ ) , std : : move ( f - > lib_extras_ ) ,
2020-02-28 10:28:47 +00:00
std : : move ( f - > result_ ) , f - > exit_code_ , std : : move ( promise ) ) ;
2020-02-02 12:53:37 +00:00
}
} ) ;
}
2019-09-07 10:03:22 +00:00
}
2020-05-07 06:35:23 +00:00
bool TestNode : : get_config_addr ( td : : Promise < ton : : StdSmcAddress > promise ) {
if ( config_addr_queried_ ) {
promise . set_result ( config_addr_ ) ;
return true ;
}
auto P = td : : PromiseCreator : : lambda ( [ this , promise = std : : move ( promise ) ] (
td : : Result < std : : unique_ptr < block : : Config > > R ) mutable {
TRY_RESULT_PROMISE_PREFIX ( promise , config , std : : move ( R ) , " cannot obtain configurator address from configuration: " ) ;
if ( config_addr_queried_ ) {
promise . set_result ( config_addr_ ) ;
} else {
promise . set_error ( td : : Status : : Error ( " cannot obtain configurator address from configuration parameter #0 " ) ) ;
}
} ) ;
return get_config_params ( mc_last_id_ , std : : move ( P ) , 0x3000 , " " , { 0 } ) ;
}
2020-04-27 12:01:46 +00:00
bool TestNode : : get_elector_addr ( td : : Promise < ton : : StdSmcAddress > promise ) {
if ( elect_addr_queried_ ) {
promise . set_result ( elect_addr_ ) ;
return true ;
}
auto P = td : : PromiseCreator : : lambda (
[ this , promise = std : : move ( promise ) ] ( td : : Result < std : : unique_ptr < block : : Config > > R ) mutable {
TRY_RESULT_PROMISE_PREFIX ( promise , config , std : : move ( R ) , " cannot obtain elector address from configuration: " ) ;
if ( elect_addr_queried_ ) {
promise . set_result ( elect_addr_ ) ;
} else {
promise . set_error ( td : : Status : : Error ( " cannot obtain elector address from configuration parameter #1 " ) ) ;
}
} ) ;
return get_config_params ( mc_last_id_ , std : : move ( P ) , 0x3000 , " " , { 1 } ) ;
}
2020-05-07 06:35:23 +00:00
bool TestNode : : get_dns_root ( td : : Promise < ton : : StdSmcAddress > promise ) {
if ( dns_root_queried_ ) {
promise . set_result ( dns_root_ ) ;
return true ;
}
auto P = td : : PromiseCreator : : lambda (
[ this , promise = std : : move ( promise ) ] ( td : : Result < std : : unique_ptr < block : : Config > > R ) mutable {
TRY_RESULT_PROMISE_PREFIX ( promise , config , std : : move ( R ) , " cannot obtain dns root address from configuration: " ) ;
if ( dns_root_queried_ ) {
promise . set_result ( dns_root_ ) ;
} else {
promise . set_error ( td : : Status : : Error ( " cannot obtain dns root address from configuration parameter #4 " ) ) ;
}
} ) ;
return get_config_params ( mc_last_id_ , std : : move ( P ) , 0x3000 , " " , { 4 } ) ;
}
bool TestNode : : get_special_smc_addr ( int addr_ext , td : : Promise < ton : : StdSmcAddress > promise ) {
switch ( addr_ext ) {
case 1 :
return get_config_addr ( std : : move ( promise ) ) ;
case 2 :
return get_elector_addr ( std : : move ( promise ) ) ;
case 3 :
return get_dns_root ( std : : move ( promise ) ) ;
default :
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " unknown special smart contract address class " < < addr_ext ) ) ;
return false ;
}
}
2020-04-27 12:01:46 +00:00
bool TestNode : : get_past_validator_sets ( ) {
return get_elector_addr ( [ this ] ( td : : Result < ton : : StdSmcAddress > res ) {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < res . move_as_error ( ) ;
} else {
send_past_vset_query ( res . move_as_ok ( ) ) ;
}
} ) ;
}
bool TestNode : : send_past_vset_query ( ton : : StdSmcAddress elector_addr ) {
std : : vector < vm : : StackEntry > params ;
auto P = td : : PromiseCreator : : lambda ( [ this ] ( td : : Result < std : : vector < vm : : StackEntry > > R ) {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < R . move_as_error ( ) ;
return ;
}
auto S = R . move_as_ok ( ) ;
if ( S . size ( ) < 1 | | ! S . back ( ) . is_list ( ) ) {
LOG ( ERROR ) < < " past_elections_list did not return a value of type tuple " ;
return ;
}
register_past_vset_info ( std : : move ( S . back ( ) ) ) ;
} ) ;
2024-02-16 13:30:43 +00:00
return start_run_method ( ton : : masterchainId , elector_addr , mc_last_id_ , " past_elections_list " , std : : move ( params ) , 0x17 ,
2020-04-27 12:01:46 +00:00
std : : move ( P ) ) ;
}
void TestNode : : register_past_vset_info ( vm : : StackEntry list ) {
try {
while ( ! list . empty ( ) ) {
auto tup = std : : move ( list ) . as_tuple_range ( 2 , 2 ) ;
if ( tup . is_null ( ) ) {
LOG ( ERROR ) < < " invalid list in the result of past_elections_list " ;
return ;
}
list = tup - > at ( 1 ) ;
auto t2 = tup - > at ( 0 ) . as_tuple_range ( 255 , 3 ) ;
if ( t2 . is_null ( ) ) {
LOG ( ERROR ) < < " invalid list entry in the result of past_elections_list " ;
return ;
}
auto x = t2 - > at ( 0 ) . as_int ( ) , y = t2 - > at ( 2 ) . as_int ( ) ;
if ( x . is_null ( ) | | y . is_null ( ) | | ! x - > unsigned_fits_bits ( 32 ) | | ! y - > unsigned_fits_bits ( 256 ) ) {
LOG ( ERROR ) < < " invalid components in a list entry in the result of past_elections_list " ;
return ;
}
td : : TerminalIO : : out ( ) < < " PAST_VSET \t " < < td : : dec_string ( x ) < < " \t " < < td : : hex_string ( y , true , 64 ) < < std : : endl ;
}
} catch ( vm : : VmError & err ) {
LOG ( ERROR ) < < " vm error while scanning result: " < < err . get_msg ( ) ;
}
}
bool TestNode : : get_complaints ( unsigned elect_id , std : : string file_pfx ) {
return get_elector_addr ( [ this , elect_id , file_pfx ] ( td : : Result < ton : : StdSmcAddress > res ) {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < res . move_as_error ( ) ;
} else {
send_get_complaints_query ( elect_id , res . move_as_ok ( ) , file_pfx ) ;
}
} ) ;
}
void TestNode : : send_get_complaints_query ( unsigned elect_id , ton : : StdSmcAddress elector_addr , std : : string file_pfx ) {
std : : vector < vm : : StackEntry > params ;
params . emplace_back ( td : : make_refint ( elect_id ) ) ;
auto P = td : : PromiseCreator : : lambda ( [ this , elect_id , file_pfx ] ( td : : Result < std : : vector < vm : : StackEntry > > R ) {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < R . move_as_error ( ) ;
return ;
}
auto S = R . move_as_ok ( ) ;
if ( S . size ( ) < 1 | | ! ( S . back ( ) . empty ( ) | | S . back ( ) . is_cell ( ) ) ) {
LOG ( ERROR ) < < " get_past_complaints did not return a value of type cell " ;
return ;
}
try {
save_complaints ( elect_id , std : : move ( S . back ( ) ) . as_cell ( ) , file_pfx ) ;
} catch ( vm : : VmError & err ) {
LOG ( ERROR ) < < " vm error: " < < err . get_msg ( ) ;
} catch ( vm : : VmVirtError & err ) {
LOG ( ERROR ) < < " vm virtualization error: " < < err . get_msg ( ) ;
}
} ) ;
2024-02-16 13:30:43 +00:00
start_run_method ( ton : : masterchainId , elector_addr , mc_last_id_ , " get_past_complaints " , std : : move ( params ) , 0x17 ,
2020-04-27 12:01:46 +00:00
std : : move ( P ) ) ;
}
void TestNode : : save_complaints ( unsigned elect_id , Ref < vm : : Cell > complaints , std : : string file_pfx ) {
vm : : Dictionary dict { std : : move ( complaints ) , 256 } ;
for ( auto entry : dict ) {
block : : gen : : ValidatorComplaintStatus : : Record rec ;
block : : gen : : ValidatorComplaint : : Record rec2 ;
if ( ! ( tlb : : csr_unpack ( std : : move ( entry . second ) , rec ) & & tlb : : unpack_cell ( rec . complaint , rec2 ) ) ) {
LOG ( ERROR ) < < " cannot unpack complaint with key " < < entry . first . to_hex ( 256 ) ;
break ;
}
if ( rec . complaint - > get_hash ( ) . as_bitslice ( ) ! = entry . first ) {
LOG ( ERROR ) < < " invalid complaint key " < < entry . first . to_hex ( 256 ) < < " : actual complaint hash is "
< < rec . complaint - > get_hash ( ) . to_hex ( ) ;
break ;
}
std : : string filename = file_pfx + entry . first . to_hex ( 256 ) + " .boc " ;
auto boc = vm : : std_boc_serialize ( rec . complaint , 2 ) ;
if ( boc . is_error ( ) ) {
LOG ( ERROR ) < < " cannot serialize complaint " ;
break ;
}
auto len = boc . ok ( ) . size ( ) ;
auto res1 = td : : write_file ( filename , boc . move_as_ok ( ) ) ;
if ( res1 . is_error ( ) ) {
LOG ( ERROR ) < < " cannot save serialized complaint to file ` " < < filename < < " ` : " < < res1 . move_as_error ( ) ;
return ;
}
LOG ( DEBUG ) < < " saved " < < len < < " bytes into file ` " < < filename < < " ` " ;
td : : TerminalIO : : out ( ) < < " SAVE_COMPLAINT \t " < < elect_id < < ' \t ' < < entry . first . to_hex ( 256 ) < < ' \t '
2020-05-07 06:35:23 +00:00
< < rec2 . validator_pubkey . to_hex ( ) < < ' \t ' < < rec2 . created_at < < ' \t ' < < filename < < std : : endl ;
2020-04-27 12:01:46 +00:00
}
}
td : : Status TestNode : : get_complaint_price ( unsigned expires_in , std : : string filename ) {
LOG ( DEBUG ) < < " reading complaint file " < < filename ;
TRY_RESULT_PREFIX ( data , td : : read_file ( filename ) , " cannot read complaint file: " ) ;
TRY_RESULT_PREFIX ( complaint , vm : : std_boc_deserialize ( data ) ,
PSLICE ( ) < < " cannot deserialize bag-of-cells read from complaint file ` " < < filename < < " `: " ) ;
if ( complaint . is_null ( ) ) {
return td : : Status : : Error ( " complaint is null " ) ;
}
block : : gen : : ValidatorComplaint : : Record rec ;
if ( ! tlb : : unpack_cell ( complaint , rec ) ) {
return td : : Status : : Error ( " cannot deserialize complaint " ) ;
}
td : : Bits256 chash = complaint - > get_hash ( ) . bits ( ) ;
vm : : VmStorageStat stat { 1 < < 22 } ;
if ( ! stat . add_storage ( std : : move ( complaint ) ) ) {
return td : : Status : : Error ( " cannot compute storage size for this complaint " ) ;
}
return get_complaint_price ( expires_in , ( unsigned ) stat . bits , ( unsigned ) stat . refs , chash , filename ) ;
}
td : : Status TestNode : : get_complaint_price ( unsigned expires_in , unsigned bits , unsigned refs , td : : Bits256 chash ,
std : : string filename ) {
LOG ( INFO ) < < " complaint ` " < < filename < < " `: " < < bits < < " bits, " < < refs < < " references " ;
return get_elector_addr ( [ this , filename , expires_in , bits , refs , chash ] ( td : : Result < ton : : StdSmcAddress > res ) {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < res . move_as_error ( ) ;
} else {
send_compute_complaint_price_query ( res . move_as_ok ( ) , expires_in , bits , refs , chash , filename ) ;
}
} )
? td : : Status : : OK ( )
: td : : Status : : Error ( " cannot obtain elector address " ) ;
}
void TestNode : : send_compute_complaint_price_query ( ton : : StdSmcAddress elector_addr , unsigned expires_in , unsigned bits ,
unsigned refs , td : : Bits256 chash , std : : string filename ) {
std : : vector < vm : : StackEntry > params ;
params . emplace_back ( td : : make_refint ( bits ) ) ;
params . emplace_back ( td : : make_refint ( refs ) ) ;
params . emplace_back ( td : : make_refint ( expires_in ) ) ;
auto P = td : : PromiseCreator : : lambda (
[ this , expires_in , bits , refs , chash , filename ] ( td : : Result < std : : vector < vm : : StackEntry > > R ) {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < R . move_as_error ( ) ;
return ;
}
auto S = R . move_as_ok ( ) ;
if ( S . size ( ) < 1 | | ! S . back ( ) . is_int ( ) ) {
LOG ( ERROR ) < < " complaint_storage_price did not return a value of type cell " ;
return ;
}
try {
auto price = std : : move ( S . back ( ) ) . as_int ( ) ;
td : : TerminalIO : : out ( ) < < " COMPLAINT_PRICE \t " < < chash . to_hex ( ) < < ' \t ' < < td : : dec_string ( price ) < < ' \t '
< < bits < < ' \t ' < < refs < < ' \t ' < < expires_in < < ' \t ' < < filename < < std : : endl ;
} catch ( vm : : VmError & err ) {
LOG ( ERROR ) < < " vm error: " < < err . get_msg ( ) ;
} catch ( vm : : VmVirtError & err ) {
LOG ( ERROR ) < < " vm virtualization error: " < < err . get_msg ( ) ;
}
} ) ;
2024-02-16 13:30:43 +00:00
start_run_method ( ton : : masterchainId , elector_addr , mc_last_id_ , " complaint_storage_price " , std : : move ( params ) , 0x17 ,
2020-04-27 12:01:46 +00:00
std : : move ( P ) ) ;
}
2024-03-26 12:55:58 +00:00
bool TestNode : : get_msg_queue_sizes ( ) {
auto q = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getOutMsgQueueSizes > ( 0 , 0 , 0 ) , true ) ;
return envelope_send_query ( std : : move ( q ) , [ Self = actor_id ( this ) ] ( td : : Result < td : : BufferSlice > res ) - > void {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " liteServer.getOutMsgQueueSizes error: " < < res . move_as_error ( ) ;
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_outMsgQueueSizes > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getOutMsgQueueSizes " ;
return ;
}
td : : actor : : send_closure_later ( Self , & TestNode : : got_msg_queue_sizes , F . move_as_ok ( ) ) ;
} ) ;
}
void TestNode : : got_msg_queue_sizes ( ton : : tl_object_ptr < ton : : lite_api : : liteServer_outMsgQueueSizes > f ) {
td : : TerminalIO : : out ( ) < < " Outbound message queue sizes: " < < std : : endl ;
for ( auto & x : f - > shards_ ) {
td : : TerminalIO : : out ( ) < < ton : : create_block_id ( x - > id_ ) . id . to_str ( ) < < " " < < x - > size_ < < std : : endl ;
}
td : : TerminalIO : : out ( ) < < " External message queue size limit: " < < f - > ext_msg_queue_size_limit_ < < std : : endl ;
}
2024-09-13 17:47:30 +00:00
bool TestNode : : get_dispatch_queue_info ( ton : : BlockIdExt block_id ) {
td : : TerminalIO : : out ( ) < < " Dispatch queue in block: " < < block_id . id . to_str ( ) < < std : : endl ;
return get_dispatch_queue_info_cont ( block_id , true , td : : Bits256 : : zero ( ) ) ;
}
bool TestNode : : get_dispatch_queue_info_cont ( ton : : BlockIdExt block_id , bool first , td : : Bits256 after_addr ) {
auto q = ton : : create_serialize_tl_object < ton : : lite_api : : liteServer_getDispatchQueueInfo > (
first ? 0 : 2 , ton : : create_tl_lite_block_id ( block_id ) , after_addr , 32 , false ) ;
return envelope_send_query ( std : : move ( q ) , [ = , Self = actor_id ( this ) ] ( td : : Result < td : : BufferSlice > res ) - > void {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " liteServer.getDispatchQueueInfo error: " < < res . move_as_error ( ) ;
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_dispatchQueueInfo > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getDispatchQueueInfo " ;
return ;
}
td : : actor : : send_closure_later ( Self , & TestNode : : got_dispatch_queue_info , block_id , F . move_as_ok ( ) ) ;
} ) ;
}
void TestNode : : got_dispatch_queue_info ( ton : : BlockIdExt block_id ,
ton : : tl_object_ptr < ton : : lite_api : : liteServer_dispatchQueueInfo > info ) {
for ( auto & acc : info - > account_dispatch_queues_ ) {
td : : TerminalIO : : out ( ) < < block_id . id . workchain < < " : " < < acc - > addr_ . to_hex ( ) < < " : size= " < < acc - > size_
< < " lt= " < < acc - > min_lt_ < < " .. " < < acc - > max_lt_ < < std : : endl ;
}
if ( info - > complete_ ) {
td : : TerminalIO : : out ( ) < < " Done " < < std : : endl ;
return ;
}
get_dispatch_queue_info_cont ( block_id , false , info - > account_dispatch_queues_ . back ( ) - > addr_ ) ;
}
bool TestNode : : get_dispatch_queue_messages ( ton : : BlockIdExt block_id , ton : : WorkchainId wc , ton : : StdSmcAddress addr ,
ton : : LogicalTime lt , bool one_account ) {
if ( wc ! = block_id . id . workchain ) {
return set_error ( " workchain mismatch " ) ;
}
auto q = ton : : create_serialize_tl_object < ton : : lite_api : : liteServer_getDispatchQueueMessages > (
one_account ? 2 : 0 , ton : : create_tl_lite_block_id ( block_id ) , addr , lt , 64 , false , one_account , false ) ;
return envelope_send_query ( std : : move ( q ) , [ = , Self = actor_id ( this ) ] ( td : : Result < td : : BufferSlice > res ) - > void {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " liteServer.getDispatchQueueMessages error: " < < res . move_as_error ( ) ;
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_dispatchQueueMessages > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getDispatchQueueMessages " ;
return ;
}
td : : actor : : send_closure_later ( Self , & TestNode : : got_dispatch_queue_messages , F . move_as_ok ( ) ) ;
} ) ;
}
void TestNode : : got_dispatch_queue_messages ( ton : : tl_object_ptr < ton : : lite_api : : liteServer_dispatchQueueMessages > msgs ) {
td : : TerminalIO : : out ( ) < < " Dispatch queue messages ( " < < msgs - > messages_ . size ( ) < < " ): \n " ;
int count = 0 ;
for ( auto & m : msgs - > messages_ ) {
auto & meta = m - > metadata_ ;
td : : TerminalIO : : out ( ) < < " Msg # " < < + + count < < " : " < < msgs - > id_ - > workchain_ < < " : " < < m - > addr_ . to_hex ( ) < < " "
< < m - > lt_ < < " : "
< < ( meta - > initiator_ - > workchain_ = = ton : : workchainInvalid
? " [ no metadata ] "
: block : : MsgMetadata { ( td : : uint32 ) meta - > depth_ , meta - > initiator_ - > workchain_ ,
meta - > initiator_ - > id_ , ( ton : : LogicalTime ) meta - > initiator_lt_ }
. to_str ( ) )
< < " \n " ;
}
if ( ! msgs - > complete_ ) {
td : : TerminalIO : : out ( ) < < " (incomplete list) \n " ;
}
}
2020-02-28 10:28:47 +00:00
bool TestNode : : dns_resolve_start ( ton : : WorkchainId workchain , ton : : StdSmcAddress addr , ton : : BlockIdExt blkid ,
2022-06-29 09:30:19 +00:00
std : : string domain , td : : Bits256 cat , int mode ) {
2020-02-28 10:28:47 +00:00
if ( domain . size ( ) > = 2 & & domain [ 0 ] = = ' " ' & & domain . back ( ) = = ' " ' ) {
domain . erase ( 0 , 1 ) ;
domain . pop_back ( ) ;
}
std : : vector < std : : string > components ;
2022-06-29 09:30:19 +00:00
if ( domain ! = " . " ) {
std : : size_t i , p = 0 ;
for ( i = 0 ; i < domain . size ( ) ; i + + ) {
if ( ! domain [ i ] | | ( unsigned char ) domain [ i ] > = 0xfe | | ( unsigned char ) domain [ i ] < = ' ' ) {
return set_error ( " invalid characters in a domain name " ) ;
2020-02-28 10:28:47 +00:00
}
2022-06-29 09:30:19 +00:00
if ( domain [ i ] = = ' . ' ) {
if ( i = = p ) {
return set_error ( " domain name cannot have an empty component " ) ;
}
components . emplace_back ( domain , p , i - p ) ;
p = i + 1 ;
}
}
if ( i > p ) {
2020-02-28 10:28:47 +00:00
components . emplace_back ( domain , p , i - p ) ;
}
}
2022-06-29 09:30:19 +00:00
std : : string qdomain ;
if ( mode & 2 ) {
qdomain + = ' \0 ' ;
2020-02-28 10:28:47 +00:00
}
while ( ! components . empty ( ) ) {
qdomain + = components . back ( ) ;
qdomain + = ' \0 ' ;
components . pop_back ( ) ;
}
2022-06-29 09:30:19 +00:00
if ( qdomain . size ( ) > 127 ) {
return set_error ( " domain name too long " ) ;
}
2020-02-28 10:28:47 +00:00
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
return set_error ( " server connection not ready " ) ;
}
if ( workchain = = ton : : workchainInvalid ) {
if ( dns_root_queried_ ) {
workchain = ton : : masterchainId ;
addr = dns_root_ ;
} else {
2020-04-27 12:01:46 +00:00
auto P =
td : : PromiseCreator : : lambda ( [ this , blkid , domain , cat , mode ] ( td : : Result < std : : unique_ptr < block : : Config > > R ) {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < " cannot obtain root dns address from configuration: " < < R . move_as_error ( ) ;
} else if ( dns_root_queried_ ) {
dns_resolve_start ( ton : : masterchainId , dns_root_ , blkid , domain , cat , mode ) ;
} else {
LOG ( ERROR ) < < " cannot obtain root dns address from configuration parameter #4 " ;
}
} ) ;
return get_config_params ( mc_last_id_ , std : : move ( P ) , 0x3000 , " " , { 4 } ) ;
2020-02-28 10:28:47 +00:00
}
}
return dns_resolve_send ( workchain , addr , blkid , domain , qdomain , cat , mode ) ;
}
bool TestNode : : dns_resolve_send ( ton : : WorkchainId workchain , ton : : StdSmcAddress addr , ton : : BlockIdExt blkid ,
2022-06-29 09:30:19 +00:00
std : : string domain , std : : string qdomain , td : : Bits256 cat , int mode ) {
2020-02-28 10:28:47 +00:00
LOG ( INFO ) < < " dns_resolve for ' " < < domain < < " ' category= " < < cat < < " mode= " < < mode
< < " starting from smart contract " < < workchain < < " : " < < addr . to_hex ( ) < < " with respect to block "
< < blkid . to_str ( ) ;
vm : : CellBuilder cb ;
Ref < vm : : Cell > cell ;
2022-06-29 09:30:19 +00:00
if ( ! ( cb . store_bytes_bool ( td : : Slice ( qdomain ) ) & & cb . finalize_to ( cell ) ) ) {
2020-02-28 10:28:47 +00:00
return set_error ( " cannot store domain name into slice " ) ;
}
std : : vector < vm : : StackEntry > params ;
2022-06-29 09:30:19 +00:00
params . emplace_back ( vm : : load_cell_slice_ref ( cell ) ) ;
params . emplace_back ( td : : bits_to_refint ( cat . cbits ( ) , 256 , false ) ) ;
2020-02-28 10:28:47 +00:00
auto P = td : : PromiseCreator : : lambda ( [ this , workchain , addr , blkid , domain , qdomain , cat ,
mode ] ( td : : Result < std : : vector < vm : : StackEntry > > R ) {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < R . move_as_error ( ) ;
return ;
}
auto S = R . move_as_ok ( ) ;
if ( S . size ( ) < 2 | | ! S [ S . size ( ) - 2 ] . is_int ( ) | | ! ( S . back ( ) . is_cell ( ) | | S . back ( ) . is_null ( ) ) ) {
LOG ( ERROR ) < < " dnsresolve did not return a value of type (int,cell) " ;
return ;
}
auto cell = S . back ( ) . as_cell ( ) ;
S . pop_back ( ) ;
auto x = S . back ( ) . as_int ( ) ;
S . clear ( ) ;
if ( ! x - > signed_fits_bits ( 32 ) ) {
LOG ( ERROR ) < < " invalid integer result of dnsresolve ( " < < x < < " ) " ;
return ;
}
return dns_resolve_finish ( workchain , addr , blkid , domain , qdomain , cat , mode , ( int ) x - > to_long ( ) , std : : move ( cell ) ) ;
} ) ;
2024-02-16 13:30:43 +00:00
return start_run_method ( workchain , addr , blkid , " dnsresolve " , std : : move ( params ) , 0x17 , std : : move ( P ) ) ;
2020-02-28 10:28:47 +00:00
}
2022-06-29 09:30:19 +00:00
bool TestNode : : show_dns_record ( std : : ostream & os , td : : Bits256 cat , Ref < vm : : CellSlice > value , bool raw_dump ) {
2020-02-28 10:28:47 +00:00
if ( raw_dump ) {
bool ok = show_dns_record ( os , cat , value , false ) ;
if ( ! ok ) {
os < < " cannot parse dns record; raw value: " ;
2022-06-29 09:30:19 +00:00
value - > print_rec ( print_limit_ , os ) ;
2020-02-28 10:28:47 +00:00
}
return ok ;
}
if ( value . is_null ( ) ) {
os < < " (null) " ;
return true ;
}
// block::gen::t_DNSRecord.print_ref(print_limit_, os, value);
2022-06-29 09:30:19 +00:00
if ( ! block : : gen : : t_DNSRecord . validate_csr ( value ) ) {
2020-02-28 10:28:47 +00:00
return false ;
}
2022-06-29 09:30:19 +00:00
block : : gen : : t_DNSRecord . print ( os , value , 0 , print_limit_ ) ;
auto cs = * value ;
2020-02-28 10:28:47 +00:00
auto tag = block : : gen : : t_DNSRecord . get_tag ( cs ) ;
ton : : WorkchainId wc ;
ton : : StdSmcAddress addr ;
switch ( tag ) {
case block : : gen : : DNSRecord : : dns_adnl_address : {
block : : gen : : DNSRecord : : Record_dns_adnl_address rec ;
if ( tlb : : unpack_exact ( cs , rec ) ) {
os < < " \n \t adnl address " < < rec . adnl_addr . to_hex ( ) < < " = " < < td : : adnl_id_encode ( rec . adnl_addr , true ) ;
}
break ;
}
case block : : gen : : DNSRecord : : dns_smc_address : {
block : : gen : : DNSRecord : : Record_dns_smc_address rec ;
if ( tlb : : unpack_exact ( cs , rec ) & & block : : tlb : : t_MsgAddressInt . extract_std_address ( rec . smc_addr , wc , addr ) ) {
2020-03-31 13:11:56 +00:00
os < < " \t smart contract " < < wc < < " : " < < addr . to_hex ( ) < < " = "
< < block : : StdAddress { wc , addr } . rserialize ( true ) ;
2020-02-28 10:28:47 +00:00
}
break ;
}
2022-12-29 14:28:50 +00:00
case block : : gen : : DNSRecord : : dns_storage_address : {
block : : gen : : DNSRecord : : Record_dns_storage_address rec ;
if ( tlb : : unpack_exact ( cs , rec ) ) {
os < < " \t storage address " < < rec . bag_id . to_hex ( ) ;
}
break ;
}
2020-02-28 10:28:47 +00:00
case block : : gen : : DNSRecord : : dns_next_resolver : {
block : : gen : : DNSRecord : : Record_dns_next_resolver rec ;
if ( tlb : : unpack_exact ( cs , rec ) & & block : : tlb : : t_MsgAddressInt . extract_std_address ( rec . resolver , wc , addr ) ) {
os < < " \t next resolver " < < wc < < " : " < < addr . to_hex ( ) < < " = " < < block : : StdAddress { wc , addr } . rserialize ( true ) ;
}
break ;
}
}
return true ;
}
void TestNode : : dns_resolve_finish ( ton : : WorkchainId workchain , ton : : StdSmcAddress addr , ton : : BlockIdExt blkid ,
2022-06-29 09:30:19 +00:00
std : : string domain , std : : string qdomain , td : : Bits256 cat , int mode , int used_bits ,
2020-02-28 10:28:47 +00:00
Ref < vm : : Cell > value ) {
if ( used_bits < = 0 ) {
td : : TerminalIO : : out ( ) < < " domain ' " < < domain < < " ' not found " < < std : : endl ;
return ;
}
if ( ( used_bits & 7 ) | | ( unsigned ) used_bits > 8 * std : : min < std : : size_t > ( qdomain . size ( ) , 126 ) ) {
LOG ( ERROR ) < < " too many bits used ( " < < used_bits < < " out of " < < qdomain . size ( ) * 8 < < " ) " ;
return ;
}
2022-06-29 09:30:19 +00:00
size_t pos = used_bits > > 3 ;
bool end = pos = = qdomain . size ( ) ;
if ( ! end & & qdomain [ pos - 1 ] & & qdomain [ pos ] ) {
2020-02-28 10:28:47 +00:00
LOG ( ERROR ) < < " domain split not at a component boundary " ;
return ;
}
if ( ! end ) {
LOG ( INFO ) < < " partial information obtained " ;
if ( value . is_null ( ) ) {
td : : TerminalIO : : out ( ) < < " domain ' " < < domain < < " ' not found: no next resolver " < < std : : endl ;
return ;
}
Ref < vm : : CellSlice > nx_address ;
ton : : WorkchainId nx_wc ;
ton : : StdSmcAddress nx_addr ;
if ( ! ( block : : gen : : t_DNSRecord . cell_unpack_dns_next_resolver ( value , nx_address ) & &
block : : tlb : : t_MsgAddressInt . extract_std_address ( std : : move ( nx_address ) , nx_wc , nx_addr ) ) ) {
2022-06-29 09:30:19 +00:00
LOG ( ERROR ) < < " cannot parse next resolver info for " < < domain . substr ( qdomain . size ( ) - pos - 1 ) ;
2020-02-28 10:28:47 +00:00
std : : ostringstream out ;
vm : : load_cell_slice ( value ) . print_rec ( print_limit_ , out ) ;
td : : TerminalIO : : err ( ) < < out . str ( ) < < std : : endl ;
return ;
}
LOG ( INFO ) < < " next resolver is " < < nx_wc < < " : " < < nx_addr . to_hex ( ) ;
if ( ( mode & 1 ) ) {
return ; // no recursive resolving
}
if ( ! ( dns_resolve_send ( nx_wc , nx_addr , blkid , domain , qdomain . substr ( pos ) , cat , mode ) ) ) {
LOG ( ERROR ) < < " cannot send next dns query " ;
return ;
}
2022-06-29 09:30:19 +00:00
LOG ( INFO ) < < " recursive dns query to ' " < < domain . substr ( qdomain . size ( ) - pos - 1 ) < < " ' sent " ;
2020-02-28 10:28:47 +00:00
return ;
}
auto out = td : : TerminalIO : : out ( ) ;
2022-06-29 09:30:19 +00:00
if ( cat . is_zero ( ) ) {
out < < " Result for domain ' " < < domain < < " ' (all categories) " < < std : : endl ;
} else {
out < < " Result for domain ' " < < domain < < " ' category " < < cat < < std : : endl ;
}
2020-02-28 10:28:47 +00:00
try {
if ( value . not_null ( ) ) {
std : : ostringstream os0 ;
vm : : load_cell_slice ( value ) . print_rec ( print_limit_ , os0 ) ;
out < < " raw data: " < < os0 . str ( ) < < std : : endl ;
}
2022-06-29 09:30:19 +00:00
if ( cat . is_zero ( ) ) {
vm : : Dictionary dict { value , 256 } ;
2020-02-28 10:28:47 +00:00
if ( ! dict . check_for_each ( [ this , & out ] ( Ref < vm : : CellSlice > cs , td : : ConstBitPtr key , int n ) {
2022-06-29 09:30:19 +00:00
CHECK ( n = = 256 ) ;
td : : Bits256 x { key } ;
2022-08-29 08:04:03 +00:00
if ( cs . is_null ( ) | | cs - > size_ext ( ) ! = 0x10000 ) {
2022-06-29 09:30:19 +00:00
out < < " category " < < x < < " : value is not a reference " < < std : : endl ;
return true ;
2022-08-29 08:04:03 +00:00
}
cs = vm : : load_cell_slice_ref ( cs - > prefetch_ref ( ) ) ;
2020-02-28 10:28:47 +00:00
std : : ostringstream os ;
2022-06-29 09:30:19 +00:00
( void ) show_dns_record ( os , x , cs , true ) ;
out < < " category " < < x < < " : " < < os . str ( ) < < std : : endl ;
2020-02-28 10:28:47 +00:00
return true ;
} ) ) {
out < < " invalid dns record dictionary " < < std : : endl ;
}
} else {
std : : ostringstream os ;
2022-06-29 09:30:19 +00:00
( void ) show_dns_record ( os , cat , value . is_null ( ) ? Ref < vm : : CellSlice > ( ) : vm : : load_cell_slice_ref ( value ) , true ) ;
out < < " category " < < cat < < " : " < < os . str ( ) < < std : : endl ;
2020-02-28 10:28:47 +00:00
}
} catch ( vm : : VmError & err ) {
LOG ( ERROR ) < < " vm error while traversing dns resolve result: " < < err . get_msg ( ) ;
} catch ( vm : : VmVirtError & err ) {
LOG ( ERROR ) < < " vm virtualization error while traversing dns resolve result: " < < err . get_msg ( ) ;
}
}
2019-09-07 10:03:22 +00:00
bool TestNode : : get_one_transaction ( ton : : BlockIdExt blkid , ton : : WorkchainId workchain , ton : : StdSmcAddress addr ,
ton : : LogicalTime lt , bool dump ) {
if ( ! blkid . is_valid_full ( ) ) {
return set_error ( " invalid block id " ) ;
}
if ( ! ton : : shard_contains ( blkid . shard_full ( ) , ton : : extract_addr_prefix ( workchain , addr ) ) ) {
return set_error ( " the shard of this block cannot contain this account " ) ;
}
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
return set_error ( " server connection not ready " ) ;
}
auto a = ton : : create_tl_object < ton : : lite_api : : liteServer_accountId > ( workchain , addr ) ;
auto b = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getOneTransaction > (
ton : : create_tl_lite_block_id ( blkid ) , std : : move ( a ) , lt ) ,
true ) ;
LOG ( INFO ) < < " requesting transaction " < < lt < < " of " < < workchain < < " : " < < addr . to_hex ( ) < < " from block "
< < blkid . to_str ( ) ;
return envelope_send_query (
2020-04-10 19:06:01 +00:00
std : : move ( b ) , [ Self = actor_id ( this ) , workchain , addr , lt , blkid , dump ] ( td : : Result < td : : BufferSlice > R ) - > void {
2019-09-07 10:03:22 +00:00
if ( R . is_error ( ) ) {
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_transactionInfo > ( R . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getOneTransaction " ;
} else {
auto f = F . move_as_ok ( ) ;
td : : actor : : send_closure_later ( Self , & TestNode : : got_one_transaction , blkid , ton : : create_block_id ( f - > id_ ) ,
std : : move ( f - > proof_ ) , std : : move ( f - > transaction_ ) , workchain , addr , lt , dump ) ;
}
} ) ;
}
bool TestNode : : get_last_transactions ( ton : : WorkchainId workchain , ton : : StdSmcAddress addr , ton : : LogicalTime lt ,
ton : : Bits256 hash , unsigned count , bool dump ) {
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
return set_error ( " server connection not ready " ) ;
}
auto a = ton : : create_tl_object < ton : : lite_api : : liteServer_accountId > ( workchain , addr ) ;
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getTransactions > ( count , std : : move ( a ) , lt , hash ) , true ) ;
LOG ( INFO ) < < " requesting " < < count < < " last transactions from " < < lt < < " : " < < hash . to_hex ( ) < < " of " < < workchain
< < " : " < < addr . to_hex ( ) ;
return envelope_send_query (
2020-04-10 19:06:01 +00:00
std : : move ( b ) , [ Self = actor_id ( this ) , workchain , addr , lt , hash , count , dump ] ( td : : Result < td : : BufferSlice > R ) {
2019-09-07 10:03:22 +00:00
if ( R . is_error ( ) ) {
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_transactionList > ( R . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getTransactions " ;
} else {
auto f = F . move_as_ok ( ) ;
std : : vector < ton : : BlockIdExt > blkids ;
for ( auto & id : f - > ids_ ) {
blkids . push_back ( ton : : create_block_id ( std : : move ( id ) ) ) ;
}
td : : actor : : send_closure_later ( Self , & TestNode : : got_last_transactions , std : : move ( blkids ) ,
std : : move ( f - > transactions_ ) , workchain , addr , lt , hash , count , dump ) ;
}
} ) ;
}
void TestNode : : got_account_state ( ton : : BlockIdExt ref_blk , ton : : BlockIdExt blk , ton : : BlockIdExt shard_blk ,
td : : BufferSlice shard_proof , td : : BufferSlice proof , td : : BufferSlice state ,
2022-11-30 05:39:32 +00:00
ton : : WorkchainId workchain , ton : : StdSmcAddress addr , std : : string filename , int mode ,
bool prunned ) {
LOG ( INFO ) < < " got " < < ( prunned ? " prunned " : " " ) < < " account state for " < < workchain < < " : " < < addr . to_hex ( )
< < " with respect to blocks " < < blk . to_str ( )
< < ( shard_blk = = blk ? " " : std : : string { " and " } + shard_blk . to_str ( ) ) ;
2019-09-07 10:03:22 +00:00
block : : AccountState account_state ;
account_state . blk = blk ;
account_state . shard_blk = shard_blk ;
account_state . shard_proof = std : : move ( shard_proof ) ;
account_state . proof = std : : move ( proof ) ;
account_state . state = std : : move ( state ) ;
2022-11-30 05:39:32 +00:00
account_state . is_virtualized = prunned ;
2019-09-07 10:03:22 +00:00
auto r_info = account_state . validate ( ref_blk , block : : StdAddress ( workchain , addr ) ) ;
if ( r_info . is_error ( ) ) {
LOG ( ERROR ) < < r_info . error ( ) . message ( ) ;
return ;
}
auto out = td : : TerminalIO : : out ( ) ;
auto info = r_info . move_as_ok ( ) ;
if ( mode < 0 ) {
if ( info . root . not_null ( ) ) {
out < < " account state is " ;
std : : ostringstream outp ;
2020-02-28 10:28:47 +00:00
block : : gen : : t_Account . print_ref ( print_limit_ , outp , info . root ) ;
vm : : load_cell_slice ( info . root ) . print_rec ( print_limit_ , outp ) ;
2019-09-07 10:03:22 +00:00
out < < outp . str ( ) ;
out < < " last transaction lt = " < < info . last_trans_lt < < " hash = " < < info . last_trans_hash . to_hex ( ) < < std : : endl ;
2019-10-16 09:00:43 +00:00
block : : gen : : Account : : Record_account acc ;
block : : gen : : AccountStorage : : Record store ;
block : : CurrencyCollection balance ;
if ( tlb : : unpack_cell ( info . root , acc ) & & tlb : : csr_unpack ( acc . storage , store ) & & balance . unpack ( store . balance ) ) {
out < < " account balance is " < < balance . to_str ( ) < < std : : endl ;
}
2019-09-07 10:03:22 +00:00
} else {
out < < " account state is empty " < < std : : endl ;
}
} else if ( info . root . not_null ( ) ) {
block : : gen : : Account : : Record_account acc ;
block : : gen : : AccountStorage : : Record store ;
2019-10-16 09:00:43 +00:00
block : : CurrencyCollection balance ;
if ( ! ( tlb : : unpack_cell ( info . root , acc ) & & tlb : : csr_unpack ( acc . storage , store ) & & balance . unpack ( store . balance ) ) ) {
2019-09-07 10:03:22 +00:00
LOG ( ERROR ) < < " error unpacking account state " ;
return ;
}
2019-10-16 09:00:43 +00:00
out < < " account balance is " < < balance . to_str ( ) < < std : : endl ;
2019-09-07 10:03:22 +00:00
int tag = block : : gen : : t_AccountState . get_tag ( * store . state ) ;
switch ( tag ) {
case block : : gen : : AccountState : : account_uninit :
out < < " account not initialized (no StateInit to save into file) " < < std : : endl ;
return ;
case block : : gen : : AccountState : : account_frozen :
out < < " account frozen (no StateInit to save into file) " < < std : : endl ;
return ;
}
CHECK ( store . state . write ( ) . fetch_ulong ( 1 ) = = 1 ) ; // account_init$1 _:StateInit = AccountState;
block : : gen : : StateInit : : Record state ;
CHECK ( tlb : : csr_unpack ( store . state , state ) ) ;
Ref < vm : : Cell > cell ;
const char * name = " <unknown-information> " ;
if ( mode = = 0 ) {
// save all state
vm : : CellBuilder cb ;
CHECK ( cb . append_cellslice_bool ( store . state ) & & cb . finalize_to ( cell ) ) ;
name = " StateInit " ;
} else if ( mode = = 1 ) {
// save code
cell = state . code - > prefetch_ref ( ) ;
name = " code " ;
} else if ( mode = = 2 ) {
// save data
cell = state . data - > prefetch_ref ( ) ;
name = " data " ;
}
if ( cell . is_null ( ) ) {
out < < " no " < < name < < " to save to file " < < std : : endl ;
return ;
}
auto res = vm : : std_boc_serialize ( std : : move ( cell ) , 2 ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot serialize extracted information from account state : " < < res . move_as_error ( ) ;
return ;
}
auto len = res . ok ( ) . size ( ) ;
auto res1 = td : : write_file ( filename , res . move_as_ok ( ) ) ;
if ( res1 . is_error ( ) ) {
LOG ( ERROR ) < < " cannot write " < < name < < " of account " < < workchain < < " : " < < addr . to_hex ( ) < < " to file ` "
< < filename < < " ` : " < < res1 . move_as_error ( ) ;
return ;
}
out < < " written " < < name < < " of account " < < workchain < < " : " < < addr . to_hex ( ) < < " to file ` " < < filename
< < " ` ( " < < len < < " bytes) " < < std : : endl ;
} else {
out < < " account state is empty (nothing saved to file ` " < < filename < < " `) " < < std : : endl ;
}
}
2020-02-02 12:53:37 +00:00
void TestNode : : run_smc_method ( int mode , ton : : BlockIdExt ref_blk , ton : : BlockIdExt blk , ton : : BlockIdExt shard_blk ,
2019-09-07 10:03:22 +00:00
td : : BufferSlice shard_proof , td : : BufferSlice proof , td : : BufferSlice state ,
ton : : WorkchainId workchain , ton : : StdSmcAddress addr , std : : string method ,
2020-02-02 12:53:37 +00:00
std : : vector < vm : : StackEntry > params , td : : BufferSlice remote_c7 ,
2020-02-28 10:28:47 +00:00
td : : BufferSlice remote_libs , td : : BufferSlice remote_result , int remote_exit_code ,
td : : Promise < std : : vector < vm : : StackEntry > > promise ) {
2020-04-10 19:06:01 +00:00
LOG ( INFO ) < < " got (partial) account state ( " < < state . size ( ) < < " bytes) with mode= " < < mode < < " for " < < workchain
< < " : " < < addr . to_hex ( ) < < " with respect to blocks " < < blk . to_str ( )
2020-02-02 12:53:37 +00:00
< < ( shard_blk = = blk ? " " : std : : string { " and " } + shard_blk . to_str ( ) ) ;
2019-09-07 10:03:22 +00:00
auto out = td : : TerminalIO : : out ( ) ;
2020-02-02 12:53:37 +00:00
try {
block : : AccountState account_state ;
account_state . blk = blk ;
account_state . shard_blk = shard_blk ;
account_state . shard_proof = std : : move ( shard_proof ) ;
account_state . proof = std : : move ( proof ) ;
LOG ( DEBUG ) < < " serialized state is " < < state . size ( ) < < " bytes " ;
LOG ( DEBUG ) < < " serialized remote c7 is " < < remote_c7 . size ( ) < < " bytes " ;
account_state . state = std : : move ( state ) ;
account_state . is_virtualized = ( mode > 0 ) ;
auto r_info = account_state . validate ( ref_blk , block : : StdAddress ( workchain , addr ) ) ;
if ( r_info . is_error ( ) ) {
LOG ( ERROR ) < < r_info . error ( ) . message ( ) ;
2020-02-28 10:28:47 +00:00
promise . set_error ( r_info . move_as_error ( ) ) ;
2019-09-07 10:03:22 +00:00
return ;
2020-02-02 12:53:37 +00:00
}
auto out = td : : TerminalIO : : out ( ) ;
auto info = r_info . move_as_ok ( ) ;
if ( info . root . is_null ( ) ) {
LOG ( ERROR ) < < " account state of " < < workchain < < " : " < < addr . to_hex ( ) < < " is empty (cannot run method ` "
< < method < < " `) " ;
2020-02-28 10:28:47 +00:00
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " account state of " < < workchain < < " : " < < addr . to_hex ( )
< < " is empty (cannot run method ` " < < method < < " `) " ) ) ;
2019-09-07 10:03:22 +00:00
return ;
2020-02-02 12:53:37 +00:00
}
if ( false ) {
// DEBUG (dump state)
std : : ostringstream os ;
2020-02-28 10:28:47 +00:00
vm : : CellSlice { vm : : NoVm ( ) , info . true_root } . print_rec ( print_limit_ , os ) ;
2020-02-02 12:53:37 +00:00
out < < " dump of account state (proof): " < < os . str ( ) < < std : : endl ;
}
2020-02-08 19:24:24 +00:00
// set deserialization limits
vm : : FakeVmStateLimits fstate ( 1000 ) ; // limit recursive (de)serialization calls
vm : : VmStateInterface : : Guard guard ( & fstate ) ;
2020-02-02 12:53:37 +00:00
if ( false & & remote_c7 . size ( ) ) {
// DEBUG (dump remote_c7)
auto r_c7 = vm : : std_boc_deserialize ( remote_c7 ) . move_as_ok ( ) ;
std : : ostringstream os ;
vm : : StackEntry val ;
bool ok = val . deserialize ( r_c7 ) ;
val . dump ( os ) ;
// os << std::endl;
2020-02-28 10:28:47 +00:00
// block::gen::t_VmStackValue.print_ref(print_limit_, os, r_c7);
2020-02-02 12:53:37 +00:00
// os << std::endl;
2020-02-28 10:28:47 +00:00
// vm::CellSlice{vm::NoVmOrd(), r_c7}.print_rec(print_limit_, os);
2020-02-02 12:53:37 +00:00
out < < " remote_c7 (deserialized= " < < ok < < " ): " < < os . str ( ) < < std : : endl ;
}
block : : gen : : Account : : Record_account acc ;
block : : gen : : AccountStorage : : Record store ;
block : : CurrencyCollection balance ;
if ( ! ( tlb : : unpack_cell ( info . root , acc ) & & tlb : : csr_unpack ( acc . storage , store ) & &
balance . validate_unpack ( store . balance ) ) ) {
LOG ( ERROR ) < < " error unpacking account state " ;
2020-02-28 10:28:47 +00:00
promise . set_error ( td : : Status : : Error ( " error unpacking account state " ) ) ;
2020-02-02 12:53:37 +00:00
return ;
}
int tag = block : : gen : : t_AccountState . get_tag ( * store . state ) ;
switch ( tag ) {
case block : : gen : : AccountState : : account_uninit :
LOG ( ERROR ) < < " account " < < workchain < < " : " < < addr . to_hex ( )
< < " not initialized yet (cannot run any methods) " ;
2020-04-06 20:08:53 +00:00
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " account " < < workchain < < " : " < < addr . to_hex ( )
< < " not initialized yet (cannot run any methods) " ) ) ;
2020-02-02 12:53:37 +00:00
return ;
case block : : gen : : AccountState : : account_frozen :
LOG ( ERROR ) < < " account " < < workchain < < " : " < < addr . to_hex ( ) < < " frozen (cannot run any methods) " ;
2020-04-06 20:08:53 +00:00
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " account " < < workchain < < " : " < < addr . to_hex ( )
< < " frozen (cannot run any methods) " ) ) ;
2020-02-02 12:53:37 +00:00
return ;
}
CHECK ( store . state . write ( ) . fetch_ulong ( 1 ) = = 1 ) ; // account_init$1 _:StateInit = AccountState;
block : : gen : : StateInit : : Record state_init ;
CHECK ( tlb : : csr_unpack ( store . state , state_init ) ) ;
auto code = state_init . code - > prefetch_ref ( ) ;
auto data = state_init . data - > prefetch_ref ( ) ;
auto stack = td : : make_ref < vm : : Stack > ( std : : move ( params ) ) ;
td : : int64 method_id = compute_method_id ( method ) ;
stack . write ( ) . push_smallint ( method_id ) ;
{
std : : ostringstream os ;
os < < " arguments: " ;
stack - > dump ( os , 3 ) ;
out < < os . str ( ) ;
}
2020-04-06 20:08:53 +00:00
long long gas_limit = /* vm::GasLimits::infty */ 10000000 ;
2020-02-02 12:53:37 +00:00
// OstreamLogger ostream_logger(ctx.error_stream);
// auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm : : GasLimits gas { gas_limit } ;
LOG ( DEBUG ) < < " creating VM " ;
2024-12-17 08:18:34 +00:00
vm : : VmState vm { code , ton : : SUPPORTED_VERSION , std : : move ( stack ) , gas , 1 , data , vm : : VmLog ( ) } ;
2020-02-02 12:53:37 +00:00
vm . set_c7 ( liteclient : : prepare_vm_c7 ( info . gen_utime , info . gen_lt , td : : make_ref < vm : : CellSlice > ( acc . addr - > clone ( ) ) ,
balance ) ) ; // tuple with SmartContractInfo
// vm.incr_stack_trace(1); // enable stack dump after each step
LOG ( INFO ) < < " starting VM to run method ` " < < method < < " ` ( " < < method_id < < " ) of smart contract " < < workchain
< < " : " < < addr . to_hex ( ) ;
2020-04-06 20:08:53 +00:00
int exit_code ;
try {
exit_code = ~ vm . run ( ) ;
} catch ( vm : : VmVirtError & err ) {
LOG ( ERROR ) < < " virtualization error while running VM to locally compute runSmcMethod result: " < < err . get_msg ( ) ;
promise . set_error (
td : : Status : : Error ( PSLICE ( ) < < " virtualization error while running VM to locally compute runSmcMethod result: "
< < err . get_msg ( ) ) ) ;
exit_code = - 1001 ;
} catch ( vm : : VmError & err ) {
LOG ( ERROR ) < < " error while running VM to locally compute runSmcMethod result: " < < err . get_msg ( ) ;
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " error while running VM to locally compute runSmcMethod result: "
< < err . get_msg ( ) ) ) ;
exit_code = - 1000 ;
}
2020-02-02 12:53:37 +00:00
LOG ( DEBUG ) < < " VM terminated with exit code " < < exit_code ;
if ( mode > 0 ) {
LOG ( DEBUG ) < < " remote VM exit code is " < < remote_exit_code ;
2020-04-06 20:08:53 +00:00
if ( remote_exit_code = = ~ ( int ) vm : : Excno : : out_of_gas ) {
LOG ( WARNING ) < < " remote server ran out of gas while performing this request; consider using runmethodfull " ;
}
2020-02-02 12:53:37 +00:00
}
if ( exit_code ! = 0 ) {
out < < " result: error " < < exit_code < < std : : endl ;
2024-02-16 13:30:43 +00:00
} else {
stack = vm . get_stack_ref ( ) ;
2020-02-02 12:53:37 +00:00
std : : ostringstream os ;
os < < " result: " ;
stack - > dump ( os , 3 ) ;
out < < os . str ( ) ;
}
2024-02-16 13:30:43 +00:00
if ( ! ( mode & 4 ) ) {
if ( exit_code ! = 0 ) {
LOG ( ERROR ) < < " VM terminated with error code " < < exit_code ;
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " VM terminated with non-zero exit code " < < exit_code ) ) ;
} else {
promise . set_result ( stack - > extract_contents ( ) ) ;
}
} else {
if ( remote_exit_code ! = 0 ) {
out < < " remote result: error " < < remote_exit_code < < std : : endl ;
LOG ( ERROR ) < < " VM terminated with error code " < < exit_code ;
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " VM terminated with non-zero exit code " < < exit_code ) ) ;
} else if ( remote_result . empty ( ) ) {
out < < " remote result: <none> " < < std : : endl ;
promise . set_value ( { } ) ;
2020-02-02 12:53:37 +00:00
} else {
auto res = vm : : std_boc_deserialize ( std : : move ( remote_result ) ) ;
if ( res . is_error ( ) ) {
2020-04-06 20:08:53 +00:00
auto err = res . move_as_error ( ) ;
LOG ( ERROR ) < < " cannot deserialize remote VM result boc: " < < err ;
promise . set_error (
td : : Status : : Error ( PSLICE ( ) < < " cannot deserialize remote VM result boc: " < < std : : move ( err ) ) ) ;
2020-02-02 12:53:37 +00:00
return ;
}
auto cs = vm : : load_cell_slice ( res . move_as_ok ( ) ) ;
Ref < vm : : Stack > remote_stack ;
if ( ! ( vm : : Stack : : deserialize_to ( cs , remote_stack , 0 ) & & cs . empty_ext ( ) ) ) {
LOG ( ERROR ) < < " remote VM result boc cannot be deserialized as a VmStack " ;
2020-04-06 20:08:53 +00:00
promise . set_error ( td : : Status : : Error ( " remote VM result boc cannot be deserialized as a VmStack " ) ) ;
2020-02-02 12:53:37 +00:00
return ;
}
std : : ostringstream os ;
os < < " remote result (not to be trusted): " ;
remote_stack - > dump ( os , 3 ) ;
out < < os . str ( ) ;
2024-02-16 13:30:43 +00:00
promise . set_value ( remote_stack - > extract_contents ( ) ) ;
2020-02-02 12:53:37 +00:00
}
}
2020-02-28 10:28:47 +00:00
out . flush ( ) ;
2020-02-02 12:53:37 +00:00
} catch ( vm : : VmVirtError & err ) {
out < < " virtualization error while parsing runSmcMethod result: " < < err . get_msg ( ) ;
2020-04-06 20:08:53 +00:00
promise . set_error (
td : : Status : : Error ( PSLICE ( ) < < " virtualization error while parsing runSmcMethod result: " < < err . get_msg ( ) ) ) ;
2020-02-02 12:53:37 +00:00
} catch ( vm : : VmError & err ) {
out < < " error while parsing runSmcMethod result: " < < err . get_msg ( ) ;
2020-04-06 20:08:53 +00:00
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " error while parsing runSmcMethod result: " < < err . get_msg ( ) ) ) ;
2019-09-07 10:03:22 +00:00
}
}
void TestNode : : got_one_transaction ( ton : : BlockIdExt req_blkid , ton : : BlockIdExt blkid , td : : BufferSlice proof ,
td : : BufferSlice transaction , ton : : WorkchainId workchain , ton : : StdSmcAddress addr ,
ton : : LogicalTime trans_lt , bool dump ) {
LOG ( INFO ) < < " got transaction " < < trans_lt < < " for " < < workchain < < " : " < < addr . to_hex ( )
< < " with respect to block " < < blkid . to_str ( ) ;
if ( blkid ! = req_blkid ) {
LOG ( ERROR ) < < " obtained TransactionInfo for a different block " < < blkid . to_str ( ) < < " instead of requested "
< < req_blkid . to_str ( ) ;
return ;
}
if ( ! ton : : shard_contains ( blkid . shard_full ( ) , ton : : extract_addr_prefix ( workchain , addr ) ) ) {
LOG ( ERROR ) < < " received data from block " < < blkid . to_str ( ) < < " that cannot contain requested account "
< < workchain < < " : " < < addr . to_hex ( ) ;
return ;
}
Ref < vm : : Cell > root ;
if ( ! transaction . empty ( ) ) {
auto R = vm : : std_boc_deserialize ( std : : move ( transaction ) ) ;
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < " cannot deserialize transaction " ;
return ;
}
root = R . move_as_ok ( ) ;
CHECK ( root . not_null ( ) ) ;
}
auto P = vm : : std_boc_deserialize ( std : : move ( proof ) ) ;
if ( P . is_error ( ) ) {
LOG ( ERROR ) < < " cannot deserialize block transaction proof " ;
return ;
}
auto proof_root = P . move_as_ok ( ) ;
try {
auto block_root = vm : : MerkleProof : : virtualize ( std : : move ( proof_root ) , 1 ) ;
if ( block_root . is_null ( ) ) {
LOG ( ERROR ) < < " transaction block proof is invalid " ;
return ;
}
auto res1 = block : : check_block_header_proof ( block_root , blkid ) ;
if ( res1 . is_error ( ) ) {
LOG ( ERROR ) < < " error in transaction block header proof : " < < res1 . move_as_error ( ) . to_string ( ) ;
return ;
}
auto trans_root_res = block : : get_block_transaction_try ( std : : move ( block_root ) , workchain , addr , trans_lt ) ;
if ( trans_root_res . is_error ( ) ) {
LOG ( ERROR ) < < trans_root_res . move_as_error ( ) . message ( ) ;
return ;
}
auto trans_root = trans_root_res . move_as_ok ( ) ;
if ( trans_root . is_null ( ) & & root . not_null ( ) ) {
LOG ( ERROR ) < < " error checking transaction proof: proof claims there is no such transaction, but we have got "
" transaction data with hash "
< < root - > get_hash ( ) . bits ( ) . to_hex ( 256 ) ;
return ;
}
if ( trans_root . not_null ( ) & & root . is_null ( ) ) {
LOG ( ERROR ) < < " error checking transaction proof: proof claims there is such a transaction with hash "
< < trans_root - > get_hash ( ) . bits ( ) . to_hex ( 256 )
< < " , but we have got no "
" transaction data " ;
return ;
}
if ( trans_root . not_null ( ) & & trans_root - > get_hash ( ) . bits ( ) . compare ( root - > get_hash ( ) . bits ( ) , 256 ) ) {
LOG ( ERROR ) < < " transaction hash mismatch: Merkle proof expects " < < trans_root - > get_hash ( ) . bits ( ) . to_hex ( 256 )
< < " but received data has " < < root - > get_hash ( ) . bits ( ) . to_hex ( 256 ) ;
return ;
}
} catch ( vm : : VmError err ) {
LOG ( ERROR ) < < " error while traversing block transaction proof : " < < err . get_msg ( ) ;
return ;
} catch ( vm : : VmVirtError err ) {
LOG ( ERROR ) < < " virtualization error while traversing block transaction proof : " < < err . get_msg ( ) ;
return ;
}
auto out = td : : TerminalIO : : out ( ) ;
if ( root . is_null ( ) ) {
out < < " transaction not found " < < std : : endl ;
} else {
out < < " transaction is " ;
std : : ostringstream outp ;
2020-02-28 10:28:47 +00:00
block : : gen : : t_Transaction . print_ref ( print_limit_ , outp , root , 0 ) ;
vm : : load_cell_slice ( root ) . print_rec ( print_limit_ , outp ) ;
2019-09-07 10:03:22 +00:00
out < < outp . str ( ) ;
}
}
bool unpack_addr ( std : : ostream & os , Ref < vm : : CellSlice > csr ) {
ton : : WorkchainId wc ;
ton : : StdSmcAddress addr ;
if ( ! block : : tlb : : t_MsgAddressInt . extract_std_address ( std : : move ( csr ) , wc , addr ) ) {
os < < " <cannot unpack address> " ;
return false ;
}
os < < wc < < " : " < < addr . to_hex ( ) ;
return true ;
}
bool unpack_message ( std : : ostream & os , Ref < vm : : Cell > msg , int mode ) {
if ( msg . is_null ( ) ) {
os < < " <message not found> " ;
return true ;
}
vm : : CellSlice cs { vm : : NoVmOrd ( ) , msg } ;
switch ( block : : gen : : t_CommonMsgInfo . get_tag ( cs ) ) {
case block : : gen : : CommonMsgInfo : : ext_in_msg_info : {
block : : gen : : CommonMsgInfo : : Record_ext_in_msg_info info ;
if ( ! tlb : : unpack ( cs , info ) ) {
LOG ( DEBUG ) < < " cannot unpack inbound external message " ;
return false ;
}
os < < " EXT-IN-MSG " ;
if ( ! ( mode & 2 ) ) {
os < < " TO: " ;
if ( ! unpack_addr ( os , std : : move ( info . dest ) ) ) {
return false ;
}
}
return true ;
}
case block : : gen : : CommonMsgInfo : : ext_out_msg_info : {
block : : gen : : CommonMsgInfo : : Record_ext_out_msg_info info ;
if ( ! tlb : : unpack ( cs , info ) ) {
LOG ( DEBUG ) < < " cannot unpack outbound external message " ;
return false ;
}
os < < " EXT-OUT-MSG " ;
if ( ! ( mode & 1 ) ) {
os < < " FROM: " ;
if ( ! unpack_addr ( os , std : : move ( info . src ) ) ) {
return false ;
}
}
os < < " LT: " < < info . created_lt < < " UTIME: " < < info . created_at ;
return true ;
}
case block : : gen : : CommonMsgInfo : : int_msg_info : {
block : : gen : : CommonMsgInfo : : Record_int_msg_info info ;
if ( ! tlb : : unpack ( cs , info ) ) {
LOG ( DEBUG ) < < " cannot unpack internal message " ;
return false ;
}
os < < " INT-MSG " ;
if ( ! ( mode & 1 ) ) {
os < < " FROM: " ;
if ( ! unpack_addr ( os , std : : move ( info . src ) ) ) {
return false ;
}
}
if ( ! ( mode & 2 ) ) {
os < < " TO: " ;
if ( ! unpack_addr ( os , std : : move ( info . dest ) ) ) {
return false ;
}
}
os < < " LT: " < < info . created_lt < < " UTIME: " < < info . created_at ;
td : : RefInt256 value ;
Ref < vm : : Cell > extra ;
if ( ! block : : unpack_CurrencyCollection ( info . value , value , extra ) ) {
LOG ( ERROR ) < < " cannot unpack message value " ;
return false ;
}
os < < " VALUE: " < < value ;
if ( extra . not_null ( ) ) {
os < < " +extra " ;
}
return true ;
}
default :
LOG ( ERROR ) < < " cannot unpack message " ;
return false ;
}
}
std : : string message_info_str ( Ref < vm : : Cell > msg , int mode ) {
std : : ostringstream os ;
if ( ! unpack_message ( os , msg , mode ) ) {
return " <cannot unpack message> " ;
} else {
return os . str ( ) ;
}
}
void TestNode : : got_last_transactions ( std : : vector < ton : : BlockIdExt > blkids , td : : BufferSlice transactions_boc ,
ton : : WorkchainId workchain , ton : : StdSmcAddress addr , ton : : LogicalTime lt ,
ton : : Bits256 hash , unsigned count , bool dump ) {
LOG ( INFO ) < < " got up to " < < count < < " transactions for " < < workchain < < " : " < < addr . to_hex ( )
< < " from last transaction " < < lt < < " : " < < hash . to_hex ( ) ;
block : : TransactionList transaction_list ;
transaction_list . blkids = blkids ;
transaction_list . lt = lt ;
transaction_list . hash = hash ;
transaction_list . transactions_boc = std : : move ( transactions_boc ) ;
auto r_account_state_info = transaction_list . validate ( ) ;
if ( r_account_state_info . is_error ( ) ) {
LOG ( ERROR ) < < " got_last_transactions: " < < r_account_state_info . error ( ) ;
return ;
}
auto account_state_info = r_account_state_info . move_as_ok ( ) ;
unsigned c = 0 ;
auto out = td : : TerminalIO : : out ( ) ;
CHECK ( ! account_state_info . transactions . empty ( ) ) ;
for ( auto & info : account_state_info . transactions ) {
const auto & blkid = info . blkid ;
out < < " transaction # " < < c < < " from block " < < blkid . to_str ( ) < < ( dump ? " is " : " \n " ) ;
if ( dump ) {
std : : ostringstream outp ;
2020-02-28 10:28:47 +00:00
block : : gen : : t_Transaction . print_ref ( print_limit_ , outp , info . transaction ) ;
vm : : load_cell_slice ( info . transaction ) . print_rec ( print_limit_ , outp ) ;
2019-09-07 10:03:22 +00:00
out < < outp . str ( ) ;
}
block : : gen : : Transaction : : Record trans ;
if ( ! tlb : : unpack_cell ( info . transaction , trans ) ) {
LOG ( ERROR ) < < " cannot unpack transaction # " < < c ;
return ;
}
out < < " time= " < < trans . now < < " outmsg_cnt= " < < trans . outmsg_cnt < < std : : endl ;
auto in_msg = trans . r1 . in_msg - > prefetch_ref ( ) ;
if ( in_msg . is_null ( ) ) {
out < < " (no inbound message) " < < std : : endl ;
} else {
out < < " inbound message: " < < message_info_str ( in_msg , 2 * 0 ) < < std : : endl ;
if ( dump ) {
out < < " " < < block : : gen : : t_Message_Any . as_string_ref ( in_msg , 4 ) ; // indentation = 4 spaces
}
}
vm : : Dictionary dict { trans . r1 . out_msgs , 15 } ;
for ( int x = 0 ; x < trans . outmsg_cnt & & x < 100 ; x + + ) {
auto out_msg = dict . lookup_ref ( td : : BitArray < 15 > { x } ) ;
out < < " outbound message # " < < x < < " : " < < message_info_str ( out_msg , 1 * 0 ) < < std : : endl ;
if ( dump ) {
out < < " " < < block : : gen : : t_Message_Any . as_string_ref ( out_msg , 4 ) ;
}
}
register_blkid ( blkid ) ; // unsafe?
}
auto & last = account_state_info . transactions . back ( ) ;
if ( last . prev_trans_lt > 0 ) {
out < < " previous transaction has lt " < < last . prev_trans_lt < < " hash " < < last . prev_trans_hash . to_hex ( )
< < std : : endl ;
if ( account_state_info . transactions . size ( ) < count ) {
LOG ( WARNING ) < < " obtained less transactions than required " ;
}
} else {
out < < " no preceding transactions (list complete) " < < std : : endl ;
}
}
bool TestNode : : get_block_transactions ( ton : : BlockIdExt blkid , int mode , unsigned count , ton : : Bits256 acc_addr ,
ton : : LogicalTime lt ) {
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
return set_error ( " server connection not ready " ) ;
}
auto a = ton : : create_tl_object < ton : : lite_api : : liteServer_transactionId3 > ( acc_addr , lt ) ;
auto b = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_listBlockTransactions > (
ton : : create_tl_lite_block_id ( blkid ) , mode , count , std : : move ( a ) , false , false ) ,
true ) ;
LOG ( INFO ) < < " requesting " < < count < < " transactions from block " < < blkid . to_str ( ) < < " starting from account "
< < acc_addr . to_hex ( ) < < " lt " < < lt ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , mode ] ( td : : Result < td : : BufferSlice > R ) {
2019-09-07 10:03:22 +00:00
if ( R . is_error ( ) ) {
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_blockTransactions > ( R . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.listBlockTransactions " ;
} else {
auto f = F . move_as_ok ( ) ;
std : : vector < TransId > transactions ;
2024-06-27 13:12:23 +00:00
std : : vector < ton : : tl_object_ptr < ton : : lite_api : : liteServer_transactionMetadata > > metadata ;
2019-09-07 10:03:22 +00:00
for ( auto & id : f - > ids_ ) {
transactions . emplace_back ( id - > account_ , id - > lt_ , id - > hash_ ) ;
2024-06-27 13:12:23 +00:00
metadata . push_back ( std : : move ( id - > metadata_ ) ) ;
2019-09-07 10:03:22 +00:00
}
td : : actor : : send_closure_later ( Self , & TestNode : : got_block_transactions , ton : : create_block_id ( f - > id_ ) , mode ,
2024-06-27 13:12:23 +00:00
f - > req_count_ , f - > incomplete_ , std : : move ( transactions ) , std : : move ( metadata ) ,
std : : move ( f - > proof_ ) ) ;
2019-09-07 10:03:22 +00:00
}
} ) ;
}
2024-06-27 13:12:23 +00:00
void TestNode : : got_block_transactions (
ton : : BlockIdExt blkid , int mode , unsigned req_count , bool incomplete , std : : vector < TestNode : : TransId > trans ,
std : : vector < ton : : tl_object_ptr < ton : : lite_api : : liteServer_transactionMetadata > > metadata , td : : BufferSlice proof ) {
2019-09-07 10:03:22 +00:00
LOG ( INFO ) < < " got up to " < < req_count < < " transactions from block " < < blkid . to_str ( ) ;
auto out = td : : TerminalIO : : out ( ) ;
int count = 0 ;
2024-06-27 13:12:23 +00:00
for ( size_t i = 0 ; i < trans . size ( ) ; + + i ) {
auto & t = trans [ i ] ;
2019-09-07 10:03:22 +00:00
out < < " transaction # " < < + + count < < " : account " < < t . acc_addr . to_hex ( ) < < " lt " < < t . trans_lt < < " hash "
< < t . trans_hash . to_hex ( ) < < std : : endl ;
2024-06-27 13:12:23 +00:00
if ( mode & 256 ) {
auto & meta = metadata . at ( i ) ;
if ( meta = = nullptr ) {
out < < " metadata: <none> " < < std : : endl ;
} else {
out < < " metadata: "
< < block : : MsgMetadata { ( td : : uint32 ) meta - > depth_ , meta - > initiator_ - > workchain_ , meta - > initiator_ - > id_ ,
( ton : : LogicalTime ) meta - > initiator_lt_ }
. to_str ( )
< < std : : endl ;
}
}
2019-09-07 10:03:22 +00:00
}
out < < ( incomplete ? " (block transaction list incomplete) " : " (end of block transaction list) " ) < < std : : endl ;
}
2020-04-08 19:49:28 +00:00
bool TestNode : : get_all_shards ( std : : string filename , bool use_last , ton : : BlockIdExt blkid ) {
2019-09-07 10:03:22 +00:00
if ( use_last ) {
blkid = mc_last_id_ ;
}
if ( ! blkid . is_valid_full ( ) ) {
return set_error ( use_last ? " must obtain last block information before making other queries "
: " invalid masterchain block id " ) ;
}
if ( ! blkid . is_masterchain ( ) ) {
return set_error ( " only masterchain blocks contain shard configuration " ) ;
}
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
return set_error ( " server connection not ready " ) ;
}
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getAllShardsInfo > ( ton : : create_tl_lite_block_id ( blkid ) ) , true ) ;
LOG ( INFO ) < < " requesting recent shard configuration " ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , filename ] ( td : : Result < td : : BufferSlice > R ) - > void {
2019-09-07 10:03:22 +00:00
if ( R . is_error ( ) ) {
return ;
}
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_allShardsInfo > ( R . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getAllShardsInfo " ;
} else {
auto f = F . move_as_ok ( ) ;
td : : actor : : send_closure_later ( Self , & TestNode : : got_all_shards , ton : : create_block_id ( f - > id_ ) , std : : move ( f - > proof_ ) ,
2020-04-08 19:49:28 +00:00
std : : move ( f - > data_ ) , filename ) ;
2019-09-07 10:03:22 +00:00
}
} ) ;
}
2020-04-08 19:49:28 +00:00
void TestNode : : got_all_shards ( ton : : BlockIdExt blk , td : : BufferSlice proof , td : : BufferSlice data , std : : string filename ) {
2019-09-07 10:03:22 +00:00
LOG ( INFO ) < < " got shard configuration with respect to block " < < blk . to_str ( ) ;
if ( data . empty ( ) ) {
td : : TerminalIO : : out ( ) < < " shard configuration is empty " < < ' \n ' ;
} else {
auto R = vm : : std_boc_deserialize ( data . clone ( ) ) ;
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < " cannot deserialize shard configuration " ;
return ;
}
auto root = R . move_as_ok ( ) ;
auto out = td : : TerminalIO : : out ( ) ;
out < < " shard configuration is " ;
std : : ostringstream outp ;
2020-02-28 10:28:47 +00:00
block : : gen : : t_ShardHashes . print_ref ( print_limit_ , outp , root ) ;
vm : : load_cell_slice ( root ) . print_rec ( print_limit_ , outp ) ;
2019-09-07 10:03:22 +00:00
out < < outp . str ( ) ;
block : : ShardConfig sh_conf ;
if ( ! sh_conf . unpack ( vm : : load_cell_slice_ref ( root ) ) ) {
out < < " cannot extract shard block list from shard configuration \n " ;
} else {
auto ids = sh_conf . get_shard_hash_ids ( true ) ;
int cnt = 0 ;
for ( auto id : ids ) {
auto ref = sh_conf . get_shard_hash ( ton : : ShardIdFull ( id ) ) ;
if ( ref . not_null ( ) ) {
register_blkid ( ref - > top_block_id ( ) ) ;
out < < " shard # " < < + + cnt < < " : " < < ref - > top_block_id ( ) . to_str ( ) < < " @ " < < ref - > created_at ( ) < < " lt "
< < ref - > start_lt ( ) < < " .. " < < ref - > end_lt ( ) < < std : : endl ;
} else {
out < < " shard # " < < + + cnt < < " : " < < id . to_str ( ) < < " (cannot unpack) \n " ;
}
}
}
2020-04-08 19:49:28 +00:00
if ( ! filename . empty ( ) ) {
auto res1 = td : : write_file ( filename , data . as_slice ( ) ) ;
if ( res1 . is_error ( ) ) {
LOG ( ERROR ) < < " cannot write shard configuration to file ` " < < filename < < " ` : " < < res1 . move_as_error ( ) ;
} else {
out < < " saved shard configuration (ShardHashes) to file ` " < < filename < < " ` ( " < < data . size ( ) < < " bytes) "
< < std : : endl ;
}
}
2019-09-07 10:03:22 +00:00
}
show_new_blkids ( ) ;
}
2020-04-27 12:01:46 +00:00
bool TestNode : : parse_get_config_params ( ton : : BlockIdExt blkid , int mode , std : : string filename , std : : vector < int > params ) {
2020-02-28 10:28:47 +00:00
if ( mode < 0 ) {
2020-04-27 12:01:46 +00:00
mode = 0x80000 ;
2020-02-28 10:28:47 +00:00
}
2020-04-27 12:01:46 +00:00
if ( ! ( mode & 0x81000 ) & & ! seekeoln ( ) ) {
2019-09-07 10:03:22 +00:00
mode | = 0x1000 ;
while ( ! seekeoln ( ) ) {
int x ;
if ( ! convert_int32 ( get_word ( ) , x ) ) {
return set_error ( " integer configuration parameter id expected " ) ;
}
params . push_back ( x ) ;
}
}
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
return set_error ( " server connection not ready " ) ;
}
if ( ! blkid . is_masterchain_ext ( ) ) {
return set_error ( " only masterchain blocks contain configuration " ) ;
}
2020-02-28 10:28:47 +00:00
if ( blkid = = mc_last_id_ ) {
mode | = 0x2000 ;
}
2020-04-27 12:01:46 +00:00
return get_config_params ( blkid , trivial_promise_of < std : : unique_ptr < block : : Config > > ( ) , mode , filename ,
std : : move ( params ) ) ;
}
bool TestNode : : get_config_params ( ton : : BlockIdExt blkid , td : : Promise < std : : unique_ptr < block : : Config > > promise , int mode ,
std : : string filename , std : : vector < int > params ) {
return get_config_params_ext ( blkid , promise . wrap ( [ ] ( ConfigInfo & & info ) { return std : : move ( info . config ) ; } ) ,
mode | 0x10000 , filename , params ) ;
}
bool TestNode : : get_config_params_ext ( ton : : BlockIdExt blkid , td : : Promise < ConfigInfo > promise , int mode ,
std : : string filename , std : : vector < int > params ) {
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
promise . set_error ( td : : Status : : Error ( " server connection not ready " ) ) ;
return false ;
}
if ( ! blkid . is_masterchain_ext ( ) ) {
promise . set_error ( td : : Status : : Error ( " masterchain reference block expected " ) ) ;
return false ;
}
if ( blkid = = mc_last_id_ ) {
mode | = 0x2000 ;
}
2019-09-07 10:03:22 +00:00
auto params_copy = params ;
2020-04-27 12:01:46 +00:00
auto b = ( mode & 0x1000 ) ? ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getConfigParams > (
mode & 0x8fff , ton : : create_tl_lite_block_id ( blkid ) , std : : move ( params_copy ) ) ,
true )
: ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getConfigAll > (
mode & 0x8fff , ton : : create_tl_lite_block_id ( blkid ) ) ,
true ) ;
2019-09-07 10:03:22 +00:00
LOG ( INFO ) < < " requesting " < < params . size ( ) < < " configuration parameters with respect to masterchain block "
< < blkid . to_str ( ) ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , mode , filename , blkid , params = std : : move ( params ) ,
2020-04-27 12:01:46 +00:00
promise = std : : move ( promise ) ] ( td : : Result < td : : BufferSlice > R ) mutable {
td : : actor : : send_closure_later ( Self , & TestNode : : got_config_params , blkid , mode , filename , std : : move ( params ) ,
std : : move ( R ) , std : : move ( promise ) ) ;
2019-09-07 10:03:22 +00:00
} ) ;
}
2020-04-27 12:01:46 +00:00
void TestNode : : got_config_params ( ton : : BlockIdExt req_blkid , int mode , std : : string filename , std : : vector < int > params ,
td : : Result < td : : BufferSlice > R , td : : Promise < ConfigInfo > promise ) {
TRY_RESULT_PROMISE ( promise , res , std : : move ( R ) ) ;
TRY_RESULT_PROMISE_PREFIX ( promise , f ,
ton : : fetch_tl_object < ton : : lite_api : : liteServer_configInfo > ( std : : move ( res ) , true ) ,
" cannot parse answer to liteServer.getConfigParams " ) ;
auto blkid = ton : : create_block_id ( f - > id_ ) ;
2019-09-07 10:03:22 +00:00
LOG ( INFO ) < < " got configuration parameters " ;
if ( ! blkid . is_masterchain_ext ( ) ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( td : : Status : : Error ( " reference block " s + blkid . to_str ( ) +
" for the configuration is not a valid masterchain block " ) ) ;
2019-09-07 10:03:22 +00:00
return ;
}
2020-04-27 12:01:46 +00:00
bool from_key = ( mode & 0x8000 ) ;
if ( blkid . seqno ( ) > req_blkid . seqno ( ) | | ( ! from_key & & blkid ! = req_blkid ) ) {
promise . set_error ( td : : Status : : Error ( " got configuration parameters with respect to block " s + blkid . to_str ( ) +
" instead of " + req_blkid . to_str ( ) ) ) ;
2019-09-07 10:03:22 +00:00
return ;
}
try {
2020-04-27 12:01:46 +00:00
Ref < vm : : Cell > state , block , state_proof , config_proof ;
if ( ! ( mode & 0x10000 ) & & ! from_key ) {
TRY_RESULT_PROMISE_PREFIX_ASSIGN ( promise , state_proof , vm : : std_boc_deserialize ( f - > state_proof_ . as_slice ( ) ) ,
" cannot deserialize state proof : " ) ;
}
if ( ! ( mode & 0x10000 ) | | from_key ) {
TRY_RESULT_PROMISE_PREFIX_ASSIGN ( promise , config_proof , vm : : std_boc_deserialize ( f - > config_proof_ . as_slice ( ) ) ,
" cannot deserialize config proof : " ) ;
}
if ( ! from_key ) {
TRY_RESULT_PROMISE_PREFIX_ASSIGN (
promise , state ,
block : : check_extract_state_proof ( blkid , f - > state_proof_ . as_slice ( ) , f - > config_proof_ . as_slice ( ) ) ,
PSLICE ( ) < < " masterchain state proof for " < < blkid . to_str ( ) < < " is invalid : " ) ;
} else {
block = vm : : MerkleProof : : virtualize ( config_proof , 1 ) ;
if ( block . is_null ( ) ) {
promise . set_error (
td : : Status : : Error ( " cannot virtualize configuration proof constructed from key block " s + blkid . to_str ( ) ) ) ;
2019-09-07 10:03:22 +00:00
return ;
}
2020-04-27 12:01:46 +00:00
//TRY_STATUS_PROMISE_PREFIX(promise, block::check_block_header_proof(block, blkid),
// PSLICE() << "incorrect header for key block " << blkid.to_str());
}
TRY_RESULT_PROMISE_PREFIX ( promise , config ,
from_key ? block : : Config : : extract_from_key_block ( block , mode & 0xfff )
: block : : Config : : extract_from_state ( state , mode & 0xfff ) ,
" cannot unpack configuration: " ) ;
ConfigInfo cinfo { std : : move ( config ) , std : : move ( state_proof ) , std : : move ( config_proof ) } ;
if ( mode & 0x80000 ) {
TRY_RESULT_PROMISE_PREFIX ( promise , boc , vm : : std_boc_serialize ( cinfo . config - > get_root_cell ( ) , 2 ) ,
" cannot serialize configuration: " ) ;
auto size = boc . size ( ) ;
TRY_STATUS_PROMISE_PREFIX ( promise , td : : write_file ( filename , std : : move ( boc ) ) ,
PSLICE ( ) < < " cannot save file ` " < < filename < < " ` : " ) ;
2019-09-07 10:03:22 +00:00
td : : TerminalIO : : out ( ) < < " saved configuration dictionary into file ` " < < filename < < " ` ( " < < size
< < " bytes written) " < < std : : endl ;
2020-04-27 12:01:46 +00:00
promise . set_result ( std : : move ( cinfo ) ) ;
return ;
}
if ( mode & 0x4000 ) {
promise . set_result ( std : : move ( cinfo ) ) ;
2019-09-07 10:03:22 +00:00
return ;
}
auto out = td : : TerminalIO : : out ( ) ;
if ( mode & 0x1000 ) {
for ( int i : params ) {
out < < " ConfigParam( " < < i < < " ) = " ;
2020-04-27 12:01:46 +00:00
auto value = cinfo . config - > get_config_param ( i ) ;
2019-09-07 10:03:22 +00:00
if ( value . is_null ( ) ) {
out < < " (null) \n " ;
} else {
std : : ostringstream os ;
if ( i > = 0 ) {
2020-02-28 10:28:47 +00:00
block : : gen : : ConfigParam { i } . print_ref ( print_limit_ , os , value ) ;
2019-09-07 10:03:22 +00:00
os < < std : : endl ;
}
2020-02-28 10:28:47 +00:00
vm : : load_cell_slice ( value ) . print_rec ( print_limit_ , os ) ;
2019-09-07 10:03:22 +00:00
out < < os . str ( ) < < std : : endl ;
2020-04-27 12:01:46 +00:00
if ( mode & 0x2000 ) {
register_config_param ( i , value ) ;
2020-02-28 10:28:47 +00:00
}
2019-09-07 10:03:22 +00:00
}
}
} else {
2020-04-27 12:01:46 +00:00
cinfo . config - > foreach_config_param ( [ this , & out , mode ] ( int i , Ref < vm : : Cell > value ) {
2019-09-07 10:03:22 +00:00
out < < " ConfigParam( " < < i < < " ) = " ;
if ( value . is_null ( ) ) {
out < < " (null) \n " ;
} else {
std : : ostringstream os ;
if ( i > = 0 ) {
2020-02-28 10:28:47 +00:00
block : : gen : : ConfigParam { i } . print_ref ( print_limit_ , os , value ) ;
2019-09-07 10:03:22 +00:00
os < < std : : endl ;
}
2020-02-28 10:28:47 +00:00
vm : : load_cell_slice ( value ) . print_rec ( print_limit_ , os ) ;
2019-09-07 10:03:22 +00:00
out < < os . str ( ) < < std : : endl ;
2020-04-27 12:01:46 +00:00
if ( mode & 0x2000 ) {
register_config_param ( i , value ) ;
2020-02-28 10:28:47 +00:00
}
2019-09-07 10:03:22 +00:00
}
return true ;
} ) ;
}
2020-04-27 12:01:46 +00:00
promise . set_result ( std : : move ( cinfo ) ) ;
2019-09-07 10:03:22 +00:00
} catch ( vm : : VmError & err ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( err . as_status ( " error while traversing configuration: " ) ) ;
return ;
2019-09-07 10:03:22 +00:00
} catch ( vm : : VmVirtError & err ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( err . as_status ( " virtualization error while traversing configuration: " ) ) ;
return ;
}
}
bool TestNode : : register_config_param ( int idx , Ref < vm : : Cell > value ) {
2020-05-07 06:35:23 +00:00
switch ( idx ) {
case 0 :
return register_config_param0 ( std : : move ( value ) ) ;
case 1 :
return register_config_param1 ( std : : move ( value ) ) ;
case 4 :
return register_config_param4 ( std : : move ( value ) ) ;
default :
return true ;
2019-09-07 10:03:22 +00:00
}
2020-02-28 10:28:47 +00:00
}
bool TestNode : : register_config_param4 ( Ref < vm : : Cell > value ) {
if ( value . is_null ( ) ) {
return false ;
}
vm : : CellSlice cs { vm : : NoVmOrd ( ) , std : : move ( value ) } ;
ton : : StdSmcAddress addr ;
if ( cs . size_ext ( ) = = 256 & & cs . fetch_bits_to ( addr ) ) {
dns_root_queried_ = true ;
if ( dns_root_ ! = addr ) {
dns_root_ = addr ;
LOG ( INFO ) < < " dns root set to -1: " < < addr . to_hex ( ) ;
}
return true ;
} else {
return false ;
}
2019-09-07 10:03:22 +00:00
}
2020-04-27 12:01:46 +00:00
bool TestNode : : register_config_param1 ( Ref < vm : : Cell > value ) {
if ( value . is_null ( ) ) {
return false ;
}
vm : : CellSlice cs { vm : : NoVmOrd ( ) , std : : move ( value ) } ;
ton : : StdSmcAddress addr ;
if ( cs . size_ext ( ) = = 256 & & cs . fetch_bits_to ( addr ) ) {
elect_addr_queried_ = true ;
if ( elect_addr_ ! = addr ) {
elect_addr_ = addr ;
LOG ( INFO ) < < " elector smart contract address set to -1: " < < addr . to_hex ( ) ;
}
return true ;
} else {
return false ;
}
}
2020-05-07 06:35:23 +00:00
bool TestNode : : register_config_param0 ( Ref < vm : : Cell > value ) {
if ( value . is_null ( ) ) {
return false ;
}
vm : : CellSlice cs { vm : : NoVmOrd ( ) , std : : move ( value ) } ;
ton : : StdSmcAddress addr ;
if ( cs . size_ext ( ) = = 256 & & cs . fetch_bits_to ( addr ) ) {
config_addr_queried_ = true ;
if ( config_addr_ ! = addr ) {
config_addr_ = addr ;
LOG ( INFO ) < < " configuration smart contract address set to -1: " < < addr . to_hex ( ) ;
}
return true ;
} else {
return false ;
}
}
2019-09-07 10:03:22 +00:00
bool TestNode : : get_block ( ton : : BlockIdExt blkid , bool dump ) {
LOG ( INFO ) < < " got block download request for " < < blkid . to_str ( ) ;
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getBlock > ( ton : : create_tl_lite_block_id ( blkid ) ) , true ) ;
return envelope_send_query (
2020-04-10 19:06:01 +00:00
std : : move ( b ) , [ Self = actor_id ( this ) , blkid , dump ] ( td : : Result < td : : BufferSlice > res ) - > void {
2019-09-07 10:03:22 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot obtain block " < < blkid . to_str ( )
< < " from server : " < < res . move_as_error ( ) . to_string ( ) ;
return ;
} else {
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_blockData > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getBlock : " < < res . move_as_error ( ) . to_string ( ) ;
} else {
auto f = F . move_as_ok ( ) ;
auto blk_id = ton : : create_block_id ( f - > id_ ) ;
LOG ( INFO ) < < " obtained block " < < blk_id . to_str ( ) < < " from server " ;
if ( blk_id ! = blkid ) {
LOG ( ERROR ) < < " block id mismatch: expected data for block " < < blkid . to_str ( ) < < " , obtained for "
< < blk_id . to_str ( ) ;
return ;
}
td : : actor : : send_closure_later ( Self , & TestNode : : got_block , blk_id , std : : move ( f - > data_ ) , dump ) ;
}
}
} ) ;
}
bool TestNode : : get_state ( ton : : BlockIdExt blkid , bool dump ) {
LOG ( INFO ) < < " got state download request for " < < blkid . to_str ( ) ;
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getState > ( ton : : create_tl_lite_block_id ( blkid ) ) , true ) ;
return envelope_send_query (
2020-04-10 19:06:01 +00:00
std : : move ( b ) , [ Self = actor_id ( this ) , blkid , dump ] ( td : : Result < td : : BufferSlice > res ) - > void {
2019-09-07 10:03:22 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot obtain state " < < blkid . to_str ( )
< < " from server : " < < res . move_as_error ( ) . to_string ( ) ;
return ;
} else {
auto F = ton : : fetch_tl_object < ton : : lite_api : : liteServer_blockState > ( res . move_as_ok ( ) , true ) ;
if ( F . is_error ( ) ) {
LOG ( ERROR ) < < " cannot parse answer to liteServer.getState " ;
} else {
auto f = F . move_as_ok ( ) ;
auto blk_id = ton : : create_block_id ( f - > id_ ) ;
LOG ( INFO ) < < " obtained state " < < blk_id . to_str ( ) < < " from server " ;
if ( blk_id ! = blkid ) {
LOG ( ERROR ) < < " block id mismatch: expected state for block " < < blkid . to_str ( ) < < " , obtained for "
< < blk_id . to_str ( ) ;
return ;
}
td : : actor : : send_closure_later ( Self , & TestNode : : got_state , blk_id , f - > root_hash_ , f - > file_hash_ ,
std : : move ( f - > data_ ) , dump ) ;
}
}
} ) ;
}
void TestNode : : got_block ( ton : : BlockIdExt blkid , td : : BufferSlice data , bool dump ) {
LOG ( INFO ) < < " obtained " < < data . size ( ) < < " data bytes for block " < < blkid . to_str ( ) ;
ton : : FileHash fhash ;
td : : sha256 ( data . as_slice ( ) , fhash . as_slice ( ) ) ;
if ( fhash ! = blkid . file_hash ) {
LOG ( ERROR ) < < " file hash mismatch for block " < < blkid . to_str ( ) < < " : expected " < < blkid . file_hash . to_hex ( )
< < " , computed " < < fhash . to_hex ( ) ;
return ;
}
register_blkid ( blkid ) ;
if ( ! db_root_ . empty ( ) ) {
auto res = save_db_file ( fhash , data . clone ( ) ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " error saving block file: " < < res . to_string ( ) ;
}
}
if ( dump ) {
auto res = vm : : std_boc_deserialize ( std : : move ( data ) ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot deserialize block data : " < < res . move_as_error ( ) . to_string ( ) ;
return ;
}
auto root = res . move_as_ok ( ) ;
ton : : RootHash rhash { root - > get_hash ( ) . bits ( ) } ;
if ( rhash ! = blkid . root_hash ) {
LOG ( ERROR ) < < " block root hash mismatch: data has " < < rhash . to_hex ( ) < < " , expected "
< < blkid . root_hash . to_hex ( ) ;
return ;
}
auto out = td : : TerminalIO : : out ( ) ;
out < < " block contents is " ;
std : : ostringstream outp ;
2020-02-28 10:28:47 +00:00
block : : gen : : t_Block . print_ref ( print_limit_ , outp , root ) ;
vm : : load_cell_slice ( root ) . print_rec ( print_limit_ , outp ) ;
2019-09-07 10:03:22 +00:00
out < < outp . str ( ) ;
show_block_header ( blkid , std : : move ( root ) , 0xffff ) ;
} else {
auto res = lazy_boc_deserialize ( std : : move ( data ) ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot lazily deserialize block data : " < < res . move_as_error ( ) . to_string ( ) ;
return ;
}
auto pair = res . move_as_ok ( ) ;
auto root = std : : move ( pair . first ) ;
ton : : RootHash rhash { root - > get_hash ( ) . bits ( ) } ;
if ( rhash ! = blkid . root_hash ) {
LOG ( ERROR ) < < " block root hash mismatch: data has " < < rhash . to_hex ( ) < < " , expected "
< < blkid . root_hash . to_hex ( ) ;
return ;
}
show_block_header ( blkid , std : : move ( root ) , 0xffff ) ;
}
show_new_blkids ( ) ;
}
void TestNode : : got_state ( ton : : BlockIdExt blkid , ton : : RootHash root_hash , ton : : FileHash file_hash , td : : BufferSlice data ,
bool dump ) {
LOG ( INFO ) < < " obtained " < < data . size ( ) < < " state bytes for block " < < blkid . to_str ( ) ;
ton : : FileHash fhash ;
td : : sha256 ( data . as_slice ( ) , fhash . as_slice ( ) ) ;
if ( fhash ! = file_hash ) {
LOG ( ERROR ) < < " file hash mismatch for state " < < blkid . to_str ( ) < < " : expected " < < file_hash . to_hex ( )
< < " , computed " < < fhash . to_hex ( ) ;
return ;
}
register_blkid ( blkid ) ;
if ( ! db_root_ . empty ( ) ) {
auto res = save_db_file ( fhash , data . clone ( ) ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " error saving state file: " < < res . to_string ( ) ;
}
}
if ( dump ) {
auto res = vm : : std_boc_deserialize ( std : : move ( data ) ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot deserialize block data : " < < res . move_as_error ( ) . to_string ( ) ;
return ;
}
auto root = res . move_as_ok ( ) ;
ton : : RootHash rhash { root - > get_hash ( ) . bits ( ) } ;
if ( rhash ! = root_hash ) {
LOG ( ERROR ) < < " block state root hash mismatch: data has " < < rhash . to_hex ( ) < < " , expected "
< < root_hash . to_hex ( ) ;
return ;
}
auto out = td : : TerminalIO : : out ( ) ;
out < < " shard state contents is " ;
std : : ostringstream outp ;
2020-02-28 10:28:47 +00:00
block : : gen : : t_ShardState . print_ref ( print_limit_ , outp , root ) ;
vm : : load_cell_slice ( root ) . print_rec ( print_limit_ , outp ) ;
2019-09-07 10:03:22 +00:00
out < < outp . str ( ) ;
show_state_header ( blkid , std : : move ( root ) , 0xffff ) ;
} else {
auto res = lazy_boc_deserialize ( std : : move ( data ) ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot lazily deserialize block data : " < < res . move_as_error ( ) . to_string ( ) ;
return ;
}
auto pair = res . move_as_ok ( ) ;
auto root = std : : move ( pair . first ) ;
ton : : RootHash rhash { root - > get_hash ( ) . bits ( ) } ;
if ( rhash ! = root_hash ) {
LOG ( ERROR ) < < " block state root hash mismatch: data has " < < rhash . to_hex ( ) < < " , expected "
< < root_hash . to_hex ( ) ;
return ;
}
show_state_header ( blkid , std : : move ( root ) , 0xffff ) ;
}
show_new_blkids ( ) ;
}
2020-04-27 12:01:46 +00:00
bool TestNode : : get_show_block_header ( ton : : BlockIdExt blkid , int mode ) {
return get_block_header ( blkid , mode , [ this , blkid ] ( td : : Result < BlockHdrInfo > R ) {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < " unable to fetch block header: " < < R . move_as_error ( ) ;
} else {
auto res = R . move_as_ok ( ) ;
show_block_header ( res . blk_id , res . virt_blk_root , res . mode ) ;
show_new_blkids ( ) ;
}
} ) ;
}
bool TestNode : : get_block_header ( ton : : BlockIdExt blkid , int mode , td : : Promise < TestNode : : BlockHdrInfo > promise ) {
2019-09-07 10:03:22 +00:00
LOG ( INFO ) < < " got block header request for " < < blkid . to_str ( ) < < " with mode " < < mode ;
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getBlockHeader > ( ton : : create_tl_lite_block_id ( blkid ) , mode ) , true ) ;
2020-04-27 12:01:46 +00:00
return envelope_send_query (
std : : move ( b ) , [ this , blkid , promise = std : : move ( promise ) ] ( td : : Result < td : : BufferSlice > R ) mutable - > void {
TRY_RESULT_PROMISE_PREFIX ( promise , res , std : : move ( R ) ,
PSLICE ( ) < < " cannot obtain block header for " < < blkid . to_str ( ) < < " from server : " ) ;
got_block_header_raw ( std : : move ( res ) , std : : move ( promise ) , blkid ) ;
} ) ;
}
bool TestNode : : lookup_show_block ( ton : : ShardIdFull shard , int mode , td : : uint64 arg ) {
return lookup_block ( shard , mode , arg , [ this ] ( td : : Result < BlockHdrInfo > R ) {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < " unable to look up block: " < < R . move_as_error ( ) ;
2019-09-07 10:03:22 +00:00
} else {
2020-04-27 12:01:46 +00:00
auto res = R . move_as_ok ( ) ;
show_block_header ( res . blk_id , res . virt_blk_root , res . mode ) ;
show_new_blkids ( ) ;
2019-09-07 10:03:22 +00:00
}
} ) ;
}
2020-04-27 12:01:46 +00:00
bool TestNode : : lookup_block ( ton : : ShardIdFull shard , int mode , td : : uint64 arg ,
td : : Promise < TestNode : : BlockHdrInfo > promise ) {
2019-09-07 10:03:22 +00:00
ton : : BlockId id { shard , mode & 1 ? ( td : : uint32 ) arg : 0 } ;
LOG ( INFO ) < < " got block lookup request for " < < id . to_str ( ) < < " with mode " < < mode < < " and argument " < < arg ;
auto b = ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_lookupBlock > (
mode , ton : : create_tl_lite_block_id_simple ( id ) , arg , ( td : : uint32 ) arg ) ,
true ) ;
return envelope_send_query (
2020-04-27 12:01:46 +00:00
std : : move ( b ) , [ this , id , mode , arg , promise = std : : move ( promise ) ] ( td : : Result < td : : BufferSlice > R ) mutable - > void {
TRY_RESULT_PROMISE_PREFIX ( promise , res , std : : move ( R ) ,
PSLICE ( ) < < " cannot look up block header for " < < id . to_str ( ) < < " with mode " < < mode
< < " and argument " < < arg < < " from server : " ) ;
got_block_header_raw ( std : : move ( res ) , std : : move ( promise ) ) ;
2019-09-07 10:03:22 +00:00
} ) ;
}
2020-04-27 12:01:46 +00:00
void TestNode : : got_block_header_raw ( td : : BufferSlice res , td : : Promise < TestNode : : BlockHdrInfo > promise ,
ton : : BlockIdExt req_blkid ) {
TRY_RESULT_PROMISE_PREFIX ( promise , f ,
ton : : fetch_tl_object < ton : : lite_api : : liteServer_blockHeader > ( std : : move ( res ) , true ) ,
" cannot parse answer to liteServer.lookupBlock : " ) ;
auto blk_id = ton : : create_block_id ( f - > id_ ) ;
LOG ( INFO ) < < " obtained block header for " < < blk_id . to_str ( ) < < " from server ( " < < f - > header_proof_ . size ( )
< < " data bytes) " ;
if ( req_blkid . is_valid ( ) & & blk_id ! = req_blkid ) {
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " block id mismatch: expected data for block " < < req_blkid . to_str ( )
< < " , obtained for " < < blk_id . to_str ( ) ) ) ;
return ;
}
TRY_RESULT_PROMISE_PREFIX ( promise , root , vm : : std_boc_deserialize ( std : : move ( f - > header_proof_ ) ) ,
" cannot deserialize block header data : " ) ;
bool ok = false ;
td : : Status E ;
try {
auto virt_root = vm : : MerkleProof : : virtualize ( root , 1 ) ;
if ( virt_root . is_null ( ) ) {
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " block header proof for block " < < blk_id . to_str ( )
< < " is not a valid Merkle proof " ) ) ;
return ;
}
ok = true ;
promise . set_result ( BlockHdrInfo { blk_id , std : : move ( root ) , std : : move ( virt_root ) , f - > mode_ } ) ;
return ;
} catch ( vm : : VmError & err ) {
E = err . as_status ( PSLICE ( ) < < " error processing header for " < < blk_id . to_str ( ) < < " : " ) ;
} catch ( vm : : VmVirtError & err ) {
E = err . as_status ( PSLICE ( ) < < " error processing header for " < < blk_id . to_str ( ) < < " : " ) ;
}
if ( ok ) {
LOG ( ERROR ) < < std : : move ( E ) ;
} else {
promise . set_error ( std : : move ( E ) ) ;
}
}
2019-09-07 10:03:22 +00:00
bool TestNode : : show_block_header ( ton : : BlockIdExt blkid , Ref < vm : : Cell > root , int mode ) {
ton : : RootHash vhash { root - > get_hash ( ) . bits ( ) } ;
if ( vhash ! = blkid . root_hash ) {
LOG ( ERROR ) < < " block header for block " < < blkid . to_str ( ) < < " has incorrect root hash " < < vhash . to_hex ( )
< < " instead of " < < blkid . root_hash . to_hex ( ) ;
return false ;
}
std : : vector < ton : : BlockIdExt > prev ;
ton : : BlockIdExt mc_blkid ;
bool after_split ;
auto res = block : : unpack_block_prev_blk_ext ( root , blkid , prev , mc_blkid , after_split ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot unpack header for block " < < blkid . to_str ( ) < < " : " < < res . to_string ( ) ;
return false ;
}
block : : gen : : Block : : Record blk ;
block : : gen : : BlockInfo : : Record info ;
if ( ! ( tlb : : unpack_cell ( root , blk ) & & tlb : : unpack_cell ( blk . info , info ) ) ) {
LOG ( ERROR ) < < " cannot unpack header for block " < < blkid . to_str ( ) ;
return false ;
}
auto out = td : : TerminalIO : : out ( ) ;
out < < " block header of " < < blkid . to_str ( ) < < " @ " < < info . gen_utime < < " lt " < < info . start_lt < < " .. "
< < info . end_lt < < std : : endl ;
out < < " global_id= " < < blk . global_id < < " version= " < < info . version < < " not_master= " < < info . not_master
< < " after_merge= " < < info . after_merge < < " after_split= " < < info . after_split
< < " before_split= " < < info . before_split < < " want_merge= " < < info . want_merge < < " want_split= " < < info . want_split
< < " validator_list_hash_short= " < < info . gen_validator_list_hash_short
< < " catchain_seqno= " < < info . gen_catchain_seqno < < " min_ref_mc_seqno= " < < info . min_ref_mc_seqno ;
if ( ! info . not_master ) {
out < < " is_key_block= " < < info . key_block < < " prev_key_block_seqno= " < < info . prev_key_block_seqno ;
}
out < < std : : endl ;
register_blkid ( blkid ) ;
int cnt = 0 ;
for ( auto id : prev ) {
out < < " previous block # " < < + + cnt < < " : " < < id . to_str ( ) < < std : : endl ;
register_blkid ( id ) ;
}
out < < " reference masterchain block : " < < mc_blkid . to_str ( ) < < std : : endl ;
register_blkid ( mc_blkid ) ;
return true ;
}
bool TestNode : : show_state_header ( ton : : BlockIdExt blkid , Ref < vm : : Cell > root , int mode ) {
return true ;
}
void TestNode : : got_block_header ( ton : : BlockIdExt blkid , td : : BufferSlice data , int mode ) {
LOG ( INFO ) < < " obtained " < < data . size ( ) < < " data bytes as block header for " < < blkid . to_str ( ) ;
auto res = vm : : std_boc_deserialize ( data . clone ( ) ) ;
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot deserialize block header data : " < < res . move_as_error ( ) . to_string ( ) ;
return ;
}
auto root = res . move_as_ok ( ) ;
std : : ostringstream outp ;
2020-02-02 12:53:37 +00:00
vm : : CellSlice cs { vm : : NoVm ( ) , root } ;
2020-02-28 10:28:47 +00:00
cs . print_rec ( print_limit_ , outp ) ;
2019-09-07 10:03:22 +00:00
td : : TerminalIO : : out ( ) < < outp . str ( ) ;
try {
auto virt_root = vm : : MerkleProof : : virtualize ( root , 1 ) ;
if ( virt_root . is_null ( ) ) {
LOG ( ERROR ) < < " block header proof for block " < < blkid . to_str ( ) < < " is not a valid Merkle proof " ;
return ;
}
show_block_header ( blkid , std : : move ( virt_root ) , mode ) ;
} catch ( vm : : VmError err ) {
LOG ( ERROR ) < < " error processing header for " < < blkid . to_str ( ) < < " : " < < err . get_msg ( ) ;
} catch ( vm : : VmVirtError err ) {
LOG ( ERROR ) < < " error processing header for " < < blkid . to_str ( ) < < " : " < < err . get_msg ( ) ;
}
show_new_blkids ( ) ;
}
2019-09-14 14:14:55 +00:00
bool TestNode : : get_block_proof ( ton : : BlockIdExt from , ton : : BlockIdExt to , int mode ) {
if ( ! ( mode & 1 ) ) {
to . invalidate_clear ( ) ;
}
if ( ! ( mode & 0x2000 ) ) {
LOG ( INFO ) < < " got block proof request from " < < from . to_str ( ) < < " to "
2019-09-16 08:06:04 +00:00
< < ( ( mode & 1 ) ? to . to_str ( ) : " last masterchain block " ) < < " with mode= " < < mode ;
2019-09-14 14:14:55 +00:00
} else {
LOG ( DEBUG ) < < " got block proof request from " < < from . to_str ( ) < < " to "
2019-09-16 08:06:04 +00:00
< < ( ( mode & 1 ) ? to . to_str ( ) : " last masterchain block " ) < < " with mode= " < < mode ;
2019-09-14 14:14:55 +00:00
}
if ( ! from . is_masterchain_ext ( ) ) {
LOG ( ERROR ) < < " source block " < < from . to_str ( ) < < " is not a valid masterchain block id " ;
return false ;
}
if ( ( mode & 1 ) & & ! to . is_masterchain_ext ( ) ) {
LOG ( ERROR ) < < " destination block " < < to . to_str ( ) < < " is not a valid masterchain block id " ;
return false ;
}
auto b =
ton : : serialize_tl_object ( ton : : create_tl_object < ton : : lite_api : : liteServer_getBlockProof > (
mode & 0xfff , ton : : create_tl_lite_block_id ( from ) , ton : : create_tl_lite_block_id ( to ) ) ,
true ) ;
2020-04-10 19:06:01 +00:00
return envelope_send_query ( std : : move ( b ) , [ Self = actor_id ( this ) , from , to , mode ] ( td : : Result < td : : BufferSlice > res ) {
2019-09-14 14:14:55 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot obtain block proof for " < < ( ( mode & 1 ) ? to . to_str ( ) : " last masterchain block " )
< < " starting from " < < from . to_str ( ) < < " from server : " < < res . move_as_error ( ) . to_string ( ) ;
} else {
td : : actor : : send_closure_later ( Self , & TestNode : : got_block_proof , from , to , mode , res . move_as_ok ( ) ) ;
}
} ) ;
}
void TestNode : : got_block_proof ( ton : : BlockIdExt from , ton : : BlockIdExt to , int mode , td : : BufferSlice pchain ) {
LOG ( INFO ) < < " got block proof from " < < from . to_str ( ) < < " to "
< < ( ( mode & 1 ) ? to . to_str ( ) : " last masterchain block " ) < < " with mode= " < < mode < < " ( " < < pchain . size ( )
< < " bytes) " ;
2019-09-18 17:46:32 +00:00
auto r_f = ton : : fetch_tl_object < ton : : lite_api : : liteServer_partialBlockProof > ( std : : move ( pchain ) , true ) ;
if ( r_f . is_error ( ) ) {
LOG ( ERROR ) < < " cannot deserialize liteServer.partialBlockProof: " < < r_f . move_as_error ( ) ;
return ;
}
auto f = r_f . move_as_ok ( ) ;
auto res = liteclient : : deserialize_proof_chain ( std : : move ( f ) ) ;
2019-09-14 14:14:55 +00:00
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " cannot deserialize liteServer.partialBlockProof: " < < res . move_as_error ( ) ;
return ;
}
auto chain = res . move_as_ok ( ) ;
2019-09-16 08:06:04 +00:00
if ( chain - > from ! = from ) {
LOG ( ERROR ) < < " block proof chain starts from block " < < chain - > from . to_str ( ) < < " , not from requested block "
< < from . to_str ( ) ;
return ;
}
2019-09-14 14:14:55 +00:00
auto err = chain - > validate ( ) ;
if ( err . is_error ( ) ) {
LOG ( ERROR ) < < " block proof chain is invalid: " < < err ;
return ;
}
// TODO: if `from` was a trusted key block, then mark `to` as a trusted key block, and update the known value of latest trusted key block if `to` is newer
if ( ! chain - > complete & & ( mode & 0x1000 ) ) {
2019-09-16 08:06:04 +00:00
LOG ( INFO ) < < " valid " < < ( chain - > complete ? " " : " in " ) < < " complete proof chain: last block is "
< < chain - > to . to_str ( ) < < " , last key block is "
< < ( chain - > has_key_block ? chain - > key_blkid . to_str ( ) : " (undefined) " ) ;
2019-09-14 14:14:55 +00:00
get_block_proof ( chain - > to , to , mode | 0x2000 ) ;
return ;
}
2019-09-16 08:06:04 +00:00
td : : TerminalIO : : out ( ) < < " valid " < < ( chain - > complete ? " " : " in " ) < < " complete proof chain: last block is "
< < chain - > to . to_str ( ) < < " , last key block is "
< < ( chain - > has_key_block ? chain - > key_blkid . to_str ( ) : " (undefined) " ) < < std : : endl ;
2019-09-14 14:14:55 +00:00
if ( chain - > has_key_block ) {
register_blkid ( chain - > key_blkid ) ;
}
register_blkid ( chain - > to ) ;
2020-04-27 12:01:46 +00:00
auto time = now ( ) ;
if ( ! ( mode & 1 ) | | ( chain - > last_utime > time - 3600 ) ) {
2019-09-16 08:06:04 +00:00
td : : TerminalIO : : out ( ) < < " last block in chain was generated at " < < chain - > last_utime < < " ( "
2020-04-27 12:01:46 +00:00
< < time - chain - > last_utime < < " seconds ago) \n " ;
2019-09-16 08:06:04 +00:00
}
2019-09-14 14:14:55 +00:00
show_new_blkids ( ) ;
}
2019-11-28 14:44:14 +00:00
bool TestNode : : get_creator_stats ( ton : : BlockIdExt blkid , int mode , unsigned req_count , ton : : Bits256 start_after ,
ton : : UnixTime min_utime ) {
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
return set_error ( " server connection not ready " ) ;
}
if ( ! blkid . is_masterchain_ext ( ) ) {
return set_error ( " only masterchain blocks contain block creator statistics " ) ;
}
if ( ! ( mode & 1 ) ) {
start_after . set_zero ( ) ;
}
2020-04-27 12:01:46 +00:00
auto osp = std : : make_unique < std : : ostringstream > ( ) ;
auto & os = * osp ;
return get_creator_stats (
blkid , mode , req_count , start_after , min_utime ,
[ min_utime , & os ] ( const td : : Bits256 & key , const block : : DiscountedCounter & mc_cnt ,
const block : : DiscountedCounter & shard_cnt ) - > bool {
os < < key . to_hex ( ) < < " mc_cnt: " < < mc_cnt < < " shard_cnt: " < < shard_cnt < < std : : endl ;
return true ;
} ,
td : : PromiseCreator : : lambda ( [ os = std : : move ( osp ) ] ( td : : Result < td : : Bits256 > res ) {
if ( res . is_error ( ) ) {
LOG ( ERROR ) < < " error obtaining creator stats: " < < res . move_as_error ( ) ;
} else {
if ( res . ok ( ) . is_zero ( ) ) {
* os < < " (complete) " < < std : : endl ;
} else {
* os < < " (incomplete, repeat query from " < < res . move_as_ok ( ) . to_hex ( ) < < " ) " < < std : : endl ;
}
td : : TerminalIO : : out ( ) < < os - > str ( ) ;
}
} ) ) ;
}
bool TestNode : : get_creator_stats ( ton : : BlockIdExt blkid , int mode , unsigned req_count , ton : : Bits256 start_after ,
ton : : UnixTime min_utime , TestNode : : creator_stats_func_t func ,
td : : Promise < td : : Bits256 > promise ) {
return get_creator_stats ( blkid , req_count , min_utime , std : : move ( func ) ,
std : : make_unique < CreatorStatsRes > ( mode | 0x10000 , start_after ) ,
promise . wrap ( [ ] ( auto & & p ) { return p - > last_key ; } ) ) ;
}
bool TestNode : : get_creator_stats ( ton : : BlockIdExt blkid , unsigned req_count , ton : : UnixTime min_utime ,
TestNode : : creator_stats_func_t func , std : : unique_ptr < TestNode : : CreatorStatsRes > state ,
td : : Promise < std : : unique_ptr < TestNode : : CreatorStatsRes > > promise ) {
if ( ! ( ready_ & & ! client_ . empty ( ) ) ) {
promise . set_error ( td : : Status : : Error ( " server connection not ready " ) ) ;
return false ;
}
if ( ! state ) {
promise . set_error ( td : : Status : : Error ( " null CreatorStatsRes " ) ) ;
return false ;
}
if ( ! blkid . is_masterchain_ext ( ) ) {
promise . set_error ( td : : Status : : Error ( " only masterchain blocks contain block creator statistics " ) ) ;
return false ;
}
if ( ! ( state - > mode & 1 ) ) {
state - > last_key . set_zero ( ) ;
}
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getValidatorStats > (
state - > mode & 0xff , ton : : create_tl_lite_block_id ( blkid ) , req_count , state - > last_key , min_utime ) ,
true ) ;
2019-11-28 14:44:14 +00:00
LOG ( INFO ) < < " requesting up to " < < req_count < < " block creator stats records with respect to masterchain block "
2020-04-27 12:01:46 +00:00
< < blkid . to_str ( ) < < " starting from validator public key " < < state - > last_key . to_hex ( ) < < " created after "
< < min_utime < < " (mode= " < < state - > mode < < " ) " ;
return envelope_send_query (
std : : move ( b ) , [ this , blkid , req_count , state = std : : move ( state ) , min_utime , func = std : : move ( func ) ,
promise = std : : move ( promise ) ] ( td : : Result < td : : BufferSlice > R ) mutable {
TRY_RESULT_PROMISE ( promise , res , std : : move ( R ) ) ;
TRY_RESULT_PROMISE_PREFIX ( promise , f ,
ton : : fetch_tl_object < ton : : lite_api : : liteServer_validatorStats > ( std : : move ( res ) , true ) ,
" cannot parse answer to liteServer.getValidatorStats " ) ;
got_creator_stats ( blkid , ton : : create_block_id ( f - > id_ ) , f - > mode_ , min_utime , std : : move ( f - > state_proof_ ) ,
std : : move ( f - > data_proof_ ) , f - > count_ , req_count , f - > complete_ , std : : move ( func ) ,
std : : move ( state ) , std : : move ( promise ) ) ;
} ) ;
2019-11-28 14:44:14 +00:00
}
2020-04-27 12:01:46 +00:00
void TestNode : : got_creator_stats ( ton : : BlockIdExt req_blkid , ton : : BlockIdExt blkid , int mode , ton : : UnixTime min_utime ,
td : : BufferSlice state_proof , td : : BufferSlice data_proof , int count , int req_count ,
bool complete , TestNode : : creator_stats_func_t func ,
std : : unique_ptr < TestNode : : CreatorStatsRes > status ,
td : : Promise < std : : unique_ptr < TestNode : : CreatorStatsRes > > promise ) {
2019-11-28 14:44:14 +00:00
LOG ( INFO ) < < " got answer to getValidatorStats query: " < < count < < " records out of " < < req_count < < " , "
< < ( complete ? " complete " : " incomplete " ) ;
if ( ! blkid . is_masterchain_ext ( ) ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " reference block " < < blkid . to_str ( )
< < " for block creator statistics is not a valid masterchain block " ) ) ;
2019-11-28 14:44:14 +00:00
return ;
}
if ( count > req_count ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( td : : Status : : Error ( PSLICE ( )
< < " obtained " < < count < < " answers to getValidatorStats query, but only "
< < req_count < < " were requested " ) ) ;
2019-11-28 14:44:14 +00:00
return ;
}
if ( blkid ! = req_blkid ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( td : : Status : : Error ( PSLICE ( )
< < " answer to getValidatorStats refers to masterchain block " < < blkid . to_str ( )
< < " different from requested " < < req_blkid . to_str ( ) ) ) ;
2019-11-28 14:44:14 +00:00
return ;
}
2020-04-27 12:01:46 +00:00
TRY_RESULT_PROMISE_PREFIX ( promise , state ,
block : : check_extract_state_proof ( blkid , state_proof . as_slice ( ) , data_proof . as_slice ( ) ) ,
PSLICE ( ) < < " masterchain state proof for " < < blkid . to_str ( ) < < " is invalid : " ) ;
if ( ! ( mode & 0x10000 ) ) {
if ( status - > state_proof . is_null ( ) ) {
TRY_RESULT_PROMISE_PREFIX (
promise , state_root , vm : : std_boc_deserialize ( state_proof . as_slice ( ) ) ,
PSLICE ( ) < < " cannot deserialize masterchain state proof for " < < blkid . to_str ( ) < < " : " ) ;
status - > state_proof = std : : move ( state_root ) ;
}
TRY_RESULT_PROMISE_PREFIX (
promise , data_root , vm : : std_boc_deserialize ( data_proof . as_slice ( ) ) ,
PSLICE ( ) < < " cannot deserialize masterchain creators data proof for " < < blkid . to_str ( ) < < " : " ) ;
if ( status - > data_proof . is_null ( ) ) {
status - > data_proof = std : : move ( data_root ) ;
} else {
TRY_RESULT_PROMISE_PREFIX ( promise , data_proof2 ,
vm : : MerkleProof : : combine_fast_status ( status - > data_proof , std : : move ( data_root ) ) ,
" cannot combine Merkle proofs for creator data " ) ;
status - > data_proof = std : : move ( data_proof2 ) ;
}
2019-11-28 14:44:14 +00:00
}
bool allow_eq = ( mode & 3 ) ! = 1 ;
2020-04-27 12:01:46 +00:00
ton : : Bits256 key { status - > last_key } ;
2019-11-28 14:44:14 +00:00
std : : ostringstream os ;
try {
2020-04-27 12:01:46 +00:00
auto dict = block : : get_block_create_stats_dict ( std : : move ( state ) ) ;
2019-11-28 14:44:14 +00:00
if ( ! dict ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( td : : Status : : Error ( " cannot extract BlockCreateStats from mc state " ) ) ;
2019-11-28 14:44:14 +00:00
return ;
}
for ( int i = 0 ; i < count + ( int ) complete ; i + + ) {
auto v = dict - > lookup_nearest_key ( key , true , allow_eq ) ;
if ( v . is_null ( ) ) {
if ( i ! = count ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " could fetch only " < < i < < " CreatorStats entries out of "
< < count < < " declared in answer to getValidatorStats " ) ) ;
2019-11-28 14:44:14 +00:00
return ;
}
break ;
}
block : : DiscountedCounter mc_cnt , shard_cnt ;
if ( ! block : : unpack_CreatorStats ( std : : move ( v ) , mc_cnt , shard_cnt ) ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( td : : Status : : Error ( PSLICE ( ) < < " invalid CreatorStats record with key " < < key . to_hex ( ) ) ) ;
2019-11-28 14:44:14 +00:00
return ;
}
2024-08-20 16:55:01 +00:00
func ( key , mc_cnt , shard_cnt ) ;
2019-11-28 14:44:14 +00:00
allow_eq = false ;
}
if ( complete ) {
2020-04-27 12:01:46 +00:00
status - > last_key . set_zero ( ) ;
status - > complete = true ;
promise . set_result ( std : : move ( status ) ) ;
} else if ( ! ( status - > mode & 0x100 ) ) {
status - > last_key = key ;
promise . set_result ( std : : move ( status ) ) ;
2019-11-28 14:44:14 +00:00
} else {
2020-04-27 12:01:46 +00:00
// incomplete, send new query to fetch next entries
status - > last_key = key ;
status - > mode | = 1 ;
get_creator_stats ( blkid , req_count , min_utime , std : : move ( func ) , std : : move ( status ) , std : : move ( promise ) ) ;
2019-11-28 14:44:14 +00:00
}
} catch ( vm : : VmError & err ) {
2020-04-27 12:01:46 +00:00
promise . set_error ( err . as_status ( " error while traversing block creator stats: " ) ) ;
} catch ( vm : : VmVirtError & err ) {
promise . set_error ( err . as_status ( " virtualization error while traversing block creator stats: " ) ) ;
}
}
bool TestNode : : check_validator_load ( int start_time , int end_time , int mode , std : : string file_pfx ) {
int time = now ( ) ;
if ( start_time < = 0 ) {
start_time + = time ;
}
if ( end_time < = 0 ) {
end_time + = time ;
}
if ( start_time > = end_time ) {
return set_error ( " end time must be later than start time " ) ;
}
LOG ( INFO ) < < " requesting masterchain blocks corresponding to unixtime " < < start_time < < " and " < < end_time ;
auto P = td : : split_promise ( [ this , mode , file_pfx ] ( td : : Result < std : : pair < BlockHdrInfo , BlockHdrInfo > > R ) {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < " cannot obtain block info: " < < R . move_as_error ( ) ;
return ;
}
auto res = R . move_as_ok ( ) ;
continue_check_validator_load ( res . first . blk_id , res . first . proof , res . second . blk_id , res . second . proof , mode ,
file_pfx ) ;
} ) ;
lookup_block ( ton : : ShardIdFull ( ton : : masterchainId ) , 4 , start_time , std : : move ( P . first ) ) ;
return lookup_block ( ton : : ShardIdFull ( ton : : masterchainId ) , 4 , end_time , std : : move ( P . second ) ) ;
}
void TestNode : : continue_check_validator_load ( ton : : BlockIdExt blkid1 , Ref < vm : : Cell > root1 , ton : : BlockIdExt blkid2 ,
Ref < vm : : Cell > root2 , int mode , std : : string file_pfx ) {
LOG ( INFO ) < < " continue_check_validator_load for blocks " < < blkid1 . to_str ( ) < < " and " < < blkid2 . to_str ( )
< < " : requesting configuration parameter #34 " ;
auto P = td : : split_promise (
[ this , blkid1 , root1 , blkid2 , root2 , mode , file_pfx ] ( td : : Result < std : : pair < ConfigInfo , ConfigInfo > > R ) mutable {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < " cannot obtain configuration parameter #34 : " < < R . move_as_error ( ) ;
return ;
}
auto res = R . move_as_ok ( ) ;
root1 = vm : : MerkleProof : : combine_fast ( std : : move ( root1 ) , std : : move ( res . first . state_proof ) ) ;
root2 = vm : : MerkleProof : : combine_fast ( std : : move ( root2 ) , std : : move ( res . second . state_proof ) ) ;
if ( root1 . is_null ( ) | | root2 . is_null ( ) ) {
LOG ( ERROR ) < < " cannot merge block header proof with block state proof " ;
return ;
}
auto info1 = std : : make_unique < ValidatorLoadInfo > ( blkid1 , std : : move ( root1 ) , std : : move ( res . first . config_proof ) ,
std : : move ( res . first . config ) ) ;
auto info2 = std : : make_unique < ValidatorLoadInfo > ( blkid2 , std : : move ( root2 ) , std : : move ( res . second . config_proof ) ,
std : : move ( res . second . config ) ) ;
continue_check_validator_load2 ( std : : move ( info1 ) , std : : move ( info2 ) , mode , file_pfx ) ;
} ) ;
get_config_params_ext ( blkid1 , std : : move ( P . first ) , 0x4000 , " " , { 28 , 34 } ) ;
get_config_params_ext ( blkid2 , std : : move ( P . second ) , 0x4000 , " " , { 28 , 34 } ) ;
}
td : : Status TestNode : : ValidatorLoadInfo : : unpack_vset ( ) {
if ( ! config ) {
return td : : Status : : Error ( " no configuration to unpack validator set " ) ;
}
auto vset_root_c = config - > get_config_param ( 34 ) ;
if ( vset_root_c . is_null ( ) ) {
vset_hash . set_zero ( ) ;
return td : : Status : : Error ( " no configuration parameter 34 for block " s + blk_id . to_str ( ) ) ;
}
if ( vset_root . not_null ( ) & & vset_root - > get_hash ( ) = = vset_root_c - > get_hash ( ) ) {
vset_root_c = vset_root ;
} else {
vset_root = vset_root_c ;
}
vset_hash = vset_root - > get_hash ( ) . bits ( ) ;
TRY_RESULT_PREFIX_ASSIGN (
vset , block : : Config : : unpack_validator_set ( vset_root ) ,
PSLICE ( ) < < " cannot unpack validator set from configuration parameter 34 of block " < < blk_id . to_str ( ) < < " : " ) ;
valid_since = vset - > utime_since ;
vset_map = vset - > compute_validator_map ( ) ;
return td : : Status : : OK ( ) ;
}
bool TestNode : : ValidatorLoadInfo : : store_record ( const td : : Bits256 & key , const block : : DiscountedCounter & mc_cnt ,
const block : : DiscountedCounter & shard_cnt ) {
if ( ! ( mc_cnt . is_valid ( ) & & shard_cnt . is_valid ( ) ) ) {
return false ;
}
if ( mc_cnt . total > = ( 1ULL < < 60 ) | | shard_cnt . total > = ( 1ULL < < 60 ) ) {
return false ;
}
if ( key . is_zero ( ) ) {
created_total . first = ( td : : int64 ) mc_cnt . total ;
created_total . second = ( td : : int64 ) shard_cnt . total ;
return true ;
}
auto it = vset_map . find ( key ) ;
if ( it = = vset_map . end ( ) ) {
return false ;
}
2021-02-09 10:15:47 +00:00
created . at ( it - > second ) = std : : make_pair ( mc_cnt . total , shard_cnt . total ) ;
2020-04-27 12:01:46 +00:00
return true ;
}
bool TestNode : : load_creator_stats ( std : : unique_ptr < TestNode : : ValidatorLoadInfo > load_to ,
td : : Promise < std : : unique_ptr < TestNode : : ValidatorLoadInfo > > promise , bool need_proofs ) {
if ( ! load_to ) {
promise . set_error ( td : : Status : : Error ( " no ValidatorLoadInfo " ) ) ;
return false ;
}
auto & info = * load_to ;
info . created_total . first = info . created_total . second = 0 ;
info . created . resize ( 0 ) ;
info . created . resize ( info . vset - > total , std : : make_pair < td : : uint64 , td : : uint64 > ( 0 , 0 ) ) ;
ton : : UnixTime min_utime = info . valid_since - 1000 ;
return get_creator_stats (
info . blk_id , 1000 , min_utime ,
[ min_utime , & info ] ( const td : : Bits256 & key , const block : : DiscountedCounter & mc_cnt ,
const block : : DiscountedCounter & shard_cnt ) - > bool {
info . store_record ( key , mc_cnt , shard_cnt ) ;
return true ;
} ,
std : : make_unique < CreatorStatsRes > ( need_proofs ? 0x100 : 0x10100 ) ,
td : : PromiseCreator : : lambda ( [ load_to = std : : move ( load_to ) , promise = std : : move ( promise ) ] (
td : : Result < std : : unique_ptr < CreatorStatsRes > > R ) mutable {
TRY_RESULT_PROMISE_PREFIX ( promise , res , std : : move ( R ) , " error obtaining creator stats: " ) ;
if ( ! res - > complete ) {
promise . set_error ( td : : Status : : Error ( " incomplete creator stats " ) ) ;
return ;
}
// merge
load_to - > state_proof =
vm : : MerkleProof : : combine_fast ( std : : move ( load_to - > state_proof ) , std : : move ( res - > state_proof ) ) ;
load_to - > data_proof = vm : : MerkleProof : : combine_fast ( std : : move ( load_to - > data_proof ) , std : : move ( res - > data_proof ) ) ;
promise . set_result ( std : : move ( load_to ) ) ;
} ) ) ;
}
void TestNode : : continue_check_validator_load2 ( std : : unique_ptr < TestNode : : ValidatorLoadInfo > info1 ,
std : : unique_ptr < TestNode : : ValidatorLoadInfo > info2 , int mode ,
std : : string file_pfx ) {
LOG ( INFO ) < < " continue_check_validator_load2 for blocks " < < info1 - > blk_id . to_str ( ) < < " and "
2024-02-02 13:59:07 +00:00
< < info2 - > blk_id . to_str ( ) < < " : requesting block creators data " ;
2020-04-27 12:01:46 +00:00
td : : Status st = info1 - > unpack_vset ( ) ;
if ( st . is_error ( ) ) {
LOG ( ERROR ) < < " cannot unpack validator set from block " < < info1 - > blk_id . to_str ( ) < < " : " < < st . move_as_error ( ) ;
return ;
}
st = info2 - > unpack_vset ( ) ;
if ( st . is_error ( ) ) {
LOG ( ERROR ) < < " cannot unpack validator set from block " < < info2 - > blk_id . to_str ( ) < < " : " < < st . move_as_error ( ) ;
return ;
}
if ( info1 - > vset_hash ! = info2 - > vset_hash | | info1 - > valid_since ! = info2 - > valid_since ) {
LOG ( ERROR ) < < " blocks appear to have different validator sets " ;
return ;
}
LOG ( INFO ) < < " validator sets valid since " < < info1 - > valid_since ;
auto P = td : : split_promise (
[ this , mode ,
file_pfx ] ( td : : Result < std : : pair < std : : unique_ptr < ValidatorLoadInfo > , std : : unique_ptr < ValidatorLoadInfo > > > R ) {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < " cannot load block creation statistics : " < < R . move_as_error ( ) ;
return ;
}
auto res = R . move_as_ok ( ) ;
continue_check_validator_load3 ( std : : move ( res . first ) , std : : move ( res . second ) , mode , file_pfx ) ;
} ) ;
load_creator_stats ( std : : move ( info1 ) , std : : move ( P . first ) , true ) ;
load_creator_stats ( std : : move ( info2 ) , std : : move ( P . second ) , true ) ;
}
2024-10-15 14:59:12 +00:00
// computes the probability of creating <= x blocks if the expected value is y
2020-04-27 12:01:46 +00:00
static double create_prob ( int x , double y ) {
if ( x < 0 | | y < 0 ) {
return .5 ;
}
if ( x > = y ) {
return .5 ;
}
if ( x < = 20 ) {
// Poisson
double t = exp ( - y ) , s = t ;
for ( int n = 1 ; n < = x ; n + + ) {
s + = t = ( t * y ) / n ;
}
return s ;
}
// normal approximation
double z = ( x - y ) / sqrt ( 2. * y ) ;
return ( 1. + erf ( z ) ) / 2 ;
}
static double shard_create_prob ( int x , double y , double chunk_size ) {
if ( x < 0 | | y < 0 ) {
return .5 ;
}
if ( x > = y ) {
return .5 ;
}
double y0 = y / chunk_size ; // expected chunks
if ( ! x ) {
return y0 > 100 ? 0 : exp ( - y0 ) ; // Poisson approximation for having participated in zero chunks
}
// at least ten chunks, normal approximation
double z = ( x - y ) / sqrt ( 2. * y * chunk_size ) ;
return ( 1. + erf ( z ) ) / 2 ;
}
void TestNode : : continue_check_validator_load3 ( std : : unique_ptr < TestNode : : ValidatorLoadInfo > info1 ,
std : : unique_ptr < TestNode : : ValidatorLoadInfo > info2 , int mode ,
std : : string file_pfx ) {
LOG ( INFO ) < < " continue_check_validator_load3 for blocks " < < info1 - > blk_id . to_str ( ) < < " and "
2024-10-15 14:59:12 +00:00
< < info2 - > blk_id . to_str ( ) < < " with mode= " < < mode < < " and file prefix ` " < < file_pfx ;
if ( mode & 4 ) {
ton : : BlockSeqno start_seqno = info1 - > blk_id . seqno ( ) ;
ton : : BlockSeqno end_seqno = info2 - > blk_id . seqno ( ) ;
block : : ValidatorSet validator_set = * info1 - > vset ;
if ( info1 - > config - > get_config_param ( 28 ) - > get_hash ( ) ! = info2 - > config - > get_config_param ( 28 ) - > get_hash ( ) ) {
LOG ( ERROR ) < < " Catchain validator config (28) changed between the first and the last block " ;
return ;
}
auto catchain_config = std : : make_unique < block : : CatchainValidatorsConfig > (
block : : Config : : unpack_catchain_validators_config ( info1 - > config - > get_config_param ( 28 ) ) ) ;
load_validator_shard_shares (
start_seqno , end_seqno , std : : move ( validator_set ) , std : : move ( catchain_config ) ,
[ = , this , info1 = std : : move ( info1 ) ,
info2 = std : : move ( info2 ) ] ( td : : Result < std : : map < td : : Bits256 , td : : uint64 > > R ) mutable {
if ( R . is_error ( ) ) {
LOG ( ERROR ) < < " failed to load validator shard shares: " < < R . move_as_error ( ) ;
} else {
continue_check_validator_load4 ( std : : move ( info1 ) , std : : move ( info2 ) , mode , file_pfx , R . move_as_ok ( ) ) ;
}
} ) ;
} else {
continue_check_validator_load4 ( std : : move ( info1 ) , std : : move ( info2 ) , mode , std : : move ( file_pfx ) , { } ) ;
}
}
void TestNode : : continue_check_validator_load4 ( std : : unique_ptr < TestNode : : ValidatorLoadInfo > info1 ,
std : : unique_ptr < TestNode : : ValidatorLoadInfo > info2 , int mode ,
std : : string file_pfx ,
std : : map < td : : Bits256 , td : : uint64 > exact_shard_shares ) {
LOG ( INFO ) < < " continue_check_validator_load4 for blocks " < < info1 - > blk_id . to_str ( ) < < " and "
< < info2 - > blk_id . to_str ( ) < < " with mode= " < < mode < < " and file prefix ` " < < file_pfx ;
2020-04-27 12:01:46 +00:00
if ( info1 - > created_total . first < = 0 | | info2 - > created_total . first < = 0 ) {
LOG ( ERROR ) < < " no total created blocks statistics " ;
return ;
}
td : : TerminalIO : : out ( ) < < " total: ( " < < info1 - > created_total . first < < " , " < < info1 - > created_total . second < < " ) -> ( "
< < info2 - > created_total . first < < " , " < < info2 - > created_total . second < < " ) \n " ;
2024-10-15 14:59:12 +00:00
auto created_total_mc = info2 - > created_total . first - info1 - > created_total . first ;
auto created_total_bc = info2 - > created_total . second - info1 - > created_total . second ;
td : : int64 created_mc_sum = 0 , created_bc_sum = 0 ;
if ( created_total_mc < = 0 | | created_total_bc < 0 | | ( created_total_mc | created_total_bc ) > = ( 1U < < 31 ) ) {
LOG ( ERROR ) < < " impossible situation: zero or no blocks created: " < < created_total_mc < < " masterchain blocks, "
< < created_total_bc < < " shardchain blocks " ;
2020-04-27 12:01:46 +00:00
return ;
}
int count = info1 - > vset - > total ;
CHECK ( info2 - > vset - > total = = count ) ;
CHECK ( ( int ) info1 - > created . size ( ) = = count ) ;
CHECK ( ( int ) info2 - > created . size ( ) = = count ) ;
2024-10-15 14:59:12 +00:00
std : : vector < std : : pair < int , int > > vals_created ;
vals_created . reserve ( count ) ;
2020-04-27 12:01:46 +00:00
for ( int i = 0 ; i < count ; i + + ) {
2024-10-15 14:59:12 +00:00
auto created_mc = info2 - > created [ i ] . first - info1 - > created [ i ] . first ;
auto created_bc = info2 - > created [ i ] . second - info1 - > created [ i ] . second ;
if ( created_mc < 0 | | created_bc < 0 | | ( created_mc | created_bc ) > = ( 1u < < 31 ) ) {
LOG ( ERROR ) < < " impossible situation: validator # " < < i < < " created a negative amount of blocks: " < < created_mc
< < " masterchain blocks, " < < created_bc < < " shardchain blocks " ;
2020-04-27 12:01:46 +00:00
return ;
}
2024-10-15 14:59:12 +00:00
created_mc_sum + = created_mc ;
created_bc_sum + = created_bc ;
vals_created . emplace_back ( ( int ) created_mc , ( int ) created_bc ) ;
td : : TerminalIO : : out ( ) < < " val # " < < i < < " : created ( " < < created_mc < < " , " < < created_bc < < " ) ; was ( "
< < info1 - > created [ i ] . first < < " , " < < info1 - > created [ i ] . second < < " ) \n " ;
2020-04-27 12:01:46 +00:00
}
2024-10-15 14:59:12 +00:00
if ( created_mc_sum ! = created_total_mc | | created_bc_sum ! = created_total_bc ) {
LOG ( ERROR ) < < " cannot account for all blocks created: total is ( " < < created_total_mc < < " , " < < created_total_bc
< < " ), but the sum for all validators is ( " < < created_mc_sum < < " , " < < created_bc_sum < < " ) " ;
2020-04-27 12:01:46 +00:00
return ;
}
2024-10-15 14:59:12 +00:00
td : : TerminalIO : : out ( ) < < " total: ( " < < created_total_mc < < " , " < < created_total_bc < < " ) \n " ;
2020-04-27 12:01:46 +00:00
auto ccfg = block : : Config : : unpack_catchain_validators_config ( info2 - > config - > get_config_param ( 28 ) ) ;
auto ccfg_old = block : : Config : : unpack_catchain_validators_config ( info1 - > config - > get_config_param ( 28 ) ) ;
if ( ccfg . shard_val_num ! = ccfg_old . shard_val_num | | ccfg . shard_val_num < = 0 ) {
LOG ( ERROR ) < < " shard validator group size changed from " < < ccfg_old . shard_val_num < < " to " < < ccfg . shard_val_num
< < " , or is not positive " ;
return ;
}
2024-10-15 14:59:12 +00:00
int shard_vals = ccfg . shard_val_num , master_vals = info2 - > vset - > main ;
if ( info1 - > vset - > main ! = master_vals | | master_vals < = 0 ) {
LOG ( ERROR ) < < " masterchain validator group size changed from " < < info1 - > vset - > main < < " to " < < master_vals
2020-04-27 12:01:46 +00:00
< < " , or is not positive " ;
return ;
}
2024-10-15 14:59:12 +00:00
bool use_exact_shard_share = mode & 4 ;
int proofs_cnt = 0 , proofs_cnt_ok = 0 ;
double chunk_size = ccfg . shard_val_lifetime / 3. / shard_vals ;
std : : vector < double > mtc_shard_share ;
if ( use_exact_shard_share ) {
LOG ( INFO ) < < " using exact shard shares " ;
td : : uint64 exact_shard_shares_sum = 0 ;
for ( auto & [ _ , count ] : exact_shard_shares ) {
exact_shard_shares_sum + = count ;
}
if ( ( td : : int64 ) exact_shard_shares_sum ! = shard_vals * created_bc_sum ) {
LOG ( ERROR ) < < " unexpected total shard shares: blocks= " < < created_bc_sum < < " , shard_vals= " < < shard_vals
< < " , expected_sum= " < < shard_vals * created_bc_sum < < " , found= " < < exact_shard_shares_sum ;
return ;
}
} else {
LOG ( INFO ) < < " using MtCarloComputeShare " ;
block : : MtCarloComputeShare mtc ( shard_vals , info2 - > vset - > export_scaled_validator_weights ( ) ) ;
if ( ! mtc . is_ok ( ) ) {
LOG ( ERROR ) < < " failed to compute shard shares " ;
return ;
}
mtc_shard_share . resize ( count ) ;
for ( size_t i = 0 ; i < count ; + + i ) {
mtc_shard_share [ i ] = mtc [ i ] ;
}
}
auto validators = info1 - > vset - > export_validator_set ( ) ;
2020-04-27 12:01:46 +00:00
for ( int i = 0 ; i < count ; i + + ) {
2024-10-15 14:59:12 +00:00
int created_mc = vals_created [ i ] . first , created_bc = vals_created [ i ] . second ;
bool is_masterchain_validator = i < master_vals ;
double expected_created_mc = ( is_masterchain_validator ? ( double ) created_mc_sum / master_vals : 0 ) ;
double prob_mc = create_prob ( created_mc , .9 * expected_created_mc ) ;
double expected_created_bc , prob_bc ;
if ( use_exact_shard_share ) {
expected_created_bc = ( double ) exact_shard_shares [ validators [ i ] . key . as_bits256 ( ) ] / shard_vals ;
prob_bc = create_prob ( created_bc , .9 * expected_created_bc ) ;
} else {
expected_created_bc = mtc_shard_share [ i ] * ( double ) created_bc_sum / shard_vals ;
prob_bc = shard_create_prob ( created_bc , .9 * expected_created_bc , chunk_size ) ;
}
2020-04-27 12:01:46 +00:00
td : : Bits256 pk = info2 - > vset - > list [ i ] . pubkey . as_bits256 ( ) ;
2024-10-15 14:59:12 +00:00
td : : TerminalIO : : out ( ) < < " val # " < < i < < " : pubkey " < < pk . to_hex ( ) < < " , blocks created ( " < < created_mc < < " , "
< < created_bc < < " ), expected ( " < < expected_created_mc < < " , " < < expected_created_bc
< < " ), probabilities " < < prob_mc < < " and " < < prob_bc < < " \n " ;
if ( ( is_masterchain_validator ? prob_mc : prob_bc ) < .00001 ) {
2020-04-27 12:01:46 +00:00
LOG ( ERROR ) < < " validator # " < < i < < " with pubkey " < < pk . to_hex ( )
< < " : serious misbehavior detected: created less than 90% of the expected amount of blocks with "
" probability 99.999% : created ( "
2024-10-15 14:59:12 +00:00
< < created_mc < < " , " < < created_bc < < " ), expected ( " < < expected_created_mc < < " , "
< < expected_created_bc < < " ) masterchain/shardchain blocks \n " ;
2020-04-27 12:01:46 +00:00
if ( mode & 2 ) {
2024-10-15 14:59:12 +00:00
auto st = write_val_create_proof ( * info1 , * info2 , i , true , file_pfx , + + proofs_cnt ) ;
2020-04-27 12:01:46 +00:00
if ( st . is_error ( ) ) {
LOG ( ERROR ) < < " cannot create proof: " < < st . move_as_error ( ) ;
} else {
2024-10-15 14:59:12 +00:00
proofs_cnt_ok + + ;
2020-04-27 12:01:46 +00:00
}
}
2024-10-15 14:59:12 +00:00
} else if ( ( is_masterchain_validator ? prob_mc : prob_bc ) < .005 ) {
2020-04-27 12:01:46 +00:00
LOG ( ERROR ) < < " validator # " < < i < < " with pubkey " < < pk . to_hex ( )
< < " : moderate misbehavior detected: created less than 90% of the expected amount of blocks with "
2024-08-27 15:17:43 +00:00
" probability 99.5% : created ( "
2024-10-15 14:59:12 +00:00
< < created_mc < < " , " < < created_bc < < " ), expected ( " < < expected_created_mc < < " , "
< < expected_created_bc < < " ) masterchain/shardchain blocks \n " ;
2020-04-27 12:01:46 +00:00
if ( ( mode & 3 ) = = 2 ) {
2024-10-15 14:59:12 +00:00
auto st = write_val_create_proof ( * info1 , * info2 , i , false , file_pfx , + + proofs_cnt ) ;
2020-04-27 12:01:46 +00:00
if ( st . is_error ( ) ) {
LOG ( ERROR ) < < " cannot create proof: " < < st . move_as_error ( ) ;
} else {
2024-10-15 14:59:12 +00:00
proofs_cnt_ok + + ;
2020-04-27 12:01:46 +00:00
}
}
}
}
2024-10-15 14:59:12 +00:00
if ( proofs_cnt > 0 ) {
LOG ( INFO ) < < proofs_cnt_ok < < " out of " < < proofs_cnt < < " proofs written to " < < file_pfx < < " -*.boc " ;
}
}
void TestNode : : load_validator_shard_shares ( ton : : BlockSeqno start_seqno , ton : : BlockSeqno end_seqno ,
block : : ValidatorSet validator_set ,
std : : unique_ptr < block : : CatchainValidatorsConfig > catchain_config ,
td : : Promise < std : : map < td : : Bits256 , td : : uint64 > > promise ) {
CHECK ( start_seqno < = end_seqno ) ;
LOG ( INFO ) < < " loading shard shares from mc blocks " < < start_seqno < < " .. " < < end_seqno < < " ( "
< < end_seqno - start_seqno + 1 < < " blocks) " ;
auto state = std : : make_shared < LoadValidatorShardSharesState > ( ) ;
state - > start_seqno = start_seqno ;
state - > end_seqno = end_seqno ;
state - > validator_set = std : : move ( validator_set ) ;
state - > catchain_config = std : : move ( catchain_config ) ;
state - > shard_configs . resize ( end_seqno - start_seqno + 1 ) ;
state - > promise = std : : move ( promise ) ;
load_validator_shard_shares_cont ( std : : move ( state ) ) ;
}
void TestNode : : load_validator_shard_shares_cont ( std : : shared_ptr < LoadValidatorShardSharesState > state ) {
if ( ! state - > promise ) {
return ;
}
if ( state - > loaded % 100 = = 0 ) {
LOG ( INFO ) < < " loaded " < < state - > loaded < < " / " < < state - > shard_configs . size ( ) < < " mc blocks " ;
}
while ( state - > cur_idx < state - > shard_configs . size ( ) & & state - > pending < 8 ) {
load_block_shard_configuration ( state - > start_seqno + state - > cur_idx ,
[ this , state , idx = state - > cur_idx ] ( td : : Result < block : : ShardConfig > R ) mutable {
if ( R . is_error ( ) ) {
state - > promise . set_error ( R . move_as_error ( ) ) ;
state - > promise = { } ;
} else {
state - > shard_configs [ idx ] = R . move_as_ok ( ) ;
- - state - > pending ;
+ + state - > loaded ;
load_validator_shard_shares_cont ( std : : move ( state ) ) ;
}
} ) ;
+ + state - > pending ;
+ + state - > cur_idx ;
}
if ( state - > loaded ! = state - > shard_configs . size ( ) ) {
return ;
2020-04-27 12:01:46 +00:00
}
2024-10-15 14:59:12 +00:00
LOG ( INFO ) < < " loaded all " < < state - > shard_configs . size ( ) < < " mc blocks, computing shard shares " ;
std : : map < td : : Bits256 , td : : uint64 > result ;
try {
for ( size_t idx = 0 ; idx + 1 < state - > shard_configs . size ( ) ; + + idx ) {
block : : ShardConfig & shards1 = state - > shard_configs [ idx ] ;
block : : ShardConfig & shards2 = state - > shard_configs [ idx + 1 ] ;
// Compute validator groups, see ValidatorManagerImpl::update_shards
auto process_shard = [ & ] ( ton : : ShardIdFull shard , ton : : BlockSeqno first_seqno ) {
auto desc2 = shards2 . get_shard_hash ( shard ) ;
if ( desc2 . is_null ( ) | | desc2 - > seqno ( ) < first_seqno ) {
return ;
}
td : : uint32 blocks_count = desc2 - > seqno ( ) - first_seqno + 1 ;
ton : : CatchainSeqno cc_seqno = shards1 . get_shard_cc_seqno ( shard ) ;
auto val_set =
block : : ConfigInfo : : do_compute_validator_set ( * state - > catchain_config , shard , state - > validator_set , cc_seqno ) ;
for ( const auto & val : val_set ) {
result [ val . key . as_bits256 ( ) ] + = blocks_count ;
}
} ;
for ( const ton : : BlockId & id : shards1 . get_shard_hash_ids ( ) ) {
ton : : ShardIdFull shard = id . shard_full ( ) ;
auto desc = shards1 . get_shard_hash ( shard ) ;
CHECK ( desc . not_null ( ) ) ;
if ( desc - > before_split ( ) ) {
ton : : ShardIdFull l_shard = shard_child ( shard , true ) ;
ton : : ShardIdFull r_shard = shard_child ( shard , false ) ;
process_shard ( l_shard , desc - > seqno ( ) + 1 ) ;
process_shard ( r_shard , desc - > seqno ( ) + 1 ) ;
} else if ( desc - > before_merge ( ) ) {
if ( is_right_child ( shard ) ) {
continue ;
}
ton : : ShardIdFull sibling_shard = shard_sibling ( shard ) ;
auto sibling_desc = shards1 . get_shard_hash ( sibling_shard ) ;
CHECK ( sibling_desc . not_null ( ) ) ;
ton : : ShardIdFull p_shard = shard_parent ( shard ) ;
process_shard ( p_shard , std : : max ( desc - > seqno ( ) , sibling_desc - > seqno ( ) ) + 1 ) ;
} else {
process_shard ( shard , desc - > seqno ( ) + 1 ) ;
}
}
}
} catch ( vm : : VmError & e ) {
state - > promise . set_error ( e . as_status ( " cannot parse shard hashes: " ) ) ;
return ;
}
state - > promise . set_value ( std : : move ( result ) ) ;
}
void TestNode : : load_block_shard_configuration ( ton : : BlockSeqno seqno , td : : Promise < block : : ShardConfig > promise ) {
lookup_block (
ton : : ShardIdFull { ton : : masterchainId } , 1 , seqno ,
[ this , promise = std : : move ( promise ) ] ( td : : Result < BlockHdrInfo > R ) mutable {
TRY_RESULT_PROMISE ( promise , res , std : : move ( R ) ) ;
auto b = ton : : serialize_tl_object (
ton : : create_tl_object < ton : : lite_api : : liteServer_getAllShardsInfo > ( ton : : create_tl_lite_block_id ( res . blk_id ) ) ,
true ) ;
envelope_send_query ( std : : move ( b ) , [ this , promise = std : : move ( promise ) ] ( td : : Result < td : : BufferSlice > R ) mutable {
TRY_RESULT_PROMISE ( promise , data , std : : move ( R ) ) ;
TRY_RESULT_PROMISE ( promise , f , ton : : fetch_tl_object < ton : : lite_api : : liteServer_allShardsInfo > ( data , true ) ) ;
TRY_RESULT_PROMISE ( promise , root , vm : : std_boc_deserialize ( f - > data_ ) ) ;
block : : ShardConfig sh_conf ;
if ( ! sh_conf . unpack ( load_cell_slice_ref ( root ) ) ) {
promise . set_error ( td : : Status : : Error ( " cannot extract shard block list from shard configuration " ) ) ;
} else {
promise . set_value ( std : : move ( sh_conf ) ) ;
}
} ) ;
} ) ;
2020-04-27 12:01:46 +00:00
}
2021-06-16 18:38:27 +00:00
bool compute_punishment_default ( int interval , bool severe , td : : RefInt256 & fine , unsigned & fine_part ) {
2020-04-27 12:01:46 +00:00
if ( interval < = 1000 ) {
return false ; // no punishments for less than 1000 seconds
}
2021-02-17 13:19:56 +00:00
fine = td : : make_refint ( 101 * 1000000000LL ) ; // 101
fine_part = 0 ;
return true ; // todo: (tolya-yanot) temporary reduction of fine
2020-04-27 12:01:46 +00:00
if ( severe ) {
fine = td : : make_refint ( 2500 * 1000000000LL ) ; // GR$2500
fine_part = ( 1 < < 30 ) ; // 1/4 of stake
} else {
fine = td : : make_refint ( 1000 * 1000000000LL ) ; // GR$1000
fine_part = ( 1 < < 28 ) ; // 1/16 of stake
}
if ( interval > = 80000 ) {
return true ;
}
if ( interval > = 20000 ) {
fine > > = 2 ;
fine_part > > = 2 ;
return true ;
}
fine > > = 4 ;
fine_part > > = 4 ;
return true ;
}
2021-06-16 18:38:27 +00:00
bool compute_punishment ( int interval , bool severe , td : : RefInt256 & fine , unsigned & fine_part , Ref < vm : : Cell > punishment_params ) {
if ( punishment_params . is_null ( ) ) {
return compute_punishment_default ( interval , severe , fine , fine_part ) ;
}
block : : gen : : MisbehaviourPunishmentConfig : : Record rec ;
if ( ! tlb : : unpack_cell ( punishment_params , rec ) ) {
return false ;
}
if ( interval < = rec . unpunishable_interval ) {
return false ;
}
fine = block : : tlb : : t_Grams . as_integer ( rec . default_flat_fine ) ;
fine_part = rec . default_proportional_fine ;
if ( severe ) {
2021-06-17 14:30:01 +00:00
fine = fine * rec . severity_flat_mult ; fine > > = 8 ;
fine_part = fine_part * rec . severity_proportional_mult ; fine_part > > = 8 ;
2021-06-16 18:38:27 +00:00
}
if ( interval > = rec . long_interval ) {
2021-06-17 14:30:01 +00:00
fine = fine * rec . long_flat_mult ; fine > > = 8 ;
fine_part = fine_part * rec . long_proportional_mult ; fine_part > > = 8 ;
2021-06-16 18:38:27 +00:00
return true ;
}
if ( interval > = rec . medium_interval ) {
2021-06-17 14:30:01 +00:00
fine = fine * rec . medium_flat_mult ; fine > > = 8 ;
fine_part = fine_part * rec . medium_proportional_mult ; fine_part > > = 8 ;
2021-06-16 18:38:27 +00:00
return true ;
}
return true ;
}
bool check_punishment ( int interval , bool severe , td : : RefInt256 fine , unsigned fine_part , Ref < vm : : Cell > punishment_params ) {
2020-04-27 12:01:46 +00:00
td : : RefInt256 computed_fine ;
unsigned computed_fine_part ;
2021-06-16 18:38:27 +00:00
return compute_punishment ( interval , severe , computed_fine , computed_fine_part , punishment_params ) & &
2020-04-27 12:01:46 +00:00
std : : llabs ( ( long long ) fine_part - ( long long ) computed_fine_part ) < =
( std : : max ( fine_part , computed_fine_part ) > > 3 ) & &
fine * 7 < = computed_fine * 8 & & computed_fine * 7 < = fine * 8 ;
}
td : : Status TestNode : : write_val_create_proof ( TestNode : : ValidatorLoadInfo & info1 , TestNode : : ValidatorLoadInfo & info2 ,
int idx , bool severe , std : : string file_pfx , int cnt ) {
std : : string filename = PSTRING ( ) < < file_pfx < < ' - ' < < cnt < < " .boc " ;
if ( ! info1 . has_data ( ) ) {
return td : : Status : : Error ( " first block information is incomplete " ) ;
}
if ( ! info2 . has_data ( ) ) {
return td : : Status : : Error ( " second block information is incomplete " ) ;
}
LOG ( INFO ) < < " creating proof file " < < filename ;
TRY_STATUS ( info1 . check_header_proof ( & info1 . block_created_at , & info1 . end_lt ) ) ;
TRY_STATUS ( info2 . check_header_proof ( & info2 . block_created_at , & info2 . end_lt ) ) ;
td : : Bits256 val_pk1 , val_pk2 ;
TRY_RESULT ( prod1 , info1 . build_producer_info ( idx , & val_pk1 ) ) ;
TRY_RESULT ( prod2 , info2 . build_producer_info ( idx , & val_pk2 ) ) ;
if ( val_pk1 ! = val_pk2 ) {
return td : : Status : : Error ( " validator public key mismatch " ) ;
}
int interval = ( int ) ( info2 . block_created_at - info1 . block_created_at ) ;
if ( interval < = 0 ) {
return td : : Status : : Error ( " non-positive time interval " ) ;
}
2021-06-16 18:38:27 +00:00
auto punishment_params = info2 . config - > get_config_param ( 40 ) ;
2020-04-27 12:01:46 +00:00
int severity = ( severe ? 2 : 1 ) ;
2021-02-17 13:19:56 +00:00
td : : RefInt256 fine = td : : make_refint ( 101000000000 ) ;
unsigned fine_part = 0 ; // todo: (tolya-yanot) temporary reduction of fine // 0xffffffff / 16; // 1/16
2021-06-16 18:38:27 +00:00
if ( ! compute_punishment ( interval , severe , fine , fine_part , punishment_params ) ) {
2020-04-27 12:01:46 +00:00
return td : : Status : : Error ( " cannot compute adequate punishment " ) ;
}
Ref < vm : : Cell > cpl_descr , complaint ;
vm : : CellBuilder cb ;
// no_blk_gen_diff prod_info_old:^ProducerInfo prod_info_new:^ProducerInfo = ComplaintDescr
if ( ! ( block : : gen : : t_ComplaintDescr . cell_pack_no_blk_gen_diff ( cpl_descr , prod1 , prod2 ) & &
cb . store_long_bool ( 0xbc , 8 ) // validator_complaint#bc
& & cb . store_bits_bool ( val_pk1 ) // validator_pubkey:uint256
& & cb . store_ref_bool ( cpl_descr ) // description:^ComplaintDescr
& & cb . store_long_bool ( now ( ) , 32 ) // created_at:uint32
& & cb . store_long_bool ( severity , 8 ) // severity:uint8
& & cb . store_zeroes_bool ( 256 ) // reward_addr:uint256
& & cb . store_zeroes_bool ( 4 ) // paid:Grams
& & block : : tlb : : t_Grams . store_integer_ref ( cb , std : : move ( fine ) ) // suggested_fine:Grams
& & cb . store_long_bool ( fine_part , 32 ) // suggested_fine_part:uint32
& & cb . finalize_to ( complaint ) ) ) {
return td : : Status : : Error ( " cannot serialize ValidatorComplaint " ) ;
}
if ( verbosity > = 5 ) {
std : : ostringstream os ;
os < < " complaint: " ;
block : : gen : : t_ValidatorComplaint . print_ref ( print_limit_ , os , complaint ) ;
td : : TerminalIO : : out ( ) < < os . str ( ) < < std : : endl ;
}
if ( ! block : : gen : : t_ComplaintDescr . validate_ref ( cpl_descr ) ) {
return td : : Status : : Error ( " created an invalid ComplaintDescr " ) ;
}
if ( ! block : : gen : : t_ValidatorComplaint . validate_ref ( complaint ) ) {
return td : : Status : : Error ( " created an invalid ValidatorComplaint " ) ;
}
TRY_RESULT_PREFIX ( boc , vm : : std_boc_serialize ( complaint , 2 ) , " cannot create boc: " ) ;
auto size = boc . size ( ) ;
TRY_STATUS_PREFIX ( td : : write_file ( filename , std : : move ( boc ) ) , PSLICE ( ) < < " cannot save file ` " < < filename < < " ` : " ) ;
td : : TerminalIO : : out ( ) < < " saved validator misbehavior proof into file ` " < < filename < < " ` ( " < < size
< < " bytes written) " < < std : : endl ;
td : : TerminalIO : : out ( ) < < " COMPLAINT_SAVED \t " < < info1 . vset_hash . to_hex ( ) < < " \t " < < complaint - > get_hash ( ) . to_hex ( )
< < " \t " < < filename < < std : : endl ;
return td : : Status : : OK ( ) ;
}
td : : Status TestNode : : ValidatorLoadInfo : : check_header_proof ( ton : : UnixTime * save_utime , ton : : LogicalTime * save_lt ) const {
auto state_virt_root = vm : : MerkleProof : : virtualize ( std : : move ( data_proof ) , 1 ) ;
if ( state_virt_root . is_null ( ) ) {
return td : : Status : : Error ( " account state proof is invalid " ) ;
}
td : : Bits256 state_hash = state_virt_root - > get_hash ( ) . bits ( ) ;
TRY_STATUS ( block : : check_block_header_proof ( vm : : MerkleProof : : virtualize ( state_proof , 1 ) , blk_id , & state_hash , true ,
save_utime , save_lt ) ) ;
return td : : Status : : OK ( ) ;
}
static bool visit ( Ref < vm : : Cell > cell ) ;
static bool visit ( const vm : : CellSlice & cs ) {
auto cnt = cs . size_refs ( ) ;
bool res = true ;
for ( unsigned i = 0 ; i < cnt ; i + + ) {
res & = visit ( cs . prefetch_ref ( i ) ) ;
}
return res ;
}
static bool visit ( Ref < vm : : Cell > cell ) {
if ( cell . is_null ( ) ) {
return true ;
}
vm : : CellSlice cs { vm : : NoVm { } , std : : move ( cell ) } ;
return visit ( cs ) ;
}
static bool visit ( Ref < vm : : CellSlice > cs_ref ) {
return cs_ref . is_null ( ) | | visit ( * cs_ref ) ;
}
td : : Result < Ref < vm : : Cell > > TestNode : : ValidatorLoadInfo : : build_proof ( int idx , td : : Bits256 * save_pubkey ) const {
try {
auto state_virt_root = vm : : MerkleProof : : virtualize ( std : : move ( data_proof ) , 1 ) ;
if ( state_virt_root . is_null ( ) ) {
return td : : Status : : Error ( " account state proof is invalid " ) ;
}
vm : : MerkleProofBuilder pb { std : : move ( state_virt_root ) } ;
TRY_RESULT ( cfg , block : : Config : : extract_from_state ( pb . root ( ) ) ) ;
visit ( cfg - > get_config_param ( 28 ) ) ;
block : : gen : : ValidatorSet : : Record_validators_ext rec ;
if ( ! tlb : : unpack_cell ( cfg - > get_config_param ( 34 ) , rec ) ) {
return td : : Status : : Error ( " cannot unpack ValidatorSet " ) ;
}
vm : : Dictionary vdict { rec . list , 16 } ;
auto entry = vdict . lookup ( td : : BitArray < 16 > ( idx ) ) ;
if ( entry . is_null ( ) ) {
return td : : Status : : Error ( " validator entry not found " ) ;
}
Ref < vm : : CellSlice > pk ;
block : : gen : : ValidatorDescr : : Record_validator rec1 ;
block : : gen : : ValidatorDescr : : Record_validator_addr rec2 ;
if ( tlb : : csr_unpack ( entry , rec1 ) ) {
pk = std : : move ( rec1 . public_key ) ;
} else if ( tlb : : csr_unpack ( std : : move ( entry ) , rec2 ) ) {
pk = std : : move ( rec2 . public_key ) ;
} else {
return td : : Status : : Error ( " cannot unpack ValidatorDescr " ) ;
}
block : : gen : : SigPubKey : : Record rec3 ;
if ( ! tlb : : csr_unpack ( std : : move ( pk ) , rec3 ) ) {
return td : : Status : : Error ( " cannot unpack ed25519_pubkey " ) ;
}
if ( save_pubkey ) {
* save_pubkey = rec3 . pubkey ;
}
visit ( std : : move ( entry ) ) ;
auto dict = block : : get_block_create_stats_dict ( pb . root ( ) ) ;
if ( ! dict ) {
return td : : Status : : Error ( " cannot extract BlockCreateStats from mc state " ) ;
}
visit ( dict - > lookup ( rec3 . pubkey ) ) ;
visit ( dict - > lookup ( td : : Bits256 : : zero ( ) ) ) ;
return pb . extract_proof ( ) ;
} catch ( vm : : VmError & err ) {
return err . as_status ( " cannot build proof: " ) ;
} catch ( vm : : VmVirtError & err ) {
return err . as_status ( " cannot build proof: " ) ;
}
}
td : : Result < Ref < vm : : Cell > > TestNode : : ValidatorLoadInfo : : build_producer_info ( int idx , td : : Bits256 * save_pubkey ) const {
TRY_RESULT ( proof , build_proof ( idx , save_pubkey ) ) ;
vm : : CellBuilder cb ;
Ref < vm : : Cell > res ;
if ( ! ( cb . store_long_bool ( 0x34 , 8 ) // prod_info#34
& & cb . store_long_bool ( block_created_at , 32 ) // utime:uint32
& & block : : tlb : : t_ExtBlkRef . store ( cb , blk_id , end_lt ) // mc_blk_ref:ExtBlkRef
& & cb . store_ref_bool ( state_proof ) // state_proof:^Cell
& & cb . store_ref_bool ( proof ) // prod_proof:^Cell = ProducerInfo
& & cb . finalize_to ( res ) ) ) {
return td : : Status : : Error ( " cannot construct ProducerInfo " ) ;
}
if ( ! block : : gen : : t_ProducerInfo . validate_ref ( res ) ) {
return td : : Status : : Error ( " constructed ProducerInfo failed to pass automated validity checks " ) ;
}
return std : : move ( res ) ;
}
td : : Status TestNode : : check_validator_load_proof ( std : : string filename , std : : string vset_filename ,
ton : : Bits256 vset_hash ) {
TRY_RESULT_PREFIX ( data , td : : read_file ( filename ) , " cannot read proof file: " ) ;
TRY_RESULT_PREFIX ( root , vm : : std_boc_deserialize ( std : : move ( data ) ) ,
PSTRING ( ) < < " cannot deserialize boc from file ` " < < filename < < " `: " ) ;
Ref < vm : : Cell > vset_root ;
if ( ! vset_filename . empty ( ) ) {
TRY_RESULT_PREFIX ( vdata , td : : read_file ( vset_filename ) , " cannot read validator set file: " ) ;
TRY_RESULT_PREFIX_ASSIGN ( vset_root , vm : : std_boc_deserialize ( std : : move ( vdata ) ) ,
PSLICE ( ) < < " cannot deserialize validator set boc from file ` " < < vset_filename < < " `: " ) ;
if ( vset_hash . is_zero ( ) ) {
vset_hash = vset_root - > get_hash ( ) . bits ( ) ;
} else if ( vset_hash ! = vset_root - > get_hash ( ) . bits ( ) ) {
return td : : Status : : Error ( " validator set hash mismatch: indicated " s + vset_hash . to_hex ( ) + " , loaded from file " +
vset_root - > get_hash ( ) . to_hex ( ) ) ;
}
}
if ( verbosity > = 5 ) {
std : : ostringstream os ;
os < < " complaint: " ;
block : : gen : : t_ValidatorComplaint . print_ref ( print_limit_ , os , root ) ;
td : : TerminalIO : : out ( ) < < os . str ( ) < < std : : endl ;
}
if ( ! block : : gen : : t_ValidatorComplaint . validate_ref ( root ) ) {
return td : : Status : : Error ( " proof file does not contain a valid ValidatorComplaint " ) ;
}
block : : gen : : ValidatorComplaint : : Record rec ;
if ( ! tlb : : unpack_cell ( root , rec ) ) {
return td : : Status : : Error ( " cannot unpack ValidatorComplaint " ) ;
}
auto cs = vm : : load_cell_slice ( rec . description ) ;
int tag = block : : gen : : t_ComplaintDescr . get_tag ( cs ) ;
if ( tag < 0 ) {
return td : : Status : : Error ( " ComplaintDescr has an unknown tag " ) ;
}
if ( tag ! = block : : gen : : ComplaintDescr : : no_blk_gen_diff ) {
return td : : Status : : Error ( " can check only ComplaintDescr of type no_blk_gen_diff " ) ;
}
block : : gen : : ComplaintDescr : : Record_no_blk_gen_diff crec ;
if ( ! tlb : : unpack_exact ( cs , crec ) ) {
return td : : Status : : Error ( " cannot unpack ComplaintDescr " ) ;
}
TRY_RESULT_PREFIX ( info1 , ValidatorLoadInfo : : preinit_from_producer_info ( crec . prod_info_old ) ,
" cannot unpack ProducerInfo in prod_info_old: " )
TRY_RESULT_PREFIX ( info2 , ValidatorLoadInfo : : preinit_from_producer_info ( crec . prod_info_new ) ,
" cannot unpack ProducerInfo in prod_info_new: " )
if ( info1 - > vset_hash ! = info2 - > vset_hash ) {
return td : : Status : : Error ( " validator hash changed between the two blocks: " s + info1 - > vset_hash . to_hex ( ) + " and " +
info2 - > vset_hash . to_hex ( ) ) ;
}
if ( vset_hash . is_zero ( ) ) {
vset_hash = info1 - > vset_hash ;
} else if ( vset_hash ! = info1 - > vset_hash ) {
return td : : Status : : Error ( " validator set hash mismatch: blocks have " s + info1 - > vset_hash . to_hex ( ) +
" , actual value is " + vset_hash . to_hex ( ) ) ;
}
auto blkid2 = info2 - > blk_id ;
if ( vset_root . not_null ( ) ) {
info1 - > vset_root = info2 - > vset_root = std : : move ( vset_root ) ;
set_error ( continue_check_validator_load_proof ( std : : move ( info1 ) , std : : move ( info2 ) , std : : move ( root ) ) ) ;
} else if ( ! get_config_params ( blkid2 ,
[ this , info1 = std : : move ( info1 ) , info2 = std : : move ( info2 ) ,
root = std : : move ( root ) ] ( td : : Result < std : : unique_ptr < block : : Config > > res ) mutable {
if ( res . is_error ( ) ) {
LOG ( ERROR )
< < " cannot fetch configuration parameters from key block corresponding to "
< < info2 - > blk_id . to_str ( ) < < " : " < < res . move_as_error ( ) ;
} else {
auto vset_root = res . move_as_ok ( ) - > get_config_param ( 34 ) ;
if ( vset_root . is_null ( ) ) {
LOG ( ERROR ) < < " no configuration parameter #34 in key block corresponding to "
< < info2 - > blk_id . to_str ( ) ;
} else if ( info2 - > vset_hash ! = vset_root - > get_hash ( ) . bits ( ) ) {
LOG ( ERROR ) < < " validator hash set mismatch for block " < < info2 - > blk_id . to_str ( ) ;
} else {
info1 - > vset_root = info2 - > vset_root = std : : move ( vset_root ) ;
set_error ( continue_check_validator_load_proof ( std : : move ( info1 ) , std : : move ( info2 ) ,
std : : move ( root ) ) ) ;
}
}
} ,
0xd000 , " " , { 28 , 34 } ) ) {
return td : : Status : : Error ( " cannot request configuration parameters from key block corresponding to " s +
blkid2 . to_str ( ) ) ;
}
return td : : Status : : OK ( ) ;
}
static void show_vote ( td : : Bits256 complaint_hash , bool outcome ) {
td : : TerminalIO : : out ( ) < < " COMPLAINT_VOTE_FOR \t " < < complaint_hash . to_hex ( ) < < " \t " < < ( outcome ? " YES " : " NO " )
< < std : : endl ;
}
td : : Status TestNode : : continue_check_validator_load_proof ( std : : unique_ptr < ValidatorLoadInfo > info1 ,
std : : unique_ptr < ValidatorLoadInfo > info2 , Ref < vm : : Cell > root ) {
TRY_STATUS ( info1 - > unpack_vset ( ) ) ;
TRY_STATUS ( info2 - > unpack_vset ( ) ) ;
int interval = ( int ) ( info2 - > block_created_at - info1 - > block_created_at ) ;
if ( interval < = 0 ) {
return td : : Status : : Error ( " non-positive time interval " ) ;
}
block : : gen : : ValidatorComplaint : : Record rec ;
block : : gen : : ComplaintDescr : : Record_no_blk_gen_diff crec ;
if ( ! ( tlb : : unpack_cell ( root , rec ) & & tlb : : unpack_cell ( rec . description , crec ) ) ) {
return td : : Status : : Error ( " cannot unpack ValidatorComplaint second time (?) " ) ;
}
td : : Bits256 val_pubkey = rec . validator_pubkey ;
int val_idx = info1 - > vset - > lookup_public_key ( val_pubkey ) ;
if ( val_idx < 0 ) {
return td : : Status : : Error ( " validator with public key " s + val_pubkey . to_hex ( ) +
" is not present in active validator set " ) ;
}
TRY_STATUS ( info1 - > load_special_creator_stat ( val_pubkey , true ) ) ;
TRY_STATUS ( info2 - > load_special_creator_stat ( val_pubkey , true ) ) ;
td : : TerminalIO : : out ( ) < < " total: ( " < < info1 - > created_total . first < < " , " < < info1 - > created_total . second < < " ) -> ( "
< < info2 - > created_total . first < < " , " < < info2 - > created_total . second < < " ) \n " ;
auto x = info2 - > created_total . first - info1 - > created_total . first ;
auto y = info2 - > created_total . second - info1 - > created_total . second ;
if ( x < = 0 | | y < 0 | | ( x | y ) > = ( 1u < < 31 ) ) {
return td : : Status : : Error ( PSLICE ( ) < < " impossible situation: zero or no blocks created: " < < x
< < " masterchain blocks, " < < y < < " shardchain blocks " ) ;
}
auto x1 = info2 - > created_special . first - info1 - > created_special . first ;
auto y1 = info2 - > created_special . second - info1 - > created_special . second ;
if ( ( x1 | y1 ) < 0 | | ( x1 | y1 ) > = ( 1u < < 31 ) ) {
return td : : Status : : Error ( PSLICE ( ) < < " impossible situation: validator " < < val_pubkey . to_hex ( ) < < " created " < < x1
< < " masterchain blocks, " < < y1 < < " shardchain blocks " ) ;
}
td : : TerminalIO : : out ( ) < < " total: ( " < < x < < " , " < < y < < " ) \n " ;
try {
auto ccfg = block : : Config : : unpack_catchain_validators_config ( info2 - > config - > get_config_param ( 28 ) ) ;
auto ccfg_old = block : : Config : : unpack_catchain_validators_config ( info1 - > config - > get_config_param ( 28 ) ) ;
if ( ccfg . shard_val_num ! = ccfg_old . shard_val_num | | ccfg . shard_val_num < = 0 ) {
return td : : Status : : Error ( PSLICE ( ) < < " shard validator group size changed from " < < ccfg_old . shard_val_num
< < " to " < < ccfg . shard_val_num < < " , or is not positive " ) ;
}
int shard_count = ccfg . shard_val_num , main_count = info2 - > vset - > main ;
if ( info1 - > vset - > main ! = main_count | | main_count < = 0 ) {
return td : : Status : : Error ( PSLICE ( ) < < " masterchain validator group size changed from " < < info1 - > vset - > main
< < " to " < < main_count < < " , or is not positive " ) ;
}
double chunk_size = ccfg . shard_val_lifetime / 3. / shard_count ;
block : : MtCarloComputeShare shard_share ( shard_count , info2 - > vset - > export_scaled_validator_weights ( ) ) ;
double xe = ( val_idx < main_count ? ( double ) x / main_count : 0 ) ;
double ye = shard_share [ val_idx ] * ( double ) y / shard_count ;
td : : Bits256 pk = info2 - > vset - > list [ val_idx ] . pubkey . as_bits256 ( ) ;
CHECK ( pk = = val_pubkey ) ;
double p1 = create_prob ( ( int ) x1 , .9 * xe ) , p2 = shard_create_prob ( ( int ) y1 , .9 * ye , chunk_size ) ;
td : : TerminalIO : : out ( ) < < " val # " < < val_idx < < " : pubkey " < < pk . to_hex ( ) < < " , blocks created ( " < < x1 < < " , " < < y1
< < " ), expected ( " < < xe < < " , " < < ye < < " ), probabilities " < < p1 < < " and " < < p2 < < " \n " ;
bool severe = ( rec . severity > = 2 ) ;
if ( severe & & std : : min ( p1 , p2 ) < .00001 ) {
LOG ( ERROR ) < < " validator # " < < val_idx < < " with pubkey " < < pk . to_hex ( )
< < " : serious misbehavior detected: created less than 90% of the expected amount of blocks with "
" probability 99.999% : created ( "
< < x1 < < " , " < < y1 < < " ), expected ( " < < xe < < " , " < < ye < < " ) masterchain/shardchain blocks \n " ;
} else if ( ! severe & & std : : min ( p1 , p2 ) < .001 ) {
LOG ( ERROR ) < < " validator # " < < val_idx < < " with pubkey " < < pk . to_hex ( )
< < " : moderate misbehavior detected: created less than 90% of the expected amount of blocks with "
" probability 99.9% : created ( "
< < x1 < < " , " < < y1 < < " ), expected ( " < < xe < < " , " < < ye < < " ) masterchain/shardchain blocks \n " ;
} else {
LOG ( ERROR ) < < " invalid (unsupported) complaint for validator # " < < val_idx < < " with pubkey " < < pk . to_hex ( ) ;
show_vote ( root - > get_hash ( ) . bits ( ) , false ) ;
return td : : Status : : OK ( ) ;
}
auto suggested_fine = block : : tlb : : t_Grams . as_integer ( rec . suggested_fine ) ;
if ( suggested_fine . is_null ( ) ) {
return td : : Status : : Error ( " cannot parse suggested fine " ) ;
}
2021-06-16 18:38:27 +00:00
if ( ! check_punishment ( interval , severe , suggested_fine , rec . suggested_fine_part , info2 - > config - > get_config_param ( 40 ) ) ) {
2020-04-27 12:01:46 +00:00
LOG ( ERROR ) < < " proposed punishment (fine " < < td : : dec_string ( suggested_fine )
< < " , fine_part= " < < ( double ) rec . suggested_fine_part / ( 1LL < < 32 ) < < " is too harsh " ;
show_vote ( root - > get_hash ( ) . bits ( ) , false ) ;
return td : : Status : : OK ( ) ;
}
LOG ( INFO ) < < " accepting suggested punishment (affirmative vote) " ;
show_vote ( root - > get_hash ( ) . bits ( ) , true ) ;
return td : : Status : : OK ( ) ;
} catch ( vm : : VmError & err ) {
return err . as_status ( " vm error while scanning configuration proof: " ) ;
} catch ( vm : : VmVirtError & err ) {
return err . as_status ( " virtualization error while scanning configuration proof: " ) ;
}
}
td : : Status TestNode : : ValidatorLoadInfo : : load_special_creator_stat ( const td : : Bits256 & spec_pubkey , bool load_total ) {
if ( ! vset ) {
return td : : Status : : Error ( " no validator set loaded " ) ;
}
int idx = vset - > lookup_public_key ( spec_pubkey ) ;
if ( idx < 0 ) {
return td : : Status : : Error ( " validator with public key " s + spec_pubkey . to_hex ( ) + " not present in validator set " ) ;
}
if ( virt_root . is_null ( ) ) {
return td : : Status : : Error ( " no virtualized block state " ) ;
}
try {
auto dict = block : : get_block_create_stats_dict ( virt_root ) ;
if ( ! dict ) {
return td : : Status : : Error ( " cannot extract BlockCreateStats from virtualized mc state " ) ;
}
for ( int i = 0 ; i < = ( int ) load_total ; i + + ) {
td : : Bits256 key = ( i ? td : : Bits256 : : zero ( ) : spec_pubkey ) ;
auto & p = * ( i ? & created_total : & created_special ) ;
auto cell = dict - > lookup ( key ) ;
if ( cell . is_null ( ) ) {
p . first = p . second = 0 ;
if ( i ) {
return td : : Status : : Error ( " no total created block statistics in BlockCreateStats " ) ;
}
} else {
block : : DiscountedCounter mc_cnt , shard_cnt ;
if ( ! block : : unpack_CreatorStats ( std : : move ( cell ) , mc_cnt , shard_cnt ) ) {
return td : : Status : : Error ( PSLICE ( ) < < " invalid CreatorStats record with key " < < key . to_hex ( ) ) ;
}
p . first = mc_cnt . total ;
p . second = shard_cnt . total ;
}
}
special_idx = idx ;
return td : : Status : : OK ( ) ;
} catch ( vm : : VmError & err ) {
return err . as_status ( " vm error while extracting block creator data: " ) ;
} catch ( vm : : VmVirtError & err ) {
return err . as_status ( " virtualization error while extracting block creator data: " ) ;
}
}
td : : Result < std : : unique_ptr < TestNode : : ValidatorLoadInfo > > TestNode : : ValidatorLoadInfo : : preinit_from_producer_info (
Ref < vm : : Cell > prod_info ) {
if ( prod_info . is_null ( ) ) {
return td : : Status : : Error ( " ProducerInfo cell is null " ) ;
}
if ( ! block : : gen : : t_ProducerInfo . validate_ref ( prod_info ) ) {
return td : : Status : : Error ( " invalid ProducerInfo " ) ;
}
block : : gen : : ProducerInfo : : Record rec ;
ton : : BlockIdExt blk_id ;
ton : : LogicalTime end_lt ;
if ( ! ( tlb : : unpack_cell ( prod_info , rec ) & &
block : : tlb : : t_ExtBlkRef . unpack ( std : : move ( rec . mc_blk_ref ) , blk_id , & end_lt ) ) ) {
return td : : Status : : Error ( " cannot unpack ProducerInfo " ) ;
}
auto info = std : : make_unique < ValidatorLoadInfo > ( blk_id , std : : move ( rec . state_proof ) , std : : move ( rec . prod_proof ) ) ;
CHECK ( info ) ;
info - > end_lt = end_lt ;
info - > block_created_at = rec . utime ;
TRY_STATUS_PREFIX ( info - > init_check_proofs ( ) , " error checking block/state proofs: " ) ;
return std : : move ( info ) ;
}
td : : Status TestNode : : ValidatorLoadInfo : : init_check_proofs ( ) {
try {
ton : : UnixTime utime ;
ton : : LogicalTime lt ;
TRY_STATUS ( check_header_proof ( & utime , & lt ) ) ;
if ( utime ! = block_created_at ) {
return td : : Status : : Error ( PSLICE ( ) < < " incorrect block creation time: declared " < < block_created_at < < " , actual "
< < utime ) ;
}
if ( lt ! = end_lt ) {
return td : : Status : : Error ( PSLICE ( ) < < " incorrect block logical time: declared " < < end_lt < < " , actual " < < lt ) ;
}
auto vstate = vm : : MerkleProof : : virtualize ( data_proof , 1 ) ;
if ( vstate . is_null ( ) ) {
return td : : Status : : Error ( PSLICE ( ) < < " cannot virtualize state of block " < < blk_id . to_str ( ) ) ;
}
TRY_RESULT_PREFIX_ASSIGN ( config , block : : Config : : extract_from_state ( vstate , 0 ) , " cannot unpack configuration: " ) ;
auto vset_root = config - > get_config_param ( 34 ) ;
if ( vset_root . is_null ( ) ) {
vset_hash . set_zero ( ) ;
return td : : Status : : Error ( PSLICE ( ) < < " no configuration parameter 34 (validator set) for block "
< < blk_id . to_str ( ) ) ;
}
vset_hash = vset_root - > get_hash ( ) . bits ( ) ;
virt_root = vstate ;
return td : : Status : : OK ( ) ;
} catch ( vm : : VmError & err ) {
return err . as_status ( " vm error: " ) ;
2019-11-28 14:44:14 +00:00
} catch ( vm : : VmVirtError & err ) {
2020-04-27 12:01:46 +00:00
return err . as_status ( " virtualization error: " ) ;
2019-11-28 14:44:14 +00:00
}
}
2019-09-07 10:03:22 +00:00
int main ( int argc , char * argv [ ] ) {
SET_VERBOSITY_LEVEL ( verbosity_INFO ) ;
td : : set_default_failure_signal_handler ( ) ;
td : : actor : : ActorOwn < TestNode > x ;
2020-07-06 14:07:20 +00:00
td : : OptionParser p ;
2019-09-07 10:03:22 +00:00
p . set_description ( " Test Lite Client for TON Blockchain " ) ;
p . add_option ( ' h ' , " help " , " prints_help " , [ & ] ( ) {
char b [ 10240 ] ;
td : : StringBuilder sb ( td : : MutableSlice { b , 10000 } ) ;
sb < < p ;
std : : cout < < sb . as_cslice ( ) . c_str ( ) ;
std : : exit ( 2 ) ;
} ) ;
2020-07-06 14:07:20 +00:00
p . add_option ( ' C ' , " global-config " , " file to read global config " ,
[ & ] ( td : : Slice fname ) { td : : actor : : send_closure ( x , & TestNode : : set_global_config , fname . str ( ) ) ; } ) ;
p . add_option ( ' r ' , " disable-readline " , " " ,
[ & ] ( ) { td : : actor : : send_closure ( x , & TestNode : : set_readline_enabled , false ) ; } ) ;
p . add_option ( ' R ' , " enable-readline " , " " ,
[ & ] ( ) { td : : actor : : send_closure ( x , & TestNode : : set_readline_enabled , true ) ; } ) ;
p . add_option ( ' D ' , " db " , " root for dbs " ,
[ & ] ( td : : Slice fname ) { td : : actor : : send_closure ( x , & TestNode : : set_db_root , fname . str ( ) ) ; } ) ;
p . add_checked_option ( ' L ' , " print-limit " , " sets maximum count of recursively printed objects " , [ & ] ( td : : Slice arg ) {
2020-02-28 10:28:47 +00:00
auto plimit = td : : to_integer < int > ( arg ) ;
td : : actor : : send_closure ( x , & TestNode : : set_print_limit , plimit ) ;
return plimit > = 0 ? td : : Status : : OK ( ) : td : : Status : : Error ( " printing limit must be non-negative " ) ;
} ) ;
2020-07-06 14:07:20 +00:00
p . add_checked_option ( ' v ' , " verbosity " , " set verbosity level " , [ & ] ( td : : Slice arg ) {
2019-09-07 10:03:22 +00:00
verbosity = td : : to_integer < int > ( arg ) ;
SET_VERBOSITY_LEVEL ( VERBOSITY_NAME ( FATAL ) + verbosity ) ;
return ( verbosity > = 0 & & verbosity < = 9 ) ? td : : Status : : OK ( ) : td : : Status : : Error ( " verbosity must be 0..9 " ) ;
} ) ;
2021-03-25 22:26:49 +00:00
p . add_option ( ' V ' , " version " , " shows lite-client build information " , [ & ] ( ) {
std : : cout < < " lite-client build information: [ Commit: " < < GitMetadata : : CommitSHA1 ( ) < < " , Date: " < < GitMetadata : : CommitDate ( ) < < " ] \n " ;
2024-09-13 17:47:30 +00:00
2021-02-27 12:34:41 +00:00
std : : exit ( 0 ) ;
} ) ;
2019-09-07 10:03:22 +00:00
p . add_option ( ' i ' , " idx " , " set liteserver idx " , [ & ] ( td : : Slice arg ) {
auto idx = td : : to_integer < int > ( arg ) ;
td : : actor : : send_closure ( x , & TestNode : : set_liteserver_idx , idx ) ;
} ) ;
2020-07-06 14:07:20 +00:00
p . add_checked_option ( ' a ' , " addr " , " connect to ip:port " , [ & ] ( td : : Slice arg ) {
2019-09-07 10:03:22 +00:00
td : : IPAddress addr ;
TRY_STATUS ( addr . init_host_port ( arg . str ( ) ) ) ;
td : : actor : : send_closure ( x , & TestNode : : set_remote_addr , addr ) ;
return td : : Status : : OK ( ) ;
} ) ;
2020-07-06 14:07:20 +00:00
p . add_option ( ' c ' , " cmd " , " schedule command " ,
[ & ] ( td : : Slice arg ) { td : : actor : : send_closure ( x , & TestNode : : add_cmd , td : : BufferSlice { arg } ) ; } ) ;
2019-09-07 10:03:22 +00:00
p . add_option ( ' t ' , " timeout " , " timeout in batch mode " , [ & ] ( td : : Slice arg ) {
auto d = td : : to_double ( arg ) ;
td : : actor : : send_closure ( x , & TestNode : : set_fail_timeout , td : : Timestamp : : in ( d ) ) ;
} ) ;
2020-07-06 14:07:20 +00:00
p . add_option ( ' p ' , " pub " , " remote public key " ,
[ & ] ( td : : Slice arg ) { td : : actor : : send_closure ( x , & TestNode : : set_public_key , td : : BufferSlice { arg } ) ; } ) ;
2022-01-10 22:07:53 +00:00
p . add_option ( ' b ' , " b64 " , " remote public key as base64 " ,
[ & ] ( td : : Slice arg ) { td : : actor : : send_closure ( x , & TestNode : : decode_public_key , td : : BufferSlice { arg } ) ; } ) ;
2019-09-07 10:03:22 +00:00
p . add_option ( ' d ' , " daemonize " , " set SIGHUP " , [ & ] ( ) {
2020-04-10 19:06:01 +00:00
td : : set_signal_handler ( td : : SignalType : : HangUp , [ ] ( int sig ) {
2019-09-07 10:03:22 +00:00
# if TD_DARWIN || TD_LINUX
2020-04-10 19:06:01 +00:00
close ( 0 ) ;
setsid ( ) ;
2019-09-07 10:03:22 +00:00
# endif
2020-04-10 19:06:01 +00:00
} ) . ensure ( ) ;
2019-09-07 10:03:22 +00:00
} ) ;
# if TD_DARWIN || TD_LINUX
p . add_option ( ' l ' , " logname " , " log to file " , [ & ] ( td : : Slice fname ) {
auto FileLog = td : : FileFd : : open ( td : : CSlice ( fname . str ( ) . c_str ( ) ) ,
td : : FileFd : : Flags : : Create | td : : FileFd : : Flags : : Append | td : : FileFd : : Flags : : Write )
. move_as_ok ( ) ;
dup2 ( FileLog . get_native_fd ( ) . fd ( ) , 1 ) ;
dup2 ( FileLog . get_native_fd ( ) . fd ( ) , 2 ) ;
} ) ;
# endif
2023-11-03 11:43:34 +00:00
vm : : init_vm ( true ) . ensure ( ) ; // enable vm debug
2019-09-07 10:03:22 +00:00
td : : actor : : Scheduler scheduler ( { 2 } ) ;
scheduler . run_in_context ( [ & ] { x = td : : actor : : create_actor < TestNode > ( " testnode " ) ; } ) ;
scheduler . run_in_context ( [ & ] { p . run ( argc , argv ) . ensure ( ) ; } ) ;
scheduler . run_in_context ( [ & ] {
td : : actor : : send_closure ( x , & TestNode : : run ) ;
x . release ( ) ;
} ) ;
scheduler . run ( ) ;
return 0 ;
}