1
0
mirror of https://github.com/danog/toncontest.git synced 2024-12-02 09:27:47 +01:00
This commit is contained in:
Daniil Gentili 2019-10-12 21:14:02 +02:00
parent 0c96b64bef
commit 49d48352b1
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
9 changed files with 168 additions and 183 deletions

View File

@ -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. * `funcompile` is a wrapper for the `func` compiler, automatically including the stdlib while compiling.
* `fift` is a simple wrapper for the fift compiler. * `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)

View File

@ -2,6 +2,48 @@
Daniil Gentili's submission (@danogentili, <daniil@daniil.it>). Daniil Gentili's submission (@danogentili, <daniil@daniil.it>).
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 <workchain-id> <wallet-name> <n> <k> <privkey1> [<pubkey2> ...]
```
Creates a new multisignature wallet in specified workchain composed of <n> (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 <k> (1-10) signatures required to send an order; load <n> pre-existing public keys from files <key1...n>.
```
usage: gen-pub.fif <privkey>
```
Create public key files from private keys; if <privkey> doesn't exist, it will be created.
Will also print the hex public key ID.
* `inspect
Upgradable multisignature wallet. Upgradable multisignature wallet.
Included signature verification scripts to avoid problems with eventual preloaded orders with invalid signatures. Included signature verification scripts to avoid problems with eventual preloaded orders with invalid signatures.

View File

@ -44,6 +44,99 @@ variable-set sig-count sig-count!
256 u>B rot 256 u>B -rot ed25519_chksign 256 u>B rot 256 u>B -rot ed25519_chksign
} : ed25519_chksignuu } : 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: " <s csr.
}
{
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@ <s
1 u@+ swap
{ // External message *$1*
."Inside: external message" cr
}
{ // Internal message int_msg_info$0
."Inside: internal message" cr
1 u@+ swap
."Instant hypercube routing disabled? " . cr
1 u@+ swap
."Bounce flag set? " . cr
1 u@+ swap
// ."Bounced flag set? " . cr
drop
2 u@+ nip // Drop src address constructor + flags
3 u@+ swap // Read dst address constructor + flags
// addr_std$10 anycast 0 => 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 // Inspects contents of cell provided on top of the stack
// c -- // c --
// //
@ -109,75 +202,7 @@ variable-set sig-count sig-count!
dup ."Message version: " . cr dup ."Message version: " . cr
abort"Unsupported message version!" abort"Unsupported message version!"
dict@+ swap inspect-multisig
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@ <s
1 u@+ swap
{ // External message *$1*
."Inside: external message" cr
}
{ // Internal message int_msg_info$0
."Inside: internal message" cr
1 u@+ swap
."Instant hypercube routing disabled? " . cr
1 u@+ swap
."Bounce flag set? " . cr
1 u@+ swap
// ."Bounced flag set? " . cr
drop
2 u@+ nip // Drop src address constructor + flags
3 u@+ swap // Read dst address constructor + flags
// addr_std$10 anycast 0 => 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 } : inspect
// storage$_ seqno:uint32 minSigs:(## 4) keys:(HashmapE 4 PubKey) messages:(HashmapE 256 (MultiSigWrapperStorage X)) = Storage X; // 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 ."Signature count: " dup . cr
sig-count! sig-count!
dict@+ swap inspect-multisig
."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@ <s
1 u@+ swap
{ // External message *$1*
."Inside: external message" cr
}
{ // Internal message int_msg_info$0
."Inside: internal message" cr
1 u@+ swap
."Instant hypercube routing disabled? " . cr
1 u@+ swap
."Bounce flag set? " . cr
1 u@+ swap
// ."Bounced flag set? " . cr
drop
2 u@+ nip // Drop src address constructor + flags
3 u@+ swap // Read dst address constructor + flags
// addr_std$10 anycast 0 => 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
-1 -1
} dictforeach drop } dictforeach drop
} : inspect-storage } : inspect-storage

View File

@ -10,7 +10,7 @@ signature$_ R:bits256 s:bits256 = Signature;
// Message + send ModeMessage // Message + send ModeMessage
modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X; modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X;
// Special code upgrade message // 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 // Actual multisigned message
wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X; wrappedMessage$_ expires_at:uint32 seqno:uint32 body:(ModeMessage X) = WrappedMessage X;

View File

@ -12,6 +12,7 @@ $# 1- 2 /c const message-count
variable-set function function! variable-set function function!
variable-set code code! variable-set code code!
variable-set storage storage! variable-set storage storage!
variable-set retcode retcode!
// c7 // c7
@ -83,15 +84,18 @@ init-boc <s
."Calling " function . cr cr ."Calling " function . cr cr
message-contents function code storage ctx runvmctx .s message-contents function code storage ctx runvmctx .s
swap swap retcode!
."Retcode: " . cr ."Retcode: " retcode . cr
dup storage! dup storage!
inspect-storage inspect-storage
retcode 0 <> abort"Exception"
// Manually drop return values of functions // Manually drop return values of functions
function -1 <> { function -1 <> {
2drop 2drop
} if } if
// rot // rot
// ."Signature: " // ."Signature: "
// 64 B@ Bx. // 64 B@ Bx.

View File

@ -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

View File

@ -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 (storedSignatureCount >= min_sigs) {
if (message_copy~load_uint(1)) { if (message_copy~load_uint(1)) {
;; Code upgrade ;; 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()); set_code(message_copy~load_ref());
} else { } else {
;; Simple message ;; Simple message
;; modeMessage$0 mode:uint8 body:^(Message X) = ModeMessage X;
;;
var (mode, message) = (message_copy~load_uint(8), message_copy~load_ref()); var (mode, message) = (message_copy~load_uint(8), message_copy~load_ref());
send_raw_message(message, mode); send_raw_message(message, mode);
} }
@ -209,4 +215,4 @@ int seqno() method_id {
return (0, begin_cell().end_cell()); return (0, begin_cell().end_cell());
} }
return (ok, begin_cell().store_slice(message).end_cell()); return (ok, begin_cell().store_slice(message).end_cell());
} }

View File

@ -163,17 +163,23 @@ PROGRAM{
1 LDU 1 LDU
SWAP SWAP
IF:<{ IF:<{
s2 POP
s2 POP
4 LDU
LDDICT
LDREF LDREF
SWAP SWAP
SETCODE SETCODE
DUMPSTK
}>ELSE<{ }>ELSE<{
8 LDU 8 LDU
LDREF LDREF
s0 s2 XCHG s0 s2 XCHG
SENDRAWMSG SENDRAWMSG
s3 s3 s0 XCHG3
}> }>
ENDS ENDS
s0 s3 XCHG2 s2 s3 XCHG2
8 PUSHPOW2 8 PUSHPOW2
DICTUDEL DICTUDEL
DROP DROP

View File

@ -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 ."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 ."Create or generate public key files from private keys using gen-pub.fif privkey" cr cr
."Min <k> (1-10) signatures required to send an order; load <n> pre-existing public keys from files <key1...n>." cr ."Min <k> (1-10) signatures required to send an order; load <n> pre-existing public keys from files <key1...n>." cr
."Optionally load a number of pre-generated partially signed orders <boc1...m> to preload into the contract." cr cr
1 halt 1 halt
} : usage } : usage
$# 5 < ' usage if $# 5 < ' usage if
@ -56,7 +55,7 @@ rot // Put length on top for times
swap swap
} swap times drop const keys-dict } swap times drop const keys-dict
.s
// code // code
"wallet-code.fif" include "wallet-code.fif" include
// data // data