1
0
mirror of https://github.com/danog/toncontest.git synced 2024-11-30 04:29:14 +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.
* `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>).
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.
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
} : 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
// 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@ <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-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@ <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
inspect-multisig
-1
} dictforeach drop
} : inspect-storage

View File

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

View File

@ -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 <s
."Calling " function . cr cr
message-contents function code storage ctx runvmctx .s
swap
."Retcode: " . cr
swap retcode!
."Retcode: " retcode . cr
dup storage!
inspect-storage
retcode 0 <> abort"Exception"
// Manually drop return values of functions
function -1 <> {
2drop
} if
// rot
// ."Signature: "
// 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 (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());
}
}

View File

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

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
."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
."Optionally load a number of pre-generated partially signed orders <boc1...m> 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