Verified Commit 01b5bb08 authored by Benjamin Canou's avatar Benjamin Canou Committed by Marco Stronati

Alpha/Michelson: do not allow spendable smart contracts

This patches forbids the creation of spendable smart contracts, a
current feature that is not necessary (it can be implemented from the
contract's code) and prevents some possible future optimisations.

It also prevents the creation of non spendable non scripted contracts,
which was mostly a design flaw allowing people to lock funds.

This is done by checking the flag in the origination operation rather
than removing it so the change is lighter for third party software.
parent f6d67b7a
......@@ -33,6 +33,8 @@ type error += Duplicate_endorsement of Signature.Public_key_hash.t (* `Branch *)
type error += Invalid_endorsement_level
type error += Invalid_commitment of { expected: bool }
type error += Internal_operation_replay of packed_internal_operation
type error += Cannot_originate_spendable_smart_contract (* `Permanent *)
type error += Cannot_originate_non_spendable_account (* `Permanent *)
type error += Invalid_double_endorsement_evidence (* `Permanent *)
type error += Inconsistent_double_endorsement_evidence
......@@ -133,6 +135,30 @@ let () =
Operation.internal_operation_encoding
(function Internal_operation_replay op -> Some op | _ -> None)
(fun op -> Internal_operation_replay op) ;
register_error_kind
`Permanent
~id:"cannot_originate_non_spendable_account"
~title:"Cannot originate non spendable account"
~description:"An origination was attempted \
that would create a non spendable, non scripted contract"
~pp:(fun ppf () ->
Format.fprintf ppf "It is not possible anymore to originate \
a non scripted contract that is not spendable.")
Data_encoding.empty
(function Cannot_originate_non_spendable_account -> Some () | _ -> None)
(fun () -> Cannot_originate_non_spendable_account) ;
register_error_kind
`Permanent
~id:"cannot_originate_spendable_smart_contract"
~title:"Cannot originate spendable smart contract"
~description:"An origination was attempted \
that would create a spendable scripted contract"
~pp:(fun ppf () ->
Format.fprintf ppf "It is not possible anymore to originate \
a scripted contract that is spendable.")
Data_encoding.empty
(function Cannot_originate_spendable_smart_contract -> Some () | _ -> None)
(fun () -> Cannot_originate_spendable_smart_contract) ;
register_error_kind
`Permanent
~id:"block.invalid_double_endorsement_evidence"
......@@ -460,15 +486,22 @@ let apply_manager_operation_content :
| Origination { manager ; delegate ; script ; preorigination ;
spendable ; delegatable ; credit } ->
begin match script with
| None -> return (None, ctxt)
| None ->
if spendable then
return (None, ctxt)
else
fail Cannot_originate_non_spendable_account
| Some script ->
Script.force_decode ctxt script.storage >>=? fun (unparsed_storage, ctxt) -> (* see [note] *)
Lwt.return (Gas.consume ctxt (Script.deserialized_cost unparsed_storage)) >>=? fun ctxt ->
Script.force_decode ctxt script.code >>=? fun (unparsed_code, ctxt) -> (* see [note] *)
Lwt.return (Gas.consume ctxt (Script.deserialized_cost unparsed_code)) >>=? fun ctxt ->
Script_ir_translator.parse_script ctxt script >>=? fun (ex_script, ctxt) ->
Script_ir_translator.big_map_initialization ctxt Optimized ex_script >>=? fun (big_map_diff, ctxt) ->
return (Some (script, big_map_diff), ctxt)
if spendable then
fail Cannot_originate_spendable_smart_contract
else
Script.force_decode ctxt script.storage >>=? fun (unparsed_storage, ctxt) -> (* see [note] *)
Lwt.return (Gas.consume ctxt (Script.deserialized_cost unparsed_storage)) >>=? fun ctxt ->
Script.force_decode ctxt script.code >>=? fun (unparsed_code, ctxt) -> (* see [note] *)
Lwt.return (Gas.consume ctxt (Script.deserialized_cost unparsed_code)) >>=? fun ctxt ->
Script_ir_translator.parse_script ctxt script >>=? fun (ex_script, ctxt) ->
Script_ir_translator.big_map_initialization ctxt Optimized ex_script >>=? fun (big_map_diff, ctxt) ->
return (Some (script, big_map_diff), ctxt)
end >>=? fun (script, ctxt) ->
spend ctxt source credit >>=? fun ctxt ->
begin match preorigination with
......
......@@ -61,16 +61,22 @@ let register_origination ?(fee=Tez.zero) ?(credit=Tez.zero) ?spendable ?delegata
(* [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; 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.
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.
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)
?spendable ?delegatable () =
?delegatable () =
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 ?spendable ?delegatable >>=? fun (operation, new_contract) ->
Op.origination (B b) contract ~fee ~credit ?delegatable >>=? 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.
......@@ -132,9 +138,6 @@ let balances_credit () =
let balances_credit_fee () =
test_origination_balances ~loc:__LOC__ ~credit:(Tez.of_int 2) ~fee:ten_tez ()
let balances_credit_unspendable () =
test_origination_balances ~loc:__LOC__ ~credit:Tez.one ~spendable:false ()
let balances_undelegatable () =
test_origination_balances ~loc:__LOC__ ~delegatable:false ()
......@@ -168,18 +171,20 @@ let pay_fee () =
(******************************************************)
(*******************)
(** The originate contract is marked as unspendable. Then ask this
contract to transfer, it will raise an error *)
(** Originating an unspendable contract w/o code reises an error. *)
(*******************)
let unspendable () =
register_origination ~credit:Tez.one ~spendable:false () >>=? fun (b, contract, new_contract) ->
Op.transaction (B b) new_contract contract Tez.one_cent >>=? fun operation ->
Block.bake ~operation b >>= fun e ->
let unspendable = function
| Proto_alpha.Contract_storage.Unspendable_contract _ -> true
| _ -> false in
Assert.proto_error ~loc:__LOC__ e unspendable
Context.init 1 >>=? fun (b, contracts) ->
Incremental.begin_construction b >>=? fun i ->
let source = List.hd contracts in
Op.origination (I i) source ~fee:Tez.zero ~credit:Tez.one ~spendable:false >>=? fun (operation, _contract) ->
Incremental.add_operation i operation >>= fun res ->
let cannot_originate = function
| Apply.Cannot_originate_non_spendable_account -> true
| _ -> false
in
Assert.proto_error ~loc:__LOC__ res cannot_originate
(*******************)
(** The originate contract is marked as undelegatable. Then do the delegation
......@@ -407,7 +412,6 @@ 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_credit_unspendable" `Quick balances_credit_unspendable ;
Test.tztest "balances_undelegatable" `Quick balances_undelegatable ;
Test.tztest "regular" `Quick regular ;
......
......@@ -534,22 +534,6 @@ let add_the_same_operation_twice () =
| _ -> false
end
(********************)
(** Do the transfer from an "unspendable" contract *)
(********************)
let unspendable_contract () =
register_two_contracts () >>=? fun (b, contract_1, contract_2) ->
Incremental.begin_construction b >>=? fun b ->
Op.origination ~spendable:false (I b) contract_1 >>=? fun (operation, unspendable_contract) ->
Incremental.add_operation b operation >>=? fun b ->
Op.transaction (I b) unspendable_contract contract_2 Alpha_context.Tez.one_cent >>=? fun operation ->
Incremental.add_operation b operation >>= fun res ->
Assert.proto_error ~loc:__LOC__ res begin function
| Contract_storage.Unspendable_contract _ -> true
| _ -> false
end
(********************)
(** check ownership *)
(********************)
......@@ -658,7 +642,6 @@ let tests = [
Test.tztest "balance too low with two transfers" `Quick (balance_too_low_two_transfers Tez.one);
Test.tztest "invalid_counter" `Quick invalid_counter ;
Test.tztest "add the same operation twice" `Quick add_the_same_operation_twice ;
Test.tztest "unspendable contract" `Quick unspendable_contract ;
Test.tztest "ownership sender" `Quick ownership_sender ;
(* Random tests *)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment