diff --git a/deku-c/client/package.json b/deku-c/client/package.json index ec61a2d9dc..03cf0741d0 100644 --- a/deku-c/client/package.json +++ b/deku-c/client/package.json @@ -1,6 +1,6 @@ { "name": "@marigold-dev/deku", - "version": "0.1.3", + "version": "0.1.6", "description": "Toolkit to interact with Deku in front-end applications", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/deku-c/client/src/deku-p/network/index.ts b/deku-c/client/src/deku-p/network/index.ts index 306cd30fb2..d76205af99 100644 --- a/deku-c/client/src/deku-p/network/index.ts +++ b/deku-c/client/src/deku-p/network/index.ts @@ -81,7 +81,9 @@ export const makeEndpoints = (root: string): endpoints => ({ ), expectedStatus: 200, parse: (json: JSONValue) => { - return json.at("balance").as_int(); + const balance = json.at("balance").as_string(); + if (balance === null) return balance; + return Number.parseInt(balance); }, }), GET_PROOF: (operation_hash: string) => ({ @@ -135,6 +137,7 @@ const parse = async ( export const get = async (endpoint: endpoint): Promise => { const uri = endpoint.uri; + console.log(uri); const response = await fetch(uri); const status = response.status; diff --git a/deku-c/deku-cli/package.json b/deku-c/deku-cli/package.json index f5b572fd09..c9cebdd79d 100644 --- a/deku-c/deku-cli/package.json +++ b/deku-c/deku-cli/package.json @@ -1,6 +1,6 @@ { "name": "@marigold-dev/deku-cli", - "version": "0.0.4", + "version": "0.1.6", "description": "CLI client for Deku-C blockchain", "bin": { "deku-cli": "./dist/index.js" @@ -18,7 +18,7 @@ "author": "", "license": "ISC", "dependencies": { - "@marigold-dev/deku": "0.1.3", + "@marigold-dev/deku": "0.1.6", "@noble/ed25519": "^1.7.1", "@taquito/signer": "^14.0.0", "commander": "^9.4.1", diff --git a/deku-c/ligo-deku-rpc/handlers.ml b/deku-c/ligo-deku-rpc/handlers.ml index 14b9b91f66..e3bc35d76a 100644 --- a/deku-c/ligo-deku-rpc/handlers.ml +++ b/deku-c/ligo-deku-rpc/handlers.ml @@ -76,6 +76,15 @@ let rec get_compilation_target = function | ("target", [ "wasm" ]) :: _ -> Wasm_target | _ :: tl -> get_compilation_target tl +module Nonce = struct + let nonce : int ref = ref 0 + + let take () = + let tmp = !nonce in + nonce := tmp + 1; + tmp +end + module Compile_contract : HANDLERS = struct type path = unit @@ -157,9 +166,9 @@ module Compile_contract : HANDLERS = struct let michelson_code, michelson_storage = match lang with | Michelson -> (source, storage) - | _ -> ( + | _ -> let lang = lang_to_string lang in - let hash = Hash.make source in + let hash = Hash.make source (Nonce.take ()) in let filename_ligo = Printf.sprintf "%s.%s" hash lang in Logs.info (fun m -> m "filename_ligo: %s" filename_ligo); let filename_tz = Printf.sprintf "%s.tz" hash in @@ -167,33 +176,19 @@ module Compile_contract : HANDLERS = struct Logs.info (fun m -> m "storage: %s" storage); let ligo_path = Eio.Path.(Eio.Stdenv.cwd env / filename_ligo) in let tz_path = Eio.Path.(Eio.Stdenv.cwd env / filename_tz) in - let tz_already_exists = - try Some (Eio.Path.load tz_path) |> Option.is_some with _ -> false + let () = Eio.Path.save ~create:(`Exclusive 0o600) ligo_path source in + let () = + Ligo_commands.compile_contract ~env ~lang ~filename_ligo + ~filename_tz () + in + let code = Eio.Path.load tz_path in + let storage = + Ligo_commands.compile_storage ~lang ~filename_ligo + ~expression:storage () in - match tz_already_exists with - | false -> - let () = - try Eio.Path.save ~create:(`Exclusive 0o600) ligo_path source - with _ -> () - in - let () = - Ligo_commands.compile_contract ~env ~lang ~filename_ligo - ~filename_tz () - in - let code = Eio.Path.load tz_path in - let storage = - Ligo_commands.compile_storage ~lang ~filename_ligo - ~expression:storage () - in - (code, storage) - | true -> - let code = Eio.Path.load tz_path in - let storage = - Ligo_commands.compile_storage ~lang ~filename_ligo - ~expression:storage () - |> String.trim - in - (code, storage)) + let () = Eio.Path.unlink ligo_path in + let () = Eio.Path.unlink tz_path in + (code, storage) in (* TODO: better error messages in Tuna *) let show_tuna_error = function @@ -263,17 +258,18 @@ module Compile_invocation : HANDLERS = struct | Michelson -> expression | _ -> let lang = lang_to_string lang in - let hash = Hash.make source in + let hash = Hash.make source (Nonce.take ()) in let filename_ligo = Printf.sprintf "%s.%s" hash lang in let ligo_path = Eio.Path.(Eio.Stdenv.cwd env / filename_ligo) in - let ligo_already_exists = - try Some (Eio.Path.load ligo_path) |> Option.is_some - with _ -> false + let () = + try Eio.Path.save ~create:(`Exclusive 0o600) ligo_path source + with _ -> () + in + let expression = + Ligo_commands.compile_parameter ~lang ~filename_ligo ~expression () in - (if not ligo_already_exists then - try Eio.Path.save ~create:(`Exclusive 0o600) ligo_path source - with _ -> ()); - Ligo_commands.compile_parameter ~lang ~filename_ligo ~expression () + let () = Eio.Path.unlink ligo_path in + expression in match get_compilation_target params with | Michelson_target -> Ok (Michelson_expression expression) diff --git a/deku-c/ligo-deku-rpc/hash.ml b/deku-c/ligo-deku-rpc/hash.ml index 05393729a3..ff6d71d27c 100644 --- a/deku-c/ligo-deku-rpc/hash.ml +++ b/deku-c/ligo-deku-rpc/hash.ml @@ -2,8 +2,11 @@ let alphabet = Base64.make_alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_" -let make content = +let make content nonce = + let nonce = + nonce |> string_of_int |> Hex.of_string |> Hex.to_bytes |> Cstruct.of_bytes + in let content = Cstruct.of_string content in - Mirage_crypto.Hash.MD5.digest content - |> Cstruct.to_string + Cstruct.append content nonce + |> Mirage_crypto.Hash.MD5.digest |> Cstruct.to_string |> Base64.encode_string ~pad:false ~alphabet diff --git a/deku-c/wasm-vm-ocaml/env.ml b/deku-c/wasm-vm-ocaml/env.ml index 0fd39daa4c..cb563d84f2 100644 --- a/deku-c/wasm-vm-ocaml/env.ml +++ b/deku-c/wasm-vm-ocaml/env.ml @@ -49,9 +49,14 @@ let rec execute t ~operation_hash ~tickets ~operation = | Imports.Type_error -> Error "type_error" | Ticket_table.Table error -> Error (Ticket_table.show_error error) - | ( Wasm.Eval.Crash _ | Wasm.Eval.Exhaustion _ | Wasm.Eval.Link _ - | Wasm.Eval.Trap _ ) as exn -> - Error (Printexc.to_string exn) + | Wasm.Eval.Crash (source_region, message) + | Wasm.Eval.Exhaustion (source_region, message) + | Wasm.Eval.Link (source_region, message) + | Wasm.Eval.Trap (source_region, message) -> + Error + (Format.sprintf "%s: %s" + (Wasm.Source.string_of_region source_region) + message) | e -> raise e); effc = (fun (type a) (eff : a t) -> diff --git a/deku-c/wasm-vm-ocaml/imports.ml b/deku-c/wasm-vm-ocaml/imports.ml index 06b8b392f2..3558289b50 100644 --- a/deku-c/wasm-vm-ocaml/imports.ml +++ b/deku-c/wasm-vm-ocaml/imports.ml @@ -1,3 +1,5 @@ +exception Type_error + module FuncType = struct open Wasm @@ -7,8 +9,6 @@ module FuncType = struct let func (name, typ, f) = (name, Instance.ExternFunc (Func.alloc_host typ f)) end -exception Type_error - let ppt (t, _) = print_endline @@ Format.sprintf "Error occurs here: %d\n" t module Vec = struct @@ -21,7 +21,11 @@ module Vec = struct type t = { mutable counter : Int64.t; contents : Value.t Table.t } - let empty = { counter = Int64.min_int; contents = Table.create 4000 } + let empty = + let table = Table.create 4000 in + Table.replace table 0L (Value.Bool 0); + Table.replace table 1L (Value.Bool 1); + { counter = Int64.min_int; contents = table } let alloc t item = let counter = t.counter in @@ -33,12 +37,20 @@ module Vec = struct let value = Table.find_opt t.contents idx in match value with | Some x -> - Table.remove t.contents idx; + (match x with Value.Bool _ -> () | _ -> Table.remove t.contents idx); x | None -> ppt (__LINE_OF__ ()); raise Type_error + let read t idx = + let value = Table.find_opt t.contents idx in + match value with + | Some x -> x + | None -> + ppt (__LINE_OF__ ()); + raise Type_error + let dup t idx = let value = Table.find_opt t.contents idx in match value with @@ -104,6 +116,8 @@ open Syntax let reset () = Vec.Table.clear vec.contents; + Vec.Table.replace vec.contents 0L (Value.Bool 0); + Vec.Table.replace vec.contents 1L (Value.Bool 1); vec.counter <- Int64.min_int let car = @@ -454,8 +468,7 @@ let neq = match args with | Wasm.Values.[ Num (I64 x) ] -> let x = Vec.get_and_remove vec x %-< int in - let result = if Z.(not @@ equal zero x) then Bool 1 else Bool 0 in - let result = Vec.alloc vec result in + let result = if Z.(not @@ equal zero x) then 1L else 0L in wasm_i64 result | _ -> ppt (__LINE_OF__ ()); @@ -470,8 +483,7 @@ let eq = match args with | Wasm.Values.[ Num (I64 x) ] -> let x = Vec.get_and_remove vec x %-< int in - let result = if Z.(equal x zero) then Bool 1 else Bool 0 in - let result = Vec.alloc vec result in + let result = if Z.(equal x zero) then 1L else 0L in wasm_i64 result | _ -> ppt (__LINE_OF__ ()); @@ -486,8 +498,7 @@ let gt = match args with | Wasm.Values.[ Num (I64 x) ] -> let x = Vec.get_and_remove vec x %-< int in - let result = if Z.(gt x zero) then Bool 1 else Bool 0 in - let result = Vec.alloc vec result in + let result = if Z.(gt x zero) then 1L else 0L in wasm_i64 result | _ -> ppt (__LINE_OF__ ()); @@ -502,8 +513,7 @@ let ge = match args with | Wasm.Values.[ Num (I64 x) ] -> let x = Vec.get_and_remove vec x %-< int in - let result = if Z.(geq x zero) then Bool 1 else Bool 0 in - let result = Vec.alloc vec result in + let result = if Z.(geq x zero) then 1L else 0L in wasm_i64 result | _ -> ppt (__LINE_OF__ ()); @@ -518,8 +528,7 @@ let lt = match args with | Wasm.Values.[ Num (I64 x) ] -> let x = Vec.get_and_remove vec x %-< int in - let result = if Z.(lt x zero) then Bool 1 else Bool 0 in - let result = Vec.alloc vec result in + let result = if Z.(lt x zero) then 1L else 0L in wasm_i64 result | _ -> ppt (__LINE_OF__ ()); @@ -534,8 +543,7 @@ let le = match args with | Wasm.Values.[ Num (I64 x) ] -> let x = Vec.get_and_remove vec x %-< int in - let result = if Z.(leq x zero) then Bool 1 else Bool 0 in - let result = Vec.alloc vec result in + let result = if Z.(leq x zero) then 1L else 0L in wasm_i64 result | _ -> ppt (__LINE_OF__ ()); @@ -543,8 +551,8 @@ let le = let or_ = let or_ = function - | Bool x, Bool y -> Bool (x lor y) - | Int x, Int y -> Int Z.(x lor y) + | Bool x, Bool y -> x lor y |> Int64.of_int + | Int x, Int y -> Int Z.(x lor y) |> Vec.alloc vec | _ -> ppt (__LINE_OF__ ()); raise Type_error @@ -559,7 +567,6 @@ let or_ = let x = Vec.get_and_remove vec x in let y = Vec.get_and_remove vec y in let result = or_ (x, y) in - let result = Vec.alloc vec result in wasm_i64 result | _ -> ppt (__LINE_OF__ ()); @@ -651,8 +658,8 @@ let map_ = let xor_ = let xor_ = function - | Bool x, Bool y -> Bool (x lxor y) - | Int x, Int y -> Int Z.(x lxor y) + | Bool x, Bool y -> x lxor y |> Int64.of_int + | Int x, Int y -> Int Z.(x lxor y) |> Vec.alloc vec | _ -> ppt (__LINE_OF__ ()); raise Type_error @@ -667,7 +674,6 @@ let xor_ = let x = Vec.get_and_remove vec x in let y = Vec.get_and_remove vec y in let result = xor_ (x, y) in - let result = Vec.alloc vec result in wasm_i64 result | _ -> ppt (__LINE_OF__ ()); @@ -675,8 +681,8 @@ let xor_ = let and_ = let and_ = function - | Bool x, Bool y -> Bool (x land y) - | Int x, Int y -> Int Z.(x land y) + | Bool x, Bool y -> x land y |> Int64.of_int + | Int x, Int y -> Int Z.(x land y) |> Vec.alloc vec | _ -> ppt (__LINE_OF__ ()); raise Type_error @@ -691,7 +697,6 @@ let and_ = let x = Vec.get_and_remove vec x in let y = Vec.get_and_remove vec y in let result = and_ (x, y) in - let result = Vec.alloc vec result in wasm_i64 result | _ -> ppt (__LINE_OF__ ()); @@ -705,10 +710,9 @@ let not = Wasm.Instance.burn_gas !inst 100L; match args with | Wasm.Values.[ Num (I64 x) ] -> - let x = Vec.get_and_remove vec x %-< bool in - let result = Bool (if Int.equal 0 x then 1 else 0) in - let result = Vec.alloc vec result in - wasm_i64 result + let x = Vec.read vec x %-< bool in + let result = if Int.equal 0 x then 1 else 0 in + wasm_i64 (Int64.of_int result) | _ -> ppt (__LINE_OF__ ()); raise Type_error ) @@ -1116,7 +1120,8 @@ let deref_bool = Wasm.Instance.burn_gas !inst 100L; match args with | Wasm.Values.[ Num (I64 x) ] -> - let x = Vec.get_and_remove vec x %-< bool in + Format.printf "arg %Ld" x; + let x = Vec.read vec x %-< bool in wasm_i32 @@ Int32.of_int x | _ -> ppt (__LINE_OF__ ()); @@ -1486,7 +1491,7 @@ let true_ = fun inst args -> Wasm.Instance.burn_gas !inst 100L; match (args : Wasm.Values.value list) with - | [] -> wasm_i64 (Vec.alloc vec (Bool 1)) + | [] -> wasm_i64 1L | _ -> ppt (__LINE_OF__ ()); raise Type_error ) @@ -1498,7 +1503,7 @@ let false_ = fun inst args -> Wasm.Instance.burn_gas !inst 100L; match (args : Wasm.Values.value list) with - | [] -> wasm_i64 (Vec.alloc vec (Bool 0)) + | [] -> wasm_i64 0L | _ -> ppt (__LINE_OF__ ()); raise Type_error ) diff --git a/deku-p/src/core/bin/api/deku_api.ml b/deku-p/src/core/bin/api/deku_api.ml index 661df5f034..dc9ca3f9bf 100644 --- a/deku-p/src/core/bin/api/deku_api.ml +++ b/deku-p/src/core/bin/api/deku_api.ml @@ -201,6 +201,7 @@ let main params style_renderer log_level = Eio_main.run @@ fun env -> Eio.Switch.run @@ fun sw -> Parallel.Pool.run ~env ~domains @@ fun () -> + Logs.info (fun m -> m "Using %d domains" domains); let net = Eio.Stdenv.net env in let clock = Eio.Stdenv.clock env in let domains = Eio.Stdenv.domain_mgr env in diff --git a/deku-p/src/core/bin/api/handlers.ml b/deku-p/src/core/bin/api/handlers.ml index 25c95bbe45..e6a81fed6a 100644 --- a/deku-p/src/core/bin/api/handlers.ml +++ b/deku-p/src/core/bin/api/handlers.ml @@ -1,6 +1,5 @@ open Deku_consensus open Deku_block_storage -open Deku_stdlib open Deku_concepts open Deku_gossip open Deku_network @@ -158,14 +157,14 @@ module Get_balance : NO_BODY_HANDLERS = struct ticket_id : Deku_ledger.Ticket_id.t; } - type response = { balance : int } + type response = { balance : Amount.t } let response_encoding = let open Data_encoding in conv (fun { balance } -> balance) (fun balance -> { balance }) - (obj1 (req "balance" int8)) + (obj1 (req "balance" Amount.encoding)) let meth = `GET @@ -184,7 +183,6 @@ module Get_balance : NO_BODY_HANDLERS = struct let Api_state.{ protocol; _ } = state in let (Protocol { ledger; _ }) = protocol in let amount = Deku_ledger.Ledger.balance address ticket_id ledger in - let amount = Amount.to_n amount |> N.to_z |> Z.to_int in Ok { balance = amount } end diff --git a/deku-p/src/core/bin/node/node.ml b/deku-p/src/core/bin/node/node.ml index 30b9379858..580d5b0989 100644 --- a/deku-p/src/core/bin/node/node.ml +++ b/deku-p/src/core/bin/node/node.ml @@ -29,27 +29,38 @@ let write_chain ~chain node = node.dump chain; node.chain <- chain -let send_blocks ~sw ~connection ~above node = +let send_blocks ~connection ~above node = match node.indexer with | Some indexer -> - Eio.Fiber.fork ~sw @@ fun () -> - let rec send_while level = + let rec send_while acc level = let level = Level.next level in + Logs.info (fun m -> + m "Sending block %a at time %.3f" Level.pp level + (Unix.gettimeofday ())); match Block_storage.find_block_and_votes_by_level ~level indexer with - | Some network -> + | Some network -> ( let (Network_message { raw_header; raw_content }) = network in Network_manager.send ~connection ~raw_header ~raw_content node.network; - send_while level + match acc >= 60 with + | true -> () + | false -> send_while (acc + 1) level) | None -> () in - send_while above + send_while 0 above | None -> () -let rec handle_chain_actions ~sw ~env ~actions node = - List.iter (fun action -> handle_chain_action ~sw ~env ~action node) actions +let rec apply_chain_actions ~sw ~env ~actions node = + Eio.Fiber.List.iter + (fun action -> + try apply_chain_action ~sw ~env ~action node + with exn -> + Logs.err (fun m -> + m "chain/action: action %a, exception %s" Chain.pp_action action + (Printexc.to_string exn))) + actions -and handle_chain_action ~sw ~env ~action node = +and apply_chain_action ~sw ~env ~action node = let open Chain in match action with | Chain_timeout { until } -> start_timeout ~sw ~env ~until node @@ -59,20 +70,15 @@ and handle_chain_action ~sw ~env ~action node = Network_manager.send ~connection ~raw_header ~raw_content node.network | Chain_send_request { raw_header; raw_content } -> Network_manager.request ~raw_header ~raw_content node.network - | Chain_fragment { fragment } -> handle_chain_fragment ~sw ~env ~fragment node + | Chain_fragment { fragment } -> apply_chain_fragment ~sw ~env ~fragment node | Chain_save_block { block; network } -> ( - let on_error exn = - Logs.err (fun m -> - m "database/sqlite: exception %s" (Printexc.to_string exn)) - in match node.indexer with | Some indexer -> - Eio.Fiber.fork_sub ~sw ~on_error @@ fun _sw -> let (Block { level; _ }) = block in Block_storage.save_block_and_votes ~level ~network indexer | None -> ()) | Chain_send_blocks { connection; above } -> - send_blocks ~sw ~connection ~above node + Eio.Fiber.fork ~sw @@ fun () -> send_blocks ~connection ~above node | Chain_commit { current_level; @@ -92,8 +98,7 @@ and handle_chain_action ~sw ~env ~action node = (* FIXME: this is probably an indication of bad abstraction but being lazy right now *) (* failwith "Node was not initialized with Tezos interop enabled.") *) -and handle_chain_fragment ~sw ~env ~fragment node = - Eio.Fiber.fork ~sw @@ fun () -> +and apply_chain_fragment ~sw ~env ~fragment node = let identity = node.identity in let default_block_size = node.default_block_size in let outcome = @@ -105,10 +110,9 @@ and handle_chain_fragment ~sw ~env ~fragment node = and on_chain_outcome ~sw ~env ~current ~outcome node = let identity = node.identity in - let chain, actions = Chain.apply ~identity ~current ~outcome node.chain in write_chain ~chain node; - handle_chain_actions ~sw ~env ~actions node + apply_chain_actions ~sw ~env ~actions node and start_timeout ~sw ~env ~until node = node.cancel (); @@ -131,18 +135,26 @@ and on_timeout ~sw ~env ~current node = let identity = node.identity in let chain, actions = Chain.timeout ~identity ~current node.chain in write_chain ~chain node; - handle_chain_actions ~sw ~env ~actions node + apply_chain_actions ~sw ~env ~actions node + +let fork_sub ~sw msg f = + let on_error exn = + Logs.err (fun m -> m "%s: exception %s" msg (Printexc.to_string exn)) + in + Eio.Fiber.fork_sub ~sw ~on_error f let on_network_message ~sw ~env ~raw_header ~raw_content node = + fork_sub ~sw "node/message" @@ fun sw -> let chain, fragment = Chain.incoming ~raw_header ~raw_content node.chain in write_chain ~chain node; match fragment with - | Some fragment -> handle_chain_fragment ~sw ~env ~fragment node + | Some fragment -> apply_chain_fragment ~sw ~env ~fragment node | None -> () let on_network_request ~sw ~env ~connection ~raw_header ~raw_content node = + fork_sub ~sw "node/request" @@ fun sw -> let fragment = Chain.request ~connection ~raw_header ~raw_content in - handle_chain_fragment ~sw ~env ~fragment node + apply_chain_fragment ~sw ~env ~fragment node let make ~identity ~default_block_size ~dump ~chain ~indexer = let network = Network_manager.make ~identity in @@ -171,6 +183,7 @@ let to_tezos_operation transaction = | _ -> None let handle_tezos_operation ~sw ~env ~operation node = + fork_sub ~sw "node/tezos" @@ fun sw -> let Tezos_interop.{ hash; transactions } = operation in let operations = List.filter_map to_tezos_operation transactions in let tezos_operation = Tezos_operation.make hash operations in @@ -178,13 +191,13 @@ let handle_tezos_operation ~sw ~env ~operation node = Chain.incoming_tezos_operation ~tezos_operation node.chain in write_chain ~chain node; - handle_chain_actions ~sw ~env ~actions node + apply_chain_actions ~sw ~env ~actions node let reload ~sw ~env node = let current = current () in let chain, actions = Chain.reload ~current node.chain in write_chain ~chain node; - handle_chain_actions ~sw ~env ~actions node + apply_chain_actions ~sw ~env ~actions node let start ~sw ~env ~port ~nodes ~tezos node = let on_connection ~connection = @@ -217,9 +230,9 @@ let start ~sw ~env ~port ~nodes ~tezos node = | None -> ()); let net = Eio.Stdenv.net env in let clock = Eio.Stdenv.clock env in + let () = reload ~sw ~env node in Eio.Fiber.all [ - (fun () -> reload ~sw ~env node); (fun () -> Network_manager.listen ~net ~clock ~port ~on_connection ~on_message ~on_request node.network); diff --git a/deku-p/src/core/chain/chain.ml b/deku-p/src/core/chain/chain.ml index 91ccaa79c7..7943c6b75c 100644 --- a/deku-p/src/core/chain/chain.ml +++ b/deku-p/src/core/chain/chain.ml @@ -44,7 +44,7 @@ type outcome = | Outcome_produce of { block : Block.t } | Outcome_apply of { block : Block.t; - votes : Verified_signature.t Key_hash.Map.t; + votes : Verified_signature.Set.t; protocol : Protocol.t; receipts : Receipt.t list; } @@ -117,6 +117,8 @@ let minimum_block_latency = | Some x -> Option.value ~default:0.0 (Float.of_string_opt x) | None -> 0.0 +let last_sleep : float ref = ref @@ Unix.gettimeofday () + (* after gossip *) let apply_consensus_action chain consensus_action = let open Consensus in @@ -136,7 +138,11 @@ let apply_consensus_action chain consensus_action = in (match minimum_block_latency with | 0. -> () - | minimum_block_latency -> Unix.sleepf minimum_block_latency); + | minimum_block_latency -> + let now = Unix.gettimeofday () in + let sleep = minimum_block_latency -. (now -. !last_sleep) in + if sleep > 0.0 then Unix.sleepf sleep; + last_sleep := Unix.gettimeofday ()); (chain, [ Chain_fragment { fragment } ]) | Consensus_vote { level; vote } -> let content = Message.Content.vote ~level ~vote in @@ -153,9 +159,7 @@ let apply_consensus_action chain consensus_action = in *) let apply = Fragment_apply { block; votes; protocol } in let apply = Chain_fragment { fragment = apply } in - let store = Fragment_store { block; votes } in - let store = Chain_fragment { fragment = store } in - (chain, [ apply; store ]) + (chain, [ apply ]) | Consensus_request { above } -> let (Chain ({ gossip; _ } as chain)) = chain in let gossip, network = Gossip.send_request ~above gossip in @@ -312,41 +316,53 @@ let apply_protocol_produce ~block chain = let apply_protocol_apply ~identity ~current ~block ~votes ~protocol ~receipts chain = let (Chain ({ consensus; producer; _ } as chain)) = chain in - match Consensus.finished ~identity ~current ~block consensus with - | Ok (consensus, actions) -> - (* TODO: make this parallel *) - let (Block { tezos_operations; _ }) = block in - let producer = Producer.clean ~receipts ~tezos_operations producer in - let chain = Chain { chain with protocol; consensus; producer } in - - (* FIXME: need a time-based procedure for tezos commits, not block-based *) - (* FIXME: rediscuss the need to commit the previous block instead *) - (* FIXME: validators have to watch when commit did not happen *) - let (Consensus { validators; _ }) = consensus in - let current_block = Consensus.trusted_block consensus in - let (Block.Block { level = current_level; author = last_block_author; _ }) - = - current_block - in - - (* TODO: only the producer should commit on Tezos *) - let chain, actions = apply_consensus_actions chain actions in - let actions = - let level = Level.to_n current_level |> N.to_z |> Z.to_int in - let self = Identity.key_hash identity in - match level mod 15 = 0 && Key_hash.equal self last_block_author with - | true -> - let validators = Validators.to_key_hash_list validators in - commit ~current_level ~block ~votes ~validators :: actions - | false -> actions - in - (chain, actions) - | Error `No_pending_block -> - Logs.warn (fun m -> m "chain: no pending block"); - (Chain chain, []) - | Error `Wrong_pending_block -> - Logs.warn (fun m -> m "chain: wrong pending block"); - (Chain chain, []) + let chain, actions = + match Consensus.finished ~identity ~current ~block consensus with + | Ok (consensus, actions) -> + (* TODO: make this parallel *) + let (Block { tezos_operations; _ }) = block in + let producer = Producer.clean ~receipts ~tezos_operations producer in + let chain = Chain { chain with protocol; consensus; producer } in + + (* FIXME: need a time-based procedure for tezos commits, not block-based *) + (* FIXME: rediscuss the need to commit the previous block instead *) + (* FIXME: validators have to watch when commit did not happen *) + let (Consensus { validators; _ }) = consensus in + let current_block = Consensus.trusted_block consensus in + let (Block.Block + { level = current_level; author = last_block_author; _ }) = + current_block + in + + (* TODO: only the producer should commit on Tezos *) + let chain, actions = apply_consensus_actions chain actions in + let actions = + let level = Level.to_n current_level |> N.to_z |> Z.to_int in + let self = Identity.key_hash identity in + match level mod 15 = 0 && Key_hash.equal self last_block_author with + | true -> + let votes = + Verified_signature.Set.fold + (fun vote map -> + let key_hash = Verified_signature.key_hash vote in + Key_hash.Map.add key_hash vote map) + votes Key_hash.Map.empty + in + let validators = Validators.to_key_hash_list validators in + commit ~current_level ~block ~votes ~validators :: actions + | false -> actions + in + (chain, actions) + | Error `No_pending_block -> + Logs.warn (fun m -> m "chain: no pending block"); + (Chain chain, []) + | Error `Wrong_pending_block -> + Logs.warn (fun m -> m "chain: wrong pending block"); + (Chain chain, []) + in + let store = Fragment_store { block; votes } in + let store = Chain_fragment { fragment = store } in + (chain, store :: actions) let apply_store_outcome ~block ~network chain = let (Chain ({ gossip; _ } as chain)) = chain in @@ -403,13 +419,7 @@ let compute ~identity ~default_block_size fragment = Logs.warn (fun m -> m "Error while applying block: %s" (Printexc.to_string error))) errors; - let votes = - Verified_signature.Set.fold - (fun vote map -> - let key_hash = Verified_signature.key_hash vote in - Key_hash.Map.add key_hash vote map) - votes Key_hash.Map.empty - in + (* TODO: this is a workaround *) let () = Gc.major () in let () = diff --git a/deku-p/src/core/constants/deku_constants.ml b/deku-p/src/core/constants/deku_constants.ml index 037858f3c7..d1eee67418 100644 --- a/deku-p/src/core/constants/deku_constants.ml +++ b/deku-p/src/core/constants/deku_constants.ml @@ -22,3 +22,4 @@ let async_on_error exn = let genesis_time = 0.0 let trusted_cycle = Option.get (N.of_z (Z.of_int 600)) let max_payload_chunks = 32 +let request_timeout = 30.0 diff --git a/deku-p/src/core/constants/deku_constants.mli b/deku-p/src/core/constants/deku_constants.mli index 91c1a789ce..b0698816a8 100644 --- a/deku-p/src/core/constants/deku_constants.mli +++ b/deku-p/src/core/constants/deku_constants.mli @@ -10,3 +10,4 @@ val async_on_error : exn -> unit val genesis_time : float val trusted_cycle : N.t val max_payload_chunks : int +val request_timeout : float diff --git a/deku-p/src/core/gossip/gossip.ml b/deku-p/src/core/gossip/gossip.ml index 94bb63853f..77135b4ace 100644 --- a/deku-p/src/core/gossip/gossip.ml +++ b/deku-p/src/core/gossip/gossip.ml @@ -5,7 +5,7 @@ open Message type gossip = | Gossip of { (* TODO: this is clearly not ideal *) - pending_request : bool; + pending_request : float option; message_pool : Message_pool.t; [@opaque] } @@ -15,7 +15,7 @@ let encoding = let open Data_encoding in conv (fun (Gossip { pending_request = _; message_pool }) -> message_pool) - (fun message_pool -> Gossip { pending_request = false; message_pool }) + (fun message_pool -> Gossip { pending_request = None; message_pool }) Message_pool.encoding type fragment = @@ -59,7 +59,7 @@ type action = | Gossip_fragment of { fragment : fragment } let initial = - Gossip { pending_request = false; message_pool = Message_pool.initial } + Gossip { pending_request = None; message_pool = Message_pool.initial } let broadcast_message ~content = let fragment = Message_pool.encode ~content in @@ -84,13 +84,22 @@ let send_message ~connection ~content = let send_request ~above gossip = let (Gossip { pending_request; message_pool }) = gossip in - match pending_request with - | true -> (gossip, None) - | false -> + (* TODO: This should be timestamp.t *) + let current = Unix.gettimeofday () in + let can_request = + match pending_request with + | Some time -> + let delta = current -. time in + delta >= Deku_constants.request_timeout + | None -> true + in + match can_request with + | true -> let (Request { hash = _; above = _; network }) = Request.encode ~above in - let pending_request = true in + let pending_request = Some current in let gossip = Gossip { pending_request; message_pool } in (gossip, Some network) + | false -> (gossip, None) let incoming_request ~connection ~raw_header ~raw_content = Fragment_incoming_request { connection; raw_header; raw_content } @@ -161,8 +170,6 @@ let apply ~outcome gossip = (gossip, None) let close ~until gossip = - let (Gossip { pending_request = _; message_pool }) = gossip in - (* TODO: if you had progress, you can probably request again *) - let pending_request = false in + let (Gossip { pending_request; message_pool }) = gossip in let message_pool = Message_pool.close ~until message_pool in Gossip { pending_request; message_pool } diff --git a/deku-p/src/core/gossip/message_pool.ml b/deku-p/src/core/gossip/message_pool.ml index 1ac5173608..63b01c0381 100644 --- a/deku-p/src/core/gossip/message_pool.ml +++ b/deku-p/src/core/gossip/message_pool.ml @@ -35,7 +35,9 @@ let message_state_encoding = let encoding = let open Data_encoding in conv - (fun (Message_pool { current; by_level }) -> (current, by_level)) + (* TOOD: maybe store by_level information? Problematic with intermediary states *) + (fun (Message_pool { current; by_level = _ }) -> + (current, Level.Map.empty)) (fun (current, by_level) -> Message_pool { current; by_level }) (tup2 Level.encoding (Level.Map.encoding (Message_hash.Map.encoding message_state_encoding))) diff --git a/deku-p/src/core/network/network_manager.ml b/deku-p/src/core/network/network_manager.ml index f04a9a39f0..0d5af71c43 100644 --- a/deku-p/src/core/network/network_manager.ml +++ b/deku-p/src/core/network/network_manager.ml @@ -3,12 +3,14 @@ open Deku_concepts open Deku_gossip open Network_protocol +type write = Network_message.message -> unit + type network = { identity : Identity.t; mutable connection_id : Connection_id.t; (* TODO: Hashtbl would do a great job here *) - mutable connections : (Network_message.message -> unit) Connection_id.Map.t; - mutable connected_to : (Network_message.message -> unit) Key_hash.Map.t; + mutable connections : write Connection_id.Map.t; + mutable connected_to : (write * write list) Key_hash.Map.t; } type t = network @@ -30,18 +32,30 @@ let set_connection ~connection_id ~owner ~write network = let key_hash = Key_hash.of_key owner in network.connections <- Connection_id.Map.add connection_id write network.connections; - network.connected_to <- Key_hash.Map.add key_hash write network.connected_to - -let close_connection ~connection_id network = + network.connected_to <- + Key_hash.Map.update key_hash + (function + | Some (current_write, writes) -> Some (write, current_write :: writes) + | None -> Some (write, [])) + network.connected_to + +let close_connection ~connection_id ~owner network = + let key_hash = Key_hash.of_key owner in network.connections <- - Connection_id.Map.remove connection_id network.connections + Connection_id.Map.remove connection_id network.connections; + network.connected_to <- + Key_hash.Map.update key_hash + (function + | Some (_current_write, []) -> None + | Some (_current_write, previous_write :: writes) -> + Some (previous_write, writes) + | None -> None) + network.connected_to let with_connection ~on_connection ~on_request ~on_message network k = let connection_id = create_connection network in - let handler ~sw connection = - let write message = - Eio.Fiber.fork ~sw @@ fun () -> Connection.write connection message - in + let handler connection = + let write message = Connection.write connection message in let on_message message = match message with | Network_message.Message { raw_header; raw_content } -> @@ -60,9 +74,10 @@ let with_connection ~on_connection ~on_request ~on_message network k = loop () in let handler connection = + let owner = Connection.owner connection in Fun.protect - ~finally:(fun () -> close_connection ~connection_id network) - (fun () -> Eio.Switch.run @@ fun sw -> handler ~sw connection) + ~finally:(fun () -> close_connection ~connection_id ~owner network) + (fun () -> handler connection) in k handler @@ -112,15 +127,14 @@ let connect ~net ~clock ~nodes ~on_connection ~on_request ~on_message network = nodes let send ~message ~write = - (* write always includes a fork *) try write message with exn -> Logs.warn (fun m -> m "write.error: %s" (Printexc.to_string exn)) let broadcast message network = - Key_hash.Map.iter - (fun _connection write -> send ~message ~write) - network.connected_to + Eio.Fiber.List.iter + (fun (_connection, (write, _writes)) -> send ~message ~write) + (Key_hash.Map.bindings network.connected_to) let request ~raw_header ~raw_content network = let request = Network_message.request ~raw_header ~raw_content in diff --git a/deku-p/src/core/protocol/protocol.ml b/deku-p/src/core/protocol/protocol.ml index 83c8c41787..3008487c82 100644 --- a/deku-p/src/core/protocol/protocol.ml +++ b/deku-p/src/core/protocol/protocol.ml @@ -99,17 +99,20 @@ let apply_operation ~current_level protocol operation : (Address.to_b58 address))) in let result () = - let%some ledger = - if operation.tickets = [] then Some ledger + let%ok ledger = + if operation.tickets = [] then Ok ledger else Ledger.with_ticket_table ledger (fun ~get_table ~set_table -> let tickets = operation.tickets in - let%some _, table = + let result = Ticket_table.take_tickets ~sender ~ticket_ids:tickets (get_table ()) - |> Result.to_option in - Some (set_table table)) + match result with + | Ok (_, table) -> Ok (set_table table) + | Error err -> ( + match err with + | `Insufficient_funds -> Error "Insufficient_funds")) in let source = sender in match @@ -125,17 +128,20 @@ let apply_operation ~current_level protocol operation : |> Env.finalize) with | Ok (vm_state, ledger) -> - Some (ledger, Some receipt, vm_state, None) - | Error _ -> None + Ok (ledger, Some receipt, vm_state, None) + | Error err -> Error err in match result () with - | Some result -> result - | None -> + | Ok result -> result + | Error err -> ( ledger, Some receipt, vm_state, - Some (Failure "Error while executing external vm transaction") - )) + Some + (Failure + (Format.sprintf + "Error while executing external vm transaction : %s" + err)) )) | Operation_noop { sender = _ } -> (ledger, None, vm_state, None) | Operation_withdraw { sender; owner; amount; ticket_id } -> ( match diff --git a/docs/Deku-Canonical/dekuc_quickstart.md b/docs/Deku-Canonical/dekuc_quickstart.md index fdc81fd900..126e4e6999 100644 --- a/docs/Deku-Canonical/dekuc_quickstart.md +++ b/docs/Deku-Canonical/dekuc_quickstart.md @@ -128,7 +128,7 @@ DK1 address of the contract you deployed above. ```js live noInline const code = { source: incrementLigoCode, kind: "jsligo" }; -const myContract = dekuC.contract("DK14bVHNFE7QMQtQ8qdscz7w88RDsWoj7gqJ", code); // 👈 Replace with your contract address +const myContract = dekuC.contract("DK1RJcZYhsgPeW8uQmyaSSP2syTJNsz8pv54", code); // 👈 Replace with your contract address println("Getting contract state..."); myContract diff --git a/examples/deku-c-nodejs/package.json b/examples/deku-c-nodejs/package.json index 1cf69c6b46..1ec2baf72a 100644 --- a/examples/deku-c-nodejs/package.json +++ b/examples/deku-c-nodejs/package.json @@ -10,7 +10,7 @@ "author": "contact@marigold.dev", "license": "ISC", "dependencies": { - "@marigold-dev/deku": "0.1.3" + "@marigold-dev/deku": "0.1.6" }, "devDependencies": { "@taquito/signer": "^14.0.0", diff --git a/examples/deku-p-nodejs/index.ts b/examples/deku-p-nodejs/index.ts index e694fd89db..26d1a391e6 100644 --- a/examples/deku-p-nodejs/index.ts +++ b/examples/deku-p-nodejs/index.ts @@ -19,7 +19,7 @@ const dekuSigner = fromMemorySigner( console.log("Getting the balance"); const balance = await deku.getBalance( "tz1L7zaWD1aRYBTQvSdxEdc9KDzfwG4DydDu", - { ticketer: "KT1WqDmVx6AEB4V4MFoTjSKKt9XhvvihrVJC", data } + { ticketer: "KT1KCkwGxAsFmy6jkF1owQyGkQoYVtajkeGb", data } ); console.log(`The new balance is: ${balance}`); diff --git a/examples/deku-p-nodejs/package.json b/examples/deku-p-nodejs/package.json index 3f748d3949..533fe1b698 100644 --- a/examples/deku-p-nodejs/package.json +++ b/examples/deku-p-nodejs/package.json @@ -11,7 +11,7 @@ "author": "", "license": "MIT", "dependencies": { - "@marigold-dev/deku": "0.1.3", + "@marigold-dev/deku": "0.1.6", "@taquito/signer": "^14.0.0", "@taquito/taquito": "^14.0.0", "axios": "^0.27.2", diff --git a/examples/deku-p-react/package.json b/examples/deku-p-react/package.json index 17431a7bf2..37eb8f89bb 100644 --- a/examples/deku-p-react/package.json +++ b/examples/deku-p-react/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@airgap/beacon-sdk": "^3.1.3", - "@marigold-dev/deku": "0.1.3", + "@marigold-dev/deku": "0.1.6", "@taquito/beacon-wallet": "^13.0.1", "@taquito/signer": "^13.0.1", "@taquito/taquito": "^13.0.1", diff --git a/examples/number-go-up/package.json b/examples/number-go-up/package.json index 17fd78db44..673fc687ac 100644 --- a/examples/number-go-up/package.json +++ b/examples/number-go-up/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@airgap/beacon-sdk": "^3.3.0", - "@marigold-dev/deku": "0.1.3", + "@marigold-dev/deku": "0.1.6", "@taquito/signer": "14.0.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", @@ -47,7 +47,7 @@ ] }, "devDependencies": { - "@marigold-dev/deku-cli": "0.0.4", + "@marigold-dev/deku-cli": "0.1.5", "os-browserify": "^0.3.0", "postcss-normalize": "^10.0.1", "stream-browserify": "^3.0.0", diff --git a/examples/number-go-up/src/App.tsx b/examples/number-go-up/src/App.tsx index 385f67c676..1c9344b740 100644 --- a/examples/number-go-up/src/App.tsx +++ b/examples/number-go-up/src/App.tsx @@ -2,7 +2,7 @@ import { DAppClient, NetworkType } from "@airgap/beacon-sdk"; import { Contract, DekuCClient } from "@marigold-dev/deku"; import { fromBeaconSigner } from "@marigold-dev/deku"; import { useEffect, useState } from "react"; -const contractAddr = "DK1APjGycpfyE94s6MxGXUSaP7Qnznz7TrqX"; +const contractAddr = "DK1Nzaoym3o6LXxLGsbQwTDi3ZYPHMUiNTE5"; const apiURL = "https://deku-canonical-vm0.deku-v1.marigold.dev"; const connectBeaconWallet = async () => { diff --git a/examples/tests/.gitignore b/examples/tests/.gitignore new file mode 100644 index 0000000000..77738287f0 --- /dev/null +++ b/examples/tests/.gitignore @@ -0,0 +1 @@ +dist/ \ No newline at end of file diff --git a/examples/tests/package.json b/examples/tests/package.json new file mode 100644 index 0000000000..f428b7c562 --- /dev/null +++ b/examples/tests/package.json @@ -0,0 +1,23 @@ +{ + "name": "@marigold-dev/deku-tester", + "description": "Test several aspects of deku", + "version": "0.1.7", + "main": "dist/index.js", + "license": "MIT", + "bin": { + "deku-tester": "dist/index.js" + }, + "dependencies": { + "@marigold-dev/deku": "0.1.6", + "@taquito/signer": "^14.1.0", + "@taquito/taquito": "^14.2.0", + "@tzstamp/helpers": "^0.3.4", + "commander": "^9.4.1" + }, + "devDependencies": { + "typescript": "^4.8.4" + }, + "scripts": { + "build": "tsc --project ." + } +} diff --git a/examples/tests/readme.md b/examples/tests/readme.md new file mode 100644 index 0000000000..b716179ee0 --- /dev/null +++ b/examples/tests/readme.md @@ -0,0 +1,42 @@ +# Deku e2e tester + +This tester can test some aspect of the deku-c blockchain: + +- invokation +- origination +- are node sync ? +- are node making progress ? +- ticket transfer +- ticket deposit +- ticket withdraw +- is deku updating the state root hash of the consensus contract ? + +I don't recommend to run the ticket tests in parralel, except if you use different secret in every tests. + +# Requirement: + +- node18 for execution + +# How to run ? + +If you are working in the deku monorepo, you will have to the following commands: + +``` +yarn install # At root position +cd deku-c/client +yarn build +cd ../../examples/tests +yarn build +``` + +Then you can run the help of the tests to check everything. + +``` +node dist/index.js --help +``` + +If you want some precise help on a command: + +``` +node dist/index.js --help +``` diff --git a/examples/tests/src/contract-level.ts b/examples/tests/src/contract-level.ts new file mode 100644 index 0000000000..daee68df09 --- /dev/null +++ b/examples/tests/src/contract-level.ts @@ -0,0 +1,27 @@ +import { DekuPClient } from "@marigold-dev/deku"; +import { TezosToolkit } from "@taquito/taquito"; + +const run = async ({ dekuRpc, tezosRpc }) => { + const deku = new DekuPClient({ dekuRpc }); + const tezos = new TezosToolkit(tezosRpc); + const { consensus } = await deku.info(); + // Retrieve the level of deku + const dekuLevel = await deku.level(); + // retrieve the level of stored in the consensus smart contract + const contract = await tezos.contract.at(consensus); + const storage = (await contract.storage()) as { + root_hash: { current_block_level: BigInt }; + }; + const contractLevel = Number.parseInt( + storage.root_hash.current_block_level.toString() + ); + // Check if there is a delta of 300 blocks between the consensus contract and the deku level + // A delta of 300 blocks represents approximately 5 minutes + if (contractLevel + 300 < dekuLevel) + throw "The consensus is not sync, last update was 5 minutes ago."; + return "Consensus smart contract is sync"; +}; + +export default { + run, +}; diff --git a/examples/tests/src/deposit.ts b/examples/tests/src/deposit.ts new file mode 100644 index 0000000000..0d13100510 --- /dev/null +++ b/examples/tests/src/deposit.ts @@ -0,0 +1,30 @@ +import { DekuPClient } from "@marigold-dev/deku"; +import { InMemorySigner } from "@taquito/signer"; +import { TezosToolkit } from "@taquito/taquito"; + +const run = async ({ dekuRpc, secret, tezosRpc, ticketer, data }) => { + const signer = new InMemorySigner(secret); + const deku = new DekuPClient({ dekuRpc }); + const tezos = new TezosToolkit(tezosRpc); + tezos.setProvider({ signer }); + const address = await signer.publicKeyHash(); + const { consensus } = await deku.info(); + const contract = await tezos.contract.at(ticketer); + // Get the previous balance on deku side + const previousBalance = await deku.getBalance(address, { ticketer, data }); + // Mint some tickets to deku and wait for it to be included + const operation = await contract.methods + .mint_to_deku(consensus, address, 10, data) + .send(); + await operation.confirmation(3); + // Get the new balance on deku side. + const nextBalance = await deku.getBalance(address, { ticketer, data }); + // The balance should have been incremented. + if (previousBalance + 10 !== nextBalance) + throw `The balance of ${address} for ticket ${ticketer} of data ${data} has not been updated`; + return "Balance updated success"; +}; + +export default { + run, +}; diff --git a/examples/tests/src/index.ts b/examples/tests/src/index.ts new file mode 100644 index 0000000000..61afb5ffbe --- /dev/null +++ b/examples/tests/src/index.ts @@ -0,0 +1,119 @@ +#!/usr/bin/env node + +import { Command, Option } from "commander"; +import { handleResult } from "./utils"; +import Origination from "./origination"; +import Invokation from "./invokation"; +import MakingProgress from "./making-progress"; +import IsSync from "./is-sync"; +import Deposit from "./deposit"; +import Transfer from "./transfer"; +import Withdraw from "./withdraw"; +import ContractLevel from "./contract-level"; +import IsCommiting from "./is-commiting"; +import { + ALL_DEKU_RPC, + CANONICAL_CONTRACT_ADDRESS, + DEKU_RPC, + LIGO_RPC, + SECRET, + TICKETER, + TEZOS_RPC, + SECRET_1_WITH_TICKETS, + SECRET_2_WITH_TICKETS, + SECRET_WITH_TEZ, + BLOCKS, + TICKET_DATA, +} from "./options"; + +const program = new Command(); + +program.name("e2e tests").version("0.1.0"); + +program + .command("origination") + .description("Will originate a counter contract on deku.") + .addOption(DEKU_RPC) + .addOption(LIGO_RPC) + .addOption(SECRET) + .action(handleResult(Origination.run)); + +program + .command("invokation") + .description( + "Will invoke all the entrypoints of the counter contract. The contract must have been originated" + ) + .addOption(DEKU_RPC) + .addOption(LIGO_RPC) + .addOption(SECRET) + .addOption(CANONICAL_CONTRACT_ADDRESS) + .action(handleResult(Invokation.run)); + +program + .command("making-progress") + .description("Check if the node is making progress") + .addOption(DEKU_RPC) + .action(handleResult(MakingProgress.run)); + +program + .command("is-sync") + .description( + "Check if the standard derivation of the node levels it not too high." + ) + .addOption(ALL_DEKU_RPC) + .action(handleResult(IsSync.run)); + +program + .command("deposit") + .description( + `Deposit 10 tickets on deku. So the secret needs to have some tez to pay the fees. The dummy ticket contract must have been originated on tezos` + ) + .addOption(DEKU_RPC) + .addOption(SECRET) // Share secret between deku and tezos. + .addOption(TEZOS_RPC) + .addOption(TICKETER) + .addOption(TICKET_DATA) + .action(handleResult(Deposit.run)); + +program + .command("transfer") + .description( + `Transfer 1 ticket from alice to bob, and bob to alice. One of the secret has to have at least one ticket.` + ) + .addOption(DEKU_RPC) + .addOption(SECRET_1_WITH_TICKETS) + .addOption(SECRET_2_WITH_TICKETS) + .addOption(TICKETER) + .addOption(TICKET_DATA) + .action(handleResult(Transfer.run)); + +program + .command("withdraw") + .description( + "Withdraw some tickets on tezos, the secret needs some tez to pay the fees. The dummy ticket contract must have been originated" + ) + .addOption(DEKU_RPC) + .addOption(SECRET_WITH_TEZ) + .addOption(TICKETER) + .addOption(TEZOS_RPC) + .addOption(TICKET_DATA) + .action(handleResult(Withdraw.run)); + +program + .command("contract-level") + .description( + "Check if the level of the consus smart contract has been updated in the last 5 minutes." + ) + .addOption(DEKU_RPC) + .addOption(TEZOS_RPC) + .action(handleResult(ContractLevel.run)); + +program + .command("is-commiting") + .description("Check if the chain is commiting on tezos") + .addOption(DEKU_RPC) + .addOption(TEZOS_RPC) + .addOption(BLOCKS) + .action(handleResult(IsCommiting.run)); + +program.parse(); diff --git a/examples/tests/src/invokation.ts b/examples/tests/src/invokation.ts new file mode 100644 index 0000000000..eea983ec43 --- /dev/null +++ b/examples/tests/src/invokation.ts @@ -0,0 +1,35 @@ +import { DekuCClient, fromMemorySigner } from "@marigold-dev/deku"; +import { InMemorySigner } from "@taquito/signer"; +import { initialStorage, wait, source } from "./utils"; + +const run = async ({ secret, dekuRpc, ligoRpc, address }): Promise => { + const signer = new InMemorySigner(secret); + const dekuSigner = fromMemorySigner(signer); + const deku = new DekuCClient({ dekuRpc, ligoRpc, dekuSigner }); + // Get the contract + const contract = deku.contract(address, { source, kind: "jsligo" }); + const initial = await contract.getState(); + // increment the state + const op1 = await contract.invokeLigo("Increment(2)"); + await wait(dekuRpc, op1); + const state1 = await contract.getState(); + if (initial + 2 !== state1) + throw "Increment did not worked, the state is not updated."; + // decrement the state + const op2 = await contract.invokeLigo("Decrement(1)"); + await wait(dekuRpc, op2); + const state2 = await contract.getState(); + if (state1 - 1 !== state2) + throw `Decrement did not worked, the state is not updated, previous state ${state1}, next state ${state2}`; + // reset the state + const op3 = await contract.invokeLigo("Reset()"); + await wait(dekuRpc, op3); + const state3 = await contract.getState(); + if (state3 !== initialStorage) + throw "Reset did not worked, the state is not updated."; + return "Contract updated as expected"; +}; + +export default { + run, +}; diff --git a/examples/tests/src/is-commiting.ts b/examples/tests/src/is-commiting.ts new file mode 100644 index 0000000000..f72f9c912b --- /dev/null +++ b/examples/tests/src/is-commiting.ts @@ -0,0 +1,35 @@ +import { DekuPClient } from "@marigold-dev/deku"; +import { RpcClient } from "@taquito/rpc"; + +const run = async ({ tezosRpc, dekuRpc, blocks }) => { + const deku = new DekuPClient({ dekuRpc }); + const { consensus } = await deku.info(); + const client = new RpcClient(tezosRpc); + // Get the last n tezos block + const lastBlocks = await Promise.all( + Array(blocks) + .fill(0) + .map((elt, i) => client.getBlock({ block: `head~${i}` })) + ); + const operations = lastBlocks + .map((block) => block.operations) // Extract the operations from the block + .flat(2) + .flatMap((operation) => operation.contents) // Extract the content of the operations + .flatMap((content) => { + // Filter operations to only keep transaction + if (content.kind === "transaction") { + return [content]; + } + return []; + }) + .filter(({ destination }) => destination === consensus) // Only transaction to the consensus are important + .filter( + ({ parameters: { entrypoint } }) => entrypoint === "update_root_hash" + ); // Only transaction from node are important + if (operations.length === 0) throw "The chain is not commiting on tezos"; + return "The chain is commiting on tezos"; +}; + +export default { + run, +}; diff --git a/examples/tests/src/is-sync.ts b/examples/tests/src/is-sync.ts new file mode 100644 index 0000000000..50fe9dc977 --- /dev/null +++ b/examples/tests/src/is-sync.ts @@ -0,0 +1,25 @@ +import { DekuPClient } from "@marigold-dev/deku"; + +const run = async ({ allDekuRpc }) => { + const nodes = allDekuRpc.map( + (dekuRpc) => new DekuPClient({ dekuRpc: dekuRpc }) + ); + // get the level for each node + const levels = await Promise.all(nodes.map((node) => node.level())); + // Get the mean level + const mean = levels.reduce((acc, level) => acc + level, 0) / levels.length; + // Compute the sum of (x - mean)² + const sum = levels + .map((level) => (level - mean) ** 2) + .reduce((acc, curr) => acc + curr, 0); + // Compute the variance: sum / n + let variance = sum / levels.length; + // Compute the deviation: sqrt(variance) + let deviation = Math.sqrt(variance); + if (deviation > 1) throw "The API is not sync"; + return "The API is sync"; +}; + +export default { + run, +}; diff --git a/examples/tests/src/making-progress.ts b/examples/tests/src/making-progress.ts new file mode 100644 index 0000000000..48114ecfdc --- /dev/null +++ b/examples/tests/src/making-progress.ts @@ -0,0 +1,20 @@ +import { DekuPClient } from "@marigold-dev/deku"; +import { sleep } from "./utils"; + +const run = async ({ dekuRpc }) => { + const deku = new DekuPClient({ dekuRpc }); + // Retrieves some levels + const level = await deku.level(); + await sleep(2000); + const nextLevel = await deku.level(); + await sleep(2000); + const nextNextLevel = await deku.level(); + // check if all the levels are different + if (level >= nextLevel || nextLevel >= nextNextLevel) + throw "The chain is not making progress"; + return "The chain is making progress"; +}; + +export default { + run, +}; diff --git a/examples/tests/src/options.ts b/examples/tests/src/options.ts new file mode 100644 index 0000000000..b53ed3c316 --- /dev/null +++ b/examples/tests/src/options.ts @@ -0,0 +1,129 @@ +import { Command, Option } from "commander"; + +type Optionnal = { + arg: string; + env: string; + description: string; + parser: (string: string) => T; + default: T; +}; + +const option = (option: Optionnal) => { + return new Option(option.arg, option.description) + .default(option.default) + .env(option.env) + .argParser(option.parser); +}; + +const dekuRpcs = [ + "https://deku-canonical-vm0.deku-v1.marigold.dev", + "https://deku-canonical-vm1.deku-v1.marigold.dev", + "https://deku-canonical-vm2.deku-v1.marigold.dev", + "https://deku-canonical-vm3.deku-v1.marigold.dev", +]; + +export const DEKU_RPC = option({ + arg: "-d, --deku-rpc ", + env: "DEKU_RPC", + description: + "The url of the deku node, if not specified it will be randomly choosen", + parser: (string) => { + const elts = string.split(","); + return elts[Math.floor(Math.random() * elts.length)]; + }, + default: dekuRpcs[Math.floor(Math.random() * dekuRpcs.length)], +}); + +export const LIGO_RPC = option({ + arg: "-l, --ligo-rpc ", + env: "LIGO_RPC", + description: "The ligo endpoint to compile ligo to michelson.", + parser: (string) => string, + default: "http://0.0.0.0:9090", +}); + +export const SECRET = option({ + arg: "-s, --secret ", + env: "USER_SECRET", + description: "A secret with no need of tez/tickets.", + parser: (string) => string, + default: "edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq", +}); + +export const CANONICAL_CONTRACT_ADDRESS = option({ + arg: "-a, --address
", + env: "CANONICAL_CONTRACT_ADDRESS", + description: "The address of an originated contract on deku-canonical.", + parser: (string) => string, + default: "DK1CAA1DVcxfwRgRMhXX4puag48kgfPtfkJq", +}); + +export const ALL_DEKU_RPC = option({ + arg: "-d, --all-deku-rpc ", + env: "ALL_DEKU_RPC", + description: "A list of several deku rpc separated by a comma.", + parser: (string) => string.split(","), + default: dekuRpcs, +}); + +export const TEZOS_RPC = option({ + arg: "-tz, --tezos-rpc ", + env: "TEZOS_RPC", + description: "The tezos rpc, default is ghostnet.", + parser: (string) => string, + default: "https://rpc.tzkt.io/ghostnet", +}); + +export const TICKETER = option({ + arg: "-t, --ticketer ", + env: "DUMMY_TICKET_CONTRACT", + description: + "The address of an originated dummy ticket contract, by default it will use the already orignated contract on ghostnet.", + parser: (string) => string, + default: "KT1KCkwGxAsFmy6jkF1owQyGkQoYVtajkeGb", +}); + +export const SECRET_WITH_TEZ = option({ + arg: "-s, --secret-with-tez ", + env: "SECRET_WITH_TEZ", + description: "This secret has to have some tez, to pay tezos fees.", + parser: (string) => string, + default: + "edskRubBsVKzfE3rH7GXWb71UewQXDYd2ZDzW8818RWL9mQbRop4V8rJwcjmFRgMApJ1m7ygWPWEum4VtK2VxLokWu7iJAiDM9", +}); + +export const SECRET_1_WITH_TICKETS = option({ + arg: "-s1, --secret-1-with-tickets ", + env: "SECRET_1_WITH_TICKETS", + description: "This secret has to have some tez.", + parser: (string) => string, + default: "edsk3gHBUthmmz15K3v1AcQYw6bXv9XjARWXQV4yLJcUiHtR6MNLhG", +}); + +export const SECRET_2_WITH_TICKETS = option({ + arg: "-s2, --secret-2-with-tickets ", + env: "SECRET_2_WITH_TICKETS", + description: "This secret has to have some tez.", + parser: (string) => string, + default: "edsk43xJ9tDYyJEuSP4cAbc1xSK3zKdCh8V5yHqd1PmNFhhwM7ksH5", +}); + +export const BLOCKS = option({ + arg: "-b, --blocks ", + env: "BLOCKS", + description: "A number of block", + parser: (string) => Number.parseInt(string), + default: 5, +}); + +export const TICKET_DATA = option({ + arg: "--data ", + env: "TICKET_DATA", + description: "The data of your ticket", + parser: (string) => { + if (!string.startsWith("0x")) throw "The data should start with 0x"; + if (string.length % 2 !== 0) throw "Invalid bytes"; + return string; + }, + default: "0x0505050505", +}); diff --git a/examples/tests/src/origination.ts b/examples/tests/src/origination.ts new file mode 100644 index 0000000000..1610668c38 --- /dev/null +++ b/examples/tests/src/origination.ts @@ -0,0 +1,29 @@ +import { DekuCClient, fromMemorySigner } from "@marigold-dev/deku"; +import { InMemorySigner } from "@taquito/signer"; +import { source, initialStorage, wait } from "./utils"; + +const run = async ({ dekuRpc, ligoRpc, secret }): Promise => { + const signer = new InMemorySigner(secret); + const dekuSigner = fromMemorySigner(signer); + const deku = new DekuCClient({ dekuRpc, ligoRpc, dekuSigner }); + // Originate a contract + const { operation, address } = await deku.originateLigo({ + kind: "jsligo", + source, + initialStorage: initialStorage.toString(), + }); + // The operation should be included + await wait(dekuRpc, operation); + // Retrieve the state of the contract + const contract = deku.contract(address); + const state = await contract.getState(); + // The contract should have a state + if (state === null) throw `The contract ${address} has an empty state`; + if (state !== initialStorage) + throw `Invalid initial storage ${state}, expected ${initialStorage}`; + return address; +}; + +export default { + run, +}; diff --git a/examples/tests/src/transfer.ts b/examples/tests/src/transfer.ts new file mode 100644 index 0000000000..f399f1bb2d --- /dev/null +++ b/examples/tests/src/transfer.ts @@ -0,0 +1,60 @@ +import { DekuPClient, fromMemorySigner } from "@marigold-dev/deku"; +import { InMemorySigner } from "@taquito/signer"; +import { wait } from "./utils"; + +const run = async ({ + dekuRpc, + secret1WithTickets, + secret2WithTickets, + ticketer, + data, +}) => { + const ticketId = { ticketer, data }; + // Instanciate deku toolkit for Alice + const aliceSigner = fromMemorySigner(new InMemorySigner(secret1WithTickets)); + const aliceAddr = await aliceSigner.publicKeyHash(); + const dekuA = new DekuPClient({ dekuRpc, dekuSigner: aliceSigner }); + // Instanciate deku toolkit for Bob + const bobSigner = fromMemorySigner(new InMemorySigner(secret2WithTickets)); + const bobAddr = await bobSigner.publicKeyHash(); + const dekuB = new DekuPClient({ dekuRpc, dekuSigner: bobSigner }); + // Get the previous balance of Alice and Bob + const previousBalanceA = await dekuA.getBalance(aliceAddr, ticketId); + const previousBalanceB = await dekuB.getBalance(bobAddr, ticketId); + // Determines who is the sender and who is the receiver + // The sender is the user who has the most balance of the ticket + const [sender, receiver] = + previousBalanceA > previousBalanceB + ? [ + { deku: dekuA, addr: aliceAddr, balance: previousBalanceA }, + { deku: dekuB, addr: bobAddr, balance: previousBalanceB }, + ] + : [ + { deku: dekuB, addr: bobAddr, balance: previousBalanceB }, + { deku: dekuA, addr: aliceAddr, balance: previousBalanceA }, + ]; + // Transfer 1 ticket from the sender to receiver + const op = await sender.deku.transferTo( + receiver.addr, + 1, + ticketer, + data.slice(2) + ); // TODO: "0x..." is not working in the toolkit + await wait(dekuRpc, op); + // Get the new balance of the sender and receiver + const nextBalanceSender = await sender.deku.getBalance(sender.addr, ticketId); + const nextBalanceReceiver = await receiver.deku.getBalance( + receiver.addr, + ticketId + ); + // Check that the balances have been correctly updated. + if (sender.balance - 1 !== nextBalanceSender) + throw "The balance of the sender has not been updated"; + if (receiver.balance + 1 !== nextBalanceReceiver) + throw "The balance of the receiver has not been updated"; + return "Transfer tokens is working"; +}; + +export default { + run, +}; diff --git a/examples/tests/src/utils.ts b/examples/tests/src/utils.ts new file mode 100644 index 0000000000..cdf1822ca9 --- /dev/null +++ b/examples/tests/src/utils.ts @@ -0,0 +1,82 @@ +import { Base58 } from "@tzstamp/helpers"; + +/** + * Exit the program when the promise raises an exception + * @param promise + * @returns exit(1) is the promise failed, exit(0) otherwise + */ +export const handleResult = + (promise) => + (...params) => + promise(...params) + .then(console.log) + .catch((err) => { + console.error(err); + process.exit(1); + }); + +/** + * Wait for an operation to be included + * @param dekuRpc the rpc of the deku node + * @param operation the operation hash to wait + * @param tries the number of try before admitting the operation is not included + * @returns void if success, raises an exception otherwise + */ +export const wait = async ( + dekuRpc: string, + operation, + tries = 10 +): Promise => { + if (tries <= 0) throw `The operation ${operation} has not been applied`; + const res = await fetch(`${dekuRpc}/api/v1/operations/${operation}`); + if (res.ok) return; + await new Promise((resolve) => setTimeout(resolve, 1000)); // 1000 is approximately the time for one block + return wait(dekuRpc, operation, tries - 1); +}; + +export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +/** + * Transform a b58 string into bytes + * @param b58 the b58 repsentation of a blake2b string + * @returns the hexadecimal representation of the hash + */ +export const toBytes = (b58) => { + const y = Base58.decode(b58); + const tmp = new Uint8Array(y.buffer).slice(0, 32 + 2); + return Buffer.from(tmp.slice(2)).toString("hex"); +}; + +/** + * Initial storage of the following contract + */ +export const initialStorage = 0; + +/** + * Counter example with 3 entrypoints: + * - Increment(int) to increment the counter + * - Decrement(int) to decrement the counter + * - Reset() to reset to 0 the counter + */ +export const source = ` +type storage = int; + +type parameter = +| ["Increment", int] +| ["Decrement", int] +| ["Reset"]; + +type return_ = + +[list, +storage]; + +const main = +(action: parameter, store: storage): return_ => { + let storage = match(action, { + Increment: n => store + n, + Decrement: n => store - n, + Reset: () => 0 + }); + return [list([]), storage]}; +`; diff --git a/examples/tests/src/withdraw.ts b/examples/tests/src/withdraw.ts new file mode 100644 index 0000000000..7855c987e7 --- /dev/null +++ b/examples/tests/src/withdraw.ts @@ -0,0 +1,49 @@ +import { DekuPClient, fromMemorySigner } from "@marigold-dev/deku"; +import { InMemorySigner } from "@taquito/signer"; +import { TezosToolkit } from "@taquito/taquito"; +import Deposit from "./deposit"; +import { toBytes, wait } from "./utils"; + +const run = async ({ dekuRpc, secret, tezosRpc, ticketer, data }) => { + // Make a deposit to so that the account have some tickets + await Deposit.run({ dekuRpc, secret, tezosRpc, ticketer, data }); + + const signer = new InMemorySigner(secret); + const address = await signer.publicKeyHash(); + const dekuSigner = fromMemorySigner(signer); + const deku = new DekuPClient({ dekuRpc, dekuSigner }); + const tezos = new TezosToolkit(tezosRpc); + tezos.setSignerProvider(signer); + + // Withdraw 5 tickets + const op = await deku.withdrawTo(address, 5, ticketer, data.slice(2)); + await wait(dekuRpc, op); + // Get the proof of the withdraw + const proof = await deku.getProof(op); + // get the consensus contract + const { consensus } = await deku.info(); + const contract = await tezos.contract.at(consensus); + + // Code from tzportal + const handles = proof.proof as any as Array<[string, string]>; // TODO: fix the proof type in the toolkit + + const withdrawOperation = await contract.methods + .withdraw( + `${ticketer}%burn_callback`, + proof.handle.id, + data.slice(2), + Number.parseInt(proof.handle.amount), + address, + ticketer, + toBytes(proof.withdrawal_handles_hash), + handles.map((pair) => pair.map(toBytes)) + ) + .send(); + const hash = await withdrawOperation.confirmation(3); + + return "Withdraw seems to be ok"; +}; + +export default { + run, +}; diff --git a/examples/tests/tsconfig.json b/examples/tests/tsconfig.json new file mode 100644 index 0000000000..5219268b05 --- /dev/null +++ b/examples/tests/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "outDir": "dist" + }, + "include": ["src/*"] +} diff --git a/networks/betanets/authorized_keys.nix b/networks/betanets/authorized_keys.nix index 91b3a3e60f..86b4db33ff 100644 Binary files a/networks/betanets/authorized_keys.nix and b/networks/betanets/authorized_keys.nix differ diff --git a/networks/betanets/deku_c/network.nix b/networks/betanets/deku_c/network.nix index acf51b4159..b75d6a7b47 100644 Binary files a/networks/betanets/deku_c/network.nix and b/networks/betanets/deku_c/network.nix differ diff --git a/networks/betanets/testnet_preprod/network.nix b/networks/betanets/testnet_preprod/network.nix index a14929a221..5601635f8b 100644 Binary files a/networks/betanets/testnet_preprod/network.nix and b/networks/betanets/testnet_preprod/network.nix differ diff --git a/nix/deku-p/default.nix b/nix/deku-p/default.nix index d273db1918..2d8f3cf157 100644 --- a/nix/deku-p/default.nix +++ b/nix/deku-p/default.nix @@ -39,7 +39,11 @@ static = true; }; - ligo = inputs.ligo.packages.${system}.ligoLight; + ligo = + # TODO: support ligo on more systems in our flake + if system == "x86_64-linux" + then inputs.ligo.packages.${system}.ligoLight + else pkgs.hello; docker = pkgs.callPackage ./docker.nix {inherit deku ligo;}; in { diff --git a/package.json b/package.json index 5c9cd7942f..04b2a4532a 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,13 @@ "examples/deku-p-nodejs", "examples/deku-p-react", "examples/number-go-up", + "examples/tests", "website" ], "scripts": { "build:client": "yarn --cwd deku-c/client build", "build:cli": "yarn --cwd deku-c/deku-cli build", + "build:tests": "yarn --cwd examples/tests build", "start:deku-p-react": "yarn --cwd examples/deku-p-react start" } } diff --git a/website/package.json b/website/package.json index 5c63526ad6..7078994995 100644 --- a/website/package.json +++ b/website/package.json @@ -42,7 +42,7 @@ "@taquito/tzip12": "^14.0.0", "@taquito/tzip16": "^14.0.0", "@taquito/utils": "^14.0.0", - "@marigold-dev/deku": "0.1.3", + "@marigold-dev/deku": "0.1.6", "abort-controller": "^3.0.0", "algoliasearch": "^4.12.2", "axios": "^0.26.0", diff --git a/yarn.lock b/yarn.lock index f1ab36d03b..9893e5a66e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4629,6 +4629,15 @@ resolved "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz" integrity sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg== +"@stablelib/hmac@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/hmac/-/hmac-1.0.1.tgz#3d4c1b8cf194cb05d28155f0eed8a299620a07ec" + integrity sha512-V2APD9NSnhVpV/QMYgCVMIYKiYG6LSqw1S65wxVoirhU/51ACio6D4yDVSwMzuTJXWZoVHbDdINioBwKy5kVmA== + dependencies: + "@stablelib/constant-time" "^1.0.1" + "@stablelib/hash" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + "@stablelib/int@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz" @@ -4652,6 +4661,16 @@ "@stablelib/x25519" "^1.0.3" "@stablelib/xsalsa20" "^1.0.2" +"@stablelib/pbkdf2@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/pbkdf2/-/pbkdf2-1.0.1.tgz#ba4d4379385db3ca46fb48e504ff7933c451be1d" + integrity sha512-d5jwK6jW1DkMyzqY8D1Io+fRXcsUVr95lk5LKX9ghaUdAITTc1ZL0bff+R0IrwSixbHluxhnivG7vDw59AZ/Nw== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/hash" "^1.0.1" + "@stablelib/hmac" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + "@stablelib/poly1305@^1.0.1": version "1.0.1" resolved "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz" @@ -4969,6 +4988,14 @@ "@taquito/taquito" "^14.0.0" libsodium-wrappers "0.7.9" +"@taquito/http-utils@*", "@taquito/http-utils@^15.0.0": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@taquito/http-utils/-/http-utils-15.0.0.tgz#b7fe912972394162705dd322605a52073670e2fb" + integrity sha512-caOofYWZzbxti1s1t3ObkRL4Ph8DpAWWY/staI1IiZJ/g/XX8FDtS71tE+IwBLXwH0kYqP0bNhTGPP04ebEHpQ== + dependencies: + "@vespaiach/axios-fetch-adapter" "^0.3.1" + axios "^0.26.0" + "@taquito/http-utils@^13.0.1": version "13.0.1" resolved "https://registry.npmjs.org/@taquito/http-utils/-/http-utils-13.0.1.tgz" @@ -4994,6 +5021,14 @@ "@taquito/utils" "^14.0.0" buffer "^6.0.3" +"@taquito/local-forging@*", "@taquito/local-forging@^15.0.0": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@taquito/local-forging/-/local-forging-15.0.0.tgz#d12084e4fb916e78124c181ef0b7e6181d53a678" + integrity sha512-8QLut19U03Tsm59RjrSZOlLZx5mglQjQFt5No1VzWfNsXv7mytmn8SlvqFxyt8hyTlm1RM1NdJJ1oO1h1A/BRQ== + dependencies: + "@taquito/utils" "^15.0.0" + bignumber.js "^9.1.0" + "@taquito/local-forging@^13.0.1": version "13.0.1" resolved "https://registry.npmjs.org/@taquito/local-forging/-/local-forging-13.0.1.tgz" @@ -5010,6 +5045,11 @@ "@taquito/utils" "^14.0.0" bignumber.js "^9.0.2" +"@taquito/michel-codec@*", "@taquito/michel-codec@^15.0.0": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@taquito/michel-codec/-/michel-codec-15.0.0.tgz#f05be1fa3811cbd2bfcb16ca2e84c602afae6824" + integrity sha512-pfnrXVSkGeeVNx7MYHTeu79iU4lGynym/7+8QbD/O28LWob8PUrwpt+jaAxsVDdNOYtKz+LO1E4FpC348tHO6Q== + "@taquito/michel-codec@^13.0.1": version "13.0.1" resolved "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-13.0.1.tgz" @@ -5020,6 +5060,16 @@ resolved "https://registry.npmjs.org/@taquito/michel-codec/-/michel-codec-14.0.0.tgz" integrity sha512-ftnBvUVddlHBqvQbGPHEb26KrS4lIcaZ1eIpYJWiz+akb4Pcfyq7j/OEsDZbB7Pl2FP9hqu7ZygOF34zY6Lrtw== +"@taquito/michelson-encoder@*", "@taquito/michelson-encoder@^15.0.0": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@taquito/michelson-encoder/-/michelson-encoder-15.0.0.tgz#8fc3492a22404ae8a8b1907a43bf10745e1b973b" + integrity sha512-TxmWCgZBuA7SrNPSN+xfeeovDGU5H5sXyXcgL3KwYfeILaWiWr1MbI9n8xV7ewJZSC9svor6/osz7Y04+TIpgw== + dependencies: + "@taquito/rpc" "^15.0.0" + "@taquito/utils" "^15.0.0" + bignumber.js "^9.1.0" + fast-json-stable-stringify "^2.1.0" + "@taquito/michelson-encoder@^13.0.1": version "13.0.1" resolved "https://registry.npmjs.org/@taquito/michelson-encoder/-/michelson-encoder-13.0.1.tgz" @@ -5052,6 +5102,15 @@ "@taquito/utils" "^14.0.0" typedarray-to-buffer "^4.0.0" +"@taquito/rpc@*", "@taquito/rpc@^15.0.0": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@taquito/rpc/-/rpc-15.0.0.tgz#096ac942be0d5d3bc5f7ad4f3dbe4b15dd1d1e12" + integrity sha512-z5oPSD2QDhLqU9scA4Pof2+DUaK9P3oDWeCqr6/JAOlt1DwDwYsHj84d4UVmpjP/DrTj4Sp1IyY7I9KpNNLlGQ== + dependencies: + "@taquito/http-utils" "^15.0.0" + "@taquito/utils" "^15.0.0" + bignumber.js "^9.1.0" + "@taquito/rpc@^13.0.1": version "13.0.1" resolved "https://registry.npmjs.org/@taquito/rpc/-/rpc-13.0.1.tgz" @@ -5115,6 +5174,39 @@ pbkdf2 "^3.1.2" typedarray-to-buffer "^4.0.0" +"@taquito/signer@^14.1.0": + version "14.2.0" + resolved "https://registry.yarnpkg.com/@taquito/signer/-/signer-14.2.0.tgz#a65d122c08bfbdc356ea7fe2ee0f044918e4733d" + integrity sha512-WSJ75F3eRE1YEQ1A3j1vqHWG59Ha8hoP1j+eF+e0JTIpvYJoQu8YV4uuoO0yd7zPMSFpbGZI3NwhfNltpCmwiQ== + dependencies: + "@stablelib/blake2b" "^1.0.1" + "@stablelib/ed25519" "^1.0.3" + "@stablelib/hmac" "^1.0.1" + "@stablelib/nacl" "^1.0.4" + "@stablelib/pbkdf2" "^1.0.1" + "@stablelib/sha512" "^1.0.1" + "@taquito/taquito" "*" + "@taquito/utils" "*" + "@types/bn.js" "^5.1.1" + bip39 "^3.0.4" + elliptic "^6.5.4" + pbkdf2 "^3.1.2" + typedarray-to-buffer "^4.0.0" + +"@taquito/taquito@*": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@taquito/taquito/-/taquito-15.0.0.tgz#5bde81d8e81a25dcf7039e08b22eccad73e631c8" + integrity sha512-pYs/hBbjapR/wN+gLhQQYEbL3D7WZXsR/+cb5oCN67vhKwyXkDuBJIEpWIoFGNcwJn0eGFGkUjfngE1bUyMypA== + dependencies: + "@taquito/http-utils" "^15.0.0" + "@taquito/local-forging" "^15.0.0" + "@taquito/michel-codec" "^15.0.0" + "@taquito/michelson-encoder" "^15.0.0" + "@taquito/rpc" "^15.0.0" + "@taquito/utils" "^15.0.0" + bignumber.js "^9.1.0" + rxjs "^6.6.3" + "@taquito/taquito@^13.0.1": version "13.0.1" resolved "https://registry.npmjs.org/@taquito/taquito/-/taquito-13.0.1.tgz" @@ -5143,6 +5235,20 @@ bignumber.js "^9.0.2" rxjs "^6.6.3" +"@taquito/taquito@^14.2.0": + version "14.2.0" + resolved "https://registry.yarnpkg.com/@taquito/taquito/-/taquito-14.2.0.tgz#5ce737663fdb06316798a32da416fedff3696800" + integrity sha512-JrAUtANIVr02TFI6N87Xnn+KfgDT8hTA6IBeRhGfQnZH8nL1LG6/6q7uHZTw6D4ddfnMvxjgA/916cxAFcskFA== + dependencies: + "@taquito/http-utils" "*" + "@taquito/local-forging" "*" + "@taquito/michel-codec" "*" + "@taquito/michelson-encoder" "*" + "@taquito/rpc" "*" + "@taquito/utils" "*" + bignumber.js "^9.1.0" + rxjs "^6.6.3" + "@taquito/tezbridge-wallet@^14.0.0": version "14.0.0" resolved "https://registry.npmjs.org/@taquito/tezbridge-wallet/-/tezbridge-wallet-14.0.0.tgz" @@ -5172,6 +5278,21 @@ bignumber.js "^9.0.2" crypto-js "^4.1.1" +"@taquito/utils@*", "@taquito/utils@^15.0.0": + version "15.0.0" + resolved "https://registry.yarnpkg.com/@taquito/utils/-/utils-15.0.0.tgz#1d9cf577dd3b4709a5100976d790d0e5fc04d346" + integrity sha512-15Eq3YarC3HuZY8fmAEmtZaFglMIHjRVeR7l4fd8NJK6EdDZydJ00o40JMof9mhSy6A5atm15IHLD5sfdfx5lg== + dependencies: + "@stablelib/blake2b" "^1.0.1" + "@stablelib/ed25519" "^1.0.3" + "@types/bs58check" "^2.1.0" + bignumber.js "^9.1.0" + blakejs "^1.2.1" + bs58check "^2.1.2" + buffer "^6.0.3" + elliptic "^6.5.4" + typedarray-to-buffer "^4.0.0" + "@taquito/utils@^13.0.1": version "13.0.1" resolved "https://registry.npmjs.org/@taquito/utils/-/utils-13.0.1.tgz" @@ -5335,6 +5456,13 @@ dependencies: "@babel/types" "^7.3.0" +"@types/bn.js@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" + integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== + dependencies: + "@types/node" "*" + "@types/body-parser@*": version "1.19.2" resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" @@ -6035,6 +6163,11 @@ resolved "https://registry.npmjs.org/@tzstamp/helpers/-/helpers-0.3.4.tgz" integrity sha512-UM/Sd15xy5EBKJx5UZL/D2H1YETJxyTnbnmEO9zXiTTQ6DAlBuKdsihhspHqymySEznCRO9b6dHkaPkQsqM6Vg== +"@vespaiach/axios-fetch-adapter@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@vespaiach/axios-fetch-adapter/-/axios-fetch-adapter-0.3.1.tgz#b0c08167bec9cc558f578a1b9ccff52ead1cf1cb" + integrity sha512-+1F52VWXmQHSRFSv4/H0wtnxfvjRMPK5531e880MIjypPdUSX6QZuoDgEVeCE1vjhzDdxCVX7rOqkub7StEUwQ== + "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz" @@ -7005,6 +7138,11 @@ bignumber.js@^9.0.2: resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz" integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== +bignumber.js@^9.1.0: + version "9.1.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" + integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== + bin-links@^3.0.0: version "3.0.3" resolved "https://registry.npmjs.org/bin-links/-/bin-links-3.0.3.tgz"