mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
updated submodules, bugfixes
- added new fift/func code for validator complaint creation - bugfixes in validator - updates in tonlib - new versions of rocksdb/abseil - hardfork support
This commit is contained in:
parent
16a4566091
commit
9f008b129f
129 changed files with 8438 additions and 879 deletions
|
@ -62,15 +62,31 @@ variable validators-weight
|
|||
validators-weight 0!
|
||||
|
||||
{ validator-dict @ second } : validator#
|
||||
// val-pubkey weight --
|
||||
{ dup 0<= abort"validator weight must be non-negative"
|
||||
dup 64 ufits not abort"validator weight must fit into 64 bits"
|
||||
64 ufits not abort"validator weight must fit into 64 bits"
|
||||
} : check-val-weight
|
||||
// ( val-pubkey weight -- c )
|
||||
{ dup check-val-weight
|
||||
over Blen 32 <> abort"validator public key must be 32 bytes long"
|
||||
tuck <b x{538e81278a} s, rot B, swap 64 u, b> <s
|
||||
validator-dict 2@ dup 1+ 3 -roll swap
|
||||
<b x{538e81278a} s, rot B, swap 64 u, b>
|
||||
} : serialize-validator
|
||||
// ( val-pubkey adnl weight -- c )
|
||||
{ dup check-val-weight
|
||||
over 256 ufits not abort"adnl address must fit into 256 bits"
|
||||
rot dup Blen 32 <> abort"validator public key must be 32 bytes long"
|
||||
<b x{738e81278a} s, swap B, swap 64 u, swap 256 u, b>
|
||||
} : serialize-adnl-validator
|
||||
// ( weight val-cell -- )
|
||||
{ swap validators-weight +!
|
||||
<s validator-dict 2@ dup 1+ 3 -roll swap
|
||||
16 udict!+ 0= abort"cannot add validator"
|
||||
swap validator-dict 2! validators-weight +!
|
||||
} : add-validator
|
||||
swap validator-dict 2!
|
||||
} : register-validator
|
||||
// val-pubkey weight --
|
||||
{ tuck serialize-validator register-validator } : add-validator
|
||||
// val-pubkey adnl-addr weight --
|
||||
{ -rot 2 pick serialize-adnl-validator register-validator
|
||||
} : add-adnl-validator
|
||||
// since-ut until-ut main-val-cnt-or-0 --
|
||||
{ ?dup 0= { validator# } if
|
||||
validator# 0= abort"no initial validators defined"
|
||||
|
@ -230,21 +246,24 @@ variable special-dict
|
|||
} : create-wallet1
|
||||
|
||||
// D x t -- D'
|
||||
{ <b rot Gram, swap rot 16 b>idict! not abort"cannot add value"
|
||||
{ <b rot Gram, swap rot 32 b>idict! not abort"cannot add value"
|
||||
} : rdict-entry
|
||||
{ 86400 * } : days*
|
||||
// balance -- dict
|
||||
{ dictnew
|
||||
over -32768 rdict-entry
|
||||
over 3/4 */ 92 rdict-entry
|
||||
over 1/2 */ 183 rdict-entry
|
||||
swap 1/4 */ 366 rdict-entry
|
||||
0 548 rdict-entry
|
||||
over 31 -1<< rdict-entry
|
||||
over 3/4 */ 91 days* rdict-entry
|
||||
over 1/2 */ 183 days* rdict-entry
|
||||
swap 1/4 */ 365 days* rdict-entry
|
||||
0 548 days* rdict-entry
|
||||
} : make-rdict
|
||||
|
||||
variable wallet2-start-at wallet2-start-at 0!
|
||||
now 86400 / 1+ 86400 * wallet2-start-at !
|
||||
// pubkey amount --
|
||||
{ over ."Key " pubkey>$ type ." -> "
|
||||
RWCode2 // code
|
||||
<b 1 32 u, 3 pick 256 u, 3 roll make-rdict dict, b> // data
|
||||
<b 1 32 u, 3 pick 256 u, 3 roll wallet2-start-at @ 32 u, make-rdict dict, b> // data
|
||||
empty_cell // libs
|
||||
3 roll // balance
|
||||
0 // split_depth
|
||||
|
|
29
crypto/smartcont/complaint-vote-req.fif
Normal file
29
crypto/smartcont/complaint-vote-req.fif
Normal file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
"GetOpt.fif" include
|
||||
|
||||
"validator-to-sign.req" =: savefile
|
||||
|
||||
{ ."usage: " @' $0 type ." <validator-idx> <elect-id> <complaint-hash> [<savefile>]" cr
|
||||
."Creates an unsigned request to vote for complaint <complaint-hash> (decimal; prefix with '0x' if needed) of past validator set <elect-id> on behalf of validator with zero-based index <validator-idx> in current validator set (as stored in configuration parameter 34)." cr
|
||||
."The result is saved into <savefile> (" savefile type ." by default) and output in hexadecimal form, to be signed later by the validator public key" cr 1 halt
|
||||
} : usage
|
||||
|
||||
$# dup 3 < swap 4 > or ' usage if
|
||||
4 :$1..n
|
||||
|
||||
$1 parse-int dup =: val-idx
|
||||
16 ufits not abort"validator index out of range"
|
||||
$2 parse-int dup =: elect-id
|
||||
32 ufits not abort"invalid election id"
|
||||
$3 parse-int dup =: compl-hash
|
||||
256 ufits not abort"invalid complaint hash"
|
||||
$4 savefile replace-if-null =: savefile
|
||||
|
||||
."Creating a request to vote for complaint 0x" compl-hash 64x. ."of past validator set " elect-id .
|
||||
."on behalf of current validator with index " val-idx . cr
|
||||
|
||||
B{56744350} val-idx 16 u>B B+ elect-id 32 u>B B+ compl-hash 256 u>B B+
|
||||
dup Bx. cr
|
||||
dup B>base64url type cr
|
||||
savefile tuck B>file ."Saved to file " type cr
|
44
crypto/smartcont/complaint-vote-signed.fif
Normal file
44
crypto/smartcont/complaint-vote-signed.fif
Normal file
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
"GetOpt.fif" include
|
||||
|
||||
"vote-query.boc" =: savefile
|
||||
|
||||
{ ."usage: " @' $0 type ." <validator-idx> <elect-id> <complaint-hash> <validator-pubkey> <validator-signature> [<savefile>]" cr
|
||||
."Creates an internal message body to be sent from any smart contract residing in the masterchain to the elections smart contract containing a signed request to vote for complaint <complaint-hash> (decimal; prefix with '0x' if needed) of past validator set <elect-id> on behalf of current validator with zero-based index <validator-idx> and (Base64) public key <validator-pubkey> in current validator set (as stored in configuration parameter 34)" cr
|
||||
."<validator-signature> must be the base64 representation of Ed25519 signature of the previously generated unsigned request by means of <validator-pubkey>" cr
|
||||
."The result is saved into <savefile> (" savefile type ." by default), to be embedded later into an internal message" cr 1 halt
|
||||
} : usage
|
||||
|
||||
$# dup 5 < swap 6 > or ' usage if
|
||||
6 :$1..n
|
||||
|
||||
$1 parse-int dup =: val-idx
|
||||
16 ufits not abort"validator index out of range"
|
||||
$2 parse-int dup =: elect-id
|
||||
32 ufits not abort"invalid election id"
|
||||
$3 parse-int dup =: compl-hash
|
||||
256 ufits not abort"invalid complaint hash"
|
||||
$4 base64>B dup Blen 36 <> abort"validator Ed25519 public key must be exactly 36 bytes long"
|
||||
32 B>u@+ 0xC6B41348 <> abort"invalid Ed25519 public key: unknown magic number"
|
||||
=: pubkey
|
||||
$5 base64>B dup Blen 64 <> abort"validator Ed25519 signature must be exactly 64 bytes long"
|
||||
=: signature
|
||||
$6 savefile replace-if-null =: savefile
|
||||
|
||||
."Creating the body of an internal message to be sent to the elections smart contract" cr }
|
||||
."containing a signed request to vote for complaint 0x" compl-hash 64x. ."of past validator set " elect-id .
|
||||
."on behalf of current validator with index " val-idx . "and public key" pubkey Bx. cr
|
||||
|
||||
B{56744350} val-idx 16 u>B B+ elect-id 32 u>B B+ compl-hash 256 u>B B+ dup =: to_sign
|
||||
."String to sign is " Bx. cr
|
||||
|
||||
to_sign signature pubkey ed25519_chksign not abort"Ed25519 signature is invalid"
|
||||
."Provided a valid Ed25519 signature " signature Bx. ." with validator public key " pubkey Bx. cr
|
||||
|
||||
now 32 << compl-hash 32 1<< mod + =: query-id
|
||||
|
||||
<b x{56744370} s, query-id 64 u, signature B, to_sign B, b>
|
||||
."Internal message body is " dup <s csr. cr
|
||||
|
||||
2 boc+>B savefile tuck B>file ."Saved to file " type cr
|
|
@ -1097,8 +1097,21 @@ tuple past_elections() method_id {
|
|||
do {
|
||||
(id, var fs, var found) = past_elections.udict_get_prev?(32, id);
|
||||
if (found) {
|
||||
var info = [unpack_past_election(fs)];
|
||||
list = cons(pair(id, info), list);
|
||||
list = cons([id, unpack_past_election(fs)], list);
|
||||
}
|
||||
} until (~ found);
|
||||
return list;
|
||||
}
|
||||
|
||||
tuple past_elections_list() method_id {
|
||||
var (elect, credits, past_elections, grams, active_id, active_hash) = load_data();
|
||||
var id = (1 << 32);
|
||||
var list = null();
|
||||
do {
|
||||
(id, var fs, var found) = past_elections.udict_get_prev?(32, id);
|
||||
if (found) {
|
||||
var (unfreeze_at, stake_held, vset_hash, frozen_dict, total_stake, bonuses, complaints) = unpack_past_election(fs);
|
||||
list = cons([id, unfreeze_at, vset_hash, stake_held], list);
|
||||
}
|
||||
} until (~ found);
|
||||
return list;
|
||||
|
@ -1117,7 +1130,7 @@ _ complete_unpack_complaint(slice cs) inline_ref {
|
|||
return [[complaint.begin_parse().unpack_complaint()], voters_list, vset_id, weight_remaining];
|
||||
}
|
||||
|
||||
cell get_past_complaints(int election_id) inline_ref {
|
||||
cell get_past_complaints(int election_id) inline_ref method_id {
|
||||
var (elect, credits, past_elections, grams, active_id, active_hash) = load_data();
|
||||
var (fs, found?) = past_elections.udict_get?(32, election_id);
|
||||
ifnot (found?) {
|
||||
|
@ -1145,3 +1158,11 @@ tuple list_complaints(int election_id) method_id {
|
|||
} until (~ found?);
|
||||
return list;
|
||||
}
|
||||
|
||||
int complaint_storage_price(int bits, int refs, int expire_in) method_id {
|
||||
;; compute complaint storage/creation price
|
||||
var (deposit, bit_price, cell_price) = get_complaint_prices();
|
||||
var pps = (bits + 1024) * bit_price + (refs + 2) * cell_price;
|
||||
var paid = pps * expire_in + deposit;
|
||||
return paid + (1 << 30);
|
||||
}
|
||||
|
|
50
crypto/smartcont/envelope-complaint.fif
Normal file
50
crypto/smartcont/envelope-complaint.fif
Normal file
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/fift -s
|
||||
"TonUtil.fif" include
|
||||
"GetOpt.fif" include
|
||||
|
||||
{ show-options-help 1 halt } : usage
|
||||
86400 3 * =: expire-in
|
||||
false =: critical
|
||||
-1 =: old-hash
|
||||
|
||||
begin-options
|
||||
" <election-id> <complaint-boc> [-x <expire-in>] [<savefile>]" +cr +tab
|
||||
+"Embeds a validator complaint loaded from file <complaint-boc> into an internal message body to be sent to the elector smart contract "
|
||||
+"and saves it as an internal message body into <savefile>.boc ('complaint-msg-body.boc' by default)"
|
||||
disable-digit-options generic-help-setopt
|
||||
"x" "--expires-in" { parse-int =: expire-in } short-long-option-arg
|
||||
"Sets complaint expiration time in seconds (default " expire-in (.) $+ +")" option-help
|
||||
"h" "--help" { usage } short-long-option
|
||||
"Shows a help message" option-help
|
||||
parse-options
|
||||
|
||||
$# dup 2 < swap 3 > or ' usage if
|
||||
3 :$1..n
|
||||
|
||||
$1 parse-int dup =: election-id
|
||||
32 ufits not abort"invalid election id"
|
||||
$2 =: boc-filename
|
||||
$3 "complaint-msg-body.boc" replace-if-null =: savefile
|
||||
expire-in now + =: expire-at
|
||||
|
||||
boc-filename dup ."Loading complaint from file `" type ."`" cr
|
||||
file>B B>boc dup =: complaint hash =: c-hash
|
||||
complaint <s 8 u@ 0xbc <> abort"Not a valid ValidatorComplaint"
|
||||
complaint <s csr.
|
||||
|
||||
." complaint envelope will expire at " expire-at . ."(in " expire-in . ."seconds)" cr
|
||||
now 32 << c-hash 0xffffffff and or =: query-id
|
||||
."Query id is " query-id . cr
|
||||
|
||||
<b x{52674370} s, query-id 64 u, election-id 32 u, expire-at 32 u,
|
||||
complaint <s s, b>
|
||||
|
||||
dup ."resulting internal message body: " <s csr. cr
|
||||
2 boc+>B dup Bx. cr
|
||||
|
||||
complaint
|
||||
totalcsize swap ."(a total of " . ."data bits, " . ."cell references -> "
|
||||
drop dup Blen . ."BoC data bytes)" cr
|
||||
|
||||
savefile tuck B>file
|
||||
."(Saved to file " type .")" cr
|
|
@ -29,8 +29,8 @@ def? $3 { @' $3 } { "rwallet" } cond constant file-base
|
|||
} : make-rdict
|
||||
|
||||
// Create new restricted wallet; code taken from `auto/restricted-wallet2-code.fif`
|
||||
"auto/restricted-wallet-code.fif" include // code
|
||||
<b 0 32 u, PubKey 256 u, amount make-rdict dict, b> // data
|
||||
"auto/restricted-wallet2-code.fif" include // code
|
||||
<b 0 32 u, PubKey 256 u, 0 32 u, amount make-rdict dict, b> // data
|
||||
null // no libraries
|
||||
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
|
||||
dup ."StateInit: " <s csr. cr
|
||||
|
|
315
crypto/smartcont/payment-channel-code.fc
Normal file
315
crypto/smartcont/payment-channel-code.fc
Normal file
|
@ -0,0 +1,315 @@
|
|||
;; WARINIG: NOT READY FOR A PRODUCTION!
|
||||
|
||||
int err:wrong_a_signature() asm "31 PUSHINT";
|
||||
int err:wrong_b_signature() asm "32 PUSHINT";
|
||||
int err:msg_value_too_small() asm "33 PUSHINT";
|
||||
int err:replay_protection() asm "34 PUSHINT";
|
||||
int err:no_timeout() asm "35 PUSHINT";
|
||||
int err:expected_init() asm "36 PUSHINT";
|
||||
int err:expected_close() asm "37 PUSHINT";
|
||||
int err:no_promise_signature() asm "38 PUSHINT";
|
||||
int err:wrong_channel_id() asm "39 PUSHINT";
|
||||
|
||||
int msg:init() asm "0x27317822 PUSHINT";
|
||||
int msg:close() asm "0xf28ae183 PUSHINT";
|
||||
int msg:timeout() asm "0x43278a28 PUSHINT";
|
||||
int msg:payout() asm "0x37fe7810 PUSHINT";
|
||||
|
||||
int state:init() asm "0 PUSHINT";
|
||||
int state:close() asm "1 PUSHINT";
|
||||
int state:payout() asm "2 PUSHINT";
|
||||
|
||||
|
||||
;; A - initial balance of Alice,
|
||||
;; B - initial balance of B
|
||||
;;
|
||||
;; To determine balance we track nondecreasing list of promises
|
||||
;; promise_A ;; promised by Alice to Bob
|
||||
;; promise_B ;; promised by Bob to Alice
|
||||
;;
|
||||
;; diff - balance between Alice and Bob. 0 in the beginning
|
||||
;; diff = promise_B - promise_A;
|
||||
;; diff = clamp(diff, -A, +B);
|
||||
;;
|
||||
;; final_A = A + diff;
|
||||
;; final_B = B + diff;
|
||||
|
||||
;; Data pack/unpack
|
||||
;;
|
||||
_ unpack_data() inline_ref {
|
||||
var cs = get_data().begin_parse();
|
||||
var res = (cs~load_ref(), cs~load_ref());
|
||||
cs.end_parse();
|
||||
return res;
|
||||
}
|
||||
|
||||
_ pack_data(cell config, cell state) impure inline_ref {
|
||||
set_data(begin_cell().store_ref(config).store_ref(state).end_cell());
|
||||
}
|
||||
|
||||
;; Config pack/unpack
|
||||
;;
|
||||
;; config$_ initTimeout:int exitTimeout:int a_key:int256 b_key:int256 a_addr b_addr channel_id:uint64 = Config;
|
||||
;;
|
||||
_ unpack_config(cell config) {
|
||||
var cs = config.begin_parse();
|
||||
var res = (
|
||||
cs~load_uint(32),
|
||||
cs~load_uint(32),
|
||||
cs~load_uint(256),
|
||||
cs~load_uint(256),
|
||||
cs~load_ref().begin_parse(),
|
||||
cs~load_ref().begin_parse(),
|
||||
cs~load_uint(64));
|
||||
cs.end_parse();
|
||||
return res;
|
||||
}
|
||||
|
||||
;; takes
|
||||
;; signedMesage$_ a_sig:Maybe<int256> b_sig:Maybe<int256> msg:Message = SignedMessage;
|
||||
;; checks signatures and unwap message.
|
||||
(slice, (int, int)) unwrap_signatures(slice cs, int a_key, int b_key) {
|
||||
int a? = cs~load_int(1);
|
||||
slice a_sig = cs;
|
||||
if (a?) {
|
||||
a_sig = cs~load_ref().begin_parse().preload_bits(512);
|
||||
}
|
||||
var b? = cs~load_int(1);
|
||||
slice b_sig = cs;
|
||||
if (b?) {
|
||||
b_sig = cs~load_ref().begin_parse().preload_bits(512);
|
||||
}
|
||||
int hash = cs.slice_hash();
|
||||
if (a?) {
|
||||
throw_unless(err:wrong_a_signature(), check_signature(hash, a_sig, a_key));
|
||||
}
|
||||
if (b?) {
|
||||
throw_unless(err:wrong_b_signature(), check_signature(hash, b_sig, b_key));
|
||||
}
|
||||
return (cs, (a?, b?));
|
||||
}
|
||||
|
||||
;; process message, give state is stateInit
|
||||
;;
|
||||
;; stateInit signed_A?:Bool signed_B?:Bool min_A:Grams min_B:Grams expire_at:uint32 A:Grams B:Grams = State;
|
||||
_ unpack_state_init(slice state) {
|
||||
return (
|
||||
state~load_int(1),
|
||||
state~load_int(1),
|
||||
state~load_grams(),
|
||||
state~load_grams(),
|
||||
state~load_uint(32),
|
||||
state~load_grams(),
|
||||
state~load_grams());
|
||||
|
||||
}
|
||||
_ pack_state_init(int signed_A?, int signed_B?, int min_A, int min_B, int expire_at, int A, int B) {
|
||||
return begin_cell()
|
||||
.store_int(state:init(), 3)
|
||||
.store_int(signed_A?, 1)
|
||||
.store_int(signed_B?, 1)
|
||||
.store_grams(min_A)
|
||||
.store_grams(min_B)
|
||||
.store_uint(expire_at, 32)
|
||||
.store_grams(A)
|
||||
.store_grams(B).end_cell();
|
||||
}
|
||||
|
||||
;; stateClosing$10 signed_A?:bool signed_B?:Bool promise_A:Grams promise_B:Grams exipire_at:uint32 A:Grams B:Grams = State;
|
||||
_ unpack_state_close(slice state) {
|
||||
return (
|
||||
state~load_int(1),
|
||||
state~load_int(1),
|
||||
state~load_grams(),
|
||||
state~load_grams(),
|
||||
state~load_uint(32),
|
||||
state~load_grams(),
|
||||
state~load_grams());
|
||||
}
|
||||
|
||||
_ pack_state_close(int signed_A?, int signed_B?, int promise_A, int promise_B, int expire_at, int A, int B) {
|
||||
return begin_cell()
|
||||
.store_int(state:close(), 3)
|
||||
.store_int(signed_A?, 1)
|
||||
.store_int(signed_B?, 1)
|
||||
.store_grams(promise_A)
|
||||
.store_grams(promise_B)
|
||||
.store_uint(expire_at, 32)
|
||||
.store_grams(A)
|
||||
.store_grams(B).end_cell();
|
||||
}
|
||||
|
||||
_ send_payout(slice s_addr, int amount, int channel_id, int flags) impure {
|
||||
send_raw_message(begin_cell()
|
||||
.store_uint(0x10, 6)
|
||||
.store_slice(s_addr)
|
||||
.store_grams(amount)
|
||||
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
||||
.store_uint(msg:payout(), 32)
|
||||
.store_uint(channel_id, 64)
|
||||
.end_cell(), flags);
|
||||
}
|
||||
|
||||
|
||||
cell do_payout(int promise_A, int promise_B, int A, int B, slice a_addr, slice b_addr, int channel_id) impure {
|
||||
accept_message();
|
||||
|
||||
int diff = promise_B - promise_A;
|
||||
if (diff < - A) {
|
||||
diff = - A;
|
||||
}
|
||||
if (diff > B) {
|
||||
diff = B;
|
||||
}
|
||||
A += diff;
|
||||
B -= diff;
|
||||
|
||||
send_payout(a_addr, A, channel_id, 3);
|
||||
send_payout(b_addr, B, channel_id, 3);
|
||||
|
||||
return begin_cell()
|
||||
.store_int(state:payout(), 3)
|
||||
.store_grams(A)
|
||||
.store_grams(B)
|
||||
.end_cell();
|
||||
}
|
||||
|
||||
|
||||
;;
|
||||
;; init$000 inc_A:Grams inc_B:Grams min_A:Grams min_B:Grams = Message;
|
||||
;;
|
||||
cell with_init(slice state, int msg_value, slice msg, int msg_signed_A?, int msg_signed_B?,
|
||||
slice a_addr, slice b_addr, int init_timeout, int channel_id) {
|
||||
;; parse state
|
||||
(int signed_A?, int signed_B?, int min_A, int min_B, int expire_at, int A, int B) = unpack_state_init(state);
|
||||
|
||||
if (expire_at == 0) {
|
||||
expire_at = now() + init_timeout;
|
||||
}
|
||||
|
||||
int op = msg~load_uint(32);
|
||||
if (op == msg:timeout()) {
|
||||
throw_unless(err:no_timeout(), expire_at < now());
|
||||
return do_payout(0, 0, A, B, a_addr, b_addr, channel_id);
|
||||
}
|
||||
throw_unless(err:expected_init(), op == msg:init());
|
||||
|
||||
;; unpack init message
|
||||
(int inc_A, int inc_B, int upd_min_A, int upd_min_B, int got_channel_id) =
|
||||
(msg~load_grams(), msg~load_grams(), msg~load_grams(), msg~load_grams(), msg~load_uint(64));
|
||||
throw_unless(err:wrong_channel_id(), got_channel_id == channel_id);
|
||||
|
||||
;; TODO: we should reserve some part of the value for comission
|
||||
throw_if(err:msg_value_too_small(), msg_value < inc_A + inc_B);
|
||||
throw_unless(err:replay_protection(), (msg_signed_A? < signed_A?) | (msg_signed_B? < signed_B?));
|
||||
|
||||
A += inc_A;
|
||||
B += inc_B;
|
||||
|
||||
signed_A? |= msg_signed_A?;
|
||||
if (min_A < upd_min_A) {
|
||||
min_A = upd_min_A;
|
||||
}
|
||||
|
||||
signed_B? |= msg_signed_B?;
|
||||
if (min_B < upd_min_B) {
|
||||
min_B = upd_min_B;
|
||||
}
|
||||
|
||||
if (signed_A? & signed_B?) {
|
||||
if ((min_A > A) | (min_B > B)) {
|
||||
return do_payout(0, 0, A, B, a_addr, b_addr, channel_id);
|
||||
}
|
||||
|
||||
return pack_state_close(0, 0, 0, 0, 0, A, B);
|
||||
}
|
||||
|
||||
return pack_state_init(signed_A?, signed_B?, min_A, min_B, expire_at, A, B);
|
||||
}
|
||||
|
||||
;; close$001 extra_A:Grams extra_B:Grams sig:Maybe<int256> promise_A:Grams promise_B:Grams = Message;
|
||||
|
||||
cell with_close(slice cs, slice msg, int msg_signed_A?, int msg_signed_B?, int a_key, int b_key,
|
||||
slice a_addr, slice b_addr, int expire_timeout, int channel_id) {
|
||||
;; parse state
|
||||
(int signed_A?, int signed_B?, int promise_A, int promise_B, int expire_at, int A, int B) = unpack_state_close(cs);
|
||||
|
||||
if (expire_at == 0) {
|
||||
expire_at = now() + expire_timeout;
|
||||
}
|
||||
|
||||
int op = msg~load_uint(32);
|
||||
if (op == msg:timeout()) {
|
||||
throw_unless(err:no_timeout(), expire_at < now());
|
||||
return do_payout(promise_A, promise_B, A, B, a_addr, b_addr, channel_id);
|
||||
}
|
||||
throw_unless(err:expected_close(), op == msg:close());
|
||||
|
||||
;; also ensures that (msg_signed_A? | msg_signed_B?) is true
|
||||
throw_unless(err:replay_protection(), (msg_signed_A? < signed_A?) | (msg_signed_B? < signed_B?));
|
||||
signed_A? |= msg_signed_A?;
|
||||
signed_B? |= msg_signed_B?;
|
||||
|
||||
;; unpack close message
|
||||
(int extra_A, int extra_B) = (msg~load_grams(), msg~load_grams());
|
||||
int has_sig = msg~load_int(1);
|
||||
if (has_sig) {
|
||||
slice sig = msg~load_ref().begin_parse().preload_bits(512);
|
||||
int hash = msg.slice_hash();
|
||||
ifnot (msg_signed_A?) {
|
||||
throw_unless(err:wrong_a_signature(), check_signature(hash, sig, a_key));
|
||||
extra_A = 0;
|
||||
}
|
||||
ifnot (msg_signed_B?) {
|
||||
throw_unless(err:wrong_b_signature(), check_signature(hash, sig, b_key));
|
||||
extra_B = 0;
|
||||
}
|
||||
} else {
|
||||
throw_unless(err:no_promise_signature(), msg_signed_A? & msg_signed_B?);
|
||||
extra_A = 0;
|
||||
extra_B = 0;
|
||||
}
|
||||
(int got_channel_id, int update_promise_A, int update_promise_B) = (msg~load_uint(64), msg~load_grams(), msg~load_grams());
|
||||
throw_unless(err:wrong_channel_id(), got_channel_id == channel_id);
|
||||
|
||||
|
||||
accept_message();
|
||||
update_promise_A += extra_A;
|
||||
if (promise_A < update_promise_A) {
|
||||
promise_A = update_promise_A;
|
||||
}
|
||||
update_promise_B += extra_B;
|
||||
if (promise_B < update_promise_B) {
|
||||
promise_B = update_promise_B;
|
||||
}
|
||||
|
||||
if (signed_A? & signed_B?) {
|
||||
return do_payout(promise_A, promise_B, A, B, a_addr, b_addr, channel_id);
|
||||
}
|
||||
return pack_state_close(signed_A?, signed_B?, promise_A, promise_B, expire_at, A, B);
|
||||
}
|
||||
|
||||
() recv_any(int msg_value, slice msg) impure {
|
||||
(cell config, cell state) = unpack_data();
|
||||
(int init_timeout, int close_timeout, int a_key, int b_key, slice a_addr, slice b_addr, int channel_id) = config.unpack_config();
|
||||
(int msg_signed_A?, int msg_signed_B?) = msg~unwrap_signatures(a_key, b_key);
|
||||
|
||||
slice cs = state.begin_parse();
|
||||
int state_type = cs~load_uint(3);
|
||||
|
||||
if (state_type == state:init()) { ;; init
|
||||
state = with_init(cs, msg_value, msg, msg_signed_A?, msg_signed_B?, a_addr, b_addr, init_timeout, channel_id);
|
||||
} if (state_type == state:close()) {
|
||||
state = with_close(cs, msg, msg_signed_A?, msg_signed_B?, a_key, b_key, a_addr, b_addr, close_timeout, channel_id);
|
||||
}
|
||||
|
||||
pack_data(config, state);
|
||||
}
|
||||
|
||||
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
|
||||
recv_any(msg_value, in_msg);
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
recv_any(0, in_msg);
|
||||
}
|
|
@ -1,13 +1,16 @@
|
|||
;; Restricted wallet (a variant of wallet-code.fc)
|
||||
;; until configuration parameter -13 is set, accepts messages only to elector smc
|
||||
;; restricts access to parts of balance until certain dates
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
_ days_passed() inline {
|
||||
var p = config_param(-13);
|
||||
return null?(p) ? -1 : (now() - begin_parse(p).preload_uint(32)) / 86400;
|
||||
_ seconds_passed(int start_at, int utime) inline_ref {
|
||||
ifnot (start_at) {
|
||||
var p = config_param(-13);
|
||||
start_at = null?(p) ? 0 : begin_parse(p).preload_uint(32);
|
||||
}
|
||||
return start_at ? utime - start_at : -1;
|
||||
}
|
||||
|
||||
() recv_external(slice in_msg) impure {
|
||||
|
@ -16,7 +19,7 @@ _ days_passed() inline {
|
|||
var (msg_seqno, valid_until) = (cs~load_uint(32), cs~load_uint(32));
|
||||
throw_if(35, valid_until <= now());
|
||||
var ds = get_data().begin_parse();
|
||||
var (stored_seqno, public_key, rdict) = (ds~load_uint(32), ds~load_uint(256), ds~load_dict());
|
||||
var (stored_seqno, public_key, start_at, rdict) = (ds~load_uint(32), ds~load_uint(256), ds~load_uint(32), ds~load_dict());
|
||||
ds.end_parse();
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
ifnot (msg_seqno) {
|
||||
|
@ -24,14 +27,15 @@ _ days_passed() inline {
|
|||
set_data(begin_cell()
|
||||
.store_uint(stored_seqno + 1, 32)
|
||||
.store_uint(public_key, 256)
|
||||
.store_uint(start_at, 32)
|
||||
.store_dict(rdict)
|
||||
.end_cell());
|
||||
return ();
|
||||
}
|
||||
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
accept_message();
|
||||
var ts = days_passed();
|
||||
var (_, value, found) = rdict.idict_get_preveq?(16, ts);
|
||||
var ts = seconds_passed(start_at, now());
|
||||
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
|
||||
if (found) {
|
||||
raw_reserve(value~load_grams(), 2);
|
||||
}
|
||||
|
@ -45,6 +49,7 @@ _ days_passed() inline {
|
|||
set_data(begin_cell()
|
||||
.store_uint(stored_seqno + 1, 32)
|
||||
.store_uint(public_key, 256)
|
||||
.store_uint(start_at, 32)
|
||||
.store_dict(rdict)
|
||||
.end_cell());
|
||||
}
|
||||
|
@ -61,15 +66,23 @@ int get_public_key() method_id {
|
|||
return cs.preload_uint(256);
|
||||
}
|
||||
|
||||
int balance() method_id {
|
||||
int compute_balance_at(int utime) inline_ref {
|
||||
var ds = get_data().begin_parse().skip_bits(32 + 256);
|
||||
var rdict = ds~load_dict();
|
||||
var (start_at, rdict) = (ds~load_uint(32), ds~load_dict());
|
||||
ds.end_parse();
|
||||
var ts = days_passed();
|
||||
var ts = seconds_passed(start_at, utime);
|
||||
var balance = get_balance().pair_first();
|
||||
var (_, value, found) = rdict.idict_get_preveq?(16, ts);
|
||||
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
|
||||
if (found) {
|
||||
balance = max(balance - value~load_grams(), 0);
|
||||
}
|
||||
return balance;
|
||||
}
|
||||
|
||||
int balance_at(int utime) method_id {
|
||||
return compute_balance_at(utime);
|
||||
}
|
||||
|
||||
int balance() method_id {
|
||||
return compute_balance_at(now());
|
||||
}
|
||||
|
|
103
crypto/smartcont/restricted-wallet3-code.fc
Normal file
103
crypto/smartcont/restricted-wallet3-code.fc
Normal file
|
@ -0,0 +1,103 @@
|
|||
;; Restricted wallet initialized by a third party (a variant of restricted-wallet2-code.fc)
|
||||
;; restricts access to parts of balance until certain dates
|
||||
|
||||
() recv_internal(slice in_msg) impure {
|
||||
;; do nothing for internal messages
|
||||
}
|
||||
|
||||
_ seconds_passed(int start_at, int utime) inline_ref {
|
||||
ifnot (start_at) {
|
||||
var p = config_param(-13);
|
||||
start_at = null?(p) ? 0 : begin_parse(p).preload_uint(32);
|
||||
}
|
||||
return start_at ? utime - start_at : -1;
|
||||
}
|
||||
|
||||
() 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 ds = get_data().begin_parse();
|
||||
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
|
||||
throw_unless(33, msg_seqno == stored_seqno);
|
||||
throw_unless(34, subwallet_id == stored_subwallet);
|
||||
throw_unless(36, check_signature(slice_hash(in_msg), signature, public_key));
|
||||
ifnot (msg_seqno) {
|
||||
public_key = ds~load_uint(256); ;; load "final" public key
|
||||
ds.end_parse();
|
||||
cs~touch();
|
||||
var (start_at, rdict) = (cs~load_uint(32), cs~load_dict());
|
||||
cs.end_parse();
|
||||
accept_message();
|
||||
set_data(begin_cell()
|
||||
.store_uint(stored_seqno + 1, 32)
|
||||
.store_uint(stored_subwallet, 32)
|
||||
.store_uint(public_key, 256)
|
||||
.store_uint(start_at, 32)
|
||||
.store_dict(rdict)
|
||||
.end_cell());
|
||||
return ();
|
||||
}
|
||||
var (start_at, rdict) = (ds~load_uint(32), ds~load_dict());
|
||||
ds.end_parse();
|
||||
accept_message();
|
||||
var ts = seconds_passed(start_at, now());
|
||||
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
|
||||
if (found) {
|
||||
raw_reserve(value~load_grams(), 2);
|
||||
}
|
||||
cs~touch();
|
||||
while (cs.slice_refs()) {
|
||||
var mode = cs~load_uint(8);
|
||||
var msg = cs~load_ref();
|
||||
send_raw_message(msg, mode);
|
||||
}
|
||||
cs.end_parse();
|
||||
set_data(begin_cell()
|
||||
.store_uint(stored_seqno + 1, 32)
|
||||
.store_uint(stored_subwallet, 32)
|
||||
.store_uint(public_key, 256)
|
||||
.store_uint(start_at, 32)
|
||||
.store_dict(rdict)
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
;; Get methods
|
||||
|
||||
int seqno() method_id {
|
||||
return get_data().begin_parse().preload_uint(32);
|
||||
}
|
||||
|
||||
int wallet_id() method_id {
|
||||
var ds = get_data().begin_parse();
|
||||
ds~load_uint(32);
|
||||
return ds.preload_uint(32);
|
||||
}
|
||||
|
||||
int get_public_key() method_id {
|
||||
var ds = get_data().begin_parse();
|
||||
ds~load_uint(32 + 32);
|
||||
return ds.preload_uint(256);
|
||||
}
|
||||
|
||||
int compute_balance_at(int utime) inline_ref {
|
||||
var ds = get_data().begin_parse().skip_bits(32 + 32 + 256);
|
||||
var (start_at, rdict) = (ds~load_uint(32), ds~load_dict());
|
||||
ds.end_parse();
|
||||
var ts = seconds_passed(start_at, utime);
|
||||
var balance = get_balance().pair_first();
|
||||
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
|
||||
if (found) {
|
||||
balance = max(balance - value~load_grams(), 0);
|
||||
}
|
||||
return balance;
|
||||
}
|
||||
|
||||
int balance_at(int utime) method_id {
|
||||
return compute_balance_at(utime);
|
||||
}
|
||||
|
||||
int balance() method_id {
|
||||
return compute_balance_at(now());
|
||||
}
|
|
@ -18,5 +18,7 @@ dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc
|
|||
|
||||
file-base +".pk" dup file-exists? {
|
||||
dup file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
|
||||
=: wallet_pk ."Private key available in file " type cr
|
||||
tuck =: wallet_pk ."Private key available in file " type cr
|
||||
priv>pub 256 B>u@
|
||||
dup ."Corresponding public key is " .pubkey ." = " 64X. cr
|
||||
} { ."Private key file " type ." not found" cr } cond
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue