open Client_embedded_proto_bootstrap open Client_bootstrap open Tezos_context open Error_monad open Hash let () = Random.self_init () let should_fail f t = t >>= function | Ok _ -> failwith "Expected error found success." | Error error -> if not (List.exists f error) then failwith "@[Unexpected error@ %a@]" pp_print_error error else begin Format.printf "-> Failure (as expected)\n%!" ; return () end let fork_node () = let init_timeout = 4 in let data_dir = Printf.sprintf "%s/tezos_node_%6X" (Filename.get_temp_dir_name ()) (Random.int 0xFF_FF_FF) in let log_file_name, log_file = Filename.open_temp_file "tezos_node_" ".log" in let log_fd = Unix.descr_of_out_channel log_file in let null_fd = Unix.(openfile "/dev/null" [O_RDONLY] 0o644) in let pid = Unix.create_process Filename.(concat (dirname (Sys.getcwd ())) "tezos-node") [| "tezos-node" ; "-sandbox"; data_dir ; "-sandbox-param"; "./sandbox.json"; "-rpc-port"; "8732" |] null_fd log_fd log_fd in Printf.printf "Created node, pid: %d, log: %s\n%!" pid log_file_name ; at_exit (fun () -> Unix.kill pid Sys.sigkill; ignore (Sys.command (Printf.sprintf "rm -fr \"%s\"" data_dir))) ; Printf.printf "Waiting %d seconds for its initialisation\n%!" init_timeout; Unix.sleep init_timeout ; match Unix.waitpid [Unix.WNOHANG] pid with | 0, _ -> () | pid, Unix.WEXITED x -> Printf.eprintf "Wait: %d, exit %d\n%!" pid x | pid, Unix.WSIGNALED x -> Printf.eprintf "Wait: %d, signaled %d\n%!" pid x | pid, Unix.WSTOPPED x -> Printf.eprintf "Wait: %d, stopped %d \n%!" pid x type account = { name : string ; secret_key : Sodium.secret Sodium.Sign.key ; public_key : Sodium.public Sodium.Sign.key ; public_key_hash : public_key_hash ; contract : Contract.t ; } let bootstrap_accounts () = Client_proto_rpcs.Constants.bootstrap `Genesis >>= fun accounts -> let cpt = ref 0 in Lwt.return (List.map (fun { Bootstrap.public_key_hash ; public_key ; secret_key } -> incr cpt ; let name = Printf.sprintf "bootstrap%d" !cpt in { name ; contract = Contract.default_contract public_key_hash; public_key_hash ; public_key ; secret_key }) accounts) let create_account name = let secret_key, public_key = Sodium.Sign.random_keypair () in let public_key_hash = Ed25519.hash public_key in let contract = Contract.default_contract public_key_hash in Lwt.return { name ; contract ; public_key_hash ; public_key ; secret_key } let transfer ?(block = `Prevalidation) ?(fee = 5L) ~src ~target amount = Cli_entries.message "Transfer %Ld from %s to %s (fee: %Ld)" amount src.name target.name fee; let fee = match Tez.of_cents fee with | Some x -> x | None -> assert false in let amount = match Tez.of_cents amount with | Some x -> x | None -> assert false in Client_proto_context.transfer block ~source:src.contract ~src_pk:src.public_key ~src_sk:src.secret_key ~destination:target.contract ~amount ~fee () let check_balance ?(block = `Prevalidation) account expected = Client_proto_rpcs.Context.Contract.balance block account.contract >>=? fun balance -> let balance = Tez.to_cents balance in if balance <> expected then failwith "Unexpected balance for %s: %Ld (expected: %Ld)" account.name balance expected else begin Cli_entries.message "Balance for %s: %Ld" account.name balance ; return () end let mine contract = let block = `Head 0 in Client_proto_rpcs.Context.level block >>=? fun level -> let seed_nonce = Client_mining_forge.generate_seed_nonce () in Client_mining_forge.forge_block ~timestamp:(Time.now ()) ~seed_nonce ~src_sk:contract.secret_key block contract.public_key_hash >>=? fun block_hash -> Cli_entries.message "Injected %a" Block_hash.pp_short block_hash ; return () let ecoproto_error f = function | Register_client_embedded_proto_bootstrap.Ecoproto_error errors -> List.exists f errors | _ -> false let main () = fork_node () ; bootstrap_accounts () >>= fun bootstrap_accounts -> let bootstrap = List.hd bootstrap_accounts in Format.printf "Received bootstrap key %a@." Ed25519.Public_key_hash.pp_short bootstrap.public_key_hash ; create_account "foo" >>= fun foo -> create_account "bar" >>= fun bar -> transfer ~src:bootstrap ~target:foo 1000_00L >>=? fun () -> transfer ~src:bootstrap ~target:bar 2000_00L >>=? fun () -> check_balance foo 1000_00L >>=? fun () -> transfer ~src:bar ~target:foo 999_95L >>=? fun () -> check_balance foo 1999_95L >>=? fun () -> check_balance bar 1000_00L >>=? fun () -> should_fail (ecoproto_error (function Contract.Too_low_balance -> true | _ -> false)) @@ transfer ~src:bar ~target:foo 1000_00L >>=? fun () -> mine bootstrap >>=? fun () -> print_endline "\nEnd of test\n" ; return () let () = try Lwt_main.run ( main () >>= function | Error exns -> Format.eprintf "%a@." pp_print_error exns ; exit 1 | Ok () -> Lwt.return_unit) with Cli_entries.Command_failed msg -> Format.eprintf "Error: %s@." msg ;