-
-
Save EmelyanenkoK/7972b8981493a2e68a881a9bd98ff9de to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/fift -s | |
"TonUtil.fif" include | |
"Asm.fif" include | |
"retranslator-code.fif" include =: contract_code | |
"wallet-retranslator.pk" load-generate-keypair | |
=: privkey | |
=: pubkey | |
<b 0 16 u, 0 32 u, pubkey B, contract_code .s ref, b> =: contract_storage | |
<b b{00110} s, contract_code ref, contract_storage ref, b> | |
dup =: state_init | |
dup hashu 0 swap 2constant contract_address | |
."new retranslator wallet address = " contract_address .addr cr | |
."Non-bounceable address (for init): " contract_address 7 .Addr cr | |
."Bounceable address (for later access): " contract_address 6 .Addr cr | |
<b | |
0 32 u, now 40 + 32 u, 0 32 u, | |
255 8 u, // mode = retranslate | |
1 8 u, // one chain | |
20000 16 u, // hops | |
5 8 u, // split hops | |
99 Gram* Gram, // first message size | |
30000 16 u, // preference | |
b> =: init_message | |
init_message ."signing message: " <s csr. cr | |
init_message hashu privkey ed25519_sign_uint =: signature | |
<b b{1000100} s, contract_address addr, b{000010} s, state_init <s s, b{0} s, signature B, init_message <s s, b> | |
dup ."External message for initialization is " <s csr. cr | |
2 boc+>B dup Bx. cr | |
"query-init-retranslator.boc" tuck B>file | |
."(Saved retranslator contract creating query to file " type .")" cr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "stdlib.func"; | |
{- | |
_# id:uint16 seqno:uint32 public_key:uint256 code:^Cell = Storage; | |
-} | |
const workchain = 0; | |
const max_retranslators = 2048; | |
const max_shard_depth = 2; | |
const preference_base = 65535; | |
const SPAM_CONFIG = -137; | |
(int, int, int, cell) load_data () inline { | |
slice ds = get_data().begin_parse(); | |
return (ds~load_uint(16), ds~load_uint(32), ds~load_uint(256), ds.preload_ref()); | |
} | |
cell pack_data(int id, int seqno, int public_key, cell code) inline { | |
return begin_cell() | |
.store_uint(id, 16) | |
.store_uint(seqno, 32) | |
.store_uint(public_key, 256) | |
.store_ref(code) | |
.end_cell(); | |
} | |
cell calc_state_init (int id, int public_key, cell code) inline { | |
return begin_cell() | |
.store_uint(0,2) | |
.store_dict(code) | |
.store_dict(pack_data(id, 0, public_key, code)) | |
.store_uint(0,1) | |
.end_cell(); | |
} | |
slice calc_address(cell init_state) inline { | |
return begin_cell().store_uint(4, 3) | |
.store_int(workchain, 8) | |
.store_uint( | |
cell_hash(init_state), 256) | |
.end_cell() | |
.begin_parse(); | |
} | |
() retranslate (int preference, int remaining_hops, int split_hops, int id, int public_key, cell code, slice payload, int amount) impure { | |
int next_hop = id; ;; send itself by default | |
randomize_lt(); | |
if( amount | ((random() % preference_base) >= preference)) { | |
next_hop = random() % max_retranslators; | |
} else { | |
(_, int my_addr) = parse_std_addr(my_address()); | |
int my_shard = my_addr >> (256 - max_shard_depth); | |
int not_found? = true; | |
while not_found? { | |
next_hop = random() % max_retranslators; | |
cell next_hop_initstate = calc_state_init(next_hop, public_key, code); | |
int next_hop_shard = cell_hash(next_hop_initstate) >> (256 - max_shard_depth); | |
not_found? = ~(my_shard == next_hop_shard); | |
} | |
} | |
cell next_hop_initstate = calc_state_init(next_hop, public_key, code); | |
slice next_hop_address = calc_address(next_hop_initstate); | |
var msg = begin_cell() | |
.store_uint(0x18, 6) | |
.store_slice(next_hop_address) | |
.store_coins(amount > 0 ? amount : 0) | |
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1) | |
.store_ref(next_hop_initstate); | |
var msg_body = begin_cell() | |
.store_uint(id, 16) | |
.store_uint(remaining_hops, 16) | |
.store_uint(split_hops, 8) | |
.store_uint(preference, 16) | |
.store_slice(payload) | |
.end_cell(); | |
msg = msg.store_ref(msg_body); | |
send_raw_message(msg.end_cell(), amount ? ( amount > 0 ? 3 : (128 + 2)) : 64); ;; revert on errors | |
} | |
() check_config() impure inline { | |
cell config = config_param(SPAM_CONFIG); | |
if (config.cell_null?()) { | |
return (); | |
} | |
throw_if(666, config.begin_parse().preload_uint(16) == 1); | |
} | |
{- | |
retransalate#_ id:uint16 remaining_hops:uint16 split_hops:uint8 preference:uint16 payload:Cell = IntMsgBody; | |
-} | |
() main (int msg_value, cell in_msg_full, slice in_msg_body) { | |
if (in_msg_body.slice_empty?()) { ;; ignore empty messages | |
return (); | |
} | |
check_config(); | |
slice cs = in_msg_full.begin_parse(); | |
int flags = cs~load_uint(4); | |
if (flags & 1) { | |
return (); | |
} | |
slice sender_address = cs~load_msg_addr(); | |
int sender_id = in_msg_body~load_uint(16); | |
(int id, _, int public_key, cell code) = load_data(); | |
throw_unless(401, equal_slice_bits( | |
calc_address(calc_state_init(sender_id, public_key, code)), | |
sender_address)); | |
int remaining_hops = in_msg_body~load_uint(16); | |
int split_hops = in_msg_body~load_uint(8); | |
int preference = in_msg_body~load_uint(16); | |
ifnot(remaining_hops) { | |
return (); | |
} | |
remaining_hops -= 1; | |
if(split_hops) { | |
split_hops -= 1; | |
;; If there were some money on balance (for intstance from previous tests) | |
;; do not use it in current retranslation | |
raw_reserve(pair_first(get_balance()) - msg_value, 2); | |
msg_value -= 27372000; ;; gas + fwd, it will not make division by 2 ideal, but good enough | |
retranslate(preference, remaining_hops, split_hops, id, public_key, code, in_msg_body, msg_value / 2); | |
retranslate(preference, remaining_hops, split_hops, id, public_key, code, in_msg_body, -1); | |
} else { | |
retranslate(preference, remaining_hops, split_hops, id, public_key, code, in_msg_body, 0); | |
} | |
} | |
() recv_external(slice in_msg) impure { | |
var signature = in_msg~load_bits(512); | |
var cs = in_msg; | |
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); | |
throw_if(35, valid_until <= now()); | |
var (id, stored_seqno, public_key, code) = load_data(); | |
throw_unless(33, msg_seqno == stored_seqno); | |
throw_unless(34, subwallet_id == id); | |
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); | |
accept_message(); | |
var mode = cs~load_uint(8); | |
if(mode == 255) { | |
(int cnt, | |
int hops, | |
int split_hops, | |
int amount, | |
int preference) = (cs~load_uint(8), | |
cs~load_uint(16), | |
cs~load_uint(8), | |
cs~load_coins(), | |
cs~load_uint(16)); | |
while (cnt > 0) { | |
retranslate(preference, hops, split_hops, id, public_key, code, cs, amount); | |
cnt -= 1; | |
} | |
} else { | |
send_raw_message(cs~load_ref(), mode); | |
} | |
set_data(pack_data(id, stored_seqno + 1, public_key, code)); | |
} | |
;; Get methods | |
int seqno() method_id { | |
var (id, stored_seqno, public_key, code) = load_data(); | |
return stored_seqno; | |
} | |
int get_public_key() method_id { | |
var (id, stored_seqno, public_key, code) = load_data(); | |
return public_key; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment