diff --git a/wallet/RS b/wallet/RS new file mode 100644 index 0000000..30e09e2 Binary files /dev/null and b/wallet/RS differ diff --git a/wallet/scheme.tlb b/wallet/scheme.tlb index 517cc10..6c1dd74 100644 --- a/wallet/scheme.tlb +++ b/wallet/scheme.tlb @@ -17,12 +17,16 @@ wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMe multiSigWrapper$0 signatures:(HashmapE 4 ^Signature) message:(WrappedMessage X) = MultiSigWrapper X; //multiSigFuture$1 = MultiSigWrapper X; -// Message constructor for wallet storage +// Message constructor for wallet storagesince // No seqno: we can't regenerate the hash of a stored message. // That's a tradeoff to save grams on storage (and storing the seqno would be pointless anyway, // since all integrity checks were already made when the message was first stored). // -storedMessage$_ expires_at:uint32 signatures:(HashmapE 4 ^Signature) body:(ModeMessage X) = StoredMessage X; +// But do we __have to__ store the body? +// I mean, each incoming order already has the full message body, why store it if you can reuse it? +// Anyway, for now I'm storing the body. (Scratch that, it's a waste of storage, just store the hash) +// +storedMessage$_ expires_at:uint32 signatures:(HashmapE 4 ^Signature) = StoredMessage X; // Not doing explicit versioning here, since the structure of the storage can only change after an update of the onchain wallet code diff --git a/wallet/wallet-code.fc b/wallet/wallet-code.fc index db6c38c..5423d7b 100644 --- a/wallet/wallet-code.fc +++ b/wallet/wallet-code.fc @@ -9,7 +9,7 @@ if (ok) { ;; expiry <= now if (cs~load_uint(32) <= now()) { - messages.udict_delete?(256, hash); + messages~udict_delete?(256, hash); } } } until (~ ok); @@ -45,7 +45,7 @@ ;; We will need the hash anyway var hash = slice_hash(in_msg); - ;; storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 (## 256)) messages:(HashmapE 8 ^(Message X)) = Storage X; + ;; storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 ^PubKey) messages:(HashmapE 8 ^(StoredMessage X)) = Storage X; var stored_data = get_data().begin_parse(); var (stored_seqno, min_sigs, keys, messages) = (stored_data~load_uint(32), stored_data~load_uint(4), stored_data~load_dict(), stored_data~load_dict()); stored_data.end_parse(); @@ -58,18 +58,21 @@ ;; If old message } else { - (var storedMessage, var ok) = messages.udict_get?(256, hash); + var (storedMessage, ok) = messages.udict_get?(256, hash); ;; Throw if old message and doesn't exist in db throw_unless(35, ok); + ;; storedMessage$_ expires_at:uint32 signatures:(HashmapE 4 ^Signature) = StoredMessage X; + ;; ;; Skip expiry - storedMessage.skip_bits(32); + ;; ;; No seqno: we can't regenerate the hash of a stored message. ;; That's a tradeoff to save grams on storage (and storing the seqno would be pointless anyway, ;; since all integrity checks were already made when the message was first stored). ;; ;; Load signatures - var storedMessageSignatures = storedMessage.load_dict(); + var storedMessageSignatures = storedMessage~load_ref().begin_parse().skip_bits(32).preload_dict(); + storedMessage.end_parse(); } accept_message(); @@ -79,15 +82,24 @@ do { (idx, var signature, var ok) = signatures.udict_get_next?(4, idx); if (ok) { - (var public_key, var kok) = keys.udict_get?(4, idx); + var (public_key, kok) = keys.udict_get?(4, idx); throw_unless(36, kok); - throw_unless(37, check_signature(hash, signature, public_key~load_uint(256))); + + var key = public_key~load_ref().begin_parse().preload_uint(256); + var signature_cell = signature~load_ref(); + var signature_slice = signature_cell.begin_parse(); + var slice_copy = signature_slice; + + signature_slice.end_parse(); + public_key.end_parse(); - storedMessageSignatures.udict_set_ref(4, idx, signature); + throw_unless(37, check_signature(hash, slice_copy, key)); + + storedMessageSignatures~udict_set_ref(4, idx, signature_cell); } } until (~ ok); - var message = message_data~load_ref(); + var (mode, message) = (message_data~load_uint(8), message_data~load_ref()); message_data.end_parse(); var count = 0; @@ -99,20 +111,20 @@ count += 1; if (count >= min_sigs) { send_raw_message(message, mode); - messages.udict_delete?(256, hash); + messages~udict_delete?(256, hash); ok = 0; } } } until (~ ok); - ;; storedMessage$_ expires_at:uint32 signatures:(HashmapE 4 ^Signature) body:^(Message X) = StoredMessage X; + ;; storedMessage$_ expires_at:uint32 signatures:(HashmapE 4 ^Signature) = StoredMessage X; ;; ;; But do we __have to__ store the body? ;; I mean, each incoming order already has the full message body, why store it if you can reuse it? - ;; Anyway, for now I'm just storing this. + ;; Anyway, for now I'm storing the body. (Scratch that, it's a waste of storage, just store the hash) ;; if (count < min_sigs) { - messages.udict_set(256, hash, begin_cell().store_uint(expires_at, 32).store_dict(storedMessageSignatures).store_ref(message).end_cell()); + messages~udict_set_ref(256, hash, begin_cell().store_uint(expires_at, 32).store_dict(storedMessageSignatures).end_cell()); } messages = collect_garbage(messages); diff --git a/wallet/wallet-code.fif b/wallet/wallet-code.fif index 420f12a..827e714 100644 --- a/wallet/wallet-code.fif +++ b/wallet/wallet-code.fif @@ -5,9 +5,6 @@ PROGRAM{ DECLPROC store_db DECLPROC recv_internal DECLPROC recv_external - DECLPROC .udict_set_ref - DECLPROC mode - DECLPROC .udict_set 85143 DECLMETHOD seqno collect_garbage PROC:<{ -1 PUSHINT @@ -25,12 +22,20 @@ PROGRAM{ NOW LEQ IF:<{ + 8 PUSHPOW2 + s1 s3 s3 PUXC2 + DICTUDEL + DROP + }>ELSE<{ + s0 s2 XCHG }> }>ELSE<{ - s2 POP + 2SWAP + DROP }> SWAP NOT + s1 s2 XCHG }> DROP }> @@ -46,6 +51,160 @@ PROGRAM{ recv_internal PROC:<{ DROP }> + recv_external PROC:<{ + 1 LDU + SWAP + 32 THROWIFNOT + DUP + 1 PLDU + 33 THROWIFNOT + LDDICT + DUP + 32 LDU + 32 LDU + s2 PUSH + NOW + LEQ + 34 THROWIF + s0 s3 XCHG + HASHSU + c4 PUSH + CTOS + 32 LDU + 4 LDU + LDDICT + LDDICT + ENDS + s3 PUSH + NEWDICT + s0 s7 XCHG + EQUAL + IF:<{ + s0 s3 XCHG + INC + }>ELSE<{ + s4 s0 PUSH2 + 8 PUSHPOW2 + DICTUGET + NULLSWAPIFNOT + 35 THROWIFNOT + LDREF + NIP + ENDS + s0 s3 XCHG + }> + ACCEPT + s0 s7 XCHG + -1 PUSHINT + UNTIL:<{ + s9 PUSH + 4 PUSHINT + DICTUGETNEXT + NULLSWAPIFNOT + NULLSWAPIFNOT + DUP + IF:<{ + s1 s4 PUSH2 + 4 PUSHINT + DICTUGET + NULLSWAPIFNOT + 36 THROWIFNOT + LDREF + SWAP + CTOS + 256 PLDU + s0 s4 XCHG + LDREF + DROP + DUP + CTOS + DUP + ENDS + s0 s2 XCHG + ENDS + s9 s(-1) s4 PUXC2 + CHKSIGNU + 37 THROWIFNOT + s1 s2 XCHG + 4 PUSHINT + s3 s9 s9 PUXC2 + DICTUSETREF + }>ELSE<{ + s1 s8 s8 XCHG3 + DROP + }> + s0 s7 XCHG + NOT + }> + DROP + s8 POP + s0 s7 XCHG + 8 LDU + LDREF + ENDS + 0 PUSHINT + -1 PUSHINT + UNTIL:<{ + s7 PUSH + 4 PUSHINT + DICTUGETNEXT + NULLSWAPIFNOT + NULLSWAPIFNOT + s2 POP + OVER + IF:<{ + s0 s2 XCHG + INC + s0 s5 PUSH2 + GEQ + IF:<{ + NIP + s2 s3 PUSH2 + SENDRAWMSG + 8 PUSHPOW2 + s7 s6 s6 PUXC2 + DICTUDEL + DROP + 0 PUSHINT + }>ELSE<{ + s6 s6 XCHG2 + }> + }>ELSE<{ + s0 s6 s6 XCHG3 + }> + NOT + s6 s6 s0 XCHG3 + }> + s1 s3 XCHG + 3 BLKDROP + OVER + LESS + IF:<{ + NEWC + s1 s5 XCHG + 32 STU + s1 s3 XCHG + STDICT + ENDC + s0 s2 XCHG + 8 PUSHPOW2 + DICTUSETREF + }>ELSE<{ + s3 s4 XCHG2 + 3 BLKDROP + }> + collect_garbage CALLDICT + s0 s2 XCHG + INC + NEWC + 32 STU + 4 STU + s1 s2 XCHG + STDICT + STDICT + ENDC + c4 POP + }> seqno PROC:<{ c4 PUSH CTOS