From 661f654f544007ffb39f4d66a98e130f5500ffc6 Mon Sep 17 00:00:00 2001 From: Aaro Altonen Date: Wed, 20 Oct 2021 15:35:38 +0300 Subject: [PATCH] tests: Implement functional tests for programmable pools Implement functional tests for PP and create assets directory which contains all smart contract related files --- .../{metadata.json => assets/c2c_tester.json} | 60 ++-- test/functional/assets/c2c_tester.wasm | Bin 0 -> 2118 bytes test/functional/assets/pooltester.json | 216 ++++++++++++ test/functional/assets/pooltester.wasm | Bin 0 -> 6455 bytes test/functional/code.wasm | Bin 2378 -> 0 bytes .../functional/feature_smart_contract_test.py | 318 +++++++++++++++--- test/functional/test_runner.py | 4 +- 7 files changed, 502 insertions(+), 96 deletions(-) rename test/functional/{metadata.json => assets/c2c_tester.json} (56%) create mode 100644 test/functional/assets/c2c_tester.wasm create mode 100644 test/functional/assets/pooltester.json create mode 100644 test/functional/assets/pooltester.wasm delete mode 100644 test/functional/code.wasm diff --git a/test/functional/metadata.json b/test/functional/assets/c2c_tester.json similarity index 56% rename from test/functional/metadata.json rename to test/functional/assets/c2c_tester.json index cf24b11..bde799b 100644 --- a/test/functional/metadata.json +++ b/test/functional/assets/c2c_tester.json @@ -1,46 +1,22 @@ { "metadataVersion": "0.1.0", "source": { - "hash": "0xd2d4276c5864e736fe51fd70c2e76ae600520dbead60bf3b8054f6d3d13e3dd7", + "hash": "0x241e86037e1803891112031bb03b7816d4b00d50effc920dae39c2004efb2ca4", "language": "ink! 3.0.0-rc4", "compiler": "rustc 1.56.0-nightly" }, "contract": { - "name": "pooltest", + "name": "c2c_tester", "version": "0.1.0", "authors": [ - "[your_name] <[your_email]>" + "RBB S.r.l" ] }, "spec": { "constructors": [ - { - "args": [ - { - "name": "init_value", - "type": { - "displayName": [ - "bool" - ], - "type": 1 - } - } - ], - "docs": [ - "Constructor that initializes the `bool` value to the given `init_value`." - ], - "name": [ - "new" - ], - "selector": "0x9bae9d5e" - }, { "args": [], - "docs": [ - "Constructor that initializes the `bool` value to `false`.", - "", - "Constructors can delegate to other constructors." - ], + "docs": [], "name": [ "default" ], @@ -51,25 +27,29 @@ "events": [], "messages": [ { - "args": [], - "docs": [ - " A message that can be called on instantiated contracts.", - " This one flips the value of the stored `bool` from `true`", - " to `false` and vice versa." + "args": [ + { + "name": "value", + "type": { + "displayName": [ + "u32" + ], + "type": 1 + } + } ], + "docs": [], "mutates": true, "name": [ - "flip" + "set_value" ], "payable": false, "returnType": null, - "selector": "0x633aa551" + "selector": "0xc6298215" }, { "args": [], - "docs": [ - " Simply returns the current value of our `bool`." - ], + "docs": [], "mutates": false, "name": [ "get" @@ -77,7 +57,7 @@ "payable": false, "returnType": { "displayName": [ - "bool" + "u32" ], "type": 1 }, @@ -103,7 +83,7 @@ "types": [ { "def": { - "primitive": "bool" + "primitive": "u32" } } ] diff --git a/test/functional/assets/c2c_tester.wasm b/test/functional/assets/c2c_tester.wasm new file mode 100644 index 0000000000000000000000000000000000000000..764fe2b6aca67ae3c36aab67dccf395ffcb9b6cd GIT binary patch literal 2118 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 literal 0 HcmV?d00001 diff --git a/test/functional/code.wasm b/test/functional/code.wasm deleted file mode 100644 index d3c74df855ead838d7d7b7045c66aba336fb3423..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2378 zcma)8&x>3|5U#53_vR&UCfgB}Wf!sEbA=7KIAX{ydvSVTckv*45IklVGX~%O%FfOP z0Gs_KRIP7j%g_`PCR zrc>JGDNlFJRF~AQ@M3H&gh}CC9&?_Zj`j}vPJeIT9ZhbZPL9WWcSmGjJ~(~pK+?gR z(c=Ez!P)5cWW0BD`qgMW9^D~I$5-R=*GKoxCNohPMdQ)rYn1njHVPmtr0EBRHVav{S$?uHr9UMQPYxc{%g9CC`e{t7-W#;KS6b4PrE95!& z##2~qZWvlOx?j}2yWBh77xVoPYO9_-MS}tcMdNb3E-v_%+{EI?3wulYGs%YTvDs)G zReFEF@w_SubYizQiK+_ zzCtuZUN_$3{uo#--_#I5SGPrNAeYadM`4kKt`-PQD|AV?*y^pDMNj1iDN}C07Pu7v z#WAf4E?pJtdax1Pz7ByPV=Gh+Sv0q>NV$atIe=QDD~f%MiV9w%Zjiu10EV}h18!W# z9elD}es{@F@(O;Gl$IY+e)NX<9{eZ=vwh+RURr)MZ15u>AW@{jMv87?LCTb10aj2S zUKAaEtV60L4NB=K$Y`HP+L!U_b3i ztk;%cM0m0l)j+y{lQnAHa8pT_%QA2!fv^gMDlkL;_*oe{LQBWXb5GBQ#iNJi5(4z* z6WoPGLM=e8aNnRnZb5q@$V9umK{m^ua>){eek?qVTVS2SiGA6f<+yFw*Wf~ttQ!|h z8-(lD`Li0Vo%F=#!x#GR?QiQ`1BFUHT*O;X9NrcVOepTh{xsnS)&@151=n?py8{=|8Kc!0kKTyau zB8*-lER@uV9R!9KF8>pqZP6LnW;1kIcwfO~;cp#Z|rH{0le-iSf6@jv;qzt*Io-BER9}cUHmLOr}=c~opN{?Iu227`EbtM#< zyjL@AjW?-CMNc|YAuP~oT>$KAQLPbGjB~$V$8(IG+4!K;t*=8B-HXYogHb;jp54`e zyH?RA_~|-JrB+vj7tD4oJz7$zUY0Ls`YWK--ns+|{$2$rB8l`PH)_z=`TUf{q)wUR zitV8TCXOIPpI|!OM^p|&TH!d?(M3B}^^;k1;)xO`^E?EHsU!A$h^Yg8SE1ZtR(kXB zvn?-U^d$xprWE*Hp;$?m-K=f?1!q%rX>04{<2utu#+K^5>KF_pZ{p|Bn+CJC(|{`- zXz1LgGCFAxCk!z{fdJY$h=up8{BZt3i=U_HB65zqk)m$n3=A;NyTGc+L(0l`SyN)m Vx0ne3&mb~*ES?Ncj;DjC_!s7WchUd= diff --git a/test/functional/feature_smart_contract_test.py b/test/functional/feature_smart_contract_test.py index d155788..487c841 100755 --- a/test/functional/feature_smart_contract_test.py +++ b/test/functional/feature_smart_contract_test.py @@ -3,9 +3,9 @@ # Copyright (c) 2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Smart contract test -""" +""" Smart contract test """ + # Imports should be in PEP8 ordering (std library first, then third party # libraries then local imports). from collections import defaultdict @@ -23,6 +23,23 @@ import os +# helper function to reduce code duplication +def submit_pp_tx(client, input_utxo, alice, value, output): + tx = utxo.Transaction( + client, + inputs=[ + utxo.Input(input_utxo.outpoint(0)), + ], + outputs=[ + utxo.Output( + value=value, + destination=utxo.DestPubkey(alice.public_key), + data=None, + ), + output + ] + ).sign(alice, [input_utxo.outputs[0]], [0]) + return tx, client.submit(alice, tx) class ExampleTest(MintlayerTestFramework): # Each functional test is a subclass of the MintlayerTestFramework class. @@ -56,97 +73,290 @@ def setup_network(self): # sync_all() should not include node2, since we're not expecting it to # sync. connect_nodes(self.nodes[0], self.nodes[1]) - # self.sync_all([self.nodes[0:1]]) def run_test(self): """Main test logic""" client = self.nodes[0].rpc_client - substrate = client.substrate - alice = Keypair.create_from_uri('//Alice') + bob = Keypair.create_from_uri('//Erin') - # Find a suitable UTXO initial_utxo = [x for x in client.utxos_for(alice) if x[1].value >= 50][0] + value = initial_utxo[1].json()["value"] + + self.log.error(initial_utxo) - tx0 = utxo.Transaction( + tx = utxo.Transaction( client, inputs=[ utxo.Input(initial_utxo[0]), ], outputs=[ utxo.Output( - value=50, + value=value, destination=utxo.DestPubkey(alice.public_key), - data=None + data=None, ), - utxo.Output( - value=10, - destination=utxo.DestCreatePP( - code=os.path.join(os.path.dirname(__file__), "code.wasm"), - data=[0xed, 0x4b, 0x9d, 0x1b], # default() constructor selector - ), - data=None - ), - # This output prevent reward overflow - utxo.Output( - value=3981553255926290448385, # = genesis amount - u64::MAX - destination=utxo.DestPubkey(alice.public_key), - data=None - ) ] ).sign(alice, [initial_utxo[1]]) + client.submit(alice, tx) - # submit transaction and get the extrinsic and block hashes - (ext, blk,_) = client.submit(alice, tx0) + # invalid bytecode + value -= 1 - # each new smart contract instantiation creates a new account - # fetch this SS58-formatted account address and return it - # and the hex-encoded account id - (ss58, acc_id) = contract.getContractAddresses(substrate, blk) + (tx, (_, blk, _)) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value=1, + destination=utxo.DestCreatePP( + code=[0x00], + data=[0xed, 0x4b, 0x9d, 0x1b], + ), + data=None, + )) + assert_equal(contract.getContractAddresses(substrate, blk), None) + + # invalid value + (invalid_tx, res) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value=0, + destination=utxo.DestCreatePP( + code=os.path.join(os.path.dirname(__file__), "assets/pooltester.wasm"), + data=[0xed, 0x4b, 0x9d, 0x1b], + ), + data=None, + )) + assert_equal(res, None) - # create new contract instance which can be used to interact - # with the instantiated contract + # valid data + value -= 1 + + (tx, (_, blk, _)) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value=1, + destination=utxo.DestCreatePP( + code=os.path.join(os.path.dirname(__file__), "assets/pooltester.wasm"), + data=[0xed, 0x4b, 0x9d, 0x1b], + ), + data=None, + )) + + (ss58, acc_id) = contract.getContractAddresses(substrate, blk) contractInstance = contract.ContractInstance( ss58, - os.path.join(os.path.dirname(__file__), "metadata.json"), + os.path.join(os.path.dirname(__file__), "assets/pooltester.json"), substrate ) - # read the value of the flipper contract + # verify the initial state of the smart contract result = contractInstance.read(alice, "get") - assert_equal(result.contract_result_data.value, False) + assert_equal(result.contract_result_data.value, 1337) + # valid contract call + value -= 1 msg_data = contractInstance.generate_message_data("flip", {}) - self.log.info("Contract msg_data: {}, {}, {}".format(ss58, acc_id, msg_data)) - tx1 = utxo.Transaction( + (tx, _) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value=1, + destination=utxo.DestCallPP( + dest_account=acc_id, + fund=False, + input_data=bytes.fromhex(msg_data.to_hex()[2:]), + ), + data=None, + )) + result = contractInstance.read(alice, "get") + assert_equal(result.contract_result_data.value, -1337) + + # invalid `value` given + msg_data = contractInstance.generate_message_data("flip", {}) + + (invalid_tx, res) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value=0, + destination=utxo.DestCallPP( + dest_account=alice.public_key, + fund=False, + input_data=bytes.fromhex(msg_data.to_hex()[2:]), + ), + data=None, + )) + assert_equal(res, None) + + # test contract-to-p2k transfer from alice to bob + # + # `send_to_pubkey()` first funds the smart contract from alice's funds + # and when the wasm code is executed, the funds are transferred to bob + msg_data = contractInstance.generate_message_data("send_to_pubkey", { "dest": bob.public_key }) + value -= 555 + + (tx, _) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value = 555, + destination = utxo.DestCallPP( + dest_account = acc_id, + fund = True, + input_data = bytes.fromhex(msg_data.to_hex()[2:]), + ), + data = None, + )) + + # verify that bob actually received the utxo + bobs_utxos = [x for x in client.utxos_for(bob)] + assert_equal(len(bobs_utxos), 1) + assert_equal(bobs_utxos[0][1].json()['value'], 555) + + # test contract-to-p2pk again but this time don't fund the contract + # meaning that after the TX, bob only has the UTXO he received in the previous test case + # and the contract has a UTXO with value 666 + msg_data = contractInstance.generate_message_data("send_to_pubkey", { "dest": bob.public_key }) + value -= 666 + + (tx, _) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value = 666, + destination = utxo.DestCallPP( + dest_account = acc_id, + fund = False, + input_data = bytes.fromhex(msg_data.to_hex()[2:]), + ), + data = None, + )) + + # verify that bob still has the same amount of UTXOs + utxos = [x for x in client.utxos_for(bob)] + assert_equal(len(utxos), 1) + + # verify that the contract has one utxo with value 666 + utxos = [x for x in client.utxos_for(acc_id[2:])] + assert_equal(len(utxos), 1) + assert_equal(utxos[0][1].json()["value"], 666) + + # try to call a contract that doesn't exist (alice's public key + # doesn't point to a valid smart contract) + # + # TODO: because we don't have gas refunding, the money is still + # spent, i.e., if the UTXO set is queried, you'll find a UTXO + # with value 888 meaning user just lost his money which is + # not the correct behavior but the implementation is still under way + msg_data = contractInstance.generate_message_data("fund", {}) + value -= 888 + + (tx, _) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value = 888, + destination = utxo.DestCallPP( + dest_account = alice.public_key, + fund = True, + input_data = bytes.fromhex(msg_data.to_hex()[2:]), + ), + data = None, + )) + + result = contractInstance.read(alice, "get") + assert_equal(result.contract_result_data.value, -1337) + + # Test cross-contract calls + # + # First instantiate another smart contract and verify it has + # been created correctly by querying its value. + # + # Then call the `set_value()` method of newly instantiated contract + # indirectly by creating a UTXO that calls the pooltester's + # `call_contract()` method which dispatches the call to `set_value()` + # + # When all that's done, query the value again and verify that it has been updated + value -= 111 + + (tx, (_, blk, _)) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value = 111, + destination = utxo.DestCreatePP( + code = os.path.join(os.path.dirname(__file__), "assets/c2c_tester.wasm"), + data = [0xed, 0x4b, 0x9d, 0x1b], + ), + data = None, + )) + + (ss58_c2c, acc_id_c2c) = contract.getContractAddresses(substrate, blk) + c2cInstance = contract.ContractInstance( + ss58_c2c, + os.path.join(os.path.dirname(__file__), "assets/c2c_tester.json"), + substrate + ) + + # verify the initial state of the smart contract + result = c2cInstance.read(alice, "get") + assert_equal(result.contract_result_data.value, 555) + + msg_data = contractInstance.generate_message_data("call_contract", { + "dest": acc_id_c2c, + "selector": "0xc6298215", + "value": 999, + }) + value -= 222 + + (tx, _) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value = 222, + destination = utxo.DestCallPP( + dest_account = acc_id, + fund = True, + input_data = bytes.fromhex(msg_data.to_hex()[2:]), + ), + data = None, + )) + + # verify that the call succeeded + result = c2cInstance.read(alice, "get") + assert_equal(result.contract_result_data.value, 999) + + # Try to spend the funds of a contract + # + # First fund the contract with some amount of UTXO, + # verify that the fund worked (updated state variable) + # and then try to spend those funds and verify that the + # spend is rejected by the local PP node because the + # smart contract has not spent them and thus the outpoint + # hash is not in the local storage + # + # NOTE: spending the DestCallPP UTXOs doesn't require signatures + # but instead the outpoint hash of the UTXO. This is queried + # from the runtime storage as the smart contract has not transferred + # these funds, the outpoint hash is **not** found from the storage + # and this TX is rejected as invalid + msg_data = contractInstance.generate_message_data("fund", {}) + value -= 555 + + self.log.info("here") + self.log.error(tx) + + (tx, _) = submit_pp_tx(client, tx, alice, value, utxo.Output( + value = 555, + destination = utxo.DestCallPP( + dest_account = acc_id, + fund = True, + input_data = bytes.fromhex(msg_data.to_hex()[2:]), + ), + data = None, + )) + + result = contractInstance.read(alice, "get") + assert_equal(result.contract_result_data.value, 1338) + + utxos = [x for x in client.utxos_for(acc_id[2:])] + assert_equal(len(utxos), 1) + assert_equal(utxos[0][1].json()["value"], 555) + + invalid_tx = utxo.Transaction( client, - inputs=[ - utxo.Input(tx0.outpoint(0)), + inputs = [ + utxo.Input(utxos[0][0]), ], outputs=[ utxo.Output( - value=49, + value=555, destination=utxo.DestPubkey(alice.public_key), - data=None - ), - utxo.Output( - value=1, - destination=utxo.DestCallPP( - dest_account=acc_id, - fund=False, - input_data=bytes.fromhex(msg_data.to_hex()[2:]), - ), - data=None + data=None, ), ] - ).sign(alice, [tx0.outputs[0]], [0]) - (ext_hash, blk_hash,_) = client.submit(alice, tx1) - - result = contractInstance.read(alice, "get") - assert_equal(result.contract_result_data.value, True) + ) + # size of the outpoint (32 bytes, 0x10) + the outpoint itself + # the outpoint in the witness field is valid but because the + # smart contract has not spent the funds, the TX is rejected + tx.inputs[0].witness = bytearray.fromhex("10" + str(utxos[0][0])[2:]) + assert_equal(client.submit(alice, invalid_tx), None) if __name__ == '__main__': ExampleTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index c4dd2ed..ad07c29 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -64,9 +64,9 @@ 'feature_staking_diff_addresses.py', 'feature_staking_unlock_not_validator.py', 'feature_staking_withdraw_no_unlock.py', - 'feature_staking_withdraw_not_validator.py' + 'feature_staking_withdraw_not_validator.py', + 'feature_smart_contract_test.py', # 'feature_staking_unlock_and_withdraw.py' ## should be ran on 20 secs - # 'feature_smart_contract_test.py', # Don't append tests at the end to avoid merge conflicts # Put them in a random line within the section that fits their approximate run-time ]