Skip to content

Commit

Permalink
Send contracts and messages via HTTP rather than the file system
Browse files Browse the repository at this point in the history
Previously, requests would contain a path to a file for:
* Scilla contracts
* Init data JSON
* Message JSON

Instead, we send these directly in the message. The maximum size
of a Scilla contract is ~75KB and the maximum size of init data
and messages is ~135KB.

I've also deleted two utilities that we aren't (yet) using in ZQ2,
so that I don't have to spend time supporting the new request
format for them.
  • Loading branch information
JamesHinshelwood committed Apr 16, 2024
1 parent 6fe9206 commit a84841e
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 312 deletions.
27 changes: 12 additions & 15 deletions src/base/Checker.ml
Expand Up @@ -63,7 +63,7 @@ module CG = ScillaCallgraph (TCSRep) (TCERep)

(* Check that the module parses *)
let check_parsing ctr syn =
let ast = FEParser.parse_file syn ctr in
let ast = FEParser.parse_string syn ctr in
if Result.is_ok ast then
plog @@ sprintf "\n[Parsing]:\n module [%s] is successfully parsed.\n" ctr;
ast
Expand Down Expand Up @@ -236,15 +236,14 @@ let check_lmodule cli =
let initial_gas = Uint64.mul Gas.scale_factor cli.gas_limit in
let%bind (lmod : ParserSyntax.lmodule) =
wrap_error_with_gas initial_gas
@@ check_parsing cli.input_file Parser.Incremental.lmodule
@@ check_parsing cli.input Parser.Incremental.lmodule
in
let this_address_opt, init_address_map =
Option.value_map cli.init_file ~f:get_init_this_address_and_extlibs
Option.value_map cli.init ~f:get_init_this_address_and_extlibs_string
~default:(None, [])
in
let this_address =
Option.value this_address_opt
~default:(FilePath.chop_extension (FilePath.basename cli.input_file))
(* this_address is mandatory *)
let this_address = Option.value_exn this_address_opt
in
let elibs = import_libs lmod.elibs init_address_map in
let%bind dis_lmod =
Expand Down Expand Up @@ -307,17 +306,16 @@ let check_cmodule cli =
let initial_gas = Uint64.mul Gas.scale_factor cli.gas_limit in
let%bind (cmod : ParserSyntax.cmodule) =
wrap_error_with_gas initial_gas
@@ check_parsing cli.input_file Parser.Incremental.cmodule
@@ check_parsing cli.input Parser.Incremental.cmodule
in
let cmod = FEParser.disambiguate_calls cmod in
(* Import whatever libs we want. *)
let this_address_opt, init_address_map =
Option.value_map cli.init_file ~f:get_init_this_address_and_extlibs
Option.value_map cli.init ~f:get_init_this_address_and_extlibs_string
~default:(None, [])
in
let this_address =
Option.value this_address_opt
~default:(FilePath.chop_extension (FilePath.basename cli.input_file))
(* this_address is mandatory *)
let this_address = Option.value_exn this_address_opt
in
let elibs = import_libs cmod.elibs init_address_map in
let%bind dis_cmod =
Expand All @@ -344,7 +342,7 @@ let check_cmodule cli =
CG.dump_callgraph stdout cg;
exit 0)
else if cli.dump_callgraph then
let out = Out_channel.create (cli.input_file ^ ".dot") ~binary:true in
let out = Out_channel.create ("callgraph.dot") ~binary:true in
CG.dump_callgraph out cg);
let%bind () =
if cli.disable_analy_warn then pure ()
Expand Down Expand Up @@ -441,10 +439,9 @@ let run args ~exe_name =
let cli = init_checker args ~exe_name in
let open FilePath in
let open GlobalConfig.StdlibTracker in
if check_extension cli.input_file file_extn_library then
if cli.is_library then
(* Check library modules. *)
check_lmodule cli |> fun (out, _) -> out
else if check_extension cli.input_file file_extn_contract then
else
(* Check contract modules. *)
check_cmodule cli |> fun (out, _) -> out
else fatal_error (mk_error0 ~kind:"Unknown file extension" ?inst:None)
8 changes: 2 additions & 6 deletions src/base/DebugMessage.ml
Expand Up @@ -23,18 +23,14 @@ open GlobalConfig
let plog msg =
match get_debug_level () with
| Debug_Normal | Debug_Verbose ->
let fname = get_log_file () in
Out_channel.with_file fname ~append:true ~f:(fun h ->
Out_channel.output_string h msg)
print_endline msg;
| Debug_None -> ()

