mirror of
https://github.com/danog/toncontest.git
synced 2024-11-30 04:29:14 +01:00
update
This commit is contained in:
parent
0c96b64bef
commit
49d48352b1
@ -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)
|
@ -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.
|
||||
|
||||
|
225
wallet/lib.fif
225
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: " <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
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user