Skip to content

Commit

Permalink
Send contracts and messages via HTTP rather than the file system (#1252)
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 adab193 commit a5a81f7
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 a5a81f7

Please sign in to comment.