(* Verbose print to log file *)
let pvlog msg =
match get_debug_level () with
| Debug_Verbose ->
let fname = get_log_file () in
Out_channel.with_file fname ~append:true ~f:(fun h ->
Out_channel.output_string h (msg ()))
print_endline (msg ());
| Debug_Normal | Debug_None -> ()

(* Prints to stdout and log file *)
Expand Down
5 changes: 5 additions & 0 deletions src/base/FrontEndParser.ml
Expand Up @@ -137,11 +137,16 @@ module ScillaFrontEndParser (Literal : ScillaLiteral) = struct

let parse_expr_from_stdin () = parse_stdin Parser.Incremental.exp_term
let parse_lmodule filename = parse_file Parser.Incremental.lmodule filename
let parse_lmodule_string s = parse_string Parser.Incremental.lmodule s

let parse_cmodule filename =
let open Result.Let_syntax in
let%bind cmod = parse_file Parser.Incremental.cmodule filename in
pure @@ disambiguate_calls cmod
let parse_cmodule_string s =
let open Result.Let_syntax in
let%bind cmod = parse_string Parser.Incremental.cmodule s in
pure @@ disambiguate_calls cmod

let get_comments () = Lexer.get_comments ()
end
80 changes: 80 additions & 0 deletions src/base/JSON.ml
Expand Up @@ -60,6 +60,10 @@ let from_file f =
let thunk () = Basic.from_file f in
json_exn_wrapper thunk ~filename:f

let from_string f =
let thunk () = Basic.from_string f in
json_exn_wrapper thunk ~filename:f

let parse_as_name n =
match String.split_on_chars ~on:[ '.' ] n with
| [ t1; t2 ] -> JSONName.parse_qualified_name t1 t2
Expand Down Expand Up @@ -329,6 +333,18 @@ module ContractState = struct
in
(curstates, List.concat extstates)

let get_json_data_string s =
let json = from_string s in
(* input json is a list of key/value pairs *)
let jlist = json |> to_list_exn in
let curstates, extstates =
List.partition_map jlist ~f:(fun j ->
match jobj_to_statevar j with
| ThisContr (n, t, v) -> First (n, t, v)
| ExtrContrs extlist -> Second extlist)
in
(curstates, List.concat extstates)

(* Get a json object from given states *)
let state_to_json states =
let states_str =
Expand Down Expand Up @@ -414,6 +430,35 @@ module ContractState = struct
~kind:"Illegal type for field specified in init json"
~inst:(JSONName.as_string ContractUtil.this_address_label)))

let get_init_this_address_and_extlibs_string s =
(* We filter out type information from init files for the time being *)
let init_data, _ = get_json_data_string s in
let extlibs = get_init_extlibs init_data in
let this_address_init_opt =
match
List.filter init_data ~f:(fun (name, _, _) ->
String.(name = JSONName.as_string ContractUtil.this_address_label))
with
| [ (_, _, adr) ] -> Some adr
| [] ->
None
(* We allow init files without a _this_address entry in scilla-checker *)
| _ ->
raise
(mk_invalid_json ~kind:"Multiple entries specified in init json"
~inst:(JSONName.as_string ContractUtil.this_address_label))
in
match this_address_init_opt with
| None -> (None, extlibs)
| Some adr -> (
match get_address_literal adr with
| Some adr -> (Some adr, extlibs)
| None ->
raise
(mk_invalid_json
~kind:"Illegal type for field specified in init json"
~inst:(JSONName.as_string ContractUtil.this_address_label)))

(* Convert a single JSON serialized literal back to its Scilla value. *)
let jstring_to_literal jstring tp =
let thunk () = Yojson.Basic.from_string jstring in
Expand Down Expand Up @@ -460,6 +505,41 @@ module Message = struct
in
tag :: amount :: origin :: sender :: params

let get_json_data_string s =
let json = from_string s in
let tags = member_exn tag_label json |> to_string_exn in
let amounts = member_exn amount_label json |> to_string_exn in
let senders = member_exn sender_label json |> to_string_exn in
let origins = member_exn origin_label json |> to_string_exn in
(* Make tag, amount and sender into a literal *)
let tag =
(tag_label, tag_type, build_prim_lit_exn JSONType.string_typ tags)
in
let amount =
(amount_label, amount_type, build_prim_lit_exn amount_type amounts)
in
let sender =
(sender_label, sender_type, build_prim_lit_exn sender_type senders)
in
let origin =
(origin_label, origin_type, build_prim_lit_exn origin_type origins)
in
let pjlist = member_exn "params" json |> to_list_exn in
let params =
List.map pjlist ~f:(fun f ->
let name, t, v =
match jobj_to_statevar f with
| ThisContr (name, t, v) -> (name, t, v)
| ExtrContrs _ ->
raise
(mk_invalid_json
~kind:"_external cannot be present in a message JSON"
?inst:None)
in
(name, t, v))
in
tag :: amount :: origin :: sender :: params

