From 49d48352b12f4c351cfbff1f313f92134c5e37e7 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 12 Oct 2019 21:14:02 +0200 Subject: [PATCH] update --- README.md | 9 ++ wallet/README.md | 42 ++++++++ wallet/lib.fif | 225 +++++++++++++++++---------------------- wallet/proto/scheme.tlb | 2 +- wallet/test.fif | 8 +- wallet/testing.sh | 46 -------- wallet/wallet-code.fc | 8 +- wallet/wallet-code.fif | 8 +- wallet/wallet-create.fif | 3 +- 9 files changed, 168 insertions(+), 183 deletions(-) delete mode 100755 wallet/testing.sh diff --git a/README.md b/README.md index 50ee153..48c98d5 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,12 @@ This will automatically build the lite client, fift and func, and will also edit * `funcompile` is a wrapper for the `func` compiler, automatically including the stdlib while compiling. * `fift` is a simple wrapper for the fift compiler. +## Contents + +* `toolchain` - Some automatic builder scripts and wrappers around the funC compiler and fift +* `wallet` - Advanced upgradable multisignature wallet +* `test` - A small bugreport about issues with fift exception traces +* [GitHub issues and bugreports](https://github.com/ton-blockchain/ton/issues?utf8=%E2%9C%93&q=author%3Adanog+): + * [#59, bug in funC compiler](https://github.com/ton-blockchain/ton/issues/59) + * [#96, issues with fift exception traces](https://github.com/ton-blockchain/ton/issues/96) + * [#87, pull request with more funC dictionary manipulation primitives](https://github.com/ton-blockchain/ton/pull/87) \ No newline at end of file diff --git a/wallet/README.md b/wallet/README.md index af8c464..ed7dece 100644 --- a/wallet/README.md +++ b/wallet/README.md @@ -2,6 +2,48 @@ Daniil Gentili's submission (@danogentili, ). +Project structure: + +Fift scripts: +* `gen-pub.fif` - Generates public/private keypair +* `wallet-create.fif` - Creates shared wallet +* `wallet-create.fif` - Creates wallet code update request +* `create.fif` - Creates simple message to be sent to wallet +* `sign.fif` - Adds signature to wallet message +* `verify.fif` - Verifies signature of message against known public key +* `merge.fif` - Merges multiple messages with same content and different signatures +* `inspect.fif` - Inspects the contents of request +* `lib.fif` - Library with functions to deserialize and inspect contents of TON messages + +FunC code: +* `wallet-code.fc` - Wallet code +* `wallet-code-update.fc` - Wallet code with minor change to test wallet code upgrade + +Scripts: +* `test.sh` - Creates wallet, set of signed requests and tests wallet in TON VM +* `test-update.sh` - Tests wallet code upgrade functionality (after `test.sh`) in TON VM + +## Testing + +The `test.sh` scripts contains a full set of commands to test every script. +``` +usage: fift -s wallet/wallet-create.fif [ ...] +``` + +Creates a new multisignature wallet in specified workchain composed of (1-10) keys. +The first of the keys must be a private key (pre-existing or not), used to generate the wallet; the rest MUST be public keys. + +Min (1-10) signatures required to send an order; load pre-existing public keys from files . + + +``` +usage: gen-pub.fif +``` + +Create public key files from private keys; if doesn't exist, it will be created. +Will also print the hex public key ID. + +* `inspect Upgradable multisignature wallet. Included signature verification scripts to avoid problems with eventual preloaded orders with invalid signatures. diff --git a/wallet/lib.fif b/wallet/lib.fif index 7106638..b9af926 100644 --- a/wallet/lib.fif +++ b/wallet/lib.fif @@ -44,6 +44,99 @@ variable-set sig-count sig-count! 256 u>B rot 256 u>B -rot ed25519_chksign } : ed25519_chksignuu + +// Inspect multisigwrapper starting from signatures +// +// multiSigWrapper$_ signatures:(HashmapE 4 Signature) message:(WrappedMessage X) = MultiSigWrapperStorage X; +// +// s -- +{ + dict@+ swap + dup null? abort"Empty signature list!" + dup 4 dictlen sig-count! + + ."Signed by the following keys: " + 4 { drop . ."- " -1 } dictforeach cr drop + + ."Hash: " dup s>c hashu dup x. cr + message-hash! + + // modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X; + // wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X; + + 32 u@+ swap + dup ."Expires: " . + dup now < { ."(already EXPIRED!)" drop } { ."(in " now - . ."seconds)" } cond cr + + 32 u@+ swap + ."Seqno: " . cr + + 1 u@+ swap + { + ."Is code message!" cr + // codeMessage$1 minSigs:(## 4) keys:(HashmapE 4 PubKey) code:^Cell = ModeMessage X; + + 4 u@+ swap + ."Minsigs: " . cr + + dict@+ swap + dup null? abort"Empty key list!" + + ."The following keys are present: " cr + 4 { swap ."* " . ."- " 32 B@ Bx. cr -1 } dictforeach cr drop + + ref@ + ."Code: " " cr ref@ 100 => 4 + 4 <> abort"Unsupported address!" // Make things simple for now + + 8 i@+ + 256 u@+ -rot + ."Destination address: " .addr cr + + Gram@+ swap + ."Grams: " .GR cr + } cond + drop + } cond +} : inspect-multisig + // Inspects contents of cell provided on top of the stack // c -- // @@ -109,75 +202,7 @@ variable-set sig-count sig-count! dup ."Message version: " . cr abort"Unsupported message version!" - dict@+ swap - dup null? abort"Empty signature list!" - dup 4 dictlen sig-count! - - ."Signed by the following keys: " - 4 { drop . ."- " -1 } dictforeach cr drop - - ."Hash: " dup s>c hashu dup x. cr - message-hash! - - // modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X; - // wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X; - - 32 u@+ swap - dup ."Expires: " . - dup now < { ."(already EXPIRED!)" drop } { ."(in " now - . ."seconds)" } cond cr - - 32 u@+ swap - ."Seqno: " . cr - - 1 u@+ swap - { ."Is code message!" cr } - { - 8 u@+ swap - ."Mode: " . cr - - // Now on to the actual message we're agreeing to sign - // - // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool - // src:MsgAddressInt dest:MsgAddressInt - // value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams - // created_lt:uint64 created_at:uint32 = CommonMsgInfo; - // ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt - // import_fee:Grams = CommonMsgInfo; - // ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt - // created_lt:uint64 created_at:uint32 = CommonMsgInfo; - ."=>" cr ref@ 100 => 4 - 4 <> abort"Unsupported address!" // Make things simple for now - - 8 i@+ - 256 u@+ -rot - ."Destination address: " .addr cr - - Gram@+ swap - ."Grams: " .GR cr - } cond - } cond - drop + inspect-multisig } : inspect // storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 PubKey) messages:(HashmapE 256 (MultiSigWrapperStorage X)) = Storage X; @@ -204,67 +229,7 @@ variable-set sig-count sig-count! ."Signature count: " dup . cr sig-count! - dict@+ swap - ."Signed by the following keys: " - 4 { drop . ."- " -1 } dictforeach cr drop - - ."Hash: " dup s>c hashu dup x. cr - message-hash! - - // modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X; - // wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X; - - 32 u@+ swap - dup ."Expires: " . - dup now < { ."(already EXPIRED!)" drop } { ."(in " now - . ."seconds)" } cond cr - - 32 u@+ swap - ."Seqno: " . cr - - 8 u@+ swap - ."Mode: " . cr - - // Now on to the actual message we're agreeing to sign - // - // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool - // src:MsgAddressInt dest:MsgAddressInt - // value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams - // created_lt:uint64 created_at:uint32 = CommonMsgInfo; - // ext_in_msg_info$10 src:MsgAddressExt dest:MsgAddressInt - // import_fee:Grams = CommonMsgInfo; - // ext_out_msg_info$11 src:MsgAddressInt dest:MsgAddressExt - // created_lt:uint64 created_at:uint32 = CommonMsgInfo; - ."=>" cr ref@ 100 => 4 - 4 <> abort"Unsupported address!" // Make things simple for now - - 8 i@+ - 256 u@+ -rot - ."Destination address: " .addr cr - - Gram@+ swap - ."Grams: " .GR cr - } cond - drop + inspect-multisig -1 } dictforeach drop } : inspect-storage \ No newline at end of file diff --git a/wallet/proto/scheme.tlb b/wallet/proto/scheme.tlb index 1c7b1fb..9bb73d8 100644 --- a/wallet/proto/scheme.tlb +++ b/wallet/proto/scheme.tlb @@ -10,7 +10,7 @@ signature$_ R:bits256 s:bits256 = Signature; // Message + send ModeMessage modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X; // Special code upgrade message -codeMessage$1 code:^Cell = ModeMessage X; +codeMessage$1 minSigs:(## 4) keys:(HashmapE 4 PubKey) code:^Cell = ModeMessage X; // Actual multisigned message wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X; diff --git a/wallet/test.fif b/wallet/test.fif index 8ff89a1..1a046f3 100644 --- a/wallet/test.fif +++ b/wallet/test.fif @@ -12,6 +12,7 @@ $# 1- 2 /c const message-count variable-set function function! variable-set code code! variable-set storage storage! +variable-set retcode retcode! // c7 @@ -83,15 +84,18 @@ init-boc abort"Exception" + // Manually drop return values of functions function -1 <> { 2drop } if + // rot // ."Signature: " // 64 B@ Bx. diff --git a/wallet/testing.sh b/wallet/testing.sh deleted file mode 100755 index 34db9ae..0000000 --- a/wallet/testing.sh +++ /dev/null @@ -1,46 +0,0 @@ - -#!/bin/bash -e -# Define some helper functions -chr() { [ "$1" -lt 256 ] || return 1; printf "\\$(printf '%03o' "$1")"; } -ord() { LC_CTYPE=C printf '%d' "'$1"; } - -mkdir -p tests -cd tests -# Create 10 public keys -for f in {a..j}; do fift -s ../gen-pub.fif $f;done - -# Create wallet with those 10 public keys on workchain 0, requiring all 10 signatures to send a message -fift -s ../wallet-create.fif 0 pony 10 10 {a..j} | tee log - -# Get wallet address -address=$(sed '/Bounceable address [(]for later access[)]: /!d;s/.* //g' log) -rm log - -# Create a new wallet query signed with key a (ID 0), transferring 10 grams to the wallet itself -fift -s ../create.fif pony a 0 $address 0 10 a - -# Sign the query using all keys separately, creating eight more boc files, each signed by two keys only (0 and 1..9) -for f in {1..9}; do fift -s ../sign.fif a $(chr $((97+f))) $(chr $((97+f))) $f;done - -# Merge all queries -fift -s ../merge.fif {a..j} merge - -# Inspect queries -fift -s ../inspect.fif merge - -# Finally run the generated files in the VM -# -# First init VM with constructor message -# Then load first file with only one signature by key a (0) -# Run seqno get-method -# Run getPartialsByKeyId get-method -# Load file with all signatures (and send message) -# Run getPartialsByKeyId get-method -# -fift -s ../test.fif \ - pony-create \ - a -1 \ - 0 85143 \ - 0 113609 \ - merge -1 \ - 0 113609 \ No newline at end of file diff --git a/wallet/wallet-code.fc b/wallet/wallet-code.fc index 491eb3a..661cccc 100644 --- a/wallet/wallet-code.fc +++ b/wallet/wallet-code.fc @@ -112,9 +112,15 @@ int udict_has?(cell dict, int key_len, int index) asm(index dict key_len) "DICTU if (storedSignatureCount >= min_sigs) { if (message_copy~load_uint(1)) { ;; Code upgrade + ;; codeMessage$1 minSigs:(## 4) keys:(HashmapE 4 PubKey) code:^Cell = ModeMessage X; + ;; + min_sigs = message_copy~load_uint(4); + keys = message_copy~load_dict(); set_code(message_copy~load_ref()); } else { ;; Simple message + ;; modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X; + ;; var (mode, message) = (message_copy~load_uint(8), message_copy~load_ref()); send_raw_message(message, mode); } @@ -209,4 +215,4 @@ int seqno() method_id { return (0, begin_cell().end_cell()); } return (ok, begin_cell().store_slice(message).end_cell()); -} \ No newline at end of file +} diff --git a/wallet/wallet-code.fif b/wallet/wallet-code.fif index 101337f..bf86cfa 100644 --- a/wallet/wallet-code.fif +++ b/wallet/wallet-code.fif @@ -163,17 +163,23 @@ PROGRAM{ 1 LDU SWAP IF:<{ + s2 POP + s2 POP + 4 LDU + LDDICT LDREF SWAP SETCODE + DUMPSTK }>ELSE<{ 8 LDU LDREF s0 s2 XCHG SENDRAWMSG + s3 s3 s0 XCHG3 }> ENDS - s0 s3 XCHG2 + s2 s3 XCHG2 8 PUSHPOW2 DICTUDEL DROP diff --git a/wallet/wallet-create.fif b/wallet/wallet-create.fif index 44aba15..a8fcad0 100644 --- a/wallet/wallet-create.fif +++ b/wallet/wallet-create.fif @@ -6,7 +6,6 @@ ."The first of the keys must be a private key (pre-existing or not), used to generate the wallet; the rest MUST be public keys." cr ."Create or generate public key files from private keys using gen-pub.fif privkey" cr cr ."Min (1-10) signatures required to send an order; load pre-existing public keys from files ." cr - ."Optionally load a number of pre-generated partially signed orders to preload into the contract." cr cr 1 halt } : usage $# 5 < ' usage if @@ -56,7 +55,7 @@ rot // Put length on top for times swap } swap times drop const keys-dict - +.s // code "wallet-code.fif" include // data