mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
slightly changed block format
- small change in block format - added config in blockchain explorer - bugfixes
This commit is contained in:
parent
7f3a22a217
commit
090e0c16eb
82 changed files with 1852 additions and 391 deletions
|
@ -35,6 +35,7 @@ set(TON_CRYPTO_SOURCE
|
|||
vm/debugops.cpp
|
||||
vm/tonops.cpp
|
||||
vm/boc.cpp
|
||||
vm/utils.cpp
|
||||
tl/tlblib.cpp
|
||||
|
||||
Ed25519.h
|
||||
|
@ -80,6 +81,7 @@ set(TON_CRYPTO_SOURCE
|
|||
vm/tupleops.h
|
||||
vm/tonops.h
|
||||
vm/vmstate.h
|
||||
vm/utils.h
|
||||
|
||||
vm/cells.h
|
||||
vm/cellslice.h
|
||||
|
|
|
@ -789,10 +789,11 @@ td::Status ShardState::unpack_state(ton::BlockIdExt blkid, Ref<vm::Cell> prev_st
|
|||
return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid global_balance");
|
||||
}
|
||||
if (extra.r1.flags & 1) {
|
||||
if (extra.r1.block_create_stats->prefetch_ulong(8) != 0x17) {
|
||||
if (extra.r1.block_create_stats->prefetch_ulong(8) == 0x17) {
|
||||
block_create_stats_ = std::make_unique<vm::Dictionary>(extra.r1.block_create_stats->prefetch_ref(), 256);
|
||||
} else {
|
||||
return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid BlockCreateStats");
|
||||
}
|
||||
block_create_stats_ = std::make_unique<vm::Dictionary>(extra.r1.block_create_stats->prefetch_ref(), 256);
|
||||
} else {
|
||||
block_create_stats_ = std::make_unique<vm::Dictionary>(256);
|
||||
}
|
||||
|
@ -1846,6 +1847,18 @@ td::Status check_block_header(Ref<vm::Cell> block_root, const ton::BlockIdExt& i
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
std::unique_ptr<vm::Dictionary> get_block_create_stats_dict(Ref<vm::Cell> state_root) {
|
||||
block::gen::ShardStateUnsplit::Record info;
|
||||
block::gen::McStateExtra::Record extra;
|
||||
block::gen::BlockCreateStats::Record_block_create_stats cstats;
|
||||
if (!(::tlb::unpack_cell(std::move(state_root), info) && info.custom->size_refs() &&
|
||||
::tlb::unpack_cell(info.custom->prefetch_ref(), extra) && (extra.r1.flags & 1) &&
|
||||
::tlb::csr_unpack(std::move(extra.r1.block_create_stats), cstats))) {
|
||||
return {};
|
||||
}
|
||||
return std::make_unique<vm::Dictionary>(std::move(cstats.counters), 256);
|
||||
}
|
||||
|
||||
std::unique_ptr<vm::AugmentedDictionary> get_prev_blocks_dict(Ref<vm::Cell> state_root) {
|
||||
block::gen::ShardStateUnsplit::Record info;
|
||||
block::gen::McStateExtra::Record extra_info;
|
||||
|
|
|
@ -163,12 +163,12 @@ struct MsgProcessedUpto {
|
|||
MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash)
|
||||
: shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) {
|
||||
}
|
||||
bool operator<(const MsgProcessedUpto& other) const& {
|
||||
bool operator<(const MsgProcessedUpto& other) const & {
|
||||
return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno);
|
||||
}
|
||||
bool contains(const MsgProcessedUpto& other) const&;
|
||||
bool contains(const MsgProcessedUpto& other) const &;
|
||||
bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
|
||||
ton::BlockSeqno other_mc_seqno) const&;
|
||||
ton::BlockSeqno other_mc_seqno) const &;
|
||||
// NB: this is for checking whether we have already imported an internal message
|
||||
bool already_processed(const EnqueuedMsgDescr& msg) const;
|
||||
};
|
||||
|
@ -514,6 +514,9 @@ struct DiscountedCounter {
|
|||
return last_updated == other.last_updated && total == other.total && cnt2048 <= other.cnt2048 + 1 &&
|
||||
other.cnt2048 <= cnt2048 + 1 && cnt65536 <= other.cnt65536 + 1 && other.cnt65536 <= cnt65536 + 1;
|
||||
}
|
||||
bool modified_since(ton::UnixTime utime) const {
|
||||
return last_updated >= utime;
|
||||
}
|
||||
bool validate();
|
||||
bool increase_by(unsigned count, ton::UnixTime now);
|
||||
bool fetch(vm::CellSlice& cs);
|
||||
|
@ -629,6 +632,8 @@ td::Status unpack_block_prev_blk_try(Ref<vm::Cell> block_root, const ton::BlockI
|
|||
td::Status check_block_header(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
|
||||
ton::Bits256* store_shard_hash_to = nullptr);
|
||||
|
||||
std::unique_ptr<vm::Dictionary> get_block_create_stats_dict(Ref<vm::Cell> state_root);
|
||||
|
||||
std::unique_ptr<vm::AugmentedDictionary> get_prev_blocks_dict(Ref<vm::Cell> state_root);
|
||||
bool get_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
|
||||
ton::LogicalTime* end_lt = nullptr);
|
||||
|
|
|
@ -366,6 +366,10 @@ action_send_msg#0ec3c86d mode:(## 8)
|
|||
action_set_code#ad4de08e new_code:^Cell = OutAction;
|
||||
action_reserve_currency#36e6b809 mode:(## 8)
|
||||
currency:CurrencyCollection = OutAction;
|
||||
libref_hash$0 lib_hash:bits256 = LibRef;
|
||||
libref_ref$1 library:^Cell = LibRef;
|
||||
action_change_library#26fa1dd4 mode:(## 7) { mode <= 2 }
|
||||
libref:LibRef = OutAction;
|
||||
|
||||
out_list_node$_ prev:^Cell action:OutAction = OutListNode;
|
||||
//
|
||||
|
@ -505,6 +509,7 @@ _ (HashmapAugE 32 KeyExtBlkRef KeyMaxLt) = OldMcBlocksInfo;
|
|||
counters#_ last_updated:uint32 total:uint64 cnt2048:uint64 cnt65536:uint64 = Counters;
|
||||
creator_info#4 mc_blocks:Counters shard_blocks:Counters = CreatorStats;
|
||||
block_create_stats#17 counters:(HashmapE 256 CreatorStats) = BlockCreateStats;
|
||||
block_create_stats_ext#34 counters:(HashmapAugE 256 CreatorStats uint32) = BlockCreateStats;
|
||||
|
||||
masterchain_state_extra#cc26
|
||||
shard_hashes:ShardHashes
|
||||
|
|
|
@ -1710,6 +1710,30 @@ std::vector<ton::ValidatorDescr> Config::compute_total_validator_set(int next) c
|
|||
return res.move_as_ok()->export_validator_set();
|
||||
}
|
||||
|
||||
td::Result<std::pair<ton::UnixTime, ton::UnixTime>> Config::unpack_validator_set_start_stop(Ref<vm::Cell> vset_root) {
|
||||
if (vset_root.is_null()) {
|
||||
return td::Status::Error("validator set absent");
|
||||
}
|
||||
gen::ValidatorSet::Record_validators_ext rec;
|
||||
if (tlb::unpack_cell(vset_root, rec)) {
|
||||
return std::pair<ton::UnixTime, ton::UnixTime>(rec.utime_since, rec.utime_until);
|
||||
}
|
||||
gen::ValidatorSet::Record_validators rec0;
|
||||
if (tlb::unpack_cell(std::move(vset_root), rec0)) {
|
||||
return std::pair<ton::UnixTime, ton::UnixTime>(rec0.utime_since, rec0.utime_until);
|
||||
}
|
||||
return td::Status::Error("validator set is invalid");
|
||||
}
|
||||
|
||||
std::pair<ton::UnixTime, ton::UnixTime> Config::get_validator_set_start_stop(int next) const {
|
||||
auto res = unpack_validator_set_start_stop(get_config_param(next < 0 ? 32 : (next ? 36 : 34)));
|
||||
if (res.is_error()) {
|
||||
return {0, 0};
|
||||
} else {
|
||||
return res.move_as_ok();
|
||||
}
|
||||
}
|
||||
|
||||
bool WorkchainInfo::unpack(ton::WorkchainId wc, vm::CellSlice& cs) {
|
||||
workchain = ton::workchainInvalid;
|
||||
if (wc == ton::workchainInvalid) {
|
||||
|
|
|
@ -50,7 +50,7 @@ struct ValidatorDescr {
|
|||
: pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) {
|
||||
adnl_addr.set_zero();
|
||||
}
|
||||
bool operator<(td::uint64 wt_pos) const& {
|
||||
bool operator<(td::uint64 wt_pos) const & {
|
||||
return cum_weight < wt_pos;
|
||||
}
|
||||
};
|
||||
|
@ -558,6 +558,7 @@ class Config {
|
|||
const ValidatorSet* get_cur_validator_set() const {
|
||||
return cur_validators_.get();
|
||||
}
|
||||
std::pair<ton::UnixTime, ton::UnixTime> get_validator_set_start_stop(int next = 0) const;
|
||||
ton::ValidatorSessionConfig get_consensus_config() const;
|
||||
bool foreach_config_param(std::function<bool(int, Ref<vm::Cell>)> scan_func) const;
|
||||
Ref<WorkchainInfo> get_workchain_info(ton::WorkchainId workchain_id) const;
|
||||
|
@ -577,6 +578,7 @@ class Config {
|
|||
static td::Result<std::unique_ptr<Config>> unpack_config(Ref<vm::CellSlice> config_csr, int mode = 0);
|
||||
static td::Result<std::unique_ptr<Config>> extract_from_state(Ref<vm::Cell> mc_state_root, int mode = 0);
|
||||
static td::Result<std::unique_ptr<Config>> extract_from_key_block(Ref<vm::Cell> key_block_root, int mode = 0);
|
||||
static td::Result<std::pair<ton::UnixTime, ton::UnixTime>> unpack_validator_set_start_stop(Ref<vm::Cell> root);
|
||||
|
||||
protected:
|
||||
Config(int _mode) : mode(_mode) {
|
||||
|
|
|
@ -1098,6 +1098,9 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
|
|||
case block::gen::OutAction::action_reserve_currency:
|
||||
err_code = try_action_reserve_currency(cs, ap, cfg);
|
||||
break;
|
||||
case block::gen::OutAction::action_change_library:
|
||||
err_code = try_action_change_library(cs, ap, cfg);
|
||||
break;
|
||||
}
|
||||
if (err_code) {
|
||||
ap.result_code = (err_code == -1 ? 34 : err_code);
|
||||
|
@ -1148,6 +1151,56 @@ int Transaction::try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const A
|
|||
return 0;
|
||||
}
|
||||
|
||||
int Transaction::try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg) {
|
||||
block::gen::OutAction::Record_action_change_library rec;
|
||||
if (!tlb::unpack_exact(cs, rec)) {
|
||||
return -1;
|
||||
}
|
||||
// mode: +0 = remove library, +1 = add private library, +2 = add public library
|
||||
Ref<vm::Cell> lib_ref = rec.libref->prefetch_ref();
|
||||
ton::Bits256 hash;
|
||||
if (lib_ref.not_null()) {
|
||||
hash = lib_ref->get_hash().bits();
|
||||
} else {
|
||||
CHECK(rec.libref.write().fetch_ulong(1) == 0 && rec.libref.write().fetch_bits_to(hash));
|
||||
}
|
||||
try {
|
||||
vm::Dictionary dict{new_library, 256};
|
||||
if (!rec.mode) {
|
||||
// remove library
|
||||
dict.lookup_delete(hash);
|
||||
LOG(DEBUG) << "removed " << ((rec.mode >> 1) ? "public" : "private") << " library with hash " << hash.to_hex();
|
||||
} else {
|
||||
auto val = dict.lookup(hash);
|
||||
if (val.not_null()) {
|
||||
bool is_public = val->prefetch_ulong(1);
|
||||
auto ref = val->prefetch_ref();
|
||||
if (hash == ref->get_hash().bits()) {
|
||||
lib_ref = ref;
|
||||
if (is_public == (rec.mode >> 1)) {
|
||||
// library already in required state
|
||||
ap.spec_actions++;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lib_ref.is_null()) {
|
||||
// library code not found
|
||||
return 41;
|
||||
}
|
||||
vm::CellBuilder cb;
|
||||
CHECK(cb.store_bool_bool(rec.mode >> 1) && cb.store_ref_bool(std::move(lib_ref)));
|
||||
CHECK(dict.set_builder(hash, cb));
|
||||
LOG(DEBUG) << "added " << ((rec.mode >> 1) ? "public" : "private") << " library with hash " << hash.to_hex();
|
||||
}
|
||||
new_library = std::move(dict).extract_root_cell();
|
||||
} catch (vm::VmError& vme) {
|
||||
return 42;
|
||||
}
|
||||
ap.spec_actions++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
|
||||
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
|
||||
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
|
||||
|
|
|
@ -65,10 +65,10 @@ struct NewOutMsg {
|
|||
NewOutMsg(ton::LogicalTime _lt, Ref<vm::Cell> _msg, Ref<vm::Cell> _trans)
|
||||
: lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) {
|
||||
}
|
||||
bool operator<(const NewOutMsg& other) const& {
|
||||
bool operator<(const NewOutMsg& other) const & {
|
||||
return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash());
|
||||
}
|
||||
bool operator>(const NewOutMsg& other) const& {
|
||||
bool operator>(const NewOutMsg& other) const & {
|
||||
return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash());
|
||||
}
|
||||
};
|
||||
|
@ -371,6 +371,7 @@ struct Transaction {
|
|||
Ref<vm::Tuple> prepare_vm_c7(const ComputePhaseConfig& cfg) const;
|
||||
bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const;
|
||||
int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
|
||||
int try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
|
||||
int try_action_send_msg(const vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg, int redoing = 0);
|
||||
int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
|
||||
bool check_replace_src_addr(Ref<vm::CellSlice>& src_addr) const;
|
||||
|
|
|
@ -1013,6 +1013,8 @@ x{FB00} @Defop SENDRAWMSG
|
|||
x{FB02} @Defop RAWRESERVE
|
||||
x{FB03} @Defop RAWRESERVEX
|
||||
x{FB04} @Defop SETCODE
|
||||
x{FB06} @Defop SETLIBCODE
|
||||
x{FB07} @Defop CHANGELIB
|
||||
|
||||
//
|
||||
// debug primitives
|
||||
|
|
|
@ -111,3 +111,4 @@ variable base
|
|||
{ true (atom) drop } : atom
|
||||
{ bl word atom 1 'nop } ::_ `
|
||||
{ hole dup 1 { @ execute } does create } : recursive
|
||||
{ 0 { 1+ dup 1 ' $() does over (.) "$" swap $+ 0 (create) } rot times drop } : :$1..n
|
||||
|
|
92
crypto/fift/lib/GetOpt.fif
Normal file
92
crypto/fift/lib/GetOpt.fif
Normal file
|
@ -0,0 +1,92 @@
|
|||
library GetOpt // Simple command-line options parser
|
||||
"Lists.fif" include
|
||||
|
||||
// May be used as follows:
|
||||
// begin-options
|
||||
// "h" { ."Help Message" 0 halt } short-option
|
||||
// "v" { parse-int =: verbosity } short-option-arg
|
||||
// "i" "--interactive" { true =: interactive } short-long-option
|
||||
// parse-options
|
||||
|
||||
// ( l -- l') computes tail of list l if non-empty; else ()
|
||||
{ dup null? ' cdr ifnot } : safe-cdr
|
||||
// ( l c -- l') deletes first c elements from list l
|
||||
{ ' safe-cdr swap times } : list-delete-first
|
||||
// ( l n c -- l' ) deletes c elements starting from n-th in list l
|
||||
recursive list-delete-range {
|
||||
dup 0<= { 2drop } {
|
||||
over 0<= { nip list-delete-first } {
|
||||
swap 1- swap rot uncons 2swap list-delete-range cons
|
||||
} cond } cond
|
||||
} swap !
|
||||
// ( n c -- ) deletes $n .. $(n+c-1) from the argument list $*
|
||||
{ swap 1- $* @ swap rot list-delete-range $* ! } : $*del..
|
||||
// ( s s' -- ? ) checks whether s' is a prefix of s
|
||||
{ tuck $len over $len over >= { $| drop $= } { 2drop drop false } cond
|
||||
} : $pfx?
|
||||
// ( s -- ? ) checks whether s is an option (a string beginning with '-')
|
||||
{ dup $len 1 > { "-" $pfx? } { drop false } cond } : is-opt?
|
||||
// ( l -- s i or 0 ) finds first string in l beginning with '-'
|
||||
{ 0 { 1+ over null? { 2drop 0 true } {
|
||||
swap uncons over is-opt? { drop swap true } { nip swap false } cond
|
||||
} cond } until
|
||||
} : list-find-opt
|
||||
// ( -- s i or 0 ) finds first option in cmdline args
|
||||
{ $* @ list-find-opt } : first-opt
|
||||
// ( s t -- ? ) checks whether short/long option s matches description t
|
||||
{ third $= } : short-option-matches
|
||||
' second : get-opt-flags
|
||||
' first : get-opt-exec
|
||||
{ dup get-opt-flags 4 and 0= 3 + [] $=
|
||||
} : long-option-matches
|
||||
// ( s l -- t -1 or 0 ) finds short/long option s in list l
|
||||
{ swap 1 { swap short-option-matches } does assoc-gen
|
||||
} : lookup-short-option
|
||||
{ swap 1 { swap long-option-matches } does assoc-gen
|
||||
} : lookup-long-option
|
||||
// ( s -- s' null or s' s'' ) Splits long option --opt=arg at '='
|
||||
{ dup "=" $pos 1+ ?dup { tuck $| swap rot 1- $| drop swap } { null } cond
|
||||
} : split-longopt
|
||||
// ( l -- i or 0 )
|
||||
// parses command line arguments according to option description list l
|
||||
// and returns index i of first incorrect option
|
||||
{ { first-opt dup 0= { true } {
|
||||
swap dup "--" $pfx? { // l i s
|
||||
dup $len 2 = { drop dup 1 $*del.. 0 true } {
|
||||
split-longopt swap 3 pick
|
||||
lookup-long-option not { drop true } { // l i s' t f
|
||||
dup get-opt-exec swap get-opt-flags 3 and // l i s' e f'
|
||||
2 pick null? { dup 1 = } { dup 0= negate } cond // l i s' e f' f''
|
||||
dup 1 = { 2drop 2drop true } {
|
||||
{ drop nip over 1+ $() swap execute 2 $*del.. false } {
|
||||
' nip ifnot execute 1 $*del.. false
|
||||
} cond } cond } cond } cond } { // l i s
|
||||
1 $| nip {
|
||||
dup $len 0= { drop 1 $*del.. false true } {
|
||||
1 $| swap 3 pick // l i s' s l
|
||||
lookup-short-option not { drop true true } { // l i s' t
|
||||
dup get-opt-exec swap get-opt-flags 3 and // l i s' e f'
|
||||
?dup 0= { execute false } {
|
||||
2 pick $len { drop execute "" false } {
|
||||
2 = { nip null swap execute "" false } { // l i e
|
||||
nip over 1+ $() swap execute 2 $*del.. false true
|
||||
} cond } cond } cond } cond } cond } until
|
||||
} cond
|
||||
} cond } until nip
|
||||
} : getopt
|
||||
// ( l -- ) Parses options and throws an error on failure
|
||||
{ getopt ?dup { $() "cannot parse command line options near `" swap $+ +"`" abort } if
|
||||
} : run-getopt
|
||||
|
||||
anon constant opt-list-marker
|
||||
' opt-list-marker : begin-options
|
||||
{ opt-list-marker list-until-marker } : end-options
|
||||
{ end-options run-getopt } : parse-options
|
||||
// ( s e -- o ) Creates short/long option s with execution token e
|
||||
{ 0 rot triple } dup : short-option : long-option
|
||||
// ( s s' e -- o ) Creates a combined short option s and long option s' with execution token e
|
||||
{ 4 2swap 4 tuple } : short-long-option
|
||||
{ 1 rot triple } dup : short-option-arg : long-option-arg
|
||||
{ 2 rot triple } dup : short-option-?arg : long-option-?arg
|
||||
{ 5 2swap 4 tuple } : short-long-option-arg
|
||||
{ 6 2swap 4 tuple } : short-long-option-?arg
|
|
@ -46,6 +46,9 @@ td::Result<std::string> load_Lists_fif(std::string dir = "") {
|
|||
td::Result<std::string> load_Lisp_fif(std::string dir = "") {
|
||||
return load_source("Lisp.fif", dir);
|
||||
}
|
||||
td::Result<std::string> load_GetOpt_fif(std::string dir = "") {
|
||||
return load_source("GetOpt.fif", dir);
|
||||
}
|
||||
|
||||
class MemoryFileLoader : public fift::FileLoader {
|
||||
public:
|
||||
|
@ -115,6 +118,10 @@ td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_
|
|||
TRY_RESULT(f, load_TonUtil_fif(dir));
|
||||
loader->add_file("/TonUtil.fif", std::move(f));
|
||||
}
|
||||
{
|
||||
TRY_RESULT(f, load_GetOpt_fif(dir));
|
||||
loader->add_file("/GetOpt.fif", std::move(f));
|
||||
}
|
||||
}
|
||||
if (need_lisp) {
|
||||
TRY_RESULT(f, load_Lisp_fif(dir));
|
||||
|
|
|
@ -604,6 +604,12 @@ void interpret_str_split(vm::Stack& stack) {
|
|||
stack.push_string(std::string{str, sz});
|
||||
}
|
||||
|
||||
void interpret_str_pos(vm::Stack& stack) {
|
||||
auto s2 = stack.pop_string(), s1 = stack.pop_string();
|
||||
auto pos = s1.find(s2);
|
||||
stack.push_smallint(pos == std::string::npos ? -1 : pos);
|
||||
}
|
||||
|
||||
void interpret_str_reverse(vm::Stack& stack) {
|
||||
std::string s = stack.pop_string();
|
||||
auto it = s.begin();
|
||||
|
@ -659,6 +665,20 @@ void interpret_utf8_str_split(vm::Stack& stack) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_utf8_str_pos(vm::Stack& stack) {
|
||||
auto s2 = stack.pop_string(), s1 = stack.pop_string();
|
||||
auto pos = s1.find(s2);
|
||||
if (pos == std::string::npos) {
|
||||
stack.push_smallint(-1);
|
||||
return;
|
||||
}
|
||||
int cnt = 0;
|
||||
for (std::size_t i = 0; i < pos; i++) {
|
||||
cnt += ((s1[i] & 0xc0) != 0x80);
|
||||
}
|
||||
stack.push_smallint(cnt);
|
||||
}
|
||||
|
||||
void interpret_str_remove_trailing_int(vm::Stack& stack, int arg) {
|
||||
char x = (char)(arg ? arg : stack.pop_long_range(127));
|
||||
std::string s = stack.pop_string();
|
||||
|
@ -2336,12 +2356,38 @@ void interpret_db_run_vm_parallel(IntCtx& ctx) {
|
|||
do_interpret_db_run_vm_parallel(ctx.error_stream, ctx.stack, ctx.ton_db, threads_n, tasks_n);
|
||||
}
|
||||
|
||||
Ref<vm::Box> cmdline_args{true};
|
||||
|
||||
void interpret_get_fixed_cmdline_arg(vm::Stack& stack, int n) {
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
auto v = cmdline_args->get();
|
||||
while (true) {
|
||||
if (v.empty()) {
|
||||
stack.push(vm::StackEntry{});
|
||||
return;
|
||||
}
|
||||
auto t = v.as_tuple_range(2, 2);
|
||||
if (t.is_null()) {
|
||||
throw IntError{"invalid cmdline arg list"};
|
||||
}
|
||||
if (!--n) {
|
||||
stack.push(t->at(0));
|
||||
return;
|
||||
}
|
||||
v = t->at(1);
|
||||
}
|
||||
}
|
||||
|
||||
// n -- executes $n
|
||||
void interpret_get_cmdline_arg(IntCtx& ctx) {
|
||||
int n = ctx.stack.pop_smallint_range(999999);
|
||||
char buffer[14];
|
||||
sprintf(buffer, "$%d ", n);
|
||||
auto entry = ctx.dictionary->lookup(std::string{buffer});
|
||||
if (n) {
|
||||
interpret_get_fixed_cmdline_arg(ctx.stack, n);
|
||||
return;
|
||||
}
|
||||
auto entry = ctx.dictionary->lookup("$0 ");
|
||||
if (!entry) {
|
||||
throw IntError{"-?"};
|
||||
} else {
|
||||
|
@ -2349,6 +2395,19 @@ void interpret_get_cmdline_arg(IntCtx& ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_get_cmdline_arg_count(vm::Stack& stack) {
|
||||
auto v = cmdline_args->get();
|
||||
int cnt;
|
||||
for (cnt = 0; !v.empty(); cnt++) {
|
||||
auto t = v.as_tuple_range(2, 2);
|
||||
if (t.is_null()) {
|
||||
throw IntError{"invalid cmdline arg list"};
|
||||
}
|
||||
v = t->at(1);
|
||||
}
|
||||
stack.push_smallint(cnt);
|
||||
}
|
||||
|
||||
void interpret_getenv(vm::Stack& stack) {
|
||||
auto str = stack.pop_string();
|
||||
auto value = str.size() < 1024 ? getenv(str.c_str()) : nullptr;
|
||||
|
@ -2568,6 +2627,7 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("$= ", interpret_str_equal);
|
||||
d.def_stack_word("$cmp ", interpret_str_cmp);
|
||||
d.def_stack_word("$reverse ", interpret_str_reverse);
|
||||
d.def_stack_word("$pos ", interpret_str_pos);
|
||||
d.def_stack_word("(-trailing) ", std::bind(interpret_str_remove_trailing_int, _1, 0));
|
||||
d.def_stack_word("-trailing ", std::bind(interpret_str_remove_trailing_int, _1, ' '));
|
||||
d.def_stack_word("-trailing0 ", std::bind(interpret_str_remove_trailing_int, _1, '0'));
|
||||
|
@ -2575,6 +2635,7 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("Blen ", interpret_bytes_len);
|
||||
d.def_stack_word("$Len ", interpret_utf8_str_len);
|
||||
d.def_stack_word("$Split ", interpret_utf8_str_split);
|
||||
d.def_stack_word("$Pos ", interpret_utf8_str_pos);
|
||||
d.def_ctx_word("Bx. ", std::bind(interpret_bytes_hex_print_raw, _1, true));
|
||||
d.def_stack_word("B>X ", std::bind(interpret_bytes_to_hex, _1, true));
|
||||
d.def_stack_word("B>x ", std::bind(interpret_bytes_to_hex, _1, false));
|
||||
|
@ -2766,6 +2827,10 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_ctx_word("quit ", interpret_quit);
|
||||
d.def_ctx_word("bye ", interpret_bye);
|
||||
d.def_stack_word("halt ", interpret_halt);
|
||||
// cmdline args
|
||||
d.def_stack_word("$* ", std::bind(interpret_literal, _1, vm::StackEntry{cmdline_args}));
|
||||
d.def_stack_word("$# ", interpret_get_cmdline_arg_count);
|
||||
d.def_ctx_word("$() ", interpret_get_cmdline_arg);
|
||||
}
|
||||
|
||||
void init_words_ton(Dictionary& d) {
|
||||
|
@ -2799,13 +2864,16 @@ void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* con
|
|||
using namespace std::placeholders;
|
||||
LOG(DEBUG) << "import_cmdlist_args(" << arg0 << "," << n << ")";
|
||||
d.def_stack_word("$0 ", std::bind(interpret_literal, _1, vm::StackEntry{arg0}));
|
||||
for (int i = 0; i < n; i++) {
|
||||
char buffer[14];
|
||||
sprintf(buffer, "$%d ", i + 1);
|
||||
d.def_stack_word(buffer, std::bind(interpret_literal, _1, vm::StackEntry{argv[i]}));
|
||||
vm::StackEntry list;
|
||||
for (int i = n - 1; i >= 0; i--) {
|
||||
list = vm::StackEntry::cons(vm::StackEntry{argv[i]}, list);
|
||||
}
|
||||
cmdline_args->set(std::move(list));
|
||||
for (int i = 1; i <= n; i++) {
|
||||
char buffer[14];
|
||||
sprintf(buffer, "$%d ", i);
|
||||
d.def_stack_word(buffer, std::bind(interpret_get_fixed_cmdline_arg, _1, i));
|
||||
}
|
||||
d.def_stack_word("$# ", std::bind(interpret_const, _1, n));
|
||||
d.def_ctx_word("$() ", interpret_get_cmdline_arg);
|
||||
}
|
||||
|
||||
std::pair<td::RefInt256, td::RefInt256> numeric_value_ext(std::string s, bool allow_frac = true) {
|
||||
|
|
|
@ -9,10 +9,9 @@
|
|||
|
||||
(cell, int, int, cell) load_data() inline {
|
||||
var cs = get_data().begin_parse();
|
||||
var (cfg_dict, stored_seqno, public_key) = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256));
|
||||
var vote_dict = cs.slice_empty?() ? new_dict() : cs~load_dict();
|
||||
var res = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256), cs~load_dict());
|
||||
cs.end_parse();
|
||||
return (cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
return res;
|
||||
}
|
||||
|
||||
() store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline {
|
||||
|
|
|
@ -796,7 +796,7 @@ _ participant_list() method_id {
|
|||
_ participant_list_extended() method_id {
|
||||
var elect = get_data().begin_parse().preload_dict();
|
||||
if (elect.null?()) {
|
||||
return nil;
|
||||
return (0, 0, 0, 0, nil, 0, 0);
|
||||
}
|
||||
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
|
||||
var l = nil;
|
||||
|
@ -809,7 +809,7 @@ _ participant_list_extended() method_id {
|
|||
l = cons(pair(id, tuple4(stake, max_factor, addr, adnl_addr)), l);
|
||||
}
|
||||
} until (~ f);
|
||||
return l;
|
||||
return (elect_at, elect_close, min_stake, total_stake, l, failed, finished);
|
||||
}
|
||||
|
||||
;; computes the return stake
|
||||
|
|
|
@ -160,7 +160,7 @@ Masterchain swap
|
|||
// 9 4 1 config.validator_num!
|
||||
1000 100 13 config.validator_num!
|
||||
// min-stake max-stake min-total-stake max-factor
|
||||
GR$10000 GR$10000000 GR$1000000 sg~10 config.validator_stake_limits!
|
||||
GR$10000 GR$10000000 GR$500000 sg~10 config.validator_stake_limits!
|
||||
// elected-for elect-start-before elect-end-before stakes-frozen-for
|
||||
// 400000 200000 4000 400000 config.election_params!
|
||||
// 4000 2000 500 1000 config.election_params! // DEBUG
|
||||
|
@ -219,7 +219,7 @@ now dup orig_vset_valid_for + 0 config.validators!
|
|||
0 32 u, // seqno
|
||||
"config-master" +suffix +".pk" load-generate-keypair drop
|
||||
B,
|
||||
newdict dict, // vote dict
|
||||
dictnew dict, // vote dict
|
||||
b> // data
|
||||
empty_cell // libraries
|
||||
GR$10 // balance
|
||||
|
@ -237,8 +237,8 @@ Masterchain swap
|
|||
*/
|
||||
|
||||
// pubkey amount `create-wallet1` or pubkey amount `create-wallet2`
|
||||
PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$100 create-wallet1
|
||||
PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$170 create-wallet0
|
||||
PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$1000000000 create-wallet1
|
||||
PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$1700000000 create-wallet0
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
|
@ -2,21 +2,32 @@
|
|||
|
||||
_ unpack_state() inline_ref {
|
||||
var ds = begin_parse(get_data());
|
||||
var res = (ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict());
|
||||
var res = (ds~load_uint(32), ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict());
|
||||
ds.end_parse();
|
||||
return res;
|
||||
}
|
||||
|
||||
_ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, int n) inline_ref {
|
||||
_ pack_state(cell pending_queries, cell owner_infos, int last_cleaned, int k, int n, int wallet_id) inline_ref {
|
||||
return begin_cell()
|
||||
.store_uint(wallet_id, 32)
|
||||
.store_uint(n, 8)
|
||||
.store_uint(k, 8)
|
||||
.store_uint(last_cleaned, 64)
|
||||
.store_dict(public_keys)
|
||||
.store_dict(owner_infos)
|
||||
.store_dict(pending_queries)
|
||||
.end_cell();
|
||||
}
|
||||
|
||||
_ pack_owner_info(int public_key, int flood) inline_ref {
|
||||
return begin_cell()
|
||||
.store_uint(public_key, 256)
|
||||
.store_uint(flood, 8);
|
||||
}
|
||||
|
||||
_ unpack_owner_info(slice cs) inline_ref {
|
||||
return (cs~load_uint(256), cs~load_uint(8));
|
||||
}
|
||||
|
||||
(int, int) check_signatures(cell public_keys, cell signatures, int hash, int cnt_bits) inline_ref {
|
||||
int cnt = 0;
|
||||
|
||||
|
@ -41,44 +52,60 @@ _ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, in
|
|||
return (cnt, cnt_bits);
|
||||
}
|
||||
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
(int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?) inline_ref {
|
||||
(int, int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?, int root_i) inline_ref {
|
||||
if (found?) {
|
||||
throw_unless(35, query~load_int(1));
|
||||
(int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(n), query);
|
||||
(int creator_i, int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(8), query~load_uint(n), query);
|
||||
throw_unless(36, slice_hash(msg) == slice_hash(in_msg));
|
||||
return (cnt, cnt_bits, msg);
|
||||
return (creator_i, cnt, cnt_bits, msg);
|
||||
}
|
||||
return (0, 0, in_msg);
|
||||
return (root_i, 0, 0, in_msg);
|
||||
}
|
||||
|
||||
() try_init() impure inline_ref {
|
||||
;; first query without signatures is always accepted
|
||||
(int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state();
|
||||
(int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state();
|
||||
throw_if(37, last_cleaned);
|
||||
accept_message();
|
||||
set_data(pack_state(pending_queries, public_keys, 1, k, n));
|
||||
set_data(pack_state(pending_queries, owner_infos, 1, k, n, wallet_id));
|
||||
}
|
||||
|
||||
cell update_pending_queries(cell pending_queries, slice msg, int query_id, int cnt, int cnt_bits, int n, int k) impure inline_ref {
|
||||
(cell, cell) update_pending_queries(cell pending_queries, cell owner_infos, slice msg, int query_id, int creator_i, int cnt, int cnt_bits, int n, int k) impure inline_ref {
|
||||
if (cnt >= k) {
|
||||
accept_message();
|
||||
while (msg.slice_refs()) {
|
||||
var mode = msg~load_uint(8);
|
||||
send_raw_message(msg~load_ref(), mode);
|
||||
}
|
||||
pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1));
|
||||
|
||||
(slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i);
|
||||
(int public_key, int flood) = unpack_owner_info(owner_info);
|
||||
owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1));
|
||||
} else {
|
||||
pending_queries~udict_set_builder(64, query_id, begin_cell()
|
||||
.store_uint(1, 1)
|
||||
.store_uint(creator_i, 8)
|
||||
.store_uint(cnt, 8)
|
||||
.store_uint(cnt_bits, n)
|
||||
.store_slice(msg));
|
||||
}
|
||||
return pending_queries;
|
||||
return (pending_queries, owner_infos);
|
||||
}
|
||||
|
||||
(int, int) calc_boc_size(int cells, int bits, slice root) {
|
||||
cells += 1;
|
||||
bits += root.slice_bits();
|
||||
|
||||
while (root.slice_refs()) {
|
||||
(cells, bits) = calc_boc_size(cells, bits, root~load_ref().begin_parse());
|
||||
}
|
||||
|
||||
return (cells, bits);
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
|
@ -92,44 +119,62 @@ cell update_pending_queries(cell pending_queries, slice msg, int query_id, int c
|
|||
int root_hash = slice_hash(in_msg);
|
||||
int root_i = in_msg~load_uint(8);
|
||||
|
||||
(int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state();
|
||||
(int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state();
|
||||
last_cleaned -= last_cleaned == 0;
|
||||
|
||||
(slice public_key, var found?) = public_keys.udict_get?(8, root_i);
|
||||
(slice owner_info, var found?) = owner_infos.udict_get?(8, root_i);
|
||||
(int public_key, int flood) = unpack_owner_info(owner_info);
|
||||
throw_unless(31, found?);
|
||||
throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256)));
|
||||
throw_unless(32, check_signature(root_hash, root_signature, public_key));
|
||||
|
||||
cell signatures = in_msg~load_dict();
|
||||
|
||||
var hash = slice_hash(in_msg);
|
||||
int query_wallet_id = in_msg~load_uint(32);
|
||||
throw_unless(42, query_wallet_id == wallet_id);
|
||||
|
||||
int query_id = in_msg~load_uint(64);
|
||||
|
||||
(int cnt, int bits) = calc_boc_size(0, 0, in_msg);
|
||||
throw_if(40, (cnt > 8) | (bits > 2048));
|
||||
|
||||
(slice query, var found?) = pending_queries.udict_get?(64, query_id);
|
||||
|
||||
ifnot (found?) {
|
||||
flood += 1;
|
||||
throw_if(39, flood > 10);
|
||||
}
|
||||
|
||||
var bound = (now() << 32);
|
||||
throw_if(33, query_id < bound);
|
||||
|
||||
(slice query, var found?) = pending_queries.udict_get?(64, query_id);
|
||||
(int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?);
|
||||
(int creator_i, int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?, root_i);
|
||||
int mask = 1 << root_i;
|
||||
throw_if(34, cnt_bits & mask);
|
||||
cnt_bits |= mask;
|
||||
cnt += 1;
|
||||
|
||||
;; TODO: reserve some gas or FAIL
|
||||
accept_message();
|
||||
set_gas_limit(100000);
|
||||
|
||||
pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k);
|
||||
set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n));
|
||||
ifnot (found?) {
|
||||
owner_infos~udict_set_builder(8, root_i, pack_owner_info(public_key, flood));
|
||||
throw_if(41, (cnt < k) & (bound + ((60 * 60) << 32) > query_id));
|
||||
}
|
||||
|
||||
(pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
|
||||
set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id));
|
||||
|
||||
commit();
|
||||
|
||||
int need_save = 0;
|
||||
ifnot (cell_null?(signatures) | (cnt >= k)) {
|
||||
(int new_cnt, cnt_bits) = check_signatures(public_keys, signatures, hash, cnt_bits);
|
||||
(int new_cnt, cnt_bits) = check_signatures(owner_infos, signatures, hash, cnt_bits);
|
||||
cnt += new_cnt;
|
||||
pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k);
|
||||
(pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
|
||||
need_save = -1;
|
||||
}
|
||||
|
||||
accept_message();
|
||||
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
|
||||
int old_last_cleaned = last_cleaned;
|
||||
do {
|
||||
|
@ -146,18 +191,18 @@ cell update_pending_queries(cell pending_queries, slice msg, int query_id, int c
|
|||
} until (~ f);
|
||||
|
||||
if (need_save) {
|
||||
set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n));
|
||||
set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id));
|
||||
}
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten)
|
||||
(int, int) get_query_state(int query_id) method_id {
|
||||
(int n, _, int last_cleaned, _, cell pending_queries) = unpack_state();
|
||||
(_, int n, _, int last_cleaned, _, cell pending_queries) = unpack_state();
|
||||
(slice cs, var found) = pending_queries.udict_get?(64, query_id);
|
||||
if (found) {
|
||||
if (cs~load_int(1)) {
|
||||
cs~load_uint(8);
|
||||
cs~load_uint(8 + 8);
|
||||
return (0, cs~load_uint(n));
|
||||
} else {
|
||||
return (-1, 0);
|
||||
|
@ -172,8 +217,8 @@ int processed?(int query_id) method_id {
|
|||
return x;
|
||||
}
|
||||
|
||||
cell create_init_state(int n, int k, cell public_keys) method_id {
|
||||
return pack_state(new_dict(), public_keys, 0, k, n);
|
||||
cell create_init_state(int wallet_id, int n, int k, cell owners_info) method_id {
|
||||
return pack_state(new_dict(), owners_info, 0, k, n, wallet_id);
|
||||
}
|
||||
|
||||
cell merge_list(cell a, cell b) {
|
||||
|
@ -196,7 +241,7 @@ cell merge_list(cell a, cell b) {
|
|||
}
|
||||
|
||||
cell get_public_keys() method_id {
|
||||
(_, _, _, cell public_keys, _) = unpack_state();
|
||||
(_, _, _, _, cell public_keys, _) = unpack_state();
|
||||
return public_keys;
|
||||
}
|
||||
|
||||
|
@ -222,14 +267,14 @@ cell get_public_keys() method_id {
|
|||
}
|
||||
|
||||
cell messages_by_mask(int mask) method_id {
|
||||
(int n, _, _, _, cell pending_queries) = unpack_state();
|
||||
(_, int n, _, _, _, cell pending_queries) = unpack_state();
|
||||
int i = -1;
|
||||
cell a = new_dict();
|
||||
do {
|
||||
(i, var cs, var f) = pending_queries.udict_get_next?(64, i);
|
||||
if (f) {
|
||||
if (cs~load_int(1)) {
|
||||
int cnt_bits = cs.skip_bits(8).preload_uint(n);
|
||||
int cnt_bits = cs.skip_bits(8 + 8).preload_uint(n);
|
||||
if (cnt_bits & mask) {
|
||||
a~udict_set_builder(64, i, begin_cell().store_slice(cs));
|
||||
}
|
||||
|
@ -248,7 +293,7 @@ cell get_messages_unsigned() method_id {
|
|||
}
|
||||
|
||||
(int, int) get_n_k() method_id {
|
||||
(int n, int k, _, _, _) = unpack_state();
|
||||
(_, int n, int k, _, _, _) = unpack_state();
|
||||
return (n, k);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base>" cr
|
||||
{ ."usage: " $0 type ." <filename-base>" cr
|
||||
."Shows the address of a simple wallet created by new-wallet.fif, with address in <filename-base>.addr "
|
||||
."and private key in file <filename-base>.pk" cr 1 halt
|
||||
} : usage
|
||||
def? $# { @' $# 1 > ' usage if } if
|
||||
def? $1 { @' $1 } { "new-wallet" } cond constant file-base
|
||||
$# 1 > ' usage if
|
||||
1 :$1..n
|
||||
$1 dup null? { drop "new-wallet" } if =: file-base
|
||||
|
||||
file-base +".addr" dup ."Loading wallet address from " type cr file>B 32 B|
|
||||
dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc
|
||||
|
|
|
@ -40,6 +40,7 @@ cont get_c3() impure asm "c3 PUSH";
|
|||
cont bless(slice s) impure asm "BLESS";
|
||||
|
||||
() accept_message() impure asm "ACCEPT";
|
||||
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
|
||||
() commit() impure asm "COMMIT";
|
||||
|
||||
int min(int x, int y) asm "MIN";
|
||||
|
|
|
@ -1,35 +1,32 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
"GetOpt.fif" include
|
||||
|
||||
{ ."usage: " @' $0 type ." <filename-base> <dest-addr> <seqno> <amount> [-n] [-B <body-boc>] [-C <transfer-comment>] [<savefile>]" cr
|
||||
."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file <filename-base>.pk "
|
||||
."and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr 1 halt
|
||||
} : usage
|
||||
|
||||
"" =: comment // comment for simple transfers
|
||||
true =: allow-bounce
|
||||
def? $5 { @' $5 "-n" $= { false =: allow-bounce [forget] $5
|
||||
def? $6 { @' $6 =: $5 [forget] $6 } if
|
||||
def? $7 { @' $7 =: $6 [forget] $7 } if
|
||||
@' $# 1- =: $#
|
||||
} if
|
||||
} if
|
||||
|
||||
def? $6 { @' $5 dup "-B" $= swap "-C" $= tuck or
|
||||
{ @' $6 swap { =: comment } { =: body-boc-file } cond [forget] $6
|
||||
def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond
|
||||
@' $# 2- =: $#
|
||||
} if
|
||||
} if
|
||||
3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
|
||||
|
||||
begin-options
|
||||
"n" "--no-bounce" { false =: allow-bounce } short-long-option
|
||||
"B" "--body" { =: body-boc-file } short-long-option-arg
|
||||
"C" "--comment" { =: comment } short-long-option-arg
|
||||
"m" "--mode" { parse-int =: send-mode } short-long-option-arg
|
||||
"h" "--help" { usage } short-long-option
|
||||
parse-options
|
||||
|
||||
$# dup 4 < swap 5 > or ' usage if
|
||||
|
||||
true constant bounce
|
||||
|
||||
5 :$1..n
|
||||
true =: bounce
|
||||
$1 =: file-base
|
||||
$2 bounce parse-load-address allow-bounce and =: bounce 2=: dest_addr
|
||||
$3 parse-int =: seqno
|
||||
$4 $>GR =: amount
|
||||
def? $5 { @' $5 } { "wallet-query" } cond constant savefile
|
||||
3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
|
||||
$5 dup null? { drop "wallet-query" } if =: savefile
|
||||
// "" 1 { 69091 * 1+ 65535 and tuck 2521 / 65 + hold swap } 1000 times drop =: comment
|
||||
|
||||
file-base +".addr" load-address
|
||||
|
|
|
@ -8,8 +8,13 @@
|
|||
|
||||
namespace ton {
|
||||
|
||||
MultisigWallet::QueryBuilder::QueryBuilder(td::int64 query_id, td::Ref<vm::Cell> msg, int mode) {
|
||||
msg_ = vm::CellBuilder().store_long(query_id, 64).store_long(mode, 8).store_ref(std::move(msg)).finalize();
|
||||
MultisigWallet::QueryBuilder::QueryBuilder(td::uint32 wallet_id, td::int64 query_id, td::Ref<vm::Cell> msg, int mode) {
|
||||
msg_ = vm::CellBuilder()
|
||||
.store_long(wallet_id, 32)
|
||||
.store_long(query_id, 64)
|
||||
.store_long(mode, 8)
|
||||
.store_ref(std::move(msg))
|
||||
.finalize();
|
||||
}
|
||||
void MultisigWallet::QueryBuilder::sign(td::int32 id, td::Ed25519::PrivateKey& pk) {
|
||||
CHECK(id < td::narrow_cast<td::int32>(mask_.size()));
|
||||
|
@ -87,26 +92,29 @@ std::vector<td::SecureString> MultisigWallet::get_public_keys() const {
|
|||
return res;
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> MultisigWallet::create_init_data(std::vector<td::SecureString> public_keys, int k) const {
|
||||
td::Ref<vm::Cell> MultisigWallet::create_init_data(td::uint32 wallet_id, std::vector<td::SecureString> public_keys,
|
||||
int k) const {
|
||||
vm::Dictionary pk(8);
|
||||
for (size_t i = 0; i < public_keys.size(); i++) {
|
||||
auto key = pk.integer_key(td::make_refint(i), 8, false);
|
||||
pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()));
|
||||
pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()).store_long(0, 8));
|
||||
}
|
||||
auto res = run_get_method("create_init_state",
|
||||
{td::make_refint(public_keys.size()), td::make_refint(k), pk.get_root_cell()});
|
||||
auto res = run_get_method("create_init_state", {td::make_refint(wallet_id), td::make_refint(public_keys.size()),
|
||||
td::make_refint(k), pk.get_root_cell()});
|
||||
CHECK(res.code == 0);
|
||||
return res.stack.write().pop_cell();
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> MultisigWallet::create_init_data_fast(std::vector<td::SecureString> public_keys, int k) {
|
||||
td::Ref<vm::Cell> MultisigWallet::create_init_data_fast(td::uint32 wallet_id, std::vector<td::SecureString> public_keys,
|
||||
int k) {
|
||||
vm::Dictionary pk(8);
|
||||
for (size_t i = 0; i < public_keys.size(); i++) {
|
||||
auto key = pk.integer_key(td::make_refint(i), 8, false);
|
||||
pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()));
|
||||
pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()).store_long(0, 8));
|
||||
}
|
||||
|
||||
vm::CellBuilder cb;
|
||||
cb.store_long(wallet_id, 32);
|
||||
cb.store_long(public_keys.size(), 8).store_long(k, 8).store_long(0, 64);
|
||||
cb.ensure_throw(cb.store_maybe_ref(pk.get_root_cell()));
|
||||
cb.ensure_throw(cb.store_maybe_ref({}));
|
||||
|
@ -156,7 +164,7 @@ std::vector<MultisigWallet::Message> MultisigWallet::get_unsigned_messaged(int i
|
|||
vm::Dictionary dict(std::move(cell), 64);
|
||||
std::vector<Message> res;
|
||||
dict.check_for_each([&](auto cs, auto ptr, auto ptr_bits) {
|
||||
cs.write().skip_first(8);
|
||||
cs.write().skip_first(8 + 8);
|
||||
Message message;
|
||||
td::BigInt256 query_id;
|
||||
query_id.import_bits(ptr, ptr_bits, false);
|
||||
|
|
|
@ -20,7 +20,7 @@ class MultisigWallet : public ton::SmartContract {
|
|||
|
||||
class QueryBuilder {
|
||||
public:
|
||||
QueryBuilder(td::int64 query_id, td::Ref<vm::Cell> msg, int mode = 3);
|
||||
QueryBuilder(td::uint32 wallet_id, td::int64 query_id, td::Ref<vm::Cell> msg, int mode = 3);
|
||||
void sign(td::int32 id, td::Ed25519::PrivateKey& pk);
|
||||
|
||||
td::Ref<vm::Cell> create_inner() const;
|
||||
|
@ -42,8 +42,9 @@ class MultisigWallet : public ton::SmartContract {
|
|||
// creation
|
||||
static td::Ref<MultisigWallet> create(td::Ref<vm::Cell> data = {});
|
||||
|
||||
td::Ref<vm::Cell> create_init_data(std::vector<td::SecureString> public_keys, int k) const;
|
||||
static td::Ref<vm::Cell> create_init_data_fast(std::vector<td::SecureString> public_keys, int k);
|
||||
td::Ref<vm::Cell> create_init_data(td::uint32 wallet_id, std::vector<td::SecureString> public_keys, int k) const;
|
||||
static td::Ref<vm::Cell> create_init_data_fast(td::uint32 wallet_id, std::vector<td::SecureString> public_keys,
|
||||
int k);
|
||||
|
||||
// get methods
|
||||
int processed(td::uint64 query_id) const;
|
||||
|
|
|
@ -30,10 +30,6 @@
|
|||
|
||||
namespace ton {
|
||||
namespace {
|
||||
td::int32 get_method_id(td::Slice method_name) {
|
||||
unsigned crc = td::crc16(method_name);
|
||||
return (crc & 0xffff) | 0x10000;
|
||||
}
|
||||
td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) {
|
||||
td::Ref<vm::Stack> stack_ref{true};
|
||||
td::RefInt256 acc_addr{true};
|
||||
|
|
|
@ -27,11 +27,7 @@ namespace ton {
|
|||
namespace {
|
||||
const auto& get_map() {
|
||||
static auto map = [] {
|
||||
class Cmp : public std::less<> {
|
||||
public:
|
||||
using is_transparent = void;
|
||||
};
|
||||
std::map<std::string, td::Ref<vm::Cell>, Cmp> map;
|
||||
std::map<std::string, td::Ref<vm::Cell>, std::less<>> map;
|
||||
auto with_tvm_code = [&](auto name, td::Slice code_str) {
|
||||
map[name] = vm::std_boc_deserialize(td::base64_decode(code_str).move_as_ok()).move_as_ok();
|
||||
};
|
||||
|
|
|
@ -455,16 +455,17 @@ TEST(Smartcon, Multisig) {
|
|||
|
||||
int n = 100;
|
||||
int k = 99;
|
||||
td::uint32 wallet_id = std::numeric_limits<td::uint32>::max() - 3;
|
||||
std::vector<td::Ed25519::PrivateKey> keys;
|
||||
for (int i = 0; i < n; i++) {
|
||||
keys.push_back(td::Ed25519::generate_private_key().move_as_ok());
|
||||
}
|
||||
auto init_state = ms_lib->create_init_data(
|
||||
td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k);
|
||||
wallet_id, td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k);
|
||||
auto ms = ton::MultisigWallet::create(init_state);
|
||||
|
||||
td::uint64 query_id = 123;
|
||||
ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize());
|
||||
td::uint64 query_id = 123 | ((100 * 60ull) << 32);
|
||||
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
|
||||
// first empty query (init)
|
||||
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code == 0);
|
||||
// first empty query
|
||||
|
@ -491,7 +492,7 @@ TEST(Smartcon, Multisig) {
|
|||
ASSERT_EQ(0, ms->processed(query_id));
|
||||
|
||||
{
|
||||
ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize());
|
||||
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
|
||||
for (int i = 50; i + 1 < 100; i++) {
|
||||
qb.sign(i, keys[i]);
|
||||
}
|
||||
|
@ -507,6 +508,7 @@ TEST(Smartcon, Multisig) {
|
|||
TEST(Smartcont, MultisigStress) {
|
||||
int n = 10;
|
||||
int k = 5;
|
||||
td::uint32 wallet_id = std::numeric_limits<td::uint32>::max() - 3;
|
||||
|
||||
std::vector<td::Ed25519::PrivateKey> keys;
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
@ -515,13 +517,14 @@ TEST(Smartcont, MultisigStress) {
|
|||
auto public_keys = td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); });
|
||||
auto ms_lib = ton::MultisigWallet::create();
|
||||
auto init_state_old =
|
||||
ms_lib->create_init_data_fast(td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
|
||||
auto init_state = ms_lib->create_init_data(td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
|
||||
ms_lib->create_init_data_fast(wallet_id, td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
|
||||
auto init_state =
|
||||
ms_lib->create_init_data(wallet_id, td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
|
||||
CHECK(init_state_old->get_hash() == init_state->get_hash());
|
||||
auto ms = ton::MultisigWallet::create(init_state);
|
||||
CHECK(ms->get_public_keys() == public_keys);
|
||||
|
||||
td::int32 now = 0;
|
||||
td::int32 now = 100 * 60;
|
||||
td::int32 qid = 1;
|
||||
using Mask = std::bitset<128>;
|
||||
struct Query {
|
||||
|
@ -566,7 +569,7 @@ TEST(Smartcont, MultisigStress) {
|
|||
};
|
||||
|
||||
auto sign_query = [&](Query& query, Mask mask) {
|
||||
auto qb = ton::MultisigWallet::QueryBuilder(query.id, query.message);
|
||||
auto qb = ton::MultisigWallet::QueryBuilder(wallet_id, query.id, query.message);
|
||||
int first_i = -1;
|
||||
for (int i = 0; i < (int)mask.size(); i++) {
|
||||
if (mask.test(i)) {
|
||||
|
|
|
@ -708,6 +708,9 @@ int VmState::step() {
|
|||
}
|
||||
|
||||
int VmState::run() {
|
||||
if (code.is_null()) {
|
||||
throw VmError{Excno::fatal, "cannot run an uninitialized VM"};
|
||||
}
|
||||
int res;
|
||||
Guard guard(this);
|
||||
do {
|
||||
|
|
|
@ -237,6 +237,11 @@ class DictionaryFixed : public DictionaryBase {
|
|||
Ref<CellSlice> get_minmax_key(T& key_buffer, bool fetch_max = false, bool invert_first = false) {
|
||||
return get_minmax_key(key_buffer.bits(), key_buffer.size(), fetch_max, invert_first);
|
||||
}
|
||||
template <typename T>
|
||||
Ref<CellSlice> lookup_nearest_key(T& key_buffer, bool fetch_next = false, bool allow_eq = false,
|
||||
bool invert_first = false) {
|
||||
return lookup_nearest_key(key_buffer.bits(), key_buffer.size(), fetch_next, allow_eq, invert_first);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int label_mode() const {
|
||||
|
|
|
@ -57,6 +57,11 @@ class Atom;
|
|||
|
||||
using Tuple = td::Cnt<std::vector<StackEntry>>;
|
||||
|
||||
template <typename... Args>
|
||||
Ref<Tuple> make_tuple_ref(Args&&... args) {
|
||||
return td::make_cnt_ref<std::vector<vm::StackEntry>>(std::vector<vm::StackEntry>{std::forward<Args>(args)...});
|
||||
}
|
||||
|
||||
struct from_object_t {};
|
||||
constexpr from_object_t from_object{};
|
||||
|
||||
|
@ -192,6 +197,10 @@ class StackEntry {
|
|||
public:
|
||||
static StackEntry make_list(std::vector<StackEntry>&& elems);
|
||||
static StackEntry make_list(const std::vector<StackEntry>& elems);
|
||||
template <typename T1, typename T2>
|
||||
static StackEntry cons(T1&& x, T2&& y) {
|
||||
return StackEntry{make_tuple_ref(std::forward<T1>(x), std::forward<T2>(y))};
|
||||
}
|
||||
template <typename T>
|
||||
static StackEntry maybe(Ref<T> ref) {
|
||||
if (ref.is_null()) {
|
||||
|
@ -268,11 +277,6 @@ inline void swap(StackEntry& se1, StackEntry& se2) {
|
|||
se1.swap(se2);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
Ref<Tuple> make_tuple_ref(Args&&... args) {
|
||||
return td::make_cnt_ref<std::vector<vm::StackEntry>>(std::vector<vm::StackEntry>{std::forward<Args>(args)...});
|
||||
}
|
||||
|
||||
const StackEntry& tuple_index(const Tuple& tup, unsigned idx);
|
||||
StackEntry tuple_extend_index(const Ref<Tuple>& tup, unsigned idx);
|
||||
unsigned tuple_extend_set_index(Ref<Tuple>& tup, unsigned idx, StackEntry&& value, bool force = false);
|
||||
|
|
|
@ -662,12 +662,49 @@ int exec_set_code(VmState* st) {
|
|||
return install_output_action(st, cb.finalize());
|
||||
}
|
||||
|
||||
int exec_set_lib_code(VmState* st) {
|
||||
VM_LOG(st) << "execute SETLIBCODE";
|
||||
Stack& stack = st->get_stack();
|
||||
stack.check_underflow(2);
|
||||
int mode = stack.pop_smallint_range(2);
|
||||
auto code = stack.pop_cell();
|
||||
CellBuilder cb;
|
||||
if (!(cb.store_ref_bool(get_actions(st)) // out_list$_ {n:#} prev:^(OutList n)
|
||||
&& cb.store_long_bool(0x26fa1dd4, 32) // action_change_library#26fa1dd4
|
||||
&& cb.store_long_bool(mode * 2 + 1, 8) // mode:(## 7) { mode <= 2 }
|
||||
&& cb.store_ref_bool(std::move(code)))) { // libref:LibRef = OutAction;
|
||||
throw VmError{Excno::cell_ov, "cannot serialize new library code into an output action cell"};
|
||||
}
|
||||
return install_output_action(st, cb.finalize());
|
||||
}
|
||||
|
||||
int exec_change_lib(VmState* st) {
|
||||
VM_LOG(st) << "execute CHANGELIB";
|
||||
Stack& stack = st->get_stack();
|
||||
stack.check_underflow(2);
|
||||
int mode = stack.pop_smallint_range(2);
|
||||
auto hash = stack.pop_int_finite();
|
||||
if (!hash->unsigned_fits_bits(256)) {
|
||||
throw VmError{Excno::range_chk, "library hash must be non-negative"};
|
||||
}
|
||||
CellBuilder cb;
|
||||
if (!(cb.store_ref_bool(get_actions(st)) // out_list$_ {n:#} prev:^(OutList n)
|
||||
&& cb.store_long_bool(0x26fa1dd4, 32) // action_change_library#26fa1dd4
|
||||
&& cb.store_long_bool(mode * 2, 8) // mode:(## 7) { mode <= 2 }
|
||||
&& cb.store_int256_bool(hash, 256, false))) { // libref:LibRef = OutAction;
|
||||
throw VmError{Excno::cell_ov, "cannot serialize library hash into an output action cell"};
|
||||
}
|
||||
return install_output_action(st, cb.finalize());
|
||||
}
|
||||
|
||||
void register_ton_message_ops(OpcodeTable& cp0) {
|
||||
using namespace std::placeholders;
|
||||
cp0.insert(OpcodeInstr::mksimple(0xfb00, 16, "SENDRAWMSG", exec_send_raw_message))
|
||||
.insert(OpcodeInstr::mksimple(0xfb02, 16, "RESERVERAW", std::bind(exec_reserve_raw, _1, 0)))
|
||||
.insert(OpcodeInstr::mksimple(0xfb03, 16, "RESERVERAWX", std::bind(exec_reserve_raw, _1, 1)))
|
||||
.insert(OpcodeInstr::mksimple(0xfb04, 16, "SETCODE", exec_set_code));
|
||||
.insert(OpcodeInstr::mksimple(0xfb04, 16, "SETCODE", exec_set_code))
|
||||
.insert(OpcodeInstr::mksimple(0xfb06, 16, "SETLIBCODE", exec_set_lib_code))
|
||||
.insert(OpcodeInstr::mksimple(0xfb07, 16, "CHANGELIB", exec_change_lib));
|
||||
}
|
||||
|
||||
void register_ton_ops(OpcodeTable& cp0) {
|
||||
|
|
134
crypto/vm/utils.cpp
Normal file
134
crypto/vm/utils.cpp
Normal file
|
@ -0,0 +1,134 @@
|
|||
#include "utils.h"
|
||||
|
||||
namespace vm {
|
||||
|
||||
td::Result<vm::StackEntry> convert_stack_entry(td::Slice word);
|
||||
td::Result<std::vector<vm::StackEntry>> parse_stack_entries_in(td::Slice& str, bool prefix_only = false);
|
||||
td::Result<vm::StackEntry> parse_stack_entry_in(td::Slice& str, bool prefix_only = false);
|
||||
|
||||
namespace {
|
||||
|
||||
td::Slice& skip_spaces(td::Slice& str, const char* delims) {
|
||||
while (str.size() > 0 && strchr(delims, str[0])) {
|
||||
str.remove_prefix(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
td::Slice get_word(td::Slice& str, const char* delims, const char* specials) {
|
||||
skip_spaces(str, delims);
|
||||
|
||||
size_t p = 0;
|
||||
while (p < str.size() && !strchr(delims, str[p])) {
|
||||
if (specials && strchr(specials, str[p])) {
|
||||
if (!p) {
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
td::Slice ret = str.copy().truncate(p);
|
||||
str.remove_prefix(p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
td::Result<vm::StackEntry> parse_stack_entry_in(td::Slice& str, bool prefix_only) {
|
||||
auto word = get_word(str, " \t", "[()]");
|
||||
if (word.empty()) {
|
||||
return td::Status::Error("stack value expected instead of end-of-line");
|
||||
}
|
||||
if (word.size() == 1 && (word[0] == '[' || word[0] == '(')) {
|
||||
int expected = (word[0] == '(' ? ')' : ']');
|
||||
TRY_RESULT(values, parse_stack_entries_in(str, true));
|
||||
word = get_word(str, " \t", "[()]");
|
||||
if (word.size() != 1 || word[0] != expected) {
|
||||
return td::Status::Error("closing bracket expected");
|
||||
}
|
||||
vm::StackEntry value;
|
||||
if (expected == ']') {
|
||||
value = vm::StackEntry{std::move(values)};
|
||||
} else {
|
||||
value = vm::StackEntry::make_list(std::move(values));
|
||||
}
|
||||
if (prefix_only || (skip_spaces(str, " \t").size() == 0)) {
|
||||
return value;
|
||||
} else {
|
||||
return td::Status::Error("extra data at the end");
|
||||
}
|
||||
} else {
|
||||
return convert_stack_entry(word);
|
||||
}
|
||||
}
|
||||
|
||||
td::Result<vm::StackEntry> convert_stack_entry(td::Slice str) {
|
||||
if (str.empty() || str.size() > 65535) {
|
||||
return td::Status::Error("too long string");
|
||||
}
|
||||
int l = (int)str.size();
|
||||
if (str[0] == '"') {
|
||||
vm::CellBuilder cb;
|
||||
if (l == 1 || str.back() != '"' || l >= 127 + 2 || !cb.store_bytes_bool(str.data() + 1, l - 2)) {
|
||||
return td::Status::Error("incomplete (or too long) string");
|
||||
}
|
||||
return vm::StackEntry{vm::load_cell_slice_ref(cb.finalize())};
|
||||
}
|
||||
if (l >= 3 && (str[0] == 'x' || str[0] == 'b') && str[1] == '{' && str.back() == '}') {
|
||||
unsigned char buff[128];
|
||||
int bits =
|
||||
(str[0] == 'x')
|
||||
? (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1)
|
||||
: (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1);
|
||||
if (bits < 0) {
|
||||
return td::Status::Error("failed to parse raw b{...}/x{...} number");
|
||||
}
|
||||
return vm::StackEntry{
|
||||
Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}};
|
||||
}
|
||||
auto num = td::RefInt256{true};
|
||||
auto& x = num.unique_write();
|
||||
if (l >= 3 && str[0] == '0' && str[1] == 'x') {
|
||||
if (x.parse_hex(str.data() + 2, l - 2) != l - 2) {
|
||||
return td::Status::Error("failed to parse 0x... hex number");
|
||||
}
|
||||
} else if (l >= 4 && str[0] == '-' && str[1] == '0' && str[2] == 'x') {
|
||||
if (x.parse_hex(str.data() + 3, l - 3) != l - 3) {
|
||||
return td::Status::Error("failed to parse -0x... hex number");
|
||||
}
|
||||
x.negate().normalize();
|
||||
} else if (!l || x.parse_dec(str.data(), l) != l) {
|
||||
return td::Status::Error("failed to parse dec number");
|
||||
}
|
||||
return vm::StackEntry{std::move(num)};
|
||||
}
|
||||
|
||||
td::Result<std::vector<vm::StackEntry>> parse_stack_entries_in(td::Slice& str, bool prefix_only) {
|
||||
std::vector<vm::StackEntry> ret;
|
||||
while (!skip_spaces(str, " \t").empty()) {
|
||||
auto c = str.copy();
|
||||
auto word = get_word(c, " \t", "[()]");
|
||||
if (word == "]" || word == ")") {
|
||||
if (prefix_only) {
|
||||
return ret;
|
||||
} else {
|
||||
return td::Status::Error("not paired closing bracket");
|
||||
}
|
||||
}
|
||||
TRY_RESULT(value, parse_stack_entry_in(str, true));
|
||||
ret.push_back(std::move(value));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
td::Result<std::vector<vm::StackEntry>> parse_stack_entries(td::Slice str, bool prefix_only) {
|
||||
return parse_stack_entries_in(str, prefix_only);
|
||||
}
|
||||
|
||||
td::Result<vm::StackEntry> parse_stack_entry(td::Slice str, bool prefix_only) {
|
||||
return parse_stack_entry_in(str, prefix_only);
|
||||
}
|
||||
|
||||
} // namespace vm
|
11
crypto/vm/utils.h
Normal file
11
crypto/vm/utils.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
#include "stack.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace vm {
|
||||
|
||||
td::Result<std::vector<vm::StackEntry>> parse_stack_entries(td::Slice str, bool prefix_only = false);
|
||||
td::Result<vm::StackEntry> parse_stack_entry(td::Slice str, bool prefix_only = false);
|
||||
|
||||
} // namespace vm
|
Loading…
Add table
Add a link
Reference in a new issue