mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
updated smartcontracts
- updated smartcontracts - updated fullnode database layout - fixed memory leak in blockchain-explorer - updated tonlib
This commit is contained in:
parent
9c9248a9ae
commit
c860ce3d1e
104 changed files with 7309 additions and 1335 deletions
|
@ -1,4 +1,5 @@
|
|||
"Asm.fif" include
|
||||
"TonUtil.fif" include
|
||||
|
||||
31 -1<< constant wc_undef
|
||||
0 constant wc_base
|
||||
|
@ -187,6 +188,7 @@ dictnew constant special-dict
|
|||
|
||||
|
||||
// restricted wallet creation
|
||||
"auto/wallet-code.fif" include =: WCode0
|
||||
"auto/restricted-wallet-code.fif" include =: RWCode1
|
||||
"auto/restricted-wallet2-code.fif" include =: RWCode2
|
||||
|
||||
|
@ -200,7 +202,7 @@ dictnew constant special-dict
|
|||
0 // ticktock
|
||||
2 // mode: create
|
||||
register_smc
|
||||
Masterchain 6 .Addr cr
|
||||
Masterchain swap 6 .Addr cr
|
||||
} : create-wallet1
|
||||
|
||||
// D x t -- D'
|
||||
|
@ -225,5 +227,18 @@ dictnew constant special-dict
|
|||
0 // ticktock
|
||||
2 // mode: create
|
||||
register_smc
|
||||
Masterchain 6 .Addr cr
|
||||
Masterchain swap 6 .Addr cr
|
||||
} : create-wallet2
|
||||
|
||||
// pubkey amount
|
||||
{ over ."Key " pubkey>$ type ." -> "
|
||||
WCode0 // code
|
||||
<b 1 32 u, 3 roll 256 u, b> // data
|
||||
empty_cell // libs
|
||||
3 roll // balance
|
||||
0 // split_depth
|
||||
0 // ticktock
|
||||
2 // mode: create
|
||||
register_smc
|
||||
Masterchain swap 6 .Addr cr
|
||||
} : create-wallet0
|
||||
|
|
|
@ -1,15 +1,32 @@
|
|||
;; Simple configuration smart contract
|
||||
|
||||
() set_conf_param(int index, cell value) impure {
|
||||
var cs = begin_parse(get_data());
|
||||
var cs = get_data().begin_parse();
|
||||
var cfg_dict = cs~load_ref();
|
||||
cfg_dict~idict_set_ref(32, index, value);
|
||||
set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell());
|
||||
}
|
||||
|
||||
(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();
|
||||
cs.end_parse();
|
||||
return (cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
}
|
||||
|
||||
() store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline {
|
||||
set_data(begin_cell()
|
||||
.store_ref(cfg_dict)
|
||||
.store_uint(stored_seqno, 32)
|
||||
.store_uint(public_key, 256)
|
||||
.store_dict(vote_dict)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
(int, int) check_validator_set(cell vset) {
|
||||
var cs = vset.begin_parse();
|
||||
throw_unless(9, cs~load_uint(8) == 0x11); ;; validators#11
|
||||
throw_if(9, (cs~load_uint(8) - 0x11) & -2); ;; validators#11 or validators_ext#12
|
||||
int utime_since = cs~load_uint(32);
|
||||
int utime_until = cs~load_uint(32);
|
||||
int total = cs~load_uint(16);
|
||||
|
@ -86,6 +103,83 @@
|
|||
.end_cell(), 0);
|
||||
}
|
||||
|
||||
() after_code_upgrade(slice param, cell old_code) impure method_id(1666) {
|
||||
}
|
||||
|
||||
_ perform_action(cfg_dict, public_key, action, cs) {
|
||||
if (action == 0x43665021) {
|
||||
;; change one configuration parameter
|
||||
var param_index = cs~load_uint(32);
|
||||
var param_value = cs~load_ref();
|
||||
cs.end_parse();
|
||||
cfg_dict~idict_set_ref(32, param_index, param_value);
|
||||
return (cfg_dict, public_key);
|
||||
} elseif (action == 0x4e436f64) {
|
||||
;; change configuration smart contract code
|
||||
var new_code = cs~load_ref();
|
||||
set_code(new_code);
|
||||
var old_code = get_c3();
|
||||
set_c3(new_code);
|
||||
after_code_upgrade(cs, old_code);
|
||||
throw(0);
|
||||
return (cfg_dict, public_key);
|
||||
} elseif (action == 0x50624b21) {
|
||||
;; change configuration master public key
|
||||
public_key = cs~load_uint(256);
|
||||
cs.end_parse();
|
||||
return (cfg_dict, public_key);
|
||||
} elseif (action == 0x4e43ef05) {
|
||||
;; change election smart contract code
|
||||
change_elector_code(cs);
|
||||
return (cfg_dict, public_key);
|
||||
} else {
|
||||
throw_if(32, action);
|
||||
return (cfg_dict, public_key);
|
||||
}
|
||||
}
|
||||
|
||||
slice get_validator_descr(int idx) inline_ref {
|
||||
var vset = config_param(34);
|
||||
if (vset.null?()) {
|
||||
return null();
|
||||
}
|
||||
var cs = begin_parse(vset);
|
||||
cs~skip_bits(8 + 32 + 32 + 16 + 16);
|
||||
var dict = begin_cell().store_slice(cs).end_cell();
|
||||
var (value, _) = dict.udict_get?(16, idx);
|
||||
return value;
|
||||
}
|
||||
|
||||
(int, int) unpack_validator_descr(slice cs) inline {
|
||||
;; ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey;
|
||||
;; validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr;
|
||||
;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr;
|
||||
throw_unless(41, (cs~load_uint(8) & ~ 0x20) == 0x53);
|
||||
throw_unless(41, cs~load_uint(32) == 0x8e81278a);
|
||||
return (cs~load_uint(256), cs~load_uint(64));
|
||||
}
|
||||
|
||||
slice create_new_entry(cs) inline {
|
||||
return begin_cell().store_int(false, 1).store_uint(0, 64).store_uint(0, 256).store_slice(cs).end_cell().begin_parse();
|
||||
}
|
||||
|
||||
cell register_vote(vote_dict, action, cs, idx, weight) {
|
||||
int hash = 0;
|
||||
var entry = null();
|
||||
if (action & 1) {
|
||||
hash = slice_hash(cs);
|
||||
(entry, var found?) = vote_dict.udict_get?(256, hash);
|
||||
ifnot (found?) {
|
||||
entry = create_new_entry(cs);
|
||||
}
|
||||
} else {
|
||||
hash = cs.preload_uint(256);
|
||||
(entry, var found?) = vote_dict.udict_get?(256, hash);
|
||||
throw_unless(42, found?);
|
||||
}
|
||||
return vote_dict;
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
var signature = in_msg~load_bits(512);
|
||||
var cs = in_msg;
|
||||
|
@ -93,36 +187,28 @@
|
|||
int msg_seqno = cs~load_uint(32);
|
||||
var valid_until = cs~load_uint(32);
|
||||
throw_if(35, valid_until < now());
|
||||
var cs2 = begin_parse(get_data());
|
||||
var cfg_dict = cs2~load_ref();
|
||||
var stored_seqno = cs2~load_uint(32);
|
||||
var public_key = cs2~load_uint(256);
|
||||
cs2.end_parse();
|
||||
var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
ifnot ((action - 0x566f7465) & -2) {
|
||||
var idx = cs~load_uint(16);
|
||||
var vdescr = get_validator_descr(idx);
|
||||
var (val_pubkey, weight) = unpack_validator_descr(vdescr);
|
||||
throw_unless(34, check_signature(slice_hash(in_msg), signature, val_pubkey));
|
||||
accept_message();
|
||||
stored_seqno += 1;
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
commit();
|
||||
vote_dict = register_vote(vote_dict, action, cs, idx, weight);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
return ();
|
||||
}
|
||||
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
accept_message();
|
||||
if (action == 0x43665021) {
|
||||
;; change one configuration parameter
|
||||
var param_index = cs~load_uint(32);
|
||||
var param_value = cs~load_ref();
|
||||
cs.end_parse();
|
||||
cfg_dict~idict_set_ref(32, param_index, param_value);
|
||||
} elseif (action == 0x4e436f64) {
|
||||
;; change configuration smart contract code
|
||||
var new_code = cs~load_ref();
|
||||
cs.end_parse();
|
||||
set_code(new_code);
|
||||
} elseif (action == 0x50624b21) {
|
||||
;; change configuration master public key
|
||||
public_key = cs~load_uint(256);
|
||||
cs.end_parse();
|
||||
} elseif (action == 0x4e43ef05) {
|
||||
;; change election smart contract code
|
||||
change_elector_code(cs);
|
||||
} else {
|
||||
throw_if(32, action);
|
||||
}
|
||||
set_data(begin_cell().store_ref(cfg_dict).store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell());
|
||||
stored_seqno += 1;
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
commit();
|
||||
(cfg_dict, public_key) = perform_action(cfg_dict, public_key, action, cs);
|
||||
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
|
||||
}
|
||||
|
||||
() run_ticktock(int is_tock) impure {
|
||||
|
|
|
@ -408,7 +408,7 @@ _ compute_total_stake(l, n, m_stake) {
|
|||
return tot_stake;
|
||||
}
|
||||
|
||||
(cell, cell, cell, int, int) try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor) {
|
||||
(cell, cell, int, cell, int, int) try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor) {
|
||||
var cs = 16.config_param().begin_parse();
|
||||
var (max_validators, _, min_validators) = (cs~load_uint(16), cs~load_uint(16), cs~load_uint(16));
|
||||
cs.end_parse();
|
||||
|
@ -435,7 +435,7 @@ _ compute_total_stake(l, n, m_stake) {
|
|||
} until (~ f);
|
||||
n = min(n, max_validators);
|
||||
if (n < min_validators) {
|
||||
return (credits, new_dict(), new_dict(), 0, 0);
|
||||
return (credits, new_dict(), 0, new_dict(), 0, 0);
|
||||
}
|
||||
var l = nil;
|
||||
do {
|
||||
|
@ -464,7 +464,7 @@ _ compute_total_stake(l, n, m_stake) {
|
|||
}
|
||||
} until (i >= n);
|
||||
if ((m == 0) | (best_stake < min_total_stake)) {
|
||||
return (credits, new_dict(), new_dict(), 0, 0);
|
||||
return (credits, new_dict(), 0, new_dict(), 0, 0);
|
||||
}
|
||||
;; we have to select first m validators from list l
|
||||
l1 = touch(l);
|
||||
|
@ -476,6 +476,7 @@ _ compute_total_stake(l, n, m_stake) {
|
|||
;; create both the new validator set and the refund set
|
||||
int i = 0;
|
||||
var tot_stake = 0;
|
||||
var tot_weight = 0;
|
||||
var vset = new_dict();
|
||||
var frozen = new_dict();
|
||||
do {
|
||||
|
@ -492,6 +493,7 @@ _ compute_total_stake(l, n, m_stake) {
|
|||
;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr;
|
||||
var weight = (true_stake << 60) / best_stake;
|
||||
tot_stake += true_stake;
|
||||
tot_weight += weight;
|
||||
var vinfo = begin_cell()
|
||||
.store_uint(adnl_addr ? 0x73 : 0x53, 8) ;; validator_addr#73 or validator#53
|
||||
.store_uint(0x8e81278a, 32) ;; ed25519_pubkey#8e81278a
|
||||
|
@ -514,7 +516,7 @@ _ compute_total_stake(l, n, m_stake) {
|
|||
i += 1;
|
||||
} until (l.null?());
|
||||
throw_unless(49, tot_stake == best_stake);
|
||||
return (credits, vset, frozen, tot_stake, m);
|
||||
return (credits, vset, tot_weight, frozen, tot_stake, m);
|
||||
}
|
||||
|
||||
int conduct_elections(ds, elect, credits) impure {
|
||||
|
@ -545,7 +547,7 @@ int conduct_elections(ds, elect, credits) impure {
|
|||
;; elections finished
|
||||
return false;
|
||||
}
|
||||
(credits, var vdict, var frozen, var total_stakes, var cnt) = try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor);
|
||||
(credits, var vdict, var total_weight, var frozen, var total_stakes, var cnt) = try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor);
|
||||
;; pack elections; if cnt==0, set failed=true, finished=false.
|
||||
failed = (cnt == 0);
|
||||
finished = ~ failed;
|
||||
|
@ -561,12 +563,13 @@ int conduct_elections(ds, elect, credits) impure {
|
|||
var start = max(now() + elect_end_before - 60, elect_at);
|
||||
var main_validators = config_param(16).begin_parse().skip_bits(16).preload_uint(16);
|
||||
var vset = begin_cell()
|
||||
.store_uint(0x11, 8) ;; validators#11
|
||||
.store_uint(0x12, 8) ;; validators_ext#12
|
||||
.store_uint(start, 32) ;; utime_since:uint32
|
||||
.store_uint(start + elect_for, 32) ;; utime_until:uint32
|
||||
.store_uint(cnt, 16) ;; total:(## 16)
|
||||
.store_uint(min(cnt, main_validators), 16) ;; main:(## 16)
|
||||
.store_slice(vdict.begin_parse()) ;; list:(Hashmap 16 ValidatorDescr)
|
||||
.store_uint(min(cnt, main_validators), 16) ;; main:(## 16)
|
||||
.store_uint(total_weight, 64) ;; total_weight:uint64
|
||||
.store_dict(vdict) ;; list:(HashmapE 16 ValidatorDescr)
|
||||
.end_cell();
|
||||
var config_addr = config_param(0).begin_parse().preload_uint(256);
|
||||
send_validator_set_to_config(config_addr, vset, elect_at);
|
||||
|
|
|
@ -225,6 +225,22 @@ Masterchain swap
|
|||
"config-master" +suffix +".addr" save-address-verbose
|
||||
// Other data
|
||||
|
||||
/*
|
||||
*
|
||||
* Initial wallets (test)
|
||||
*
|
||||
*/
|
||||
|
||||
// pubkey amount `create-wallet1` or pubkey amount `create-wallet2`
|
||||
PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$100000000 create-wallet1
|
||||
PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$1700000000 create-wallet0
|
||||
|
||||
/*
|
||||
*
|
||||
* Create state
|
||||
*
|
||||
*/
|
||||
|
||||
create_state
|
||||
cr cr ."new state is:" cr dup <s csr. cr
|
||||
dup 31 boc+>B dup Bx. cr
|
||||
|
|
265
crypto/smartcont/multisig-code.fc
Normal file
265
crypto/smartcont/multisig-code.fc
Normal file
|
@ -0,0 +1,265 @@
|
|||
;; Simple wallet smart contract
|
||||
|
||||
_ 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());
|
||||
ds.end_parse();
|
||||
return res;
|
||||
}
|
||||
|
||||
_ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, int n) inline_ref {
|
||||
return begin_cell()
|
||||
.store_uint(n, 8)
|
||||
.store_uint(k, 8)
|
||||
.store_uint(last_cleaned, 64)
|
||||
.store_dict(public_keys)
|
||||
.store_dict(pending_queries)
|
||||
.end_cell();
|
||||
}
|
||||
|
||||
(int, int) check_signatures(cell public_keys, cell signatures, int hash, int cnt_bits) inline_ref {
|
||||
int cnt = 0;
|
||||
|
||||
do {
|
||||
slice cs = signatures.begin_parse();
|
||||
slice signature = cs~load_bits(512);
|
||||
|
||||
int i = cs~load_uint(8);
|
||||
signatures = cs~load_dict();
|
||||
|
||||
(slice public_key, var found?) = public_keys.udict_get?(8, i);
|
||||
throw_unless(37, found?);
|
||||
throw_unless(38, check_signature(hash, signature, public_key.preload_uint(256)));
|
||||
|
||||
int mask = (1 << i);
|
||||
int old_cnt_bits = cnt_bits;
|
||||
cnt_bits |= mask;
|
||||
int should_check = cnt_bits != old_cnt_bits;
|
||||
cnt -= should_check;
|
||||
} until (cell_null?(signatures));
|
||||
|
||||
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 {
|
||||
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);
|
||||
throw_unless(36, slice_hash(msg) == slice_hash(in_msg));
|
||||
return (cnt, cnt_bits, msg);
|
||||
}
|
||||
return (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();
|
||||
throw_if(37, last_cleaned);
|
||||
accept_message();
|
||||
set_data(pack_state(pending_queries, public_keys, 1, k, n));
|
||||
}
|
||||
|
||||
cell update_pending_queries(cell pending_queries, slice msg, int query_id, int cnt, int cnt_bits, int n, int k) impure inline_ref {
|
||||
if (cnt >= k) {
|
||||
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));
|
||||
} else {
|
||||
pending_queries~udict_set_builder(64, query_id, begin_cell()
|
||||
.store_uint(1, 1)
|
||||
.store_uint(cnt, 8)
|
||||
.store_uint(cnt_bits, n)
|
||||
.store_slice(msg));
|
||||
}
|
||||
return pending_queries;
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
;; empty message triggers init
|
||||
if (slice_empty?(in_msg)) {
|
||||
return try_init();
|
||||
}
|
||||
|
||||
;; Check root signature
|
||||
slice root_signature = in_msg~load_bits(512);
|
||||
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();
|
||||
last_cleaned -= last_cleaned == 0;
|
||||
|
||||
(slice public_key, var found?) = public_keys.udict_get?(8, root_i);
|
||||
throw_unless(31, found?);
|
||||
throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256)));
|
||||
|
||||
cell signatures = in_msg~load_dict();
|
||||
|
||||
var hash = slice_hash(in_msg);
|
||||
int query_id = in_msg~load_uint(64);
|
||||
|
||||
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 mask = 1 << root_i;
|
||||
throw_if(34, cnt_bits & mask);
|
||||
cnt_bits |= mask;
|
||||
cnt += 1;
|
||||
|
||||
;; TODO: reserve some gas or FAIL
|
||||
accept_message();
|
||||
|
||||
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));
|
||||
|
||||
commit();
|
||||
|
||||
int need_save = 0;
|
||||
ifnot (cell_null?(signatures) | (cnt >= k)) {
|
||||
(int new_cnt, cnt_bits) = check_signatures(public_keys, signatures, hash, cnt_bits);
|
||||
cnt += new_cnt;
|
||||
pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k);
|
||||
need_save = -1;
|
||||
}
|
||||
|
||||
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
|
||||
int old_last_cleaned = last_cleaned;
|
||||
do {
|
||||
var (pending_queries', i, _, f) = pending_queries.udict_delete_get_min(64);
|
||||
f~touch();
|
||||
if (f) {
|
||||
f = (i < bound);
|
||||
}
|
||||
if (f) {
|
||||
pending_queries = pending_queries';
|
||||
last_cleaned = i;
|
||||
need_save = -1;
|
||||
}
|
||||
} until (~ f);
|
||||
|
||||
if (need_save) {
|
||||
set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n));
|
||||
}
|
||||
}
|
||||
|
||||
;; 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();
|
||||
(slice cs, var found) = pending_queries.udict_get?(64, query_id);
|
||||
if (found) {
|
||||
if (cs~load_int(1)) {
|
||||
cs~load_uint(8);
|
||||
return (0, cs~load_uint(n));
|
||||
} else {
|
||||
return (-1, 0);
|
||||
}
|
||||
} else {
|
||||
return (-(query_id <= last_cleaned), 0);
|
||||
}
|
||||
}
|
||||
|
||||
int processed?(int query_id) method_id {
|
||||
(int x, _) = get_query_state(query_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 merge_list(cell a, cell b) {
|
||||
if (cell_null?(a)) {
|
||||
return b;
|
||||
}
|
||||
if (cell_null?(b)) {
|
||||
return a;
|
||||
}
|
||||
slice as = a.begin_parse();
|
||||
if (as.slice_refs() != 0) {
|
||||
cell tail = merge_list(as~load_ref(), b);
|
||||
return begin_cell().store_slice(as).store_ref(tail).end_cell();
|
||||
}
|
||||
|
||||
as~skip_last_bits(1);
|
||||
;; as~skip_bits(1);
|
||||
return begin_cell().store_slice(as).store_dict(b).end_cell();
|
||||
|
||||
}
|
||||
|
||||
cell get_public_keys() method_id {
|
||||
(_, _, _, cell public_keys, _) = unpack_state();
|
||||
return public_keys;
|
||||
}
|
||||
|
||||
(int, int) check_query_signatures(cell query) method_id {
|
||||
slice cs = query.begin_parse();
|
||||
slice root_signature = cs~load_bits(512);
|
||||
int root_hash = slice_hash(cs);
|
||||
int root_i = cs~load_uint(8);
|
||||
|
||||
cell public_keys = get_public_keys();
|
||||
(slice public_key, var found?) = public_keys.udict_get?(8, root_i);
|
||||
throw_unless(31, found?);
|
||||
throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256)));
|
||||
|
||||
int mask = 1 << root_i;
|
||||
|
||||
cell signatures = cs~load_dict();
|
||||
if (cell_null?(signatures)) {
|
||||
return (1, mask);
|
||||
}
|
||||
(int cnt, mask) = check_signatures(public_keys, signatures, slice_hash(cs), mask);
|
||||
return (cnt + 1, mask);
|
||||
}
|
||||
|
||||
cell messages_by_mask(int mask) method_id {
|
||||
(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);
|
||||
if (cnt_bits & mask) {
|
||||
a~udict_set_builder(64, i, begin_cell().store_slice(cs));
|
||||
}
|
||||
}
|
||||
}
|
||||
} until (~ f);
|
||||
return a;
|
||||
}
|
||||
|
||||
cell get_messages_unsigned_by_id(int id) method_id {
|
||||
return messages_by_mask(1 << id);
|
||||
}
|
||||
|
||||
cell get_messages_unsigned() method_id {
|
||||
return messages_by_mask(~ 0);
|
||||
}
|
||||
|
||||
(int, int) get_n_k() method_id {
|
||||
(int n, int k, _, _, _) = unpack_state();
|
||||
return (n, k);
|
||||
}
|
||||
|
||||
cell merge_inner_queries(cell a, cell b) method_id {
|
||||
slice ca = a.begin_parse();
|
||||
slice cb = b.begin_parse();
|
||||
cell list_a = ca~load_dict();
|
||||
cell list_b = cb~load_dict();
|
||||
throw_unless(31, slice_hash(ca) == slice_hash(cb));
|
||||
return begin_cell()
|
||||
.store_dict(merge_list(list_a, list_b))
|
||||
.store_slice(ca)
|
||||
.end_cell();
|
||||
}
|
67
crypto/smartcont/simple-wallet-ext-code.fc
Normal file
67
crypto/smartcont/simple-wallet-ext-code.fc
Normal file
|
@ -0,0 +1,67 @@
|
|||
;; Simple wallet smart contract
|
||||
|
||||
cell create_state(int seqno, int public_key) {
|
||||
return begin_cell().store_uint(seqno, 32).store_uint(public_key, 256).end_cell();
|
||||
}
|
||||
|
||||
(int, int) load_state() {
|
||||
var cs2 = begin_parse(get_data());
|
||||
return (cs2~load_uint(32), cs2~load_uint(256));
|
||||
}
|
||||
|
||||
() save_state(int seqno, int public_key) impure {
|
||||
set_data(create_state(seqno, public_key));
|
||||
}
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
slice do_verify_message(slice in_msg, int seqno, int public_key) {
|
||||
var signature = in_msg~load_bits(512);
|
||||
var cs = in_msg;
|
||||
int msg_seqno = cs~load_uint(32);
|
||||
throw_unless(33, msg_seqno == seqno);
|
||||
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
return cs;
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
(int stored_seqno, int public_key) = load_state();
|
||||
var cs = do_verify_message(in_msg, stored_seqno, public_key);
|
||||
accept_message();
|
||||
cs~touch_slice();
|
||||
if (cs.slice_refs()) {
|
||||
var mode = cs~load_uint(8);
|
||||
send_raw_message(cs~load_ref(), mode);
|
||||
}
|
||||
cs.end_parse();
|
||||
save_state(stored_seqno + 1, public_key);
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
|
||||
int seqno() method_id {
|
||||
return get_data().begin_parse().preload_uint(32);
|
||||
}
|
||||
|
||||
cell create_init_state(int public_key) method_id {
|
||||
return create_state(0, public_key);
|
||||
}
|
||||
|
||||
cell prepare_send_message_with_seqno(int mode, cell msg, int seqno) method_id {
|
||||
return begin_cell().store_uint(seqno, 32).store_uint(mode, 8).store_ref(msg).end_cell();
|
||||
}
|
||||
|
||||
cell prepare_send_message(int mode, cell msg) method_id {
|
||||
return prepare_send_message_with_seqno(mode, msg, seqno());
|
||||
}
|
||||
|
||||
|
||||
slice verify_message(slice msg) method_id {
|
||||
var (stored_seqno, public_key) = load_state();
|
||||
return do_verify_message(msg, stored_seqno, public_key);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -16,6 +16,7 @@ forall X -> X first(tuple t) asm "FIRST";
|
|||
forall X -> X second(tuple t) asm "SECOND";
|
||||
forall X -> X third(tuple t) asm "THIRD";
|
||||
forall X -> X fourth(tuple t) asm "3 INDEX";
|
||||
forall X -> X null() asm "PUSHNULL";
|
||||
|
||||
int now() asm "NOW";
|
||||
slice my_address() asm "MYADDR";
|
||||
|
@ -34,8 +35,10 @@ int check_data_signature(slice data, slice signature, int public_key) asm "CHKSI
|
|||
|
||||
cell get_data() asm "c4 PUSH";
|
||||
() set_data(cell c) impure asm "c4 POP";
|
||||
cell get_c3() impure asm "c3 PUSH";
|
||||
() set_c3(cell c) impure asm "c3 POP";
|
||||
() accept_message() impure asm "ACCEPT";
|
||||
() commit() impure asm "COMMIT";
|
||||
|
||||
int min(int x, int y) asm "MIN";
|
||||
int max(int x, int y) asm "MAX";
|
||||
|
@ -52,7 +55,10 @@ cell preload_ref(slice s) asm "PLDREF";
|
|||
;; slice preload_bits(slice s, int len) asm "PLDSLICEX";
|
||||
(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS";
|
||||
slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
|
||||
(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST";
|
||||
slice first_bits(slice s, int len) asm "SDCUTFIRST";
|
||||
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
|
||||
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
|
||||
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
|
||||
cell preload_dict(slice s) asm "PLDDICT";
|
||||
slice skip_dict(slice s) asm "SKIPDICT";
|
||||
|
@ -94,6 +100,16 @@ cell udict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "D
|
|||
(cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
|
||||
(cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
|
||||
(cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT";
|
||||
cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
|
||||
(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET";
|
||||
cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
|
||||
(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET";
|
||||
cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
|
||||
(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET";
|
||||
(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD";
|
||||
(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE";
|
||||
(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD";
|
||||
(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE";
|
||||
cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
|
||||
(cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB";
|
||||
cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue