diff --git a/toolchain/gen-zerostate.fif b/toolchain/gen-zerostate.fif new file mode 100644 index 0000000..ed80bb1 --- /dev/null +++ b/toolchain/gen-zerostate.fif @@ -0,0 +1,242 @@ +"TonUtil.fif" include +"Asm.fif" include + +def? $1 { @' $1 } { "" } cond constant suffix +{ suffix $+ } : +suffix + +wc_master setworkchain +-17 setglobalid // negative value means a test instance of the blockchain + +// Initial state of Workchain 0 (Basic workchain) + +0 mkemptyShardState + +cr ."initial basechain state is:" cr dup B dup Bx. cr +dup "basestate0" +suffix +".boc" tuck B>file +."(Initial basechain state saved to file " type .")" cr +Bhashu dup =: basestate0_fhash +."file hash=" dup x. space 256 u>B dup B>base64url type cr +"basestate0" +suffix +".fhash" B>file +hashu dup =: basestate0_rhash +."root hash=" dup x. space 256 u>B dup B>base64url type cr +"basestate0" +suffix +".rhash" B>file + +basestate0_rhash basestate0_fhash now 0 2 32 0 add-std-workchain +config.workchains! + +// SmartContract #1 (Simple wallet) + +<{ SETCP0 DUP IFNOTRET // return if recv_internal + DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method + DROP c4 PUSHCTR CTOS 32 PLDU // cnt + }> + INC 32 THROWIF // fail unless recv_external + 512 INT LDSLICEX DUP 32 PLDU // sign cs cnt + c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk + s1 s2 XCPU // sign cs cnt pubk cnt' cnt + EQUAL 33 THROWIFNOT // ( seqno mismatch? ) + s2 PUSH HASHSU // sign cs cnt pubk hash + s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk + CHKSIGNU // pubk cs cnt ? + 34 THROWIFNOT // signature mismatch + ACCEPT + SWAP 32 LDU NIP 8 LDU LDREF ENDS // pubk cnt mode msg + SWAP SENDRAWMSG // pubk cnt ; ( message sent ) + INC NEWC 32 STU 256 STU ENDC c4 POPCTR +}>c +// code + // data +Libs{ + x{ABACABADABACABA} s>c public_lib + x{1234} x{5678} |_ s>c private_lib +}Libs // libraries +GR$1700000000 // balance +0 // split_depth +0 // ticktock +2 // mode: create +register_smc +dup make_special dup constant smc1_addr +Masterchain over +2dup ."wallet address = " .addr cr 2dup 6 .Addr cr +"main-wallet" +suffix +".addr" save-address-verbose + +// SmartContract #2 (Simple money giver for test network) +<{ SETCP0 DUP IFNOTRET // return if recv_internal + DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method + DROP c4 PUSHCTR CTOS 32 PLDU // cnt + }> + INC 32 THROWIF // fail unless recv_external + 32 LDU SWAP // cs cnt + c4 PUSHCTR CTOS 32 LDU ENDS // cs cnt cnt' + TUCK EQUAL 33 THROWIFNOT // ( seqno mismatch? ) + ACCEPT // cs cnt' + SWAP 8 LDU LDREF ENDS // cnt'' mode msg + GR$20 INT 3 INT RAWRESERVE // reserve all but 20 Grams from the balance + SWAP SENDRAWMSG + INC NEWC 32 STU ENDC c4 POPCTR // store cnt'' +}>c +// code + // data +empty_cell // libraries +GR$1000000 // initial balance (1m test Grams) +0 0 2 register_smc +dup make_special dup constant smc2_addr +Masterchain over +2dup ."free test gram giver address = " .addr cr 2dup 6 .Addr cr +"testgiver" +suffix +".addr" save-address-verbose + +// SmartContract #3 +PROGRAM{ + recv_internal x{} PROC + run_ticktock PROC:<{ + c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS + NEWC ROT INC 32 STUR OVER 256 STUR ENDC + c4 POPCTR + // first 32 bits of persistent data have been increased + // remaining 256 bits with an address have been fetched + // create new empty message with 0.1 Grams to that address + NEWC b{00100010011111111} STSLICECONST TUCK 256 STU + 100000000 INT STGRAMS // store 0.1 Grams + 1 4 + 4 + 64 + 32 + 1+ 1+ INT STZEROES ENDC + // send raw message from Cell + ZERO SENDRAWMSG + -17 INT 256 STIR 130000000 INT STGRAMS + 107 INT STZEROES ENDC + ZERO // another message with 0.13 Grams to account -17 + NEWC b{11000100100000} "test" $>s |+ STSLICECONST + 123456789 INT STGRAMS + 107 INT STZEROES "Hello, world!" $>s STSLICECONST ENDC + ZERO SENDRAWMSG SENDRAWMSG // external message to address "test" + }> +}END>c +// code + // data +// empty_cell // libraries +Libs{ + x{ABACABADABACABA} s>c public_lib + x{1234} x{5678} |_ s>c public_lib +}Libs // libraries +0x333333333 // balance +0 // split_depth +3 // ticktock: tick +2 // mode: create +register_smc +dup make_special dup constant smc3_addr +."address = " x. cr + +/* + * + * SmartContract #4 (elector) + * + */ +"elector-code.fif" include // code in separate source file + // data: dict dict dict grams uint32 uint256 +empty_cell // libraries +GR$10 // balance: 10 grams +0 // split_depth +2 // ticktock: tick +2 // mode: create +register_smc +dup make_special dup constant smc4_addr dup constant elector_addr +Masterchain swap +."elector smart contract address = " 2dup .addr cr 2dup 7 .Addr cr +"elector" +suffix +".addr" save-address-verbose + +/* + * + * Configuration Parameters + * + */ +// version capabilities +0 capCreateStats config.version! +// max-validators max-main-validators min-validators +// 9 4 1 config.validator_num! +1000 100 5 config.validator_num! +// min-stake max-stake min-total-stake max-factor +GR$10000 GR$10000000 GR$1000000 sg~10 config.validator_stake_limits! +// elected-for elect-start-before elect-end-before stakes-frozen-for +// 400000 200000 4000 400000 config.election_params! +4000 2000 500 1000 config.election_params! // DEBUG +// config-addr = -1:5555...5555 +256 1<<1- 3 / constant config_addr +config_addr config.config_smc! +// elector-addr +elector_addr config.elector_smc! + +// 1 sg* 100 sg* 1000 sg* 1000000 sg* config.storage_prices! // old values (too high) +1 500 1000 500000 config.storage_prices! +config.special! + +// gas_price gas_limit special_gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit flat_gas_limit flat_gas_price -- +1000 sg* 1 *M dup 10000 10 *M GR$0.1 GR$1.0 100 100000 config.gas_prices! +10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 100 1000000 config.mc_gas_prices! +// lump_price bit_price cell_price ihr_factor first_frac next_frac +1000000 1000 sg* 100000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.fwd_prices! +10000000 10000 sg* 1000000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.mc_fwd_prices! +// mc-cc-lifetime sh-cc-lifetime sh-val-lifetime sh-val-num +250 250 1000 7 config.catchain_params! +// round-candidates next-cand-delay-ms consensus-timeout-ms fast-attempts attempt-duration cc-max-deps max-block-size max-collated-size +3 2000 16000 3 8 4 2 *Mi 2 *Mi config.consensus_params! + + +128 *Ki 512 *Ki 1 *Mi triple // [ underload soft hard ] : block bytes limit +100000 500000 1000000 triple // gas limits +1000 5000 10000 triple // lt limits +triple dup untriple config.mc_block_limits! +untriple config.block_limits! + +GR$1.7 GR$1 config.block_create_fees! +// smc1_addr config.collector_smc! +smc1_addr config.minter_smc! + +1000000000000 -17 of-cc 666666666666 239 of-cc cc+ config.to_mint! + +"validator-keys" +suffix +".pk" load-generate-keypair drop +{ dup Blen } { 32 B| swap dup ."Validator public key = " Bx. cr + 17 add-validator } while drop +// newkeypair nip dup ."Validator #1 public key = " Bx. cr +// 17 add-validator +// newkeypair nip dup ."Validator #2 public key = " Bx. cr +// 239 add-validator +// 100000 =: orig_vset_valid_for +100 =: orig_vset_valid_for // original validator set valid 100 seconds only (DEBUG) +now dup orig_vset_valid_for + 0 config.validators! + +/* + * + * SmartContract #5 (Configuration smart contract) + * + */ +"config-code.fif" include // code in separate source file + // data +empty_cell // libraries +GR$10 // balance +0 1 config_addr 6 register_smc // tock +dup set_config_smc +Masterchain swap +."config smart contract address = " 2dup .addr cr 2dup 7 .Addr cr +"config-master" +suffix +".addr" save-address-verbose +// Other data + +create_state +cr cr ."new state is:" cr dup B dup Bx. cr +dup "zerostate" +suffix +".boc" tuck B>file +."(Initial masterchain state saved to file " type .")" cr +Bhashu dup =: zerostate_fhash +."file hash= " dup X. space 256 u>B dup B>base64url type cr +"zerostate" +suffix +".fhash" B>file +hashu dup =: zerostate_rhash ."root hash= " dup X. space 256 u>B dup B>base64url type cr +"zerostate" +suffix +".rhash" B>file +basestate0_rhash ."Basestate0 root hash= " dup X. space 256 u>B B>base64url type cr +basestate0_fhash ."Basestate0 file hash= " dup X. space 256 u>B B>base64url type cr +zerostate_rhash ."Zerostate root hash= " dup X. space 256 u>B B>base64url type cr +zerostate_fhash ."Zerostate file hash= " dup X. space 256 u>B B>base64url type cr diff --git a/toolchain/testgiver.fif b/toolchain/testgiver.fif new file mode 100755 index 0000000..78726b4 --- /dev/null +++ b/toolchain/testgiver.fif @@ -0,0 +1,35 @@ +#!/usr/bin/fift -s +"TonUtil.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a request to TestGiver and saves it into .boc" cr + ."('testgiver-query.boc' by default)" cr 1 halt +} : usage + +$# 3 - -2 and ' usage if + +"testgiver.addr" load-address +// Masterchain 0xfcb91a3a3816d0f7b8c2c76108b8a9bc5a6b7a55bd79f8ab101c52db29232260 +2constant giver_addr + ."Test giver address = " giver_addr 2dup .addr cr 6 .Addr cr + +$1 true parse-load-address =: bounce 2=: dest_addr +$2 parse-int =: seqno +$3 $>GR =: amount +def? $4 { @' $4 } { "testgiver-query" } cond constant savefile + +."Requesting " amount .GR ."to account " +dest_addr 2dup bounce 7 + .Addr ." = " .addr +."seqno=0x" seqno x. ."bounce=" bounce . cr + +// create a message (NB: 01b00.., b = bounce) + + +dup ."enveloping message: " +dup ."resulting external message: " B dup Bx. cr +savefile +".boc" tuck B>file +."(Saved to file " type .")" cr diff --git a/wallet/README.md b/wallet/README.md index 42f5896..e83e267 100644 --- a/wallet/README.md +++ b/wallet/README.md @@ -6,12 +6,11 @@ Upgradable multisignature wallet, with custom scripts to deserialize and inspect All custom data structures used in the wallet can be viewed as a custom TL-B scheme in `proto/scheme.tlb` (some basic TON constructors are also included for reference). Most smart contact get-methods (except for the basic seqno and getPartials methods) return an integer, indicating whether the operation was successful and the requested data was found, followed by a cell/integer with the found data (or an empty cell/0 in case of failure). -I've been having some issues generating a TON zerostate in order to test my contract using `test-ton-collator`, which is why I have created a full testing platform with a collator emulator in FIFT that parses the generated external messages and runs the required logic (including code and data initialization from the StateInit of constructor messages, with support for multiple consecutive method calls and data persistence) in the TVM, fully emulating the logic of `Transaction::unpack_input_msg` and `Collator::create_ordinary_transaction`. +I created a full testing platform with a collator emulator in FIFT that parses the generated external messages and runs the required logic (including code and data initialization from the StateInit of constructor messages, with support for multiple consecutive method calls and data persistence) in the TVM, partially emulating the logic of `Transaction::unpack_input_msg` and `Collator::create_ordinary_transaction`. -I have rechecked the validity of generated BOC files against the TL-B schemes for blockchain messages, and everything seems to work in my offchain test suite, but my messages (at least the constructor messages) aren't accepted by the testnet: I've tried using the `test-ton-collator` program to simulate the workflow (and get logs!) of the collators that reject my messages, but I encountered problems with the creation of a zerostate for the blockchain collator, explained in detail in issue [#144](https://github.com/ton-blockchain/ton/issues/144). -I've tested throughly the smart contract using my local collator emulator (`test.fif`), but I couldn't get the actual TON collator to start due to missing documentation. +I've tested throughly the smart contract using my local collator emulator (`test.fif`), and the actual TON collator using a slightly tweaked [zerostate generator](https://github.com/ton-blockchain/ton/pull/145) and the custom `test-collator.sh` script. -Anyway, I had loads of fun working with funC and especially fift, and I'm really looking forward to building stuff on TON; especially TON services on the P2P ADNL network, I'll start by adding support for the ADNL protocol in my MTProto client, [MadelineProto](https://github.com/danog/MadelineProto); but I'll also experiment more with the TON blockchain, creating s'more smart contracts. +I had loads of fun working with funC and especially fift, and I'm really looking forward to building stuff on TON; especially TON services on the P2P ADNL network, I'll start by adding support for the ADNL protocol in my MTProto client, [MadelineProto](https://github.com/danog/MadelineProto); but I'll also experiment more with the TON blockchain, creating s'more smart contracts. ## Project structure @@ -189,3 +188,15 @@ fift -s ../test.fif \ If we were running on the blockchain, at this point the smart contract code root would be updated to point to the new code, allowing us to use the new `getMagic` (77784) method that returns (420, 69) when called. Since we're running in a local TVM instance, and fift's `runvm` primitives have no way of returning the output action list (including changes to the code), the code isn't actually updated, but the modified signature list and minSig data structures in the persistent contract storage are indeed returned to the fift stack and re-used for the next TVM method call, allowing us to test the feature. + + +## Collator testing + +After running `test.sh` and `test-update.sh` (thus generating all BOC files), you can run the `test-collator.sh` script, to: + +* Automatically configure a TON collator instance +* Make a query to the testgiver on the local collator instance +* Load the wallet smart contract +* Load a query with 2 signatures (`a`, `b`) +* Upgrade the smart contract to accept 3 signatures +* Load one more signature and send the message \ No newline at end of file diff --git a/wallet/test-collator.sh b/wallet/test-collator.sh new file mode 100755 index 0000000..b4f0fc2 --- /dev/null +++ b/wallet/test-collator.sh @@ -0,0 +1,22 @@ +#!/bin/bash -ex +{ + + address=$(cat tests/naddr.addr) + + cd ../lib/build + rm -rf new + mkdir -p new + cd new + mkdir -p db/static + + ../crypto/create-state ../../../toolchain/gen-zerostate.fif | tee log + mv basestate0.boc db/static/$(sed '/Basestate0 file hash= /!d;s/Basestate0 file hash= //g;s/ .*//g' log) + cp zerostate.boc db/static/$(sed '/Zerostate file hash= /!d;s/Zerostate file hash= //g;s/ .*//g' log) + + rm log + + fift -s ../../../toolchain/testgiver.fif $address 0 20 + + ../test-ton-collator -z zerostate.boc -D db -v5 -m testgiver-query.boc + ../test-ton-collator -z zerostate.boc -D db -v5 -w 0 -m ../../../wallet/tests/pony-create.boc -m ../../../wallet/tests/b.boc -m ../../../wallet/tests/code-update-merge.boc -m ../../../wallet/tests/c.boc +} 2>&1 | less -R diff --git a/wallet/test.sh b/wallet/test.sh index 99c4f88..e16fd4e 100755 --- a/wallet/test.sh +++ b/wallet/test.sh @@ -13,6 +13,8 @@ for f in {a..j}; do fift -s ../gen-pub.fif $f;done fift -s ../wallet-create.fif 0 pony 10 10 {a..j} | tee log # Get wallet address +sed '/Non-bounceable address [(]for init[)]: /!d;s/.* //g' log > naddr.addr + address=$(sed '/Bounceable address [(]for later access[)]: /!d;s/.* //g' log) rm log