(*****************************************************************************) (* *) (* Open Source License *) (* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) (* to deal in the Software without restriction, including without limitation *) (* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) (* and/or sell copies of the Software, and to permit persons to whom the *) (* Software is furnished to do so, subject to the following conditions: *) (* *) (* The above copyright notice and this permission notice shall be included *) (* in all copies or substantial portions of the Software. *) (* *) (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) (* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) (* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) (* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) (* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) (* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) (* DEALINGS IN THE SOFTWARE. *) (* *) (*****************************************************************************) open Protocol open Test_utils open Test_tez let ten_tez = Tez.of_int 10 (** [register_origination fee credit spendable delegatable] takes four optional parameter: fee for the fee need to be paid if set to create an originated contract; credit is the amount of tez that send to this originated contract; spendable default is set to true meaning that this contract is spendable; delegatable default is set to true meaning that this contract is able to delegate. *) let register_origination ?(fee=Tez.zero) ?(credit=Tez.zero) () = Context.init 1 >>=? fun (b, contracts) -> let source = List.hd contracts in Context.Contract.balance (B b) source >>=? fun source_balance -> Op.origination (B b) source ~fee ~credit ~script:Op.dummy_script >>=? fun (operation, originated) -> Block.bake ~operation b >>=? fun b -> (* fee + credit + block security deposit were debited from source *) Context.get_constants (B b) >>=? fun {parametric = { origination_size ; cost_per_byte ; block_security_deposit ; _ }; _ } -> Tez.(cost_per_byte *? Int64.of_int origination_size) >>?= fun origination_burn -> Lwt.return ( Tez.(+?) credit block_security_deposit >>? Tez.(+?) fee >>? Tez.(+?) origination_burn >>? Tez.(+?) Op.dummy_script_cost ) >>=? fun total_fee -> Assert.balance_was_debited ~loc:__LOC__ (B b) source source_balance total_fee >>=? fun () -> (* originated contract has been credited *) Assert.balance_was_credited ~loc:__LOC__ (B b) originated Tez.zero credit >>=? fun () -> (* TODO spendable or not and delegatable or not if relevant for some test. Not the case at the moment, cf. uses of register_origination *) return (b, source, originated) (* [test_origination_balances fee credit spendable delegatable] takes four optional parameter: fee is the fee that pay if require to create an originated contract; credit is the amount of tez that will send to this contract; delegatable default is set to true meaning that this contract is able to delegate. This function will create a contract, get the balance of this contract, call the origination operation to create a new originated contract from this contract with all the possible fees; and check the balance before/after originated operation valid. - the source contract has payed all the fees - the originated has been credited correctly *) let test_origination_balances ~loc:_ ?(fee=Tez.zero) ?(credit=Tez.zero) () = Context.init 1 >>=? fun (b, contracts) -> let contract = List.hd contracts in Context.Contract.balance (B b) contract >>=? fun balance -> Op.origination (B b) contract ~fee ~credit ~script:Op.dummy_script >>=? fun (operation, new_contract) -> (* The possible fees are: a given credit, an origination burn fee (constants_repr.default.origination_burn = 257 mtez), a fee that is paid when creating an originate contract. We also take into account a block security deposit. Note that it is not related to origination but to the baking done in the tests.*) Context.get_constants (B b) >>=? fun { parametric = { origination_size ; cost_per_byte ; block_security_deposit ; _ } ; _ } -> Tez.(cost_per_byte *? Int64.of_int origination_size) >>?= fun origination_burn -> Lwt.return ( Tez.(+?) credit block_security_deposit >>? Tez.(+?) fee >>? Tez.(+?) origination_burn >>? Tez.(+?) Op.dummy_script_cost ) >>=? fun total_fee -> Block.bake ~operation b >>=? fun b -> (* check that after the block has been baked the source contract was debited all the fees *) Assert.balance_was_debited ~loc:__LOC__ (B b) contract balance total_fee >>=? fun _ -> (* check the balance of the originate contract is equal to credit *) Assert.balance_is ~loc:__LOC__ (B b) new_contract credit (******************************************************) (** Tests *) (******************************************************) (** compute half of the balance and divided it by nth times *) let two_nth_of_balance incr contract nth = Context.Contract.balance (I incr) contract >>=? fun balance -> Tez.(/?) balance nth >>?= fun res -> Tez.( *?) res 2L >>?= fun balance -> return balance (*******************) (** Basic test *) (*******************) let balances_simple () = test_origination_balances ~loc:__LOC__ () let balances_credit () = test_origination_balances ~loc:__LOC__ ~credit:ten_tez () let balances_credit_fee () = test_origination_balances ~loc:__LOC__ ~credit:(Tez.of_int 2) ~fee:ten_tez () let balances_undelegatable () = test_origination_balances ~loc:__LOC__ () (*******************) (** ask source contract to pay a fee when originating a contract *) (*******************) let pay_fee () = register_origination ~credit:(Tez.of_int 2) ~fee:ten_tez () >>=? fun (_b, _contract, _new_contract) -> return_unit (******************************************************) (** Errors *) (******************************************************) (*******************) (** create an originate contract where the contract does not have enough tez to pay for the fee *) (*******************) let not_tez_in_contract_to_pay_fee () = Context.init 2 >>=? fun (b, contracts) -> let contract_1 = List.nth contracts 0 in let contract_2 = List.nth contracts 1 in Incremental.begin_construction b >>=? fun inc -> (* transfer everything but one tez from 1 to 2 and check balance of 1 *) Context.Contract.balance (I inc) contract_1 >>=? fun balance -> Lwt.return @@ Tez.(-?) balance Tez.one >>=? fun amount -> Op.transaction (I inc) contract_1 contract_2 amount >>=? fun operation -> Incremental.add_operation inc operation >>=? fun inc -> Assert.balance_was_debited ~loc:__LOC__ (I inc) contract_1 balance amount >>=? fun _ -> (* use this source contract to create an originate contract where it requires to pay a fee and add an amount of credit into this new contract *) Op.origination (I inc) ~fee:ten_tez ~credit:Tez.one contract_1 ~script:Op.dummy_script >>=? fun (op, _) -> Incremental.add_operation inc op >>= fun inc -> Assert.proto_error ~loc:__LOC__ inc begin function | Contract_storage.Balance_too_low _ -> true | _ -> false end (***************************************************) (* set the endorser of the block as manager/delegate of the originated account *) (***************************************************) let register_contract_get_endorser () = Context.init 1 >>=? fun (b, contracts) -> let contract = List.hd contracts in Incremental.begin_construction b >>=? fun inc -> Context.get_endorser (I inc) >>=? fun (account_endorser, _slots) -> return (inc, contract, account_endorser) (*******************) (** create multiple originated contracts and ask contract to pay the fee *) (*******************) let n_originations n ?credit ?fee () = fold_left_s (fun new_contracts _ -> register_origination ?fee ?credit () >>=? fun (_b, _source, new_contract) -> let contracts = new_contract :: new_contracts in return contracts ) [] (1 -- n) let multiple_originations () = n_originations 100 ~credit:(Tez.of_int 2) ~fee:ten_tez () >>=? fun contracts -> Assert.equal_int ~loc:__LOC__ (List.length contracts) 100 (*******************) (** cannot originate two contracts with the same context's counter *) (*******************) let counter () = Context.init 1 >>=? fun (b, contracts) -> let contract = List.hd contracts in Incremental.begin_construction b >>=? fun inc -> Op.origination (I inc) ~credit:Tez.one contract ~script:Op.dummy_script >>=? fun (op1, _) -> Op.origination (I inc) ~credit:Tez.one contract ~script:Op.dummy_script >>=? fun (op2, _) -> Incremental.add_operation inc op1 >>=? fun inc -> Incremental.add_operation inc op2 >>= fun res -> Assert.proto_error ~loc:__LOC__ res begin function | Contract_storage.Counter_in_the_past _ -> true | _ -> false end (******************************************************) let tests = [ Test.tztest "balances_simple" `Quick balances_simple ; Test.tztest "balances_credit" `Quick balances_credit ; Test.tztest "balances_credit_fee" `Quick balances_credit_fee ; Test.tztest "balances_undelegatable" `Quick balances_undelegatable ; Test.tztest "pay_fee" `Quick pay_fee; Test.tztest "not enough tez in contract to pay fee" `Quick not_tez_in_contract_to_pay_fee; Test.tztest "multiple originations" `Quick multiple_originations; Test.tztest "counter" `Quick counter; ]