Revision bf51bd7372b1703c825c607ffbacc7a79b6059bf authored by Arvid Jakobsson on 27 February 2020, 14:38:50 UTC, committed by Pierre Boutillier on 09 March 2020, 20:48:03 UTC
The latter was added in 9aebc957 but the former was not removed.
1 parent ede1919
liquidity.ml
open Internal_pervasives
module MFmt = Experiments.Markup_fmt
let failf ?attach fmt =
ksprintf (fun s -> fail ?attach (`Scenario_error s)) fmt
module Data = struct
type t = Fmt of (Caml.Format.formatter -> unit)
let to_string = function
| Fmt f -> Caml.Format.asprintf "%a" (fun ppf () -> f ppf) ()
let pp ppf (Fmt f) =
let open Fmt in
box (fun ppf () -> f ppf) ppf ()
let rawf fmt =
Caml.Format.kasprintf (fun s -> Fmt (fun ppf -> Fmt.string ppf s)) fmt
let fmt f = Fmt f
let address s = rawf "%s" s
let list_like ~sep ~delimiter l =
Fmt
Fmt.(
fun ppf ->
delimiter
(fun ppf () -> list ~sep (fun ppf (Fmt f) -> f ppf) ppf l)
ppf ())
let tuple l = list_like ~sep:Fmt.comma ~delimiter:Fmt.parens l
let int i = Fmt (fun ppf -> Fmt.int ppf i)
let string = rawf "%S"
let nat d = rawf "%dp" d
let tez (`Mutez d) =
let million = 1_000_000. in
rawf "%ftz" (Float.of_int d /. million)
let semi_colon = Fmt.(fun ppf () -> string ppf ";" ; sp ppf ())
let list l = list_like ~sep:semi_colon ~delimiter:Fmt.brackets l
let set l =
list_like ~sep:semi_colon l
~delimiter:Fmt.(fun x ppf -> pf ppf "@[<6>(Set [%a]@])" x)
let empty_set = set []
let key_hash s = rawf "%s" s
let key s = rawf "%s" s
let account_key a = Tezos_protocol.Account.pubkey a |> key
let account_key_hash a = Tezos_protocol.Account.pubkey_hash a |> key_hash
let signature s = rawf "%s" s
let bytes s = rawf "0x%s" s
let some s = fmt (fun ppf -> Fmt.pf ppf "@[<5>(Some %a)@]" pp s)
let none = rawf "None"
let typed_none t = rawf "(None : %s)" t
let record l =
list_like ~sep:semi_colon ~delimiter:Fmt.braces
(List.map l ~f:(fun (k, v) ->
fmt Fmt.(fun ppf -> pf ppf "@[<2>%s =@ %a@]" k pp v)))
end
module Contract = struct
type t = {name: string; paths: string list; main_name: string option}
let make ?(library = []) ?main_name name ~path =
{name; paths= library @ [path]; main_name}
let build_dir state t =
Paths.root state // sprintf "liquidity-build-%s" t.name
let ensure_build_dir state t =
let dir = build_dir state t in
Running_processes.run_successful_cmdf state "mkdir -p %s"
(Caml.Filename.quote dir)
>>= fun _ -> return dir
let base_liquidity_command _state t =
sprintf "liquidity %s %s"
(List.map t.paths ~f:Caml.Filename.quote |> String.concat ~sep:" ")
(Option.value_map t.main_name ~default:"" ~f:(sprintf "--main %s"))
let michelson state t =
let f = build_dir state t // sprintf "%s.tz" t.name in
ensure_build_dir state t
>>= fun _ ->
Running_processes.run_successful_cmdf state "%s --no-annot -o %s"
(base_liquidity_command state t)
(Caml.Filename.quote f)
>>= fun _ -> return f
let storage_initialization state t ~tezos_node ~storage =
ensure_build_dir state t
>>= fun dir ->
let out = dir // sprintf "%s-initial-storage.tz" t.name in
Running_processes.run_successful_cmdf state
"%s -o %s --tezos-node %s --init-storage %s"
(base_liquidity_command state t)
(Caml.Filename.quote out)
(Caml.Filename.quote tezos_node)
( List.map storage ~f:(fun item ->
Caml.Filename.quote (Data.to_string item))
|> String.concat ~sep:" " )
>>= fun _ -> System.read_file state out >>= fun content -> return content
let arguments state t ~entry_point ~data =
Running_processes.run_successful_cmdf state "%s --data %s %s"
(base_liquidity_command state t)
(Caml.Filename.quote entry_point)
(Caml.Filename.quote (Data.to_string data))
>>= fun res -> return (String.concat ~sep:" " res#out)
let cmdliner_term ~prefix ~name () =
let contract_name = name in
let open Cmdliner in
let open Term in
let flag_name s = sprintf "%s-%s" prefix s in
Arg.(
pure (fun path main_name library ->
make ~library ?main_name ~path contract_name)
$ required
(opt (some non_dir_file) None
(info [flag_name "path"]
~doc:
(sprintf "Path to the liquidity %s contract." contract_name)))
$ value
(opt (some string) None
(info [flag_name "main"]
~doc:
(sprintf "Name of “main” contract for %s." contract_name)))
$ value
(opt
(list ~sep:',' non_dir_file)
[]
(info [flag_name "library"]
~doc:
(sprintf
"Paths to extra liquidity %s contract-library files."
contract_name))))
end
module On_chain = struct
let tezos_client_keyed_originate_contract ?(force = false)
?(transferring = 0) ?(burn_cap = 0.5) state keyed ~name ~source ~storage
=
let client = keyed.Tezos_client.Keyed.client in
Tezos_client.successful_client_cmd state ~client
( [ "--wait"; "none"; "originate"; "contract"; name; "for"; keyed.key_name
; "transferring"; Int.to_string transferring; "from"; keyed.key_name
; "running"; source; "--init"; storage; "--burn-cap"
; Float.to_string burn_cap ]
@ if force then ["--force"] else [] )
let build_and_deploy ?(burn_cap = 10.1) state contract ~keyed_client ~storage
~balance =
let name = contract.Contract.name in
let tezos_node =
sprintf "http://localhost:%d" keyed_client.Tezos_client.Keyed.client.port
in
Contract.michelson state contract
>>= fun michetz ->
Contract.storage_initialization state contract ~tezos_node
~storage:(List.map storage ~f:snd)
>>= fun init ->
tezos_client_keyed_originate_contract state keyed_client ~name
~transferring:balance ~source:michetz ~storage:init ~burn_cap ~force:true
>>= fun _ ->
Tezos_client.Keyed.bake state keyed_client (sprintf "%s origination" name)
>>= fun () ->
Tezos_client.successful_client_cmd state
~client:keyed_client.Tezos_client.Keyed.client
["show"; "known"; "contract"; name]
>>= fun res ->
let address = String.strip (String.concat ~sep:"" res#out) in
Console.sayf state
MFmt.(
par (t "Deployed " @ hlf "“%s”" name)
@ itemize [tf "Script: `%s`" michetz; tf "Address: `%s`" address]
@ par (tf "Storage:")
@ itemize
(List.map storage ~f:(fun (name, data) ->
tf "%s:@ %a" name Data.pp data))
|> to_fmt)
>>= fun () -> return address
(* This should go to flextesa soon... *)
let silent_client_cmd state ~client args =
Running_processes.run_cmdf state "sh -c %s"
( Tezos_client.client_command state client args
|> Genspio.Compile.to_one_liner |> Caml.Filename.quote )
>>= fun res ->
let success = Poly.equal res#status (Lwt_unix.WEXITED 0) in
return (success, res)
let call ?msg ?(should = `Be_ok) ?(transferring = 0) ?(burn_cap = 0.3) state
contract ~keyed_client ~entry_point ~data =
Contract.arguments state contract ~entry_point ~data
>>= fun low_level_arg ->
silent_client_cmd state ~client:keyed_client.Tezos_client.Keyed.client
[ "--wait"; "none"; "transfer"; Int.to_string transferring; "from"
; keyed_client.key_name; "to"; contract.name; "--burn-cap"
; Float.to_string burn_cap; "--arg"; low_level_arg ]
>>= fun (succeeds, res) ->
( match succeeds with
| false -> (
match should with
| `Fail -> return (`Expected `Failure)
| `Script_failwith_re re ->
let intersting_part =
List.drop_while res#err ~f:(fun line ->
String.is_prefix line ~prefix:"script reached FAILWITH")
|> String.concat ~sep:" " in
if Re.execp re intersting_part then return (`Expected `Failure)
else return (`Failed `With_error_does_not_match)
| `Command_stderr_re re ->
if Re.execp re (String.concat ~sep:"\n" res#err) then
return (`Expected `Failure)
else return (`Failed `With_error_does_not_match)
| `Be_ok -> return (`Failed `Not_ok) )
| true when Poly.equal should `Be_ok ->
silent_client_cmd state ~client:keyed_client.client
[ "bake"; "for"; keyed_client.key_name; "--force"
; "--minimal-timestamp" ]
>>= fun (_bake, _) -> return (`Expected `Ok)
| true (* should is no ok *) -> return (`Failed `Unexpected_ok) )
>>= fun test_status ->
let test_full_name =
sprintf "%s#%s%s" contract.name entry_point
(Option.value_map msg ~default:"" ~f:(sprintf " (%s)")) in
Console.sayf state
MFmt.(
let details =
match test_status with
| `Expected _ -> []
| `Failed _ ->
par (tf "Data:")
@ verbatim [Data.to_string data]
@ par (tf "Std-out:")
@ verbatim res#out
@ par (tf "Std-err:")
@ verbatim res#err in
par (tf "Test-call %s" test_full_name)
@ itemize
[ ( match test_status with
| `Expected exp ->
hlf "Success: %s"
( match exp with
| `Ok -> "op-baked"
| `Failure -> "expected-failure" )
| `Failed reason ->
hlf "FAILURE: %s"
( match reason with
| `Not_ok -> "Not-OK"
| `Unexpected_ok -> "Should-have-failed"
| `With_error_does_not_match ->
"Error-message-does-not-match" ) ) ]
@ details
|> to_fmt)
>>= fun () ->
match test_status with
| `Expected _ -> return res
| `Failed _ -> failf "Test failed: %s" test_full_name
let key_with_type_json key =
let open Ezjsonm in
match key with
| `Nat n ->
( dict [("int", `String (Int.to_string n))]
, dict [("prim", `String "nat")] )
let big_map_get state ~client ~address ~key =
let post_json =
let open Ezjsonm in
let k, t = key_with_type_json key in
dict [("key", k); ("type", t)] |> to_string ~minify:false in
Tezos_client.rpc state ~client (`Post post_json)
~path:
(sprintf "/chains/main/blocks/head/context/contracts/%s/big_map_get"
address)
>>= fun json ->
return
(object
method post = post_json
method result = json
end)
let show_contract_command state ~client ~name ~address ~pp_error =
Console.Prompt.unit_and_loop
~description:(Fmt.str "Show status of the contract %s." address)
[sprintf "show-%s" name] (fun _sexps ->
Asynchronous_result.transform_error
~f:(fun e ->
Caml.Format.kasprintf
(fun s -> `Command_line s)
"show-contract: %a" pp_error e)
( List.fold ["storage"; "balance"] ~init:(return [])
~f:(fun pm endpoint ->
pm
>>= fun l ->
Tezos_client.rpc state ~client `Get
~path:
(sprintf "/chains/main/blocks/head/context/contracts/%s/%s"
address endpoint)
>>= fun json ->
return
EF.(
desc (wf "/%s" endpoint)
(markdown_verbatim
(Ezjsonm.value_to_string ~minify:false json))
:: l))
>>= fun l ->
Console.say state
EF.(
desc
(haf "Contract %s@%s" name address)
(list ~sep:"" ~delimiters:("", "") l)) ))
let big_map_get_command state ~names ~thing ~client ~name ~address
~key_of_string ~pp_error =
Console.Prompt.unit_and_loop
~description:
(Fmt.str "Get %s from the big-map of the contract %s@%s." thing name
address) names (fun sexps ->
Asynchronous_result.transform_error
~f:(fun e ->
Caml.Format.kasprintf
(fun s -> `Command_line s)
"%s: %a" (List.hd_exn names) pp_error e)
( match sexps with
| [Sexplib.Sexp.Atom s] ->
key_of_string s
>>= fun key ->
big_map_get state ~client ~address ~key
>>= fun getthing ->
Console.sayf state
MFmt.(
par (tf "Posted:")
@ verbatim_raw getthing#post
@ par (tf "Got:")
@ verbatim_ezjson getthing#result
|> to_fmt)
| _ -> failf "Wrong s-exp command line" ))
end
Computing file changes ...