2019-09-05 17:21:01 +04:00
|
|
|
(*****************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* Open Source License *)
|
|
|
|
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* 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 Alpha_context
|
|
|
|
open Script
|
|
|
|
open Script_typed_ir
|
|
|
|
open Script_ir_translator
|
|
|
|
|
|
|
|
(* ---- Run-time errors -----------------------------------------------------*)
|
|
|
|
|
|
|
|
type execution_trace =
|
|
|
|
(Script.location * Gas.t * (Script.expr * string option) list) list
|
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
type error +=
|
|
|
|
| Reject of Script.location * Script.expr * execution_trace option
|
|
|
|
|
2019-09-05 17:21:01 +04:00
|
|
|
type error += Overflow of Script.location * execution_trace option
|
2020-02-12 20:40:17 +04:00
|
|
|
|
2019-09-05 17:21:01 +04:00
|
|
|
type error += Runtime_contract_error : Contract.t * Script.expr -> error
|
2020-02-12 20:40:17 +04:00
|
|
|
|
2019-09-05 17:21:01 +04:00
|
|
|
type error += Bad_contract_parameter of Contract.t (* `Permanent *)
|
2020-02-12 20:40:17 +04:00
|
|
|
|
2019-09-05 17:21:01 +04:00
|
|
|
type error += Cannot_serialize_log
|
2020-02-12 20:40:17 +04:00
|
|
|
|
2019-09-05 17:21:01 +04:00
|
|
|
type error += Cannot_serialize_failure
|
2020-02-12 20:40:17 +04:00
|
|
|
|
2019-09-05 17:21:01 +04:00
|
|
|
type error += Cannot_serialize_storage
|
|
|
|
|
|
|
|
let () =
|
|
|
|
let open Data_encoding in
|
|
|
|
let trace_encoding =
|
2020-02-12 20:40:17 +04:00
|
|
|
list
|
|
|
|
@@ obj3
|
|
|
|
(req "location" Script.location_encoding)
|
|
|
|
(req "gas" Gas.encoding)
|
|
|
|
(req
|
|
|
|
"stack"
|
|
|
|
(list (obj2 (req "item" Script.expr_encoding) (opt "annot" string))))
|
|
|
|
in
|
2019-09-05 17:21:01 +04:00
|
|
|
(* Reject *)
|
|
|
|
register_error_kind
|
|
|
|
`Temporary
|
|
|
|
~id:"michelson_v1.script_rejected"
|
2020-02-12 20:40:17 +04:00
|
|
|
~title:"Script failed"
|
|
|
|
~description:"A FAILWITH instruction was reached"
|
2019-09-05 17:21:01 +04:00
|
|
|
(obj3
|
|
|
|
(req "location" Script.location_encoding)
|
|
|
|
(req "with" Script.expr_encoding)
|
|
|
|
(opt "trace" trace_encoding))
|
|
|
|
(function Reject (loc, v, trace) -> Some (loc, v, trace) | _ -> None)
|
2020-02-12 20:40:17 +04:00
|
|
|
(fun (loc, v, trace) -> Reject (loc, v, trace)) ;
|
2019-09-05 17:21:01 +04:00
|
|
|
(* Overflow *)
|
|
|
|
register_error_kind
|
|
|
|
`Temporary
|
|
|
|
~id:"michelson_v1.script_overflow"
|
2020-02-12 20:40:17 +04:00
|
|
|
~title:"Script failed (overflow error)"
|
|
|
|
~description:
|
|
|
|
"A FAIL instruction was reached due to the detection of an overflow"
|
2019-09-05 17:21:01 +04:00
|
|
|
(obj2
|
|
|
|
(req "location" Script.location_encoding)
|
|
|
|
(opt "trace" trace_encoding))
|
|
|
|
(function Overflow (loc, trace) -> Some (loc, trace) | _ -> None)
|
2020-02-12 20:40:17 +04:00
|
|
|
(fun (loc, trace) -> Overflow (loc, trace)) ;
|
2019-09-05 17:21:01 +04:00
|
|
|
(* Runtime contract error *)
|
|
|
|
register_error_kind
|
|
|
|
`Temporary
|
|
|
|
~id:"michelson_v1.runtime_error"
|
2020-02-12 20:40:17 +04:00
|
|
|
~title:"Script runtime error"
|
|
|
|
~description:"Toplevel error for all runtime script errors"
|
2019-09-05 17:21:01 +04:00
|
|
|
(obj2
|
|
|
|
(req "contract_handle" Contract.encoding)
|
|
|
|
(req "contract_code" Script.expr_encoding))
|
|
|
|
(function
|
|
|
|
| Runtime_contract_error (contract, expr) ->
|
|
|
|
Some (contract, expr)
|
2020-02-12 20:40:17 +04:00
|
|
|
| _ ->
|
|
|
|
None)
|
|
|
|
(fun (contract, expr) -> Runtime_contract_error (contract, expr)) ;
|
2019-09-05 17:21:01 +04:00
|
|
|
(* Bad contract parameter *)
|
|
|
|
register_error_kind
|
|
|
|
`Permanent
|
|
|
|
~id:"michelson_v1.bad_contract_parameter"
|
|
|
|
~title:"Contract supplied an invalid parameter"
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:
|
|
|
|
"Either no parameter was supplied to a contract with a non-unit \
|
|
|
|
parameter type, a non-unit parameter was passed to an account, or a \
|
|
|
|
parameter was supplied of the wrong type"
|
2019-09-05 17:21:01 +04:00
|
|
|
Data_encoding.(obj1 (req "contract" Contract.encoding))
|
|
|
|
(function Bad_contract_parameter c -> Some c | _ -> None)
|
|
|
|
(fun c -> Bad_contract_parameter c) ;
|
|
|
|
(* Cannot serialize log *)
|
|
|
|
register_error_kind
|
|
|
|
`Temporary
|
|
|
|
~id:"michelson_v1.cannot_serialize_log"
|
|
|
|
~title:"Not enough gas to serialize execution trace"
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:
|
|
|
|
"Execution trace with stacks was to big to be serialized with the \
|
|
|
|
provided gas"
|
2019-09-05 17:21:01 +04:00
|
|
|
Data_encoding.empty
|
|
|
|
(function Cannot_serialize_log -> Some () | _ -> None)
|
|
|
|
(fun () -> Cannot_serialize_log) ;
|
|
|
|
(* Cannot serialize failure *)
|
|
|
|
register_error_kind
|
|
|
|
`Temporary
|
|
|
|
~id:"michelson_v1.cannot_serialize_failure"
|
|
|
|
~title:"Not enough gas to serialize argument of FAILWITH"
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:
|
|
|
|
"Argument of FAILWITH was too big to be serialized with the provided gas"
|
2019-09-05 17:21:01 +04:00
|
|
|
Data_encoding.empty
|
|
|
|
(function Cannot_serialize_failure -> Some () | _ -> None)
|
|
|
|
(fun () -> Cannot_serialize_failure) ;
|
|
|
|
(* Cannot serialize storage *)
|
|
|
|
register_error_kind
|
|
|
|
`Temporary
|
|
|
|
~id:"michelson_v1.cannot_serialize_storage"
|
|
|
|
~title:"Not enough gas to serialize execution storage"
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:
|
|
|
|
"The returned storage was too big to be serialized with the provided gas"
|
2019-09-05 17:21:01 +04:00
|
|
|
Data_encoding.empty
|
|
|
|
(function Cannot_serialize_storage -> Some () | _ -> None)
|
|
|
|
(fun () -> Cannot_serialize_storage)
|
|
|
|
|
|
|
|
(* ---- interpreter ---------------------------------------------------------*)
|
|
|
|
|
|
|
|
type 'tys stack =
|
|
|
|
| Item : 'ty * 'rest stack -> ('ty * 'rest) stack
|
|
|
|
| Empty : end_of_stack stack
|
|
|
|
|
|
|
|
let unparse_stack ctxt (stack, stack_ty) =
|
|
|
|
(* We drop the gas limit as this function is only used for debugging/errors. *)
|
|
|
|
let ctxt = Gas.set_unlimited ctxt in
|
2020-02-12 20:40:17 +04:00
|
|
|
let rec unparse_stack :
|
|
|
|
type a.
|
|
|
|
a stack * a stack_ty -> (Script.expr * string option) list tzresult Lwt.t
|
|
|
|
= function
|
|
|
|
| (Empty, Empty_t) ->
|
|
|
|
return_nil
|
|
|
|
| (Item (v, rest), Item_t (ty, rest_ty, annot)) ->
|
|
|
|
unparse_data ctxt Readable ty v
|
|
|
|
>>=? fun (data, _ctxt) ->
|
|
|
|
unparse_stack (rest, rest_ty)
|
|
|
|
>>=? fun rest ->
|
|
|
|
let annot =
|
|
|
|
match Script_ir_annot.unparse_var_annot annot with
|
|
|
|
| [] ->
|
|
|
|
None
|
|
|
|
| [a] ->
|
|
|
|
Some a
|
|
|
|
| _ ->
|
|
|
|
assert false
|
|
|
|
in
|
|
|
|
let data = Micheline.strip_locations data in
|
|
|
|
return ((data, annot) :: rest)
|
|
|
|
in
|
2019-09-05 17:21:01 +04:00
|
|
|
unparse_stack (stack, stack_ty)
|
|
|
|
|
2019-10-17 13:45:27 +04:00
|
|
|
module Interp_costs = Michelson_v1_gas.Cost_of.Interpreter
|
2019-09-05 17:21:01 +04:00
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let rec interp_stack_prefix_preserving_operation :
|
|
|
|
type fbef bef faft aft result.
|
|
|
|
(fbef stack -> (faft stack * result) tzresult Lwt.t) ->
|
|
|
|
(fbef, faft, bef, aft) stack_prefix_preservation_witness ->
|
|
|
|
bef stack ->
|
|
|
|
(aft stack * result) tzresult Lwt.t =
|
|
|
|
fun f n stk ->
|
|
|
|
match (n, stk) with
|
|
|
|
| ( Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix
|
|
|
|
(Prefix (Prefix (Prefix (Prefix (Prefix n))))))))))))))),
|
|
|
|
Item
|
|
|
|
( v0,
|
|
|
|
Item
|
|
|
|
( v1,
|
|
|
|
Item
|
|
|
|
( v2,
|
|
|
|
Item
|
|
|
|
( v3,
|
|
|
|
Item
|
|
|
|
( v4,
|
|
|
|
Item
|
|
|
|
( v5,
|
|
|
|
Item
|
|
|
|
( v6,
|
|
|
|
Item
|
|
|
|
( v7,
|
|
|
|
Item
|
|
|
|
( v8,
|
|
|
|
Item
|
|
|
|
( v9,
|
|
|
|
Item
|
|
|
|
( va,
|
|
|
|
Item
|
|
|
|
( vb,
|
|
|
|
Item
|
|
|
|
( vc,
|
|
|
|
Item
|
|
|
|
( vd,
|
|
|
|
Item
|
|
|
|
( ve,
|
|
|
|
Item
|
|
|
|
(vf, rest)
|
|
|
|
) ) ) ) ) ) )
|
|
|
|
) ) ) ) ) ) ) ) ) ->
|
|
|
|
interp_stack_prefix_preserving_operation f n rest
|
|
|
|
>>=? fun (rest', result) ->
|
|
|
|
return
|
|
|
|
( Item
|
|
|
|
( v0,
|
|
|
|
Item
|
|
|
|
( v1,
|
|
|
|
Item
|
|
|
|
( v2,
|
|
|
|
Item
|
|
|
|
( v3,
|
|
|
|
Item
|
|
|
|
( v4,
|
|
|
|
Item
|
|
|
|
( v5,
|
|
|
|
Item
|
|
|
|
( v6,
|
|
|
|
Item
|
|
|
|
( v7,
|
|
|
|
Item
|
|
|
|
( v8,
|
|
|
|
Item
|
|
|
|
( v9,
|
|
|
|
Item
|
|
|
|
( va,
|
|
|
|
Item
|
|
|
|
( vb,
|
|
|
|
Item
|
|
|
|
( vc,
|
|
|
|
Item
|
|
|
|
( vd,
|
|
|
|
Item
|
|
|
|
( ve,
|
|
|
|
Item
|
|
|
|
( vf,
|
|
|
|
rest'
|
|
|
|
) ) )
|
|
|
|
) ) ) ) ) ) ) ) )
|
|
|
|
) ) ) ),
|
|
|
|
result )
|
|
|
|
| ( Prefix (Prefix (Prefix (Prefix n))),
|
|
|
|
Item (v0, Item (v1, Item (v2, Item (v3, rest)))) ) ->
|
|
|
|
interp_stack_prefix_preserving_operation f n rest
|
|
|
|
>>=? fun (rest', result) ->
|
|
|
|
return (Item (v0, Item (v1, Item (v2, Item (v3, rest')))), result)
|
|
|
|
| (Prefix n, Item (v, rest)) ->
|
|
|
|
interp_stack_prefix_preserving_operation f n rest
|
|
|
|
>>=? fun (rest', result) -> return (Item (v, rest'), result)
|
|
|
|
| (Rest, v) ->
|
|
|
|
f v
|
2019-10-17 13:45:27 +04:00
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
type step_constants = {
|
|
|
|
source : Contract.t;
|
|
|
|
payer : Contract.t;
|
|
|
|
self : Contract.t;
|
|
|
|
amount : Tez.t;
|
|
|
|
chain_id : Chain_id.t;
|
|
|
|
}
|
2019-10-17 13:45:27 +04:00
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let rec step :
|
|
|
|
type b a.
|
|
|
|
?log:execution_trace ref ->
|
|
|
|
context ->
|
|
|
|
step_constants ->
|
|
|
|
(b, a) descr ->
|
|
|
|
b stack ->
|
|
|
|
(a stack * context) tzresult Lwt.t =
|
|
|
|
fun ?log ctxt step_constants ({instr; loc; _} as descr) stack ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.cycle)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let logged_return :
|
|
|
|
type a b.
|
|
|
|
(b, a) descr -> a stack * context -> (a stack * context) tzresult Lwt.t =
|
|
|
|
fun descr (ret, ctxt) ->
|
|
|
|
match log with
|
|
|
|
| None ->
|
|
|
|
return (ret, ctxt)
|
|
|
|
| Some log ->
|
|
|
|
trace Cannot_serialize_log (unparse_stack ctxt (ret, descr.aft))
|
|
|
|
>>=? fun stack ->
|
|
|
|
log := (descr.loc, Gas.level ctxt, stack) :: !log ;
|
|
|
|
return (ret, ctxt)
|
|
|
|
in
|
|
|
|
let get_log (log : execution_trace ref option) =
|
|
|
|
Option.map ~f:(fun l -> List.rev !l) log
|
|
|
|
in
|
|
|
|
let consume_gas_terop :
|
|
|
|
type ret arg1 arg2 arg3 rest.
|
2019-10-17 13:45:27 +04:00
|
|
|
(_ * (_ * (_ * rest)), ret * rest) descr ->
|
2020-02-12 20:40:17 +04:00
|
|
|
(arg1 -> arg2 -> arg3 -> ret) * arg1 * arg2 * arg3 ->
|
2019-10-17 13:45:27 +04:00
|
|
|
(arg1 -> arg2 -> arg3 -> Gas.cost) ->
|
|
|
|
rest stack ->
|
|
|
|
((ret * rest) stack * context) tzresult Lwt.t =
|
2020-02-12 20:40:17 +04:00
|
|
|
fun descr (op, x1, x2, x3) cost_func rest ->
|
|
|
|
Lwt.return (Gas.consume ctxt (cost_func x1 x2 x3))
|
|
|
|
>>=? fun ctxt -> logged_return descr (Item (op x1 x2 x3, rest), ctxt)
|
|
|
|
in
|
|
|
|
let consume_gas_binop :
|
|
|
|
type ret arg1 arg2 rest.
|
2019-10-17 13:45:27 +04:00
|
|
|
(_ * (_ * rest), ret * rest) descr ->
|
2020-02-12 20:40:17 +04:00
|
|
|
(arg1 -> arg2 -> ret) * arg1 * arg2 ->
|
2019-10-17 13:45:27 +04:00
|
|
|
(arg1 -> arg2 -> Gas.cost) ->
|
|
|
|
rest stack ->
|
|
|
|
context ->
|
|
|
|
((ret * rest) stack * context) tzresult Lwt.t =
|
2020-02-12 20:40:17 +04:00
|
|
|
fun descr (op, x1, x2) cost_func rest ctxt ->
|
|
|
|
Lwt.return (Gas.consume ctxt (cost_func x1 x2))
|
|
|
|
>>=? fun ctxt -> logged_return descr (Item (op x1 x2, rest), ctxt)
|
|
|
|
in
|
|
|
|
let consume_gas_unop :
|
|
|
|
type ret arg rest.
|
2019-10-17 13:45:27 +04:00
|
|
|
(_ * rest, ret * rest) descr ->
|
2020-02-12 20:40:17 +04:00
|
|
|
(arg -> ret) * arg ->
|
2019-10-17 13:45:27 +04:00
|
|
|
(arg -> Gas.cost) ->
|
|
|
|
rest stack ->
|
|
|
|
context ->
|
|
|
|
((ret * rest) stack * context) tzresult Lwt.t =
|
2020-02-12 20:40:17 +04:00
|
|
|
fun descr (op, arg) cost_func rest ctxt ->
|
|
|
|
Lwt.return (Gas.consume ctxt (cost_func arg))
|
|
|
|
>>=? fun ctxt -> logged_return descr (Item (op arg, rest), ctxt)
|
|
|
|
in
|
|
|
|
let logged_return : a stack * context -> (a stack * context) tzresult Lwt.t =
|
|
|
|
logged_return descr
|
|
|
|
in
|
|
|
|
match (instr, stack) with
|
|
|
|
(* stack ops *)
|
|
|
|
| (Drop, Item (_, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.stack_op)
|
|
|
|
>>=? fun ctxt -> logged_return (rest, ctxt)
|
|
|
|
| (Dup, Item (v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.stack_op)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (v, Item (v, rest)), ctxt)
|
|
|
|
| (Swap, Item (vi, Item (vo, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.stack_op)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (vo, Item (vi, rest)), ctxt)
|
|
|
|
| (Const v, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.push)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (v, rest), ctxt)
|
|
|
|
(* options *)
|
|
|
|
| (Cons_some, Item (v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.wrap)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (Some v, rest), ctxt)
|
|
|
|
| (Cons_none _, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.variant_no_data)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (None, rest), ctxt)
|
|
|
|
| (If_none (bt, _), Item (None, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.branch)
|
|
|
|
>>=? fun ctxt -> step ?log ctxt step_constants bt rest
|
|
|
|
| (If_none (_, bf), Item (Some v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.branch)
|
|
|
|
>>=? fun ctxt -> step ?log ctxt step_constants bf (Item (v, rest))
|
|
|
|
(* pairs *)
|
|
|
|
| (Cons_pair, Item (a, Item (b, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.pair)
|
|
|
|
>>=? fun ctxt -> logged_return (Item ((a, b), rest), ctxt)
|
|
|
|
| (Car, Item ((a, _), rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.pair_access)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (a, rest), ctxt)
|
|
|
|
| (Cdr, Item ((_, b), rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.pair_access)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (b, rest), ctxt)
|
|
|
|
(* unions *)
|
|
|
|
| (Left, Item (v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.wrap)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (L v, rest), ctxt)
|
|
|
|
| (Right, Item (v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.wrap)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (R v, rest), ctxt)
|
|
|
|
| (If_left (bt, _), Item (L v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.branch)
|
|
|
|
>>=? fun ctxt -> step ?log ctxt step_constants bt (Item (v, rest))
|
|
|
|
| (If_left (_, bf), Item (R v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.branch)
|
|
|
|
>>=? fun ctxt -> step ?log ctxt step_constants bf (Item (v, rest))
|
|
|
|
(* lists *)
|
|
|
|
| (Cons_list, Item (hd, Item (tl, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.cons)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (hd :: tl, rest), ctxt)
|
|
|
|
| (Nil, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.variant_no_data)
|
|
|
|
>>=? fun ctxt -> logged_return (Item ([], rest), ctxt)
|
|
|
|
| (If_cons (_, bf), Item ([], rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.branch)
|
|
|
|
>>=? fun ctxt -> step ?log ctxt step_constants bf rest
|
|
|
|
| (If_cons (bt, _), Item (hd :: tl, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.branch)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
step ?log ctxt step_constants bt (Item (hd, Item (tl, rest)))
|
|
|
|
| (List_map body, Item (l, rest)) ->
|
|
|
|
let rec loop rest ctxt l acc =
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.loop_map)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match l with
|
|
|
|
| [] ->
|
|
|
|
return (Item (List.rev acc, rest), ctxt)
|
|
|
|
| hd :: tl ->
|
|
|
|
step ?log ctxt step_constants body (Item (hd, rest))
|
|
|
|
>>=? fun (Item (hd, rest), ctxt) -> loop rest ctxt tl (hd :: acc)
|
|
|
|
in
|
|
|
|
loop rest ctxt l [] >>=? fun (res, ctxt) -> logged_return (res, ctxt)
|
|
|
|
| (List_size, Item (list, rest)) ->
|
|
|
|
Lwt.return
|
|
|
|
(List.fold_left
|
|
|
|
(fun acc _ ->
|
|
|
|
acc
|
|
|
|
>>? fun (size, ctxt) ->
|
|
|
|
Gas.consume ctxt Interp_costs.loop_size
|
|
|
|
>>? fun ctxt -> ok (size + 1 (* FIXME: overflow *), ctxt))
|
|
|
|
(ok (0, ctxt))
|
|
|
|
list)
|
|
|
|
>>=? fun (len, ctxt) ->
|
|
|
|
logged_return (Item (Script_int.(abs (of_int len)), rest), ctxt)
|
|
|
|
| (List_iter body, Item (l, init)) ->
|
|
|
|
let rec loop ctxt l stack =
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.loop_iter)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match l with
|
|
|
|
| [] ->
|
|
|
|
return (stack, ctxt)
|
|
|
|
| hd :: tl ->
|
|
|
|
step ?log ctxt step_constants body (Item (hd, stack))
|
|
|
|
>>=? fun (stack, ctxt) -> loop ctxt tl stack
|
|
|
|
in
|
|
|
|
loop ctxt l init >>=? fun (res, ctxt) -> logged_return (res, ctxt)
|
|
|
|
(* sets *)
|
|
|
|
| (Empty_set t, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.empty_set)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (empty_set t, rest), ctxt)
|
|
|
|
| (Set_iter body, Item (set, init)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.set_to_list set))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let l = List.rev (set_fold (fun e acc -> e :: acc) set []) in
|
|
|
|
let rec loop ctxt l stack =
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.loop_iter)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match l with
|
|
|
|
| [] ->
|
|
|
|
return (stack, ctxt)
|
|
|
|
| hd :: tl ->
|
|
|
|
step ?log ctxt step_constants body (Item (hd, stack))
|
|
|
|
>>=? fun (stack, ctxt) -> loop ctxt tl stack
|
|
|
|
in
|
|
|
|
loop ctxt l init >>=? fun (res, ctxt) -> logged_return (res, ctxt)
|
|
|
|
| (Set_mem, Item (v, Item (set, rest))) ->
|
|
|
|
consume_gas_binop descr (set_mem, v, set) Interp_costs.set_mem rest ctxt
|
|
|
|
| (Set_update, Item (v, Item (presence, Item (set, rest)))) ->
|
|
|
|
consume_gas_terop
|
|
|
|
descr
|
|
|
|
(set_update, v, presence, set)
|
|
|
|
Interp_costs.set_update
|
|
|
|
rest
|
|
|
|
| (Set_size, Item (set, rest)) ->
|
|
|
|
consume_gas_unop
|
|
|
|
descr
|
|
|
|
(set_size, set)
|
|
|
|
(fun _ -> Interp_costs.set_size)
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
(* maps *)
|
|
|
|
| (Empty_map (t, _), rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.empty_map)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (empty_map t, rest), ctxt)
|
|
|
|
| (Map_map body, Item (map, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.map_to_list map))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let l = List.rev (map_fold (fun k v acc -> (k, v) :: acc) map []) in
|
|
|
|
let rec loop rest ctxt l acc =
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.loop_map)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match l with
|
|
|
|
| [] ->
|
|
|
|
return (Item (acc, rest), ctxt)
|
|
|
|
| ((k, _) as hd) :: tl ->
|
|
|
|
step ?log ctxt step_constants body (Item (hd, rest))
|
|
|
|
>>=? fun (Item (hd, rest), ctxt) ->
|
|
|
|
loop rest ctxt tl (map_update k (Some hd) acc)
|
|
|
|
in
|
|
|
|
loop rest ctxt l (empty_map (map_key_ty map))
|
|
|
|
>>=? fun (res, ctxt) -> logged_return (res, ctxt)
|
|
|
|
| (Map_iter body, Item (map, init)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.map_to_list map))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let l = List.rev (map_fold (fun k v acc -> (k, v) :: acc) map []) in
|
|
|
|
let rec loop ctxt l stack =
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.loop_iter)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match l with
|
|
|
|
| [] ->
|
|
|
|
return (stack, ctxt)
|
|
|
|
| hd :: tl ->
|
|
|
|
step ?log ctxt step_constants body (Item (hd, stack))
|
|
|
|
>>=? fun (stack, ctxt) -> loop ctxt tl stack
|
|
|
|
in
|
|
|
|
loop ctxt l init >>=? fun (res, ctxt) -> logged_return (res, ctxt)
|
|
|
|
| (Map_mem, Item (v, Item (map, rest))) ->
|
|
|
|
consume_gas_binop descr (map_mem, v, map) Interp_costs.map_mem rest ctxt
|
|
|
|
| (Map_get, Item (v, Item (map, rest))) ->
|
|
|
|
consume_gas_binop descr (map_get, v, map) Interp_costs.map_get rest ctxt
|
|
|
|
| (Map_update, Item (k, Item (v, Item (map, rest)))) ->
|
|
|
|
consume_gas_terop
|
|
|
|
descr
|
|
|
|
(map_update, k, v, map)
|
|
|
|
Interp_costs.map_update
|
|
|
|
rest
|
|
|
|
| (Map_size, Item (map, rest)) ->
|
|
|
|
consume_gas_unop
|
|
|
|
descr
|
|
|
|
(map_size, map)
|
|
|
|
(fun _ -> Interp_costs.map_size)
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
(* Big map operations *)
|
|
|
|
| (Empty_big_map (tk, tv), rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.empty_map)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
logged_return
|
|
|
|
(Item (Script_ir_translator.empty_big_map tk tv, rest), ctxt)
|
|
|
|
| (Big_map_mem, Item (key, Item (map, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.map_mem key map.diff))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
Script_ir_translator.big_map_mem ctxt key map
|
|
|
|
>>=? fun (res, ctxt) -> logged_return (Item (res, rest), ctxt)
|
|
|
|
| (Big_map_get, Item (key, Item (map, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.map_get key map.diff))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
Script_ir_translator.big_map_get ctxt key map
|
|
|
|
>>=? fun (res, ctxt) -> logged_return (Item (res, rest), ctxt)
|
|
|
|
| (Big_map_update, Item (key, Item (maybe_value, Item (map, rest)))) ->
|
|
|
|
consume_gas_terop
|
|
|
|
descr
|
|
|
|
(Script_ir_translator.big_map_update, key, maybe_value, map)
|
|
|
|
(fun k v m -> Interp_costs.map_update k (Some v) m.diff)
|
|
|
|
rest
|
|
|
|
(* timestamp operations *)
|
|
|
|
| (Add_seconds_to_timestamp, Item (n, Item (t, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_timestamp.add_delta, t, n)
|
|
|
|
Interp_costs.add_timestamp
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Add_timestamp_to_seconds, Item (t, Item (n, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_timestamp.add_delta, t, n)
|
|
|
|
Interp_costs.add_timestamp
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Sub_timestamp_seconds, Item (t, Item (s, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_timestamp.sub_delta, t, s)
|
|
|
|
Interp_costs.sub_timestamp
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Diff_timestamps, Item (t1, Item (t2, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_timestamp.diff, t1, t2)
|
|
|
|
Interp_costs.diff_timestamps
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
(* string operations *)
|
|
|
|
| (Concat_string_pair, Item (x, Item (y, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.concat_string [x; y]))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let s = String.concat "" [x; y] in
|
|
|
|
logged_return (Item (s, rest), ctxt)
|
|
|
|
| (Concat_string, Item (ss, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.concat_string ss))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let s = String.concat "" ss in
|
|
|
|
logged_return (Item (s, rest), ctxt)
|
|
|
|
| (Slice_string, Item (offset, Item (length, Item (s, rest)))) ->
|
|
|
|
let s_length = Z.of_int (String.length s) in
|
|
|
|
let offset = Script_int.to_zint offset in
|
|
|
|
let length = Script_int.to_zint length in
|
|
|
|
if Compare.Z.(offset < s_length && Z.add offset length <= s_length) then
|
2019-10-17 13:45:27 +04:00
|
|
|
Lwt.return
|
2020-02-12 20:40:17 +04:00
|
|
|
(Gas.consume ctxt (Interp_costs.slice_string (Z.to_int length)))
|
|
|
|
>>=? fun ctxt ->
|
2019-10-17 13:45:27 +04:00
|
|
|
logged_return
|
2020-02-12 20:40:17 +04:00
|
|
|
( Item (Some (String.sub s (Z.to_int offset) (Z.to_int length)), rest),
|
|
|
|
ctxt )
|
|
|
|
else
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.slice_string 0))
|
|
|
|
>>=? fun ctxt -> logged_return (Item (None, rest), ctxt)
|
|
|
|
| (String_size, Item (s, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.push)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
logged_return
|
|
|
|
(Item (Script_int.(abs (of_int (String.length s))), rest), ctxt)
|
|
|
|
(* bytes operations *)
|
|
|
|
| (Concat_bytes_pair, Item (x, Item (y, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.concat_bytes [x; y]))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let s = MBytes.concat "" [x; y] in
|
|
|
|
logged_return (Item (s, rest), ctxt)
|
|
|
|
| (Concat_bytes, Item (ss, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.concat_bytes ss))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let s = MBytes.concat "" ss in
|
|
|
|
logged_return (Item (s, rest), ctxt)
|
|
|
|
| (Slice_bytes, Item (offset, Item (length, Item (s, rest)))) ->
|
|
|
|
let s_length = Z.of_int (MBytes.length s) in
|
|
|
|
let offset = Script_int.to_zint offset in
|
|
|
|
let length = Script_int.to_zint length in
|
|
|
|
if Compare.Z.(offset < s_length && Z.add offset length <= s_length) then
|
|
|
|
Lwt.return
|
|
|
|
(Gas.consume ctxt (Interp_costs.slice_string (Z.to_int length)))
|
|
|
|
>>=? fun ctxt ->
|
2019-10-17 13:45:27 +04:00
|
|
|
logged_return
|
2020-02-12 20:40:17 +04:00
|
|
|
( Item (Some (MBytes.sub s (Z.to_int offset) (Z.to_int length)), rest),
|
|
|
|
ctxt )
|
|
|
|
else
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.slice_string 0))
|
|
|
|
>>=? fun ctxt -> logged_return (Item (None, rest), ctxt)
|
|
|
|
| (Bytes_size, Item (s, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.push)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
logged_return
|
|
|
|
(Item (Script_int.(abs (of_int (MBytes.length s))), rest), ctxt)
|
|
|
|
(* currency operations *)
|
|
|
|
| (Add_tez, Item (x, Item (y, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.int64_op)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
Lwt.return Tez.(x +? y)
|
|
|
|
>>=? fun res -> logged_return (Item (res, rest), ctxt)
|
|
|
|
| (Sub_tez, Item (x, Item (y, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.int64_op)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
Lwt.return Tez.(x -? y)
|
|
|
|
>>=? fun res -> logged_return (Item (res, rest), ctxt)
|
|
|
|
| (Mul_teznat, Item (x, Item (y, rest))) -> (
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.int64_op)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.z_to_int64)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match Script_int.to_int64 y with
|
|
|
|
| None ->
|
|
|
|
fail (Overflow (loc, get_log log))
|
|
|
|
| Some y ->
|
|
|
|
Lwt.return Tez.(x *? y)
|
|
|
|
>>=? fun res -> logged_return (Item (res, rest), ctxt) )
|
|
|
|
| (Mul_nattez, Item (y, Item (x, rest))) -> (
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.int64_op)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.z_to_int64)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match Script_int.to_int64 y with
|
|
|
|
| None ->
|
|
|
|
fail (Overflow (loc, get_log log))
|
|
|
|
| Some y ->
|
|
|
|
Lwt.return Tez.(x *? y)
|
|
|
|
>>=? fun res -> logged_return (Item (res, rest), ctxt) )
|
|
|
|
(* boolean operations *)
|
|
|
|
| (Or, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop descr (( || ), x, y) Interp_costs.bool_binop rest ctxt
|
|
|
|
| (And, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop descr (( && ), x, y) Interp_costs.bool_binop rest ctxt
|
|
|
|
| (Xor, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Compare.Bool.( <> ), x, y)
|
|
|
|
Interp_costs.bool_binop
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Not, Item (x, rest)) ->
|
|
|
|
consume_gas_unop descr (not, x) Interp_costs.bool_unop rest ctxt
|
|
|
|
(* integer operations *)
|
|
|
|
| (Is_nat, Item (x, rest)) ->
|
|
|
|
consume_gas_unop descr (Script_int.is_nat, x) Interp_costs.abs rest ctxt
|
|
|
|
| (Abs_int, Item (x, rest)) ->
|
|
|
|
consume_gas_unop descr (Script_int.abs, x) Interp_costs.abs rest ctxt
|
|
|
|
| (Int_nat, Item (x, rest)) ->
|
|
|
|
consume_gas_unop descr (Script_int.int, x) Interp_costs.int rest ctxt
|
|
|
|
| (Neg_int, Item (x, rest)) ->
|
|
|
|
consume_gas_unop descr (Script_int.neg, x) Interp_costs.neg rest ctxt
|
|
|
|
| (Neg_nat, Item (x, rest)) ->
|
|
|
|
consume_gas_unop descr (Script_int.neg, x) Interp_costs.neg rest ctxt
|
|
|
|
| (Add_intint, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop descr (Script_int.add, x, y) Interp_costs.add rest ctxt
|
|
|
|
| (Add_intnat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop descr (Script_int.add, x, y) Interp_costs.add rest ctxt
|
|
|
|
| (Add_natint, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop descr (Script_int.add, x, y) Interp_costs.add rest ctxt
|
|
|
|
| (Add_natnat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.add_n, x, y)
|
|
|
|
Interp_costs.add
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Sub_int, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop descr (Script_int.sub, x, y) Interp_costs.sub rest ctxt
|
|
|
|
| (Mul_intint, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop descr (Script_int.mul, x, y) Interp_costs.mul rest ctxt
|
|
|
|
| (Mul_intnat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop descr (Script_int.mul, x, y) Interp_costs.mul rest ctxt
|
|
|
|
| (Mul_natint, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop descr (Script_int.mul, x, y) Interp_costs.mul rest ctxt
|
|
|
|
| (Mul_natnat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.mul_n, x, y)
|
|
|
|
Interp_costs.mul
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Ediv_teznat, Item (x, Item (y, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.int64_to_z)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let x = Script_int.of_int64 (Tez.to_mutez x) in
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
( (fun x y ->
|
|
|
|
match Script_int.ediv x y with
|
|
|
|
| None ->
|
|
|
|
None
|
|
|
|
| Some (q, r) -> (
|
|
|
|
match (Script_int.to_int64 q, Script_int.to_int64 r) with
|
|
|
|
| (Some q, Some r) -> (
|
|
|
|
match (Tez.of_mutez q, Tez.of_mutez r) with
|
|
|
|
| (Some q, Some r) ->
|
|
|
|
Some (q, r)
|
|
|
|
(* Cannot overflow *)
|
|
|
|
| _ ->
|
|
|
|
assert false )
|
|
|
|
(* Cannot overflow *)
|
|
|
|
| _ ->
|
|
|
|
assert false )),
|
|
|
|
x,
|
|
|
|
y )
|
|
|
|
Interp_costs.div
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Ediv_tez, Item (x, Item (y, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.int64_to_z)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.int64_to_z)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let x = Script_int.abs (Script_int.of_int64 (Tez.to_mutez x)) in
|
|
|
|
let y = Script_int.abs (Script_int.of_int64 (Tez.to_mutez y)) in
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
( (fun x y ->
|
|
|
|
match Script_int.ediv_n x y with
|
|
|
|
| None ->
|
|
|
|
None
|
|
|
|
| Some (q, r) -> (
|
|
|
|
match Script_int.to_int64 r with
|
|
|
|
| None ->
|
|
|
|
assert false (* Cannot overflow *)
|
|
|
|
| Some r -> (
|
|
|
|
match Tez.of_mutez r with
|
|
|
|
| None ->
|
|
|
|
assert false (* Cannot overflow *)
|
|
|
|
| Some r ->
|
|
|
|
Some (q, r) ) )),
|
|
|
|
x,
|
|
|
|
y )
|
|
|
|
Interp_costs.div
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Ediv_intint, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.ediv, x, y)
|
|
|
|
Interp_costs.div
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Ediv_intnat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.ediv, x, y)
|
|
|
|
Interp_costs.div
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Ediv_natint, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.ediv, x, y)
|
|
|
|
Interp_costs.div
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Ediv_natnat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.ediv_n, x, y)
|
|
|
|
Interp_costs.div
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Lsl_nat, Item (x, Item (y, rest))) -> (
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.shift_left x y))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match Script_int.shift_left_n x y with
|
|
|
|
| None ->
|
|
|
|
fail (Overflow (loc, get_log log))
|
|
|
|
| Some x ->
|
|
|
|
logged_return (Item (x, rest), ctxt) )
|
|
|
|
| (Lsr_nat, Item (x, Item (y, rest))) -> (
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.shift_right x y))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match Script_int.shift_right_n x y with
|
|
|
|
| None ->
|
|
|
|
fail (Overflow (loc, get_log log))
|
|
|
|
| Some r ->
|
|
|
|
logged_return (Item (r, rest), ctxt) )
|
|
|
|
| (Or_nat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.logor, x, y)
|
|
|
|
Interp_costs.logor
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (And_nat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.logand, x, y)
|
|
|
|
Interp_costs.logand
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (And_int_nat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.logand, x, y)
|
|
|
|
Interp_costs.logand
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Xor_nat, Item (x, Item (y, rest))) ->
|
|
|
|
consume_gas_binop
|
|
|
|
descr
|
|
|
|
(Script_int.logxor, x, y)
|
|
|
|
Interp_costs.logxor
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Not_int, Item (x, rest)) ->
|
|
|
|
consume_gas_unop
|
|
|
|
descr
|
|
|
|
(Script_int.lognot, x)
|
|
|
|
Interp_costs.lognot
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
| (Not_nat, Item (x, rest)) ->
|
|
|
|
consume_gas_unop
|
|
|
|
descr
|
|
|
|
(Script_int.lognot, x)
|
|
|
|
Interp_costs.lognot
|
|
|
|
rest
|
|
|
|
ctxt
|
|
|
|
(* control *)
|
|
|
|
| (Seq (hd, tl), stack) ->
|
|
|
|
step ?log ctxt step_constants hd stack
|
|
|
|
>>=? fun (trans, ctxt) -> step ?log ctxt step_constants tl trans
|
|
|
|
| (If (bt, _), Item (true, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.branch)
|
|
|
|
>>=? fun ctxt -> step ?log ctxt step_constants bt rest
|
|
|
|
| (If (_, bf), Item (false, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.branch)
|
|
|
|
>>=? fun ctxt -> step ?log ctxt step_constants bf rest
|
|
|
|
| (Loop body, Item (true, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.loop_cycle)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
step ?log ctxt step_constants body rest
|
|
|
|
>>=? fun (trans, ctxt) -> step ?log ctxt step_constants descr trans
|
|
|
|
| (Loop _, Item (false, rest)) ->
|
|
|
|
logged_return (rest, ctxt)
|
|
|
|
| (Loop_left body, Item (L v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.loop_cycle)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
step ?log ctxt step_constants body (Item (v, rest))
|
|
|
|
>>=? fun (trans, ctxt) -> step ?log ctxt step_constants descr trans
|
|
|
|
| (Loop_left _, Item (R v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.loop_cycle)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (v, rest), ctxt)
|
|
|
|
| (Dip b, Item (ign, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.stack_op)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
step ?log ctxt step_constants b rest
|
|
|
|
>>=? fun (res, ctxt) -> logged_return (Item (ign, res), ctxt)
|
|
|
|
| (Exec, Item (arg, Item (lam, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.exec)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
interp ?log ctxt step_constants lam arg
|
|
|
|
>>=? fun (res, ctxt) -> logged_return (Item (res, rest), ctxt)
|
|
|
|
| (Apply capture_ty, Item (capture, Item (lam, rest))) -> (
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.apply)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let (Lam (descr, expr)) = lam in
|
|
|
|
let (Item_t (full_arg_ty, _, _)) = descr.bef in
|
|
|
|
unparse_data ctxt Optimized capture_ty capture
|
|
|
|
>>=? fun (const_expr, ctxt) ->
|
|
|
|
unparse_ty ctxt capture_ty
|
|
|
|
>>=? fun (ty_expr, ctxt) ->
|
|
|
|
match full_arg_ty with
|
|
|
|
| Pair_t ((capture_ty, _, _), (arg_ty, _, _), _, _) ->
|
|
|
|
let arg_stack_ty = Item_t (arg_ty, Empty_t, None) in
|
|
|
|
let const_descr =
|
|
|
|
( {
|
|
|
|
loc = descr.loc;
|
|
|
|
bef = arg_stack_ty;
|
|
|
|
aft = Item_t (capture_ty, arg_stack_ty, None);
|
|
|
|
instr = Const capture;
|
|
|
|
}
|
|
|
|
: (_, _) descr )
|
|
|
|
in
|
|
|
|
let pair_descr =
|
|
|
|
( {
|
|
|
|
loc = descr.loc;
|
|
|
|
bef = Item_t (capture_ty, arg_stack_ty, None);
|
|
|
|
aft = Item_t (full_arg_ty, Empty_t, None);
|
|
|
|
instr = Cons_pair;
|
|
|
|
}
|
|
|
|
: (_, _) descr )
|
|
|
|
in
|
|
|
|
let seq_descr =
|
|
|
|
( {
|
|
|
|
loc = descr.loc;
|
|
|
|
bef = arg_stack_ty;
|
|
|
|
aft = Item_t (full_arg_ty, Empty_t, None);
|
|
|
|
instr = Seq (const_descr, pair_descr);
|
|
|
|
}
|
|
|
|
: (_, _) descr )
|
|
|
|
in
|
|
|
|
let full_descr =
|
|
|
|
( {
|
|
|
|
loc = descr.loc;
|
|
|
|
bef = arg_stack_ty;
|
|
|
|
aft = descr.aft;
|
|
|
|
instr = Seq (seq_descr, descr);
|
|
|
|
}
|
|
|
|
: (_, _) descr )
|
|
|
|
in
|
|
|
|
let full_expr =
|
|
|
|
Micheline.Seq
|
|
|
|
( 0,
|
|
|
|
[ Prim (0, I_PUSH, [ty_expr; const_expr], []);
|
|
|
|
Prim (0, I_PAIR, [], []);
|
|
|
|
expr ] )
|
|
|
|
in
|
|
|
|
let lam' = Lam (full_descr, full_expr) in
|
|
|
|
logged_return (Item (lam', rest), ctxt)
|
|
|
|
| _ ->
|
|
|
|
assert false )
|
|
|
|
| (Lambda lam, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.push)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (lam, rest), ctxt)
|
|
|
|
| (Failwith tv, Item (v, _)) ->
|
|
|
|
trace Cannot_serialize_failure (unparse_data ctxt Optimized tv v)
|
|
|
|
>>=? fun (v, _ctxt) ->
|
|
|
|
let v = Micheline.strip_locations v in
|
|
|
|
fail (Reject (loc, v, get_log log))
|
|
|
|
| (Nop, stack) ->
|
|
|
|
logged_return (stack, ctxt)
|
|
|
|
(* comparison *)
|
|
|
|
| (Compare ty, Item (a, Item (b, rest))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.compare ty a b))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
logged_return
|
|
|
|
( Item
|
|
|
|
( Script_int.of_int
|
|
|
|
@@ Script_ir_translator.compare_comparable ty a b,
|
|
|
|
rest ),
|
|
|
|
ctxt )
|
|
|
|
(* comparators *)
|
|
|
|
| (Eq, Item (cmpres, rest)) ->
|
|
|
|
let cmpres = Script_int.compare cmpres Script_int.zero in
|
|
|
|
let cmpres = Compare.Int.(cmpres = 0) in
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.compare_res)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (cmpres, rest), ctxt)
|
|
|
|
| (Neq, Item (cmpres, rest)) ->
|
|
|
|
let cmpres = Script_int.compare cmpres Script_int.zero in
|
|
|
|
let cmpres = Compare.Int.(cmpres <> 0) in
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.compare_res)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (cmpres, rest), ctxt)
|
|
|
|
| (Lt, Item (cmpres, rest)) ->
|
|
|
|
let cmpres = Script_int.compare cmpres Script_int.zero in
|
|
|
|
let cmpres = Compare.Int.(cmpres < 0) in
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.compare_res)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (cmpres, rest), ctxt)
|
|
|
|
| (Le, Item (cmpres, rest)) ->
|
|
|
|
let cmpres = Script_int.compare cmpres Script_int.zero in
|
|
|
|
let cmpres = Compare.Int.(cmpres <= 0) in
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.compare_res)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (cmpres, rest), ctxt)
|
|
|
|
| (Gt, Item (cmpres, rest)) ->
|
|
|
|
let cmpres = Script_int.compare cmpres Script_int.zero in
|
|
|
|
let cmpres = Compare.Int.(cmpres > 0) in
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.compare_res)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (cmpres, rest), ctxt)
|
|
|
|
| (Ge, Item (cmpres, rest)) ->
|
|
|
|
let cmpres = Script_int.compare cmpres Script_int.zero in
|
|
|
|
let cmpres = Compare.Int.(cmpres >= 0) in
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.compare_res)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (cmpres, rest), ctxt)
|
|
|
|
(* packing *)
|
|
|
|
| (Pack t, Item (value, rest)) ->
|
|
|
|
Script_ir_translator.pack_data ctxt t value
|
|
|
|
>>=? fun (bytes, ctxt) -> logged_return (Item (bytes, rest), ctxt)
|
|
|
|
| (Unpack t, Item (bytes, rest)) ->
|
|
|
|
Lwt.return (Gas.check_enough ctxt (Script.serialized_cost bytes))
|
|
|
|
>>=? fun () ->
|
|
|
|
if
|
|
|
|
Compare.Int.(MBytes.length bytes >= 1)
|
|
|
|
&& Compare.Int.(MBytes.get_uint8 bytes 0 = 0x05)
|
|
|
|
then
|
|
|
|
let bytes = MBytes.sub bytes 1 (MBytes.length bytes - 1) in
|
|
|
|
match Data_encoding.Binary.of_bytes Script.expr_encoding bytes with
|
|
|
|
| None ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.unpack_failed bytes))
|
|
|
|
>>=? fun ctxt -> logged_return (Item (None, rest), ctxt)
|
|
|
|
| Some expr -> (
|
|
|
|
Lwt.return (Gas.consume ctxt (Script.deserialized_cost expr))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
parse_data ctxt ~legacy:false t (Micheline.root expr)
|
|
|
|
>>= function
|
|
|
|
| Ok (value, ctxt) ->
|
|
|
|
logged_return (Item (Some value, rest), ctxt)
|
|
|
|
| Error _ignored ->
|
|
|
|
Lwt.return
|
|
|
|
(Gas.consume ctxt (Interp_costs.unpack_failed bytes))
|
|
|
|
>>=? fun ctxt -> logged_return (Item (None, rest), ctxt) )
|
|
|
|
else logged_return (Item (None, rest), ctxt)
|
|
|
|
(* protocol *)
|
|
|
|
| (Address, Item ((_, address), rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.address)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (address, rest), ctxt)
|
|
|
|
| (Contract (t, entrypoint), Item (contract, rest)) -> (
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.contract)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
match (contract, entrypoint) with
|
|
|
|
| ((contract, "default"), entrypoint)
|
|
|
|
| ((contract, entrypoint), "default") ->
|
|
|
|
Script_ir_translator.parse_contract_for_script
|
|
|
|
~legacy:false
|
|
|
|
ctxt
|
|
|
|
loc
|
|
|
|
t
|
|
|
|
contract
|
|
|
|
~entrypoint
|
|
|
|
>>=? fun (ctxt, maybe_contract) ->
|
|
|
|
logged_return (Item (maybe_contract, rest), ctxt)
|
|
|
|
| _ ->
|
|
|
|
logged_return (Item (None, rest), ctxt) )
|
|
|
|
| ( Transfer_tokens,
|
|
|
|
Item (p, Item (amount, Item ((tp, (destination, entrypoint)), rest))) )
|
|
|
|
->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.transfer)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
collect_big_maps ctxt tp p
|
|
|
|
>>=? fun (to_duplicate, ctxt) ->
|
|
|
|
let to_update = no_big_map_id in
|
|
|
|
extract_big_map_diff
|
|
|
|
ctxt
|
|
|
|
Optimized
|
|
|
|
tp
|
|
|
|
p
|
|
|
|
~to_duplicate
|
|
|
|
~to_update
|
|
|
|
~temporary:true
|
|
|
|
>>=? fun (p, big_map_diff, ctxt) ->
|
|
|
|
unparse_data ctxt Optimized tp p
|
|
|
|
>>=? fun (p, ctxt) ->
|
|
|
|
let operation =
|
|
|
|
Transaction
|
|
|
|
{
|
|
|
|
amount;
|
|
|
|
destination;
|
|
|
|
entrypoint;
|
|
|
|
parameters = Script.lazy_expr (Micheline.strip_locations p);
|
|
|
|
}
|
|
|
|
in
|
|
|
|
Lwt.return (fresh_internal_nonce ctxt)
|
|
|
|
>>=? fun (ctxt, nonce) ->
|
|
|
|
logged_return
|
|
|
|
( Item
|
|
|
|
( ( Internal_operation
|
|
|
|
{source = step_constants.self; operation; nonce},
|
|
|
|
big_map_diff ),
|
|
|
|
rest ),
|
|
|
|
ctxt )
|
|
|
|
| ( Create_account,
|
|
|
|
Item (manager, Item (delegate, Item (_delegatable, Item (credit, rest))))
|
|
|
|
) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.create_account)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
Contract.fresh_contract_from_current_nonce ctxt
|
|
|
|
>>=? fun (ctxt, contract) ->
|
|
|
|
(* store in optimized binary representation - as unparsed with [Optimized]. *)
|
|
|
|
let manager_bytes =
|
|
|
|
Data_encoding.Binary.to_bytes_exn
|
|
|
|
Signature.Public_key_hash.encoding
|
|
|
|
manager
|
|
|
|
in
|
|
|
|
let storage =
|
|
|
|
Script_repr.lazy_expr @@ Micheline.strip_locations
|
|
|
|
@@ Micheline.Bytes (0, manager_bytes)
|
|
|
|
in
|
|
|
|
let script = {code = Legacy_support.manager_script_code; storage} in
|
|
|
|
let operation =
|
|
|
|
Origination {credit; delegate; preorigination = Some contract; script}
|
|
|
|
in
|
|
|
|
Lwt.return (fresh_internal_nonce ctxt)
|
|
|
|
>>=? fun (ctxt, nonce) ->
|
|
|
|
logged_return
|
|
|
|
( Item
|
|
|
|
( ( Internal_operation
|
|
|
|
{source = step_constants.self; operation; nonce},
|
|
|
|
None ),
|
|
|
|
Item ((contract, "default"), rest) ),
|
|
|
|
ctxt )
|
|
|
|
| (Implicit_account, Item (key, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.implicit_account)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let contract = Contract.implicit_contract key in
|
|
|
|
logged_return (Item ((Unit_t None, (contract, "default")), rest), ctxt)
|
|
|
|
| ( Create_contract (storage_type, param_type, Lam (_, code), root_name),
|
|
|
|
Item
|
|
|
|
( manager,
|
|
|
|
Item
|
|
|
|
( delegate,
|
|
|
|
Item
|
|
|
|
( spendable,
|
|
|
|
Item (delegatable, Item (credit, Item (init, rest))) ) ) ) )
|
|
|
|
->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.create_contract)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
unparse_ty ctxt param_type
|
|
|
|
>>=? fun (unparsed_param_type, ctxt) ->
|
|
|
|
let unparsed_param_type =
|
|
|
|
Script_ir_translator.add_field_annot
|
|
|
|
(Option.map ~f:(fun n -> `Field_annot n) root_name)
|
|
|
|
None
|
|
|
|
unparsed_param_type
|
|
|
|
in
|
|
|
|
unparse_ty ctxt storage_type
|
|
|
|
>>=? fun (unparsed_storage_type, ctxt) ->
|
|
|
|
let code =
|
|
|
|
Script.lazy_expr
|
|
|
|
@@ Micheline.strip_locations
|
|
|
|
(Seq
|
|
|
|
( 0,
|
|
|
|
[ Prim (0, K_parameter, [unparsed_param_type], []);
|
|
|
|
Prim (0, K_storage, [unparsed_storage_type], []);
|
|
|
|
Prim (0, K_code, [code], []) ] ))
|
|
|
|
in
|
|
|
|
collect_big_maps ctxt storage_type init
|
|
|
|
>>=? fun (to_duplicate, ctxt) ->
|
|
|
|
let to_update = no_big_map_id in
|
|
|
|
extract_big_map_diff
|
|
|
|
ctxt
|
|
|
|
Optimized
|
|
|
|
storage_type
|
|
|
|
init
|
|
|
|
~to_duplicate
|
|
|
|
~to_update
|
|
|
|
~temporary:true
|
|
|
|
>>=? fun (init, big_map_diff, ctxt) ->
|
|
|
|
unparse_data ctxt Optimized storage_type init
|
|
|
|
>>=? fun (storage, ctxt) ->
|
|
|
|
let storage = Script.lazy_expr @@ Micheline.strip_locations storage in
|
|
|
|
( if spendable then
|
|
|
|
Legacy_support.add_do
|
|
|
|
~manager_pkh:manager
|
|
|
|
~script_code:code
|
|
|
|
~script_storage:storage
|
|
|
|
else if delegatable then
|
|
|
|
Legacy_support.add_set_delegate
|
|
|
|
~manager_pkh:manager
|
|
|
|
~script_code:code
|
|
|
|
~script_storage:storage
|
|
|
|
else if Legacy_support.has_default_entrypoint code then
|
|
|
|
Legacy_support.add_root_entrypoint code
|
|
|
|
>>=? fun code -> return (code, storage)
|
|
|
|
else return (code, storage) )
|
|
|
|
>>=? fun (code, storage) ->
|
|
|
|
Contract.fresh_contract_from_current_nonce ctxt
|
|
|
|
>>=? fun (ctxt, contract) ->
|
|
|
|
let operation =
|
|
|
|
Origination
|
|
|
|
{
|
|
|
|
credit;
|
|
|
|
delegate;
|
|
|
|
preorigination = Some contract;
|
|
|
|
script = {code; storage};
|
|
|
|
}
|
|
|
|
in
|
|
|
|
Lwt.return (fresh_internal_nonce ctxt)
|
|
|
|
>>=? fun (ctxt, nonce) ->
|
|
|
|
logged_return
|
|
|
|
( Item
|
|
|
|
( ( Internal_operation
|
|
|
|
{source = step_constants.self; operation; nonce},
|
|
|
|
big_map_diff ),
|
|
|
|
Item ((contract, "default"), rest) ),
|
|
|
|
ctxt )
|
|
|
|
| ( Create_contract_2 (storage_type, param_type, Lam (_, code), root_name),
|
|
|
|
(* Removed the instruction's arguments manager, spendable and delegatable *)
|
|
|
|
Item (delegate, Item (credit, Item (init, rest))) ) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.create_contract)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
unparse_ty ctxt param_type
|
|
|
|
>>=? fun (unparsed_param_type, ctxt) ->
|
|
|
|
let unparsed_param_type =
|
|
|
|
Script_ir_translator.add_field_annot
|
|
|
|
(Option.map ~f:(fun n -> `Field_annot n) root_name)
|
|
|
|
None
|
|
|
|
unparsed_param_type
|
|
|
|
in
|
|
|
|
unparse_ty ctxt storage_type
|
|
|
|
>>=? fun (unparsed_storage_type, ctxt) ->
|
|
|
|
let code =
|
|
|
|
Micheline.strip_locations
|
|
|
|
(Seq
|
|
|
|
( 0,
|
|
|
|
[ Prim (0, K_parameter, [unparsed_param_type], []);
|
|
|
|
Prim (0, K_storage, [unparsed_storage_type], []);
|
|
|
|
Prim (0, K_code, [code], []) ] ))
|
|
|
|
in
|
|
|
|
collect_big_maps ctxt storage_type init
|
|
|
|
>>=? fun (to_duplicate, ctxt) ->
|
|
|
|
let to_update = no_big_map_id in
|
|
|
|
extract_big_map_diff
|
|
|
|
ctxt
|
|
|
|
Optimized
|
|
|
|
storage_type
|
|
|
|
init
|
|
|
|
~to_duplicate
|
|
|
|
~to_update
|
|
|
|
~temporary:true
|
|
|
|
>>=? fun (init, big_map_diff, ctxt) ->
|
|
|
|
unparse_data ctxt Optimized storage_type init
|
|
|
|
>>=? fun (storage, ctxt) ->
|
|
|
|
let storage = Micheline.strip_locations storage in
|
|
|
|
Contract.fresh_contract_from_current_nonce ctxt
|
|
|
|
>>=? fun (ctxt, contract) ->
|
|
|
|
let operation =
|
|
|
|
Origination
|
|
|
|
{
|
|
|
|
credit;
|
|
|
|
delegate;
|
|
|
|
preorigination = Some contract;
|
|
|
|
script =
|
|
|
|
{
|
|
|
|
code = Script.lazy_expr code;
|
|
|
|
storage = Script.lazy_expr storage;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
in
|
|
|
|
Lwt.return (fresh_internal_nonce ctxt)
|
|
|
|
>>=? fun (ctxt, nonce) ->
|
|
|
|
logged_return
|
|
|
|
( Item
|
|
|
|
( ( Internal_operation
|
|
|
|
{source = step_constants.self; operation; nonce},
|
|
|
|
big_map_diff ),
|
|
|
|
Item ((contract, "default"), rest) ),
|
|
|
|
ctxt )
|
|
|
|
| (Set_delegate, Item (delegate, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.create_account)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let operation = Delegation delegate in
|
|
|
|
Lwt.return (fresh_internal_nonce ctxt)
|
|
|
|
>>=? fun (ctxt, nonce) ->
|
|
|
|
logged_return
|
|
|
|
( Item
|
|
|
|
( ( Internal_operation
|
|
|
|
{source = step_constants.self; operation; nonce},
|
|
|
|
None ),
|
|
|
|
rest ),
|
|
|
|
ctxt )
|
|
|
|
| (Balance, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.balance)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
Contract.get_balance ctxt step_constants.self
|
|
|
|
>>=? fun balance -> logged_return (Item (balance, rest), ctxt)
|
|
|
|
| (Now, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.now)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let now = Script_timestamp.now ctxt in
|
|
|
|
logged_return (Item (now, rest), ctxt)
|
|
|
|
| (Check_signature, Item (key, Item (signature, Item (message, rest)))) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.check_signature key message))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let res = Signature.check key signature message in
|
|
|
|
logged_return (Item (res, rest), ctxt)
|
|
|
|
| (Hash_key, Item (key, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.hash_key)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
logged_return (Item (Signature.Public_key.hash key, rest), ctxt)
|
|
|
|
| (Blake2b, Item (bytes, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.hash_blake2b bytes))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let hash = Raw_hashes.blake2b bytes in
|
|
|
|
logged_return (Item (hash, rest), ctxt)
|
|
|
|
| (Sha256, Item (bytes, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.hash_sha256 bytes))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let hash = Raw_hashes.sha256 bytes in
|
|
|
|
logged_return (Item (hash, rest), ctxt)
|
|
|
|
| (Sha512, Item (bytes, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.hash_sha512 bytes))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let hash = Raw_hashes.sha512 bytes in
|
|
|
|
logged_return (Item (hash, rest), ctxt)
|
|
|
|
| (Steps_to_quota, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.steps_to_quota)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
let steps =
|
|
|
|
match Gas.level ctxt with
|
|
|
|
| Limited {remaining} ->
|
|
|
|
remaining
|
|
|
|
| Unaccounted ->
|
|
|
|
Z.of_string "99999999"
|
|
|
|
in
|
|
|
|
logged_return (Item (Script_int.(abs (of_zint steps)), rest), ctxt)
|
|
|
|
| (Source, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.source)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
logged_return (Item ((step_constants.payer, "default"), rest), ctxt)
|
|
|
|
| (Sender, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.source)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
logged_return (Item ((step_constants.source, "default"), rest), ctxt)
|
|
|
|
| (Self (t, entrypoint), rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.self)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
logged_return (Item ((t, (step_constants.self, entrypoint)), rest), ctxt)
|
|
|
|
| (Amount, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.amount)
|
|
|
|
>>=? fun ctxt -> logged_return (Item (step_constants.amount, rest), ctxt)
|
|
|
|
| (Dig (n, n'), stack) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.stack_n_op n))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
interp_stack_prefix_preserving_operation
|
|
|
|
(fun (Item (v, rest)) -> return (rest, v))
|
|
|
|
n'
|
|
|
|
stack
|
|
|
|
>>=? fun (aft, x) -> logged_return (Item (x, aft), ctxt)
|
|
|
|
| (Dug (n, n'), Item (v, rest)) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.stack_n_op n))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
interp_stack_prefix_preserving_operation
|
|
|
|
(fun stk -> return (Item (v, stk), ()))
|
|
|
|
n'
|
|
|
|
rest
|
|
|
|
>>=? fun (aft, ()) -> logged_return (aft, ctxt)
|
|
|
|
| (Dipn (n, n', b), stack) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.stack_n_op n))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
interp_stack_prefix_preserving_operation
|
|
|
|
(fun stk ->
|
|
|
|
step ?log ctxt step_constants b stk
|
|
|
|
>>=? fun (res, ctxt') -> return (res, ctxt'))
|
|
|
|
n'
|
|
|
|
stack
|
|
|
|
>>=? fun (aft, ctxt') -> logged_return (aft, ctxt')
|
|
|
|
| (Dropn (n, n'), stack) ->
|
|
|
|
Lwt.return (Gas.consume ctxt (Interp_costs.stack_n_op n))
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
interp_stack_prefix_preserving_operation
|
|
|
|
(fun stk -> return (stk, stk))
|
|
|
|
n'
|
|
|
|
stack
|
|
|
|
>>=? fun (_, rest) -> logged_return (rest, ctxt)
|
|
|
|
| (ChainId, rest) ->
|
|
|
|
Lwt.return (Gas.consume ctxt Interp_costs.chain_id)
|
|
|
|
>>=? fun ctxt ->
|
|
|
|
logged_return (Item (step_constants.chain_id, rest), ctxt)
|
2019-10-17 13:45:27 +04:00
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
and interp :
|
|
|
|
type p r.
|
|
|
|
?log:execution_trace ref ->
|
|
|
|
context ->
|
|
|
|
step_constants ->
|
|
|
|
(p, r) lambda ->
|
|
|
|
p ->
|
|
|
|
(r * context) tzresult Lwt.t =
|
|
|
|
fun ?log ctxt step_constants (Lam (code, _)) arg ->
|
|
|
|
let stack = Item (arg, Empty) in
|
|
|
|
( match log with
|
|
|
|
| None ->
|
|
|
|
return_unit
|
|
|
|
| Some log ->
|
|
|
|
trace Cannot_serialize_log (unparse_stack ctxt (stack, code.bef))
|
|
|
|
>>=? fun stack ->
|
|
|
|
log := (code.loc, Gas.level ctxt, stack) :: !log ;
|
|
|
|
return_unit )
|
|
|
|
>>=? fun () ->
|
|
|
|
step ?log ctxt step_constants code stack
|
|
|
|
>>=? fun (Item (ret, Empty), ctxt) -> return (ret, ctxt)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
(* ---- contract handling ---------------------------------------------------*)
|
2019-10-17 13:45:27 +04:00
|
|
|
and execute ?log ctxt mode step_constants ~entrypoint unparsed_script arg :
|
2020-02-12 20:40:17 +04:00
|
|
|
( Script.expr
|
|
|
|
* packed_internal_operation list
|
|
|
|
* context
|
|
|
|
* Contract.big_map_diff option )
|
|
|
|
tzresult
|
|
|
|
Lwt.t =
|
2019-10-17 13:45:27 +04:00
|
|
|
parse_script ctxt unparsed_script ~legacy:true
|
2020-02-12 20:40:17 +04:00
|
|
|
>>=? fun (Ex_script {code; arg_type; storage; storage_type; root_name}, ctxt) ->
|
2019-10-17 13:45:27 +04:00
|
|
|
trace
|
|
|
|
(Bad_contract_parameter step_constants.self)
|
2020-02-12 20:40:17 +04:00
|
|
|
(Lwt.return (find_entrypoint arg_type ~root_name entrypoint))
|
|
|
|
>>=? fun (box, _) ->
|
2019-09-05 17:21:01 +04:00
|
|
|
trace
|
2019-10-17 13:45:27 +04:00
|
|
|
(Bad_contract_parameter step_constants.self)
|
2020-02-12 20:40:17 +04:00
|
|
|
(parse_data ctxt ~legacy:false arg_type (box arg))
|
|
|
|
>>=? fun (arg, ctxt) ->
|
|
|
|
Script.force_decode ctxt unparsed_script.code
|
|
|
|
>>=? fun (script_code, ctxt) ->
|
|
|
|
Script_ir_translator.collect_big_maps ctxt arg_type arg
|
|
|
|
>>=? fun (to_duplicate, ctxt) ->
|
|
|
|
Script_ir_translator.collect_big_maps ctxt storage_type storage
|
|
|
|
>>=? fun (to_update, ctxt) ->
|
2019-09-05 17:21:01 +04:00
|
|
|
trace
|
2019-10-17 13:45:27 +04:00
|
|
|
(Runtime_contract_error (step_constants.self, script_code))
|
|
|
|
(interp ?log ctxt step_constants code (arg, storage))
|
|
|
|
>>=? fun ((ops, storage), ctxt) ->
|
2020-02-12 20:40:17 +04:00
|
|
|
Script_ir_translator.extract_big_map_diff
|
|
|
|
ctxt
|
|
|
|
mode
|
|
|
|
~temporary:false
|
|
|
|
~to_duplicate
|
|
|
|
~to_update
|
|
|
|
storage_type
|
|
|
|
storage
|
2019-10-17 13:45:27 +04:00
|
|
|
>>=? fun (storage, big_map_diff, ctxt) ->
|
2020-02-12 20:40:17 +04:00
|
|
|
trace Cannot_serialize_storage (unparse_data ctxt mode storage_type storage)
|
|
|
|
>>=? fun (storage, ctxt) ->
|
|
|
|
let (ops, op_diffs) = List.split ops in
|
|
|
|
let big_map_diff =
|
|
|
|
match
|
|
|
|
List.flatten
|
|
|
|
(List.map (Option.unopt ~default:[]) (op_diffs @ [big_map_diff]))
|
2019-10-17 13:45:27 +04:00
|
|
|
with
|
2020-02-12 20:40:17 +04:00
|
|
|
| [] ->
|
|
|
|
None
|
|
|
|
| diff ->
|
|
|
|
Some diff
|
|
|
|
in
|
2019-10-17 13:45:27 +04:00
|
|
|
return (Micheline.strip_locations storage, ops, ctxt, big_map_diff)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
type execution_result = {
|
|
|
|
ctxt : context;
|
|
|
|
storage : Script.expr;
|
|
|
|
big_map_diff : Contract.big_map_diff option;
|
|
|
|
operations : packed_internal_operation list;
|
|
|
|
}
|
2019-09-05 17:21:01 +04:00
|
|
|
|
2019-10-17 13:45:27 +04:00
|
|
|
let trace ctxt mode step_constants ~script ~entrypoint ~parameter =
|
2019-09-05 17:21:01 +04:00
|
|
|
let log = ref [] in
|
2020-02-12 20:40:17 +04:00
|
|
|
execute
|
|
|
|
~log
|
|
|
|
ctxt
|
|
|
|
mode
|
|
|
|
step_constants
|
|
|
|
~entrypoint
|
|
|
|
script
|
|
|
|
(Micheline.root parameter)
|
2019-10-17 13:45:27 +04:00
|
|
|
>>=? fun (storage, operations, ctxt, big_map_diff) ->
|
2019-09-05 17:21:01 +04:00
|
|
|
let trace = List.rev !log in
|
2020-02-12 20:40:17 +04:00
|
|
|
return ({ctxt; storage; big_map_diff; operations}, trace)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
2019-10-17 13:45:27 +04:00
|
|
|
let execute ctxt mode step_constants ~script ~entrypoint ~parameter =
|
2020-02-12 20:40:17 +04:00
|
|
|
execute
|
|
|
|
ctxt
|
|
|
|
mode
|
|
|
|
step_constants
|
|
|
|
~entrypoint
|
|
|
|
script
|
|
|
|
(Micheline.root parameter)
|
2019-10-17 13:45:27 +04:00
|
|
|
>>=? fun (storage, operations, ctxt, big_map_diff) ->
|
2020-02-12 20:40:17 +04:00
|
|
|
return {ctxt; storage; big_map_diff; operations}
|