Michelson, Proto: show execution trace on failure also
When doing calling `trace code`, e.g. with option `--trace-stack` in the client.
This commit is contained in:
parent
eb5837943f
commit
1b67e538d8
@ -73,21 +73,7 @@ let print_trace_result (cctxt : #Client_context.printer) ~show_source ~parsed =
|
|||||||
print_expr storage
|
print_expr storage
|
||||||
(Format.pp_print_list Operation_result.pp_internal_operation) operations
|
(Format.pp_print_list Operation_result.pp_internal_operation) operations
|
||||||
print_big_map_diff maybe_big_map_diff
|
print_big_map_diff maybe_big_map_diff
|
||||||
(Format.pp_print_list
|
print_execution_trace trace >>= fun () ->
|
||||||
(fun ppf (loc, gas, stack) ->
|
|
||||||
Format.fprintf ppf
|
|
||||||
"- @[<v 0>location: %d (remaining gas: %a)@,\
|
|
||||||
[ @[<v 0>%a ]@]@]"
|
|
||||||
loc Gas.pp gas
|
|
||||||
(Format.pp_print_list
|
|
||||||
(fun ppf (e, annot) ->
|
|
||||||
Format.fprintf ppf
|
|
||||||
"@[<v 0>%a \t%s@]"
|
|
||||||
print_expr e
|
|
||||||
(match annot with None -> "" | Some a -> a)
|
|
||||||
))
|
|
||||||
stack))
|
|
||||||
trace >>= fun () ->
|
|
||||||
return ()
|
return ()
|
||||||
| Error errs ->
|
| Error errs ->
|
||||||
print_errors cctxt errs ~show_source ~parsed
|
print_errors cctxt errs ~show_source ~parsed
|
||||||
|
@ -90,8 +90,8 @@ let collect_error_locations errs =
|
|||||||
| Invalid_constant (loc, _, _)
|
| Invalid_constant (loc, _, _)
|
||||||
| Invalid_contract (loc, _)
|
| Invalid_contract (loc, _)
|
||||||
| Comparable_type_expected (loc, _)
|
| Comparable_type_expected (loc, _)
|
||||||
| Overflow loc
|
| Overflow (loc, _)
|
||||||
| Reject loc) :: rest ->
|
| Reject (loc, _)) :: rest ->
|
||||||
collect (loc :: acc) rest
|
collect (loc :: acc) rest
|
||||||
| _ :: rest -> collect acc rest in
|
| _ :: rest -> collect acc rest in
|
||||||
collect [] errs
|
collect [] errs
|
||||||
@ -431,12 +431,26 @@ let report_errors ~details ~show_source ?parsed ppf errs =
|
|||||||
@[<hov 2>is not compatible with type@ %a.@]@]"
|
@[<hov 2>is not compatible with type@ %a.@]@]"
|
||||||
print_ty tya
|
print_ty tya
|
||||||
print_ty tyb
|
print_ty tyb
|
||||||
| Reject loc ->
|
| Reject (loc, trace) ->
|
||||||
Format.fprintf ppf "%ascript reached FAIL instruction"
|
Format.fprintf ppf
|
||||||
|
"%ascript reached FAIL instruction@ \
|
||||||
|
%a"
|
||||||
print_loc loc
|
print_loc loc
|
||||||
| Overflow loc ->
|
(fun ppf -> function
|
||||||
Format.fprintf ppf "%aunexpected arithmetic overflow"
|
| None -> ()
|
||||||
|
| Some trace ->
|
||||||
|
Format.fprintf ppf "@,@[<v 2>trace@,%a@]"
|
||||||
|
print_execution_trace trace)
|
||||||
|
trace
|
||||||
|
| Overflow (loc, trace) ->
|
||||||
|
Format.fprintf ppf "%aunexpected arithmetic overflow%a"
|
||||||
print_loc loc
|
print_loc loc
|
||||||
|
(fun ppf -> function
|
||||||
|
| None -> ()
|
||||||
|
| Some trace ->
|
||||||
|
Format.fprintf ppf "@,@[<v 2>trace@,%a@]"
|
||||||
|
print_execution_trace trace)
|
||||||
|
trace
|
||||||
| err -> Format.fprintf ppf "%a" Alpha_environment.Error_monad.pp err
|
| err -> Format.fprintf ppf "%a" Alpha_environment.Error_monad.pp err
|
||||||
end ;
|
end ;
|
||||||
if rest <> [] then Format.fprintf ppf "@," ;
|
if rest <> [] then Format.fprintf ppf "@," ;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
(**************************************************************************)
|
(**************************************************************************)
|
||||||
|
|
||||||
open Proto_alpha
|
open Proto_alpha
|
||||||
|
open Alpha_context
|
||||||
open Tezos_micheline
|
open Tezos_micheline
|
||||||
open Micheline
|
open Micheline
|
||||||
open Micheline_printer
|
open Micheline_printer
|
||||||
@ -43,6 +44,24 @@ let print_stack ppf = function
|
|||||||
print_annot_expr_unwrapped)
|
print_annot_expr_unwrapped)
|
||||||
more
|
more
|
||||||
|
|
||||||
|
let print_execution_trace ppf trace =
|
||||||
|
Format.pp_print_list
|
||||||
|
(fun ppf (loc, gas, stack) ->
|
||||||
|
Format.fprintf ppf
|
||||||
|
"- @[<v 0>location: %d (remaining gas: %a)@,\
|
||||||
|
[ @[<v 0>%a ]@]@]"
|
||||||
|
loc Gas.pp gas
|
||||||
|
(Format.pp_print_list
|
||||||
|
(fun ppf (e, annot) ->
|
||||||
|
Format.fprintf ppf
|
||||||
|
"@[<v 0>%a \t%s@]"
|
||||||
|
print_expr e
|
||||||
|
(match annot with None -> "" | Some a -> a)
|
||||||
|
))
|
||||||
|
stack)
|
||||||
|
ppf
|
||||||
|
trace
|
||||||
|
|
||||||
let inject_types type_map parsed =
|
let inject_types type_map parsed =
|
||||||
let rec inject_expr = function
|
let rec inject_expr = function
|
||||||
| Seq (loc, items) ->
|
| Seq (loc, items) ->
|
||||||
|
@ -17,6 +17,10 @@ val print_expr :
|
|||||||
val print_expr_unwrapped :
|
val print_expr_unwrapped :
|
||||||
Format.formatter -> Script_repr.expr -> unit
|
Format.formatter -> Script_repr.expr -> unit
|
||||||
|
|
||||||
|
val print_execution_trace:
|
||||||
|
Format.formatter ->
|
||||||
|
(Script.location * Gas.t * (Script.expr * string option) list) list -> unit
|
||||||
|
|
||||||
(** Insert the type map returned by the typechecker as comments in a
|
(** Insert the type map returned by the typechecker as comments in a
|
||||||
printable Micheline AST. *)
|
printable Micheline AST. *)
|
||||||
val inject_types :
|
val inject_types :
|
||||||
|
@ -14,31 +14,47 @@ open Script_ir_translator
|
|||||||
|
|
||||||
(* ---- Run-time errors -----------------------------------------------------*)
|
(* ---- Run-time errors -----------------------------------------------------*)
|
||||||
|
|
||||||
type error += Reject of Script.location
|
type execution_trace =
|
||||||
type error += Overflow of Script.location
|
(Script.location * Gas.t * (Script.expr * string option) list) list
|
||||||
|
|
||||||
|
type error += Reject of Script.location * execution_trace option
|
||||||
|
type error += Overflow of Script.location * execution_trace option
|
||||||
type error += Runtime_contract_error : Contract.t * Script.expr -> error
|
type error += Runtime_contract_error : Contract.t * Script.expr -> error
|
||||||
type error += Bad_contract_parameter of Contract.t (* `Permanent *)
|
type error += Bad_contract_parameter of Contract.t (* `Permanent *)
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
let open Data_encoding in
|
let open Data_encoding in
|
||||||
|
let trace_encoding =
|
||||||
|
(list @@ obj3
|
||||||
|
(req "location" Script.location_encoding)
|
||||||
|
(req "gas" Gas.encoding)
|
||||||
|
(req "stack"
|
||||||
|
(list
|
||||||
|
(obj2
|
||||||
|
(req "item" (Script.expr_encoding))
|
||||||
|
(opt "annot" string))))) in
|
||||||
(* Reject *)
|
(* Reject *)
|
||||||
register_error_kind
|
register_error_kind
|
||||||
`Temporary
|
`Temporary
|
||||||
~id:"scriptRejectedRuntimeError"
|
~id:"scriptRejectedRuntimeError"
|
||||||
~title: "Script failed (runtime script error)"
|
~title: "Script failed (runtime script error)"
|
||||||
~description: "A FAIL instruction was reached"
|
~description: "A FAIL instruction was reached"
|
||||||
(obj1 (req "location" Script.location_encoding))
|
(obj2
|
||||||
(function Reject loc -> Some loc | _ -> None)
|
(req "location" Script.location_encoding)
|
||||||
(fun loc -> Reject loc);
|
(opt "trace" trace_encoding))
|
||||||
|
(function Reject (loc, trace) -> Some (loc, trace) | _ -> None)
|
||||||
|
(fun (loc, trace) -> Reject (loc, trace));
|
||||||
(* Overflow *)
|
(* Overflow *)
|
||||||
register_error_kind
|
register_error_kind
|
||||||
`Temporary
|
`Temporary
|
||||||
~id:"scriptOverflowRuntimeError"
|
~id:"scriptOverflowRuntimeError"
|
||||||
~title: "Script failed (overflow error)"
|
~title: "Script failed (overflow error)"
|
||||||
~description: "A FAIL instruction was reached due to the detection of an overflow"
|
~description: "A FAIL instruction was reached due to the detection of an overflow"
|
||||||
(obj1 (req "location" Script.location_encoding))
|
(obj2
|
||||||
(function Overflow loc -> Some loc | _ -> None)
|
(req "location" Script.location_encoding)
|
||||||
(fun loc -> Overflow loc);
|
(opt "trace" trace_encoding))
|
||||||
|
(function Overflow (loc, trace) -> Some (loc, trace) | _ -> None)
|
||||||
|
(fun (loc, trace) -> Overflow (loc, trace));
|
||||||
(* Runtime contract error *)
|
(* Runtime contract error *)
|
||||||
register_error_kind
|
register_error_kind
|
||||||
`Temporary
|
`Temporary
|
||||||
@ -93,9 +109,6 @@ let unparse_stack ctxt (stack, stack_ty) =
|
|||||||
|
|
||||||
module Interp_costs = Michelson_v1_gas.Cost_of
|
module Interp_costs = Michelson_v1_gas.Cost_of
|
||||||
|
|
||||||
type execution_trace =
|
|
||||||
(Script.location * Gas.t * (Script.expr * string option) list) list
|
|
||||||
|
|
||||||
let rec interp
|
let rec interp
|
||||||
: type p r.
|
: type p r.
|
||||||
(?log: execution_trace ref ->
|
(?log: execution_trace ref ->
|
||||||
@ -121,6 +134,8 @@ let rec interp
|
|||||||
unparse_stack ctxt (ret, descr.aft) >>=? fun stack ->
|
unparse_stack ctxt (ret, descr.aft) >>=? fun stack ->
|
||||||
log := (descr.loc, Gas.level ctxt, stack) :: !log ;
|
log := (descr.loc, Gas.level ctxt, stack) :: !log ;
|
||||||
return (ret, ctxt) in
|
return (ret, ctxt) in
|
||||||
|
let get_log (log : execution_trace ref option) =
|
||||||
|
Option.map ~f:(!) log in
|
||||||
let consume_gas_terop : type ret arg1 arg2 arg3 rest.
|
let consume_gas_terop : type ret arg1 arg2 arg3 rest.
|
||||||
(_ * (_ * (_ * rest)), ret * rest) descr ->
|
(_ * (_ * (_ * rest)), ret * rest) descr ->
|
||||||
((arg1 -> arg2 -> arg3 -> ret) * arg1 * arg2 * arg3) ->
|
((arg1 -> arg2 -> arg3 -> ret) * arg1 * arg2 * arg3) ->
|
||||||
@ -364,7 +379,7 @@ let rec interp
|
|||||||
Lwt.return (Gas.consume ctxt Interp_costs.z_to_int64) >>=? fun ctxt ->
|
Lwt.return (Gas.consume ctxt Interp_costs.z_to_int64) >>=? fun ctxt ->
|
||||||
begin
|
begin
|
||||||
match Script_int.to_int64 y with
|
match Script_int.to_int64 y with
|
||||||
| None -> fail (Overflow loc)
|
| None -> fail (Overflow (loc, get_log log))
|
||||||
| Some y ->
|
| Some y ->
|
||||||
Lwt.return Tez.(x *? y) >>=? fun res ->
|
Lwt.return Tez.(x *? y) >>=? fun res ->
|
||||||
logged_return (Item (res, rest), ctxt)
|
logged_return (Item (res, rest), ctxt)
|
||||||
@ -374,7 +389,7 @@ let rec interp
|
|||||||
Lwt.return (Gas.consume ctxt Interp_costs.z_to_int64) >>=? fun ctxt ->
|
Lwt.return (Gas.consume ctxt Interp_costs.z_to_int64) >>=? fun ctxt ->
|
||||||
begin
|
begin
|
||||||
match Script_int.to_int64 y with
|
match Script_int.to_int64 y with
|
||||||
| None -> fail (Overflow loc)
|
| None -> fail (Overflow (loc, get_log log))
|
||||||
| Some y ->
|
| Some y ->
|
||||||
Lwt.return Tez.(x *? y) >>=? fun res ->
|
Lwt.return Tez.(x *? y) >>=? fun res ->
|
||||||
logged_return (Item (res, rest), ctxt)
|
logged_return (Item (res, rest), ctxt)
|
||||||
@ -471,14 +486,14 @@ let rec interp
|
|||||||
Lwt.return (Gas.consume ctxt (Interp_costs.shift_left x y)) >>=? fun ctxt ->
|
Lwt.return (Gas.consume ctxt (Interp_costs.shift_left x y)) >>=? fun ctxt ->
|
||||||
begin
|
begin
|
||||||
match Script_int.shift_left_n x y with
|
match Script_int.shift_left_n x y with
|
||||||
| None -> fail (Overflow loc)
|
| None -> fail (Overflow (loc, get_log log))
|
||||||
| Some x -> logged_return (Item (x, rest), ctxt)
|
| Some x -> logged_return (Item (x, rest), ctxt)
|
||||||
end
|
end
|
||||||
| Lsr_nat, Item (x, Item (y, rest)) ->
|
| Lsr_nat, Item (x, Item (y, rest)) ->
|
||||||
Lwt.return (Gas.consume ctxt (Interp_costs.shift_right x y)) >>=? fun ctxt ->
|
Lwt.return (Gas.consume ctxt (Interp_costs.shift_right x y)) >>=? fun ctxt ->
|
||||||
begin
|
begin
|
||||||
match Script_int.shift_right_n x y with
|
match Script_int.shift_right_n x y with
|
||||||
| None -> fail (Overflow loc)
|
| None -> fail (Overflow (loc, get_log log))
|
||||||
| Some r -> logged_return (Item (r, rest), ctxt)
|
| Some r -> logged_return (Item (r, rest), ctxt)
|
||||||
end
|
end
|
||||||
| Or_nat, Item (x, Item (y, rest)) ->
|
| Or_nat, Item (x, Item (y, rest)) ->
|
||||||
@ -528,7 +543,7 @@ let rec interp
|
|||||||
Lwt.return (Gas.consume ctxt Interp_costs.push) >>=? fun ctxt ->
|
Lwt.return (Gas.consume ctxt Interp_costs.push) >>=? fun ctxt ->
|
||||||
logged_return (Item (lam, rest), ctxt)
|
logged_return (Item (lam, rest), ctxt)
|
||||||
| Fail, _ ->
|
| Fail, _ ->
|
||||||
fail (Reject loc)
|
fail (Reject (loc, get_log log))
|
||||||
| Nop, stack ->
|
| Nop, stack ->
|
||||||
logged_return (stack, ctxt)
|
logged_return (stack, ctxt)
|
||||||
(* comparison *)
|
(* comparison *)
|
||||||
|
@ -9,8 +9,11 @@
|
|||||||
|
|
||||||
open Alpha_context
|
open Alpha_context
|
||||||
|
|
||||||
type error += Overflow of Script.location
|
type execution_trace =
|
||||||
type error += Reject of Script.location
|
(Script.location * Gas.t * (Script.expr * string option) list) list
|
||||||
|
|
||||||
|
type error += Reject of Script.location * execution_trace option
|
||||||
|
type error += Overflow of Script.location * execution_trace option
|
||||||
type error += Runtime_contract_error : Contract.t * Script.expr -> error
|
type error += Runtime_contract_error : Contract.t * Script.expr -> error
|
||||||
type error += Bad_contract_parameter of Contract.t (* `Permanent *)
|
type error += Bad_contract_parameter of Contract.t (* `Permanent *)
|
||||||
|
|
||||||
@ -30,9 +33,6 @@ val execute:
|
|||||||
amount: Tez.t ->
|
amount: Tez.t ->
|
||||||
execution_result tzresult Lwt.t
|
execution_result tzresult Lwt.t
|
||||||
|
|
||||||
type execution_trace =
|
|
||||||
(Script.location * Gas.t * (Script.expr * string option) list) list
|
|
||||||
|
|
||||||
val trace:
|
val trace:
|
||||||
Alpha_context.t ->
|
Alpha_context.t ->
|
||||||
Script_ir_translator.unparsing_mode ->
|
Script_ir_translator.unparsing_mode ->
|
||||||
|
Loading…
Reference in New Issue
Block a user