(* Same as message_to_jstring, but instead gives out raw json, not it's string *)
let message_to_json message =
(* extract out "_tag", "_amount", "_accepted" and "_recipient" parts of the message *)
Expand Down
42 changes: 32 additions & 10 deletions src/base/RunnerUtil.ml
Expand Up @@ -65,6 +65,24 @@ let get_init_this_address_and_extlibs filename =
fatal_error
(s @ mk_error0 ~kind:"Unable to parse JSON file" ~inst:filename)

let get_init_this_address_and_extlibs_string str =
try
let this_address, name_addr_pairs =
JSON.ContractState.get_init_this_address_and_extlibs_string str
in
if
List.contains_dup
~compare:(fun a b -> String.compare (fst a) (fst b))
name_addr_pairs
then
fatal_error
@@ mk_error0 ~kind:"Duplicate extlib map entries in init JSON file"
~inst:str
else (this_address, name_addr_pairs)
with Invalid_json s ->
fatal_error
(s @ mk_error0 ~kind:"Unable to parse JSON file" ~inst:str)

(* Find (by looking for in StdlibTracker) and parse library named "id.scillib".
* If "id.json" exists, parse it's extlibs info and provide that also. *)
let import_lib name sloc =
Expand Down Expand Up @@ -191,12 +209,13 @@ let import_all_libs ldirs =
import_libs names' []

type runner_cli = {
input_file : string;
input : string;
is_library : bool;
stdlib_dirs : string list;
gas_limit : Stdint.uint64;
(* Run gas use analysis? *)
gua_flag : bool;
init_file : string option;
init : string option;
cf_flag : bool;
cf_token_fields : string list;
p_contract_info : bool;
Expand All @@ -209,8 +228,9 @@ type runner_cli = {
let parse_cli args ~exe_name =
let r_stdlib_dir = ref [] in
let r_gas_limit = ref None in
let r_input_file = ref "" in
let r_init_file = ref None in
let r_input = ref "" in
let r_is_library = ref false in
let r_init = ref None in
let r_json_errors = ref false in
let r_gua = ref false in
let r_contract_info = ref false in
Expand Down Expand Up @@ -247,8 +267,9 @@ let parse_cli args ~exe_name =
Arg.Unit (fun () -> r_gua := true),
"Run gas use analysis and print use polynomial." );
( "-init",
Arg.String (fun x -> r_init_file := Some x),
"Path to initialization json" );
Arg.String (fun x -> r_init := Some x),
"Initialization json" );
( "-islibrary", Arg.Unit (fun () -> r_is_library := true), "Is the contract a library?");
( "-cf",
Arg.Unit (fun () -> r_cf := true),
"Run cashflow checker and print results" );
Expand Down Expand Up @@ -298,7 +319,7 @@ let parse_cli args ~exe_name =
let usage = mandatory_usage ^ "\n " ^ optional_usage ^ "\n" in

(* Only one input file allowed, so the last anonymous argument will be *it*. *)
let anon_handler s = r_input_file := s in
let anon_handler s = r_input := s in
let () =
match args with
| None -> Arg.parse speclist anon_handler mandatory_usage
Expand All @@ -310,21 +331,22 @@ let parse_cli args ~exe_name =
with Arg.Bad msg | Arg.Help msg ->
fatal_error_noformat (Printf.sprintf "%s\n" msg))
in
if String.is_empty !r_input_file then fatal_error_noformat usage;
if String.is_empty !r_input then fatal_error_noformat usage;
let gas_limit =
match !r_gas_limit with Some g -> g | None -> fatal_error_noformat usage
in
if not @@ List.is_empty !r_cf_token_fields then r_cf := true;
GlobalConfig.set_use_json_errors !r_json_errors;
{
input_file = !r_input_file;
input = !r_input;
is_library = !r_is_library;
stdlib_dirs = !r_stdlib_dir;
gas_limit;
gua_flag = !r_gua;
p_contract_info = !r_contract_info;
cf_flag = !r_cf;
cf_token_fields = !r_cf_token_fields;
init_file = !r_init_file;
init = !r_init;
p_type_info = !r_type_info;
disable_analy_warn = !r_disable_analy_warn;
dump_callgraph = !r_dump_callgraph;
Expand Down

0 comments on commit a84841e

Please sign in to comment.