From 4750599a840828ff4d8093ea97664795fa37bf4f Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:18:30 -0700 Subject: [PATCH 01/15] expgose add_lagrange_basis function on srs --- .../stubs/binding_generation/src/main.rs | 2 ++ .../crypto/kimchi_bindings/stubs/kimchi_bindings.ml | 6 ++++++ src/lib/crypto/kimchi_bindings/stubs/src/srs.rs | 12 ++++++++++++ src/lib/crypto/kimchi_bindings/wasm/src/srs.rs | 11 +++++++++++ 4 files changed, 31 insertions(+) diff --git a/src/lib/crypto/kimchi_bindings/stubs/binding_generation/src/main.rs b/src/lib/crypto/kimchi_bindings/stubs/binding_generation/src/main.rs index 7b4792b87a9..ad05dcc8d47 100644 --- a/src/lib/crypto/kimchi_bindings/stubs/binding_generation/src/main.rs +++ b/src/lib/crypto/kimchi_bindings/stubs/binding_generation/src/main.rs @@ -334,6 +334,7 @@ fn generate_kimchi_bindings(mut w: impl std::io::Write, env: &mut Env) { decl_func!(w, env, caml_fp_srs_write => "write"); decl_func!(w, env, caml_fp_srs_read => "read"); decl_func!(w, env, caml_fp_srs_lagrange_commitment => "lagrange_commitment"); + decl_func!(w, env, caml_fp_srs_add_lagrange_basis=> "add_lagrange_basis"); decl_func!(w, env, caml_fp_srs_commit_evaluations => "commit_evaluations"); decl_func!(w, env, caml_fp_srs_b_poly_commitment => "b_poly_commitment"); decl_func!(w, env, caml_fp_srs_batch_accumulator_check => "batch_accumulator_check"); @@ -347,6 +348,7 @@ fn generate_kimchi_bindings(mut w: impl std::io::Write, env: &mut Env) { decl_func!(w, env, caml_fq_srs_write => "write"); decl_func!(w, env, caml_fq_srs_read => "read"); decl_func!(w, env, caml_fq_srs_lagrange_commitment => "lagrange_commitment"); + decl_func!(w, env, caml_fq_srs_add_lagrange_basis=> "add_lagrange_basis"); decl_func!(w, env, caml_fq_srs_commit_evaluations => "commit_evaluations"); decl_func!(w, env, caml_fq_srs_b_poly_commitment => "b_poly_commitment"); decl_func!(w, env, caml_fq_srs_batch_accumulator_check => "batch_accumulator_check"); diff --git a/src/lib/crypto/kimchi_bindings/stubs/kimchi_bindings.ml b/src/lib/crypto/kimchi_bindings/stubs/kimchi_bindings.ml index 28b497c0009..f2e1daf5530 100644 --- a/src/lib/crypto/kimchi_bindings/stubs/kimchi_bindings.ml +++ b/src/lib/crypto/kimchi_bindings/stubs/kimchi_bindings.ml @@ -105,6 +105,9 @@ module Protocol = struct -> Pasta_bindings.Fq.t Kimchi_types.or_infinity Kimchi_types.poly_comm = "caml_fp_srs_lagrange_commitment" + external add_lagrange_basis : t -> int -> unit + = "caml_fp_srs_add_lagrange_basis" + external commit_evaluations : t -> int @@ -144,6 +147,9 @@ module Protocol = struct -> Pasta_bindings.Fp.t Kimchi_types.or_infinity Kimchi_types.poly_comm = "caml_fq_srs_lagrange_commitment" + external add_lagrange_basis : t -> int -> unit + = "caml_fq_srs_add_lagrange_basis" + external commit_evaluations : t -> int diff --git a/src/lib/crypto/kimchi_bindings/stubs/src/srs.rs b/src/lib/crypto/kimchi_bindings/stubs/src/srs.rs index 33d5c48efce..df66351e922 100644 --- a/src/lib/crypto/kimchi_bindings/stubs/src/srs.rs +++ b/src/lib/crypto/kimchi_bindings/stubs/src/srs.rs @@ -94,6 +94,18 @@ macro_rules! impl_srs { Ok(srs.commit_non_hiding(&p, None).into()) } + #[ocaml_gen::func] + #[ocaml::func] + pub fn [<$name:snake _add_lagrange_basis>]( + srs: $name, + log2_size: ocaml::Int, + ) { + let ptr: &mut commitment_dlog::srs::SRS = + unsafe { &mut *(std::sync::Arc::as_ptr(&srs) as *mut _) }; + let domain = EvaluationDomain::<$F>::new(1 << (log2_size as usize)).expect("invalid domain size"); + ptr.add_lagrange_basis(domain); + } + #[ocaml_gen::func] #[ocaml::func] pub fn [<$name:snake _commit_evaluations>]( diff --git a/src/lib/crypto/kimchi_bindings/wasm/src/srs.rs b/src/lib/crypto/kimchi_bindings/wasm/src/srs.rs index 28ab76f3011..5886380e67a 100644 --- a/src/lib/crypto/kimchi_bindings/wasm/src/srs.rs +++ b/src/lib/crypto/kimchi_bindings/wasm/src/srs.rs @@ -73,6 +73,17 @@ macro_rules! impl_srs { Arc::new(SRS::create(depth as usize)).into() } + #[wasm_bindgen] + pub fn [<$name:snake _add_lagrange_basis>]( + srs: &[], + log2_size: i32, + ) { + let ptr: &mut commitment_dlog::srs::SRS<$G> = + unsafe { &mut *(std::sync::Arc::as_ptr(&srs) as *mut _) }; + let domain = EvaluationDomain::<$F>::new(1 << (log2_size as usize)).expect("invalid domain size"); + ptr.add_lagrange_basis(domain); + } + #[wasm_bindgen] pub fn [<$name:snake _write>]( append: Option, From 6470bac3e1c67170346b4ef66e54d2b9649a03e2 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:20:34 -0700 Subject: [PATCH 02/15] separate out proofs_verified --- .../pickles/composition_types/branch_data.ml | 54 +------- .../pickles/composition_types/branch_data.mli | 21 +-- src/lib/pickles_base/proofs_verified.ml | 126 ++++++++++++++++++ 3 files changed, 135 insertions(+), 66 deletions(-) create mode 100644 src/lib/pickles_base/proofs_verified.ml diff --git a/src/lib/pickles/composition_types/branch_data.ml b/src/lib/pickles/composition_types/branch_data.ml index befbab43e75..3f0bcbbaf7d 100644 --- a/src/lib/pickles/composition_types/branch_data.ml +++ b/src/lib/pickles/composition_types/branch_data.ml @@ -4,46 +4,7 @@ open Pickles_types (** Data specific to a branch of a proof-system that's necessary for finalizing the deferred-values in a wrap proof of that branch. *) -(** Inside the circuit, we will represent a value of this type - as a sequence of 2 bits: - 00: N0 - 10: N1 - 11: N2 *) -module Proofs_verified = struct - [%%versioned - module Stable = struct - module V1 = struct - type t = N0 | N1 | N2 - [@@deriving sexp, sexp, compare, yojson, hash, equal] - - let to_latest = Fn.id - end - end] - - module Checked = struct - type 'f boolean = 'f Snarky_backendless.Cvar.t Snarky_backendless.Boolean.t - - type 'f t = ('f boolean, Nat.N2.n) Vector.t - end - - let to_mask : t -> (bool, Nat.N2.n) Vector.t = function - | N0 -> - [ false; false ] - | N1 -> - [ true; false ] - | N2 -> - [ true; true ] - - let of_mask_exn : (bool, Nat.N2.n) Vector.t -> t = function - | [ false; false ] -> - N0 - | [ true; false ] -> - N1 - | [ true; true ] -> - N2 - | [ false; true ] -> - failwith "Invalid mask" -end +module Proofs_verified = Pickles_base.Proofs_verified module Domain_log2 = struct [%%versioned @@ -98,14 +59,15 @@ let pack (type f) (* shift domain_log2 over by 2 bits (multiply by 4) *) times4 domain_log2 + project - (Pickles_types.Vector.to_list (Proofs_verified.to_mask proofs_verified)) + (Pickles_types.Vector.to_list + (Proofs_verified.Prefix_mask.there proofs_verified) ) let unpack (type f) (module Impl : Snarky_backendless.Snark_intf.Run with type field = f) (x : f) : t = match Impl.Field.Constant.unpack x with | x0 :: x1 :: y0 :: y1 :: y2 :: y3 :: y4 :: y5 :: y6 :: y7 :: _ -> - { proofs_verified = Proofs_verified.of_mask_exn [ x0; x1 ] + { proofs_verified = Proofs_verified.Prefix_mask.back [ x0; x1 ] ; domain_log2 = Domain_log2.of_bits_msb [ y7; y6; y5; y4; y3; y2; y1; y0 ] } | _ -> @@ -113,7 +75,7 @@ let unpack (type f) module Checked = struct type 'f t = - { proofs_verified_mask : 'f Proofs_verified.Checked.t + { proofs_verified_mask : 'f Proofs_verified.Prefix_mask.Checked.t ; domain_log2 : 'f Snarky_backendless.Cvar.t } [@@deriving hlist] @@ -141,10 +103,8 @@ let typ (type f) (assert_16_bits : Impl.Field.t -> unit) : (f Checked.t, t) Impl.Typ.t = let open Impl in let proofs_verified_mask : - (f Proofs_verified.Checked.t, Proofs_verified.t) Typ.t = - Typ.transport - (Vector.typ Boolean.typ Nat.N2.n) - ~there:Proofs_verified.to_mask ~back:Proofs_verified.of_mask_exn + (f Proofs_verified.Prefix_mask.Checked.t, Proofs_verified.t) Typ.t = + Proofs_verified.Prefix_mask.typ (module Impl) in let domain_log2 : (Field.t, Domain_log2.t) Typ.t = let (Typ t) = diff --git a/src/lib/pickles/composition_types/branch_data.mli b/src/lib/pickles/composition_types/branch_data.mli index db377665f4c..2e5a530f578 100644 --- a/src/lib/pickles/composition_types/branch_data.mli +++ b/src/lib/pickles/composition_types/branch_data.mli @@ -1,22 +1,5 @@ open Core_kernel - -module Proofs_verified : sig - [%%versioned: - module Stable : sig - module V1 : sig - type t = N0 | N1 | N2 - [@@deriving sexp, sexp, compare, yojson, hash, equal] - end - end] - - module Checked : sig - type 'f boolean = 'f Snarky_backendless.Cvar.t Snarky_backendless.Boolean.t - - open Pickles_types - - type 'f t = ('f boolean, Nat.N2.n) Vector.t - end -end +module Proofs_verified = Pickles_base.Proofs_verified module Domain_log2 : sig [%%versioned: @@ -42,7 +25,7 @@ end] module Checked : sig type 'f t = - { proofs_verified_mask : 'f Proofs_verified.Checked.t + { proofs_verified_mask : 'f Proofs_verified.Prefix_mask.Checked.t ; domain_log2 : 'f Snarky_backendless.Cvar.t } diff --git a/src/lib/pickles_base/proofs_verified.ml b/src/lib/pickles_base/proofs_verified.ml new file mode 100644 index 00000000000..bdfa1823c4e --- /dev/null +++ b/src/lib/pickles_base/proofs_verified.ml @@ -0,0 +1,126 @@ +open Core_kernel +open Pickles_types + +[%%versioned +module Stable = struct + module V1 = struct + type t = N0 | N1 | N2 + [@@deriving sexp, sexp, compare, yojson, hash, equal] + + let to_latest = Fn.id + end +end] + +let to_int : t -> int = function N0 -> 0 | N1 -> 1 | N2 -> 2 + +(** Inside the circuit, we use two different representations for this type, + depending on what we need it for. + + Sometimes, we use it for masking out a list of 2 points by taking the + a prefix of length 0, 1, or 2. In this setting, we we will represent a value + of this type as a sequence of 2 bits like so: + 00: N0 + 10: N1 + 11: N2 + + We call this a **prefix mask**. + + Sometimes, we use it to select something from a list of 3 values. In this + case, we will represent a value of this type as a sequence of 3 bits like so: + + 100: N0 + 010: N1 + 001: N2 + + We call this a **one-hot vector** as elsewhere. +*) + +type proofs_verified = t + +let of_nat (type n) (n : n Nat.t) : t = + match n with + | Z -> + N0 + | S Z -> + N1 + | S (S Z) -> + N2 + | _ -> + failwithf "Proofs_verified.of_nat: got %d" (Nat.to_int n) () + +type 'f boolean = 'f Snarky_backendless.Cvar.t Snarky_backendless.Boolean.t + +module Prefix_mask = struct + module Checked = struct + type 'f t = ('f boolean, Nat.N2.n) Vector.t + end + + let there : proofs_verified -> (bool, Nat.N2.n) Vector.t = function + | N0 -> + [ false; false ] + | N1 -> + [ true; false ] + | N2 -> + [ true; true ] + + let back : (bool, Nat.N2.n) Vector.t -> proofs_verified = function + | [ false; false ] -> + N0 + | [ true; false ] -> + N1 + | [ true; true ] -> + N2 + | [ false; true ] -> + failwith "Invalid mask" + + let create = there + + let typ (type f) + (module Impl : Snarky_backendless.Snark_intf.Run with type field = f) : + (f Checked.t, proofs_verified) Impl.Typ.t = + let open Impl in + Typ.transport (Vector.typ Boolean.typ Nat.N2.n) ~there ~back +end + +module One_hot = struct + module Checked = struct + type 'f t = ('f, Nat.N3.n) One_hot_vector.t + + let to_input (type f) (t : f t) = + Random_oracle_input.Chunked.packeds + (Array.map + (Vector.to_array (t :> (f boolean, Nat.N3.n) Vector.t)) + ~f:(fun b -> ((b :> f Snarky_backendless.Cvar.t), 1)) ) + end + + let there : proofs_verified -> int = function N0 -> 0 | N1 -> 1 | N2 -> 2 + + let back : int -> proofs_verified = function + | 0 -> + N0 + | 1 -> + N1 + | 2 -> + N2 + | _ -> + failwith "Invalid mask" + + let to_input ~zero ~one (type f) (t : t) = + let one_hot = + match t with + | N0 -> + [| one; zero; zero |] + | N1 -> + [| zero; one; zero |] + | N2 -> + [| zero; zero; one |] + in + Random_oracle_input.Chunked.packeds (Array.map one_hot ~f:(fun b -> (b, 1))) + + let typ (type f) + (module Impl : Snarky_backendless.Snark_intf.Run with type field = f) : + (f Checked.t, proofs_verified) Impl.Typ.t = + let module M = One_hot_vector.Make (Impl) in + let open Impl in + Typ.transport (M.typ Nat.N3.n) ~there ~back +end From 0675d0e7531c4329682ac37bccea12694993831b Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:20:57 -0700 Subject: [PATCH 03/15] add convenience functions to vector --- src/lib/pickles_types/vector.ml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/pickles_types/vector.ml b/src/lib/pickles_types/vector.ml index 45027c65bc6..af079c4151a 100644 --- a/src/lib/pickles_types/vector.ml +++ b/src/lib/pickles_types/vector.ml @@ -15,6 +15,10 @@ end include T +let hd (type a n) (t : (a, n s) t) : a = match t with x :: _ -> x + +let unsingleton (type a) ([ x ] : (a, z s) t) : a = x + let rec iter : type a n. (a, n) t -> f:(a -> unit) -> unit = fun t ~f -> match t with [] -> () | x :: xs -> f x ; iter xs ~f From af720707aa7f998d5186719368cc7b5bac3e9b50 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:21:42 -0700 Subject: [PATCH 04/15] replace max_width field with max_proofs_verified --- src/lib/pickles_base/dune | 1 + .../pickles_base/side_loaded_verification_key.ml | 14 +++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib/pickles_base/dune b/src/lib/pickles_base/dune index 1ab6335a75e..a142c81e5e2 100644 --- a/src/lib/pickles_base/dune +++ b/src/lib/pickles_base/dune @@ -18,4 +18,5 @@ snarky.backendless random_oracle_input pickles_types + pickles.one_hot_vector )) diff --git a/src/lib/pickles_base/side_loaded_verification_key.ml b/src/lib/pickles_base/side_loaded_verification_key.ml index 9f7cb15dd06..b894a8a9d3f 100644 --- a/src/lib/pickles_base/side_loaded_verification_key.ml +++ b/src/lib/pickles_base/side_loaded_verification_key.ml @@ -148,7 +148,7 @@ module Repr = struct module Stable = struct module V2 = struct type 'g t = - { max_width : Width.Stable.V1.t + { max_proofs_verified : Proofs_verified.Stable.V1.t ; wrap_index : 'g Plonk_verification_key_evals.Stable.V2.t } [@@deriving sexp, equal, compare, yojson] @@ -162,8 +162,8 @@ module Poly = struct [%%versioned module Stable = struct module V2 = struct - type ('g, 'vk) t = - { max_width : Width.Stable.V1.t + type ('g, 'proofs_verified, 'vk) t = + { max_proofs_verified : 'proofs_verified ; wrap_index : 'g Plonk_verification_key_evals.Stable.V2.t ; wrap_vk : 'vk option } @@ -191,12 +191,12 @@ let wrap_index_to_input (type gs f) (g : gs -> f array) t = let width_size = Nat.to_int Width.Length.n let to_input (type a) ~(field_of_int : int -> a) : - (a * a, _) Poly.t -> a Random_oracle_input.Chunked.t = + (a * a, _, _) Poly.t -> a Random_oracle_input.Chunked.t = let open Random_oracle_input.Chunked in - fun { max_width; wrap_index } : _ Random_oracle_input.Chunked.t -> - let width w = (field_of_int (Width.to_int w), width_size) in + fun { max_proofs_verified; wrap_index } : _ Random_oracle_input.Chunked.t -> List.reduce_exn ~f:append - [ packed (width max_width) + [ Proofs_verified.One_hot.to_input ~zero:(field_of_int 0) + ~one:(field_of_int 1) max_proofs_verified ; wrap_index_to_input (Fn.compose Array.of_list (fun (x, y) -> [ x; y ])) wrap_index From 84ebb3981d45073ae0c992853c11ff2165ec2b37 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:22:14 -0700 Subject: [PATCH 05/15] expose one_hot_vector type outside functor --- src/lib/pickles/one_hot_vector/one_hot_vector.ml | 9 ++++++--- src/lib/pickles/one_hot_vector/one_hot_vector.mli | 6 +++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/lib/pickles/one_hot_vector/one_hot_vector.ml b/src/lib/pickles/one_hot_vector/one_hot_vector.ml index 08f77674fd0..fc902fb0e83 100644 --- a/src/lib/pickles/one_hot_vector/one_hot_vector.ml +++ b/src/lib/pickles/one_hot_vector/one_hot_vector.ml @@ -5,10 +5,13 @@ module Constant = struct type t = int end +(* TODO: Optimization(?) Have this have length n - 1 since the last one is + determined by the remaining ones. *) +type ('f, 'n) t = + ('f Snarky_backendless.Cvar.t Snarky_backendless.Boolean.t, 'n) Vector.t + module T (Impl : Snarky_backendless.Snark_intf.Run) = struct - (* TODO: Optimization. Have this have length n - 1 since the last one is - determined by the remaining ones. *) - type 'n t = (Impl.Boolean.var, 'n) Vector.t + type nonrec 'n t = (Impl.field, 'n) t end module Make (Impl : Snarky_backendless.Snark_intf.Run) = struct diff --git a/src/lib/pickles/one_hot_vector/one_hot_vector.mli b/src/lib/pickles/one_hot_vector/one_hot_vector.mli index 50d558f2e25..7591d259399 100644 --- a/src/lib/pickles/one_hot_vector/one_hot_vector.mli +++ b/src/lib/pickles/one_hot_vector/one_hot_vector.mli @@ -4,8 +4,12 @@ module Constant : sig type t = int end +type ('f, 'n) t = + private + ('f Snarky_backendless.Cvar.t Snarky_backendless.Boolean.t, 'n) Vector.t + module T (Impl : Snarky_backendless.Snark_intf.Run) : sig - type 'n t = private (Impl.Boolean.var, 'n) Vector.t + type nonrec 'n t = (Impl.field, 'n) t end module Make (Impl : Snarky_backendless.Snark_intf.Run) : sig From 7458cf711a9a218c72f988d38db0f8c974bd7f9a Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:22:40 -0700 Subject: [PATCH 06/15] update side loaded verification key --- .../pickles/side_loaded_verification_key.ml | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/lib/pickles/side_loaded_verification_key.ml b/src/lib/pickles/side_loaded_verification_key.ml index b9d6c0ca510..96d0cd04ac4 100644 --- a/src/lib/pickles/side_loaded_verification_key.ml +++ b/src/lib/pickles/side_loaded_verification_key.ml @@ -178,7 +178,11 @@ end module Stable = struct module V2 = struct module T = struct - type t = (Backend.Tock.Curve.Affine.t, Vk.t) Poly.Stable.V2.t + type t = + ( Backend.Tock.Curve.Affine.t + , Pickles_base.Proofs_verified.Stable.V1.t + , Vk.t ) + Poly.Stable.V2.t [@@deriving hash] let to_latest = Fn.id @@ -187,13 +191,17 @@ module Stable = struct let version_byte = Base58_check.Version_bytes.verification_key - let to_repr { Poly.max_width; wrap_index; wrap_vk = _ } = - { Repr.Stable.V2.max_width; wrap_index } + let to_repr { Poly.max_proofs_verified; wrap_index; wrap_vk = _ } = + { Repr.Stable.V2.max_proofs_verified; wrap_index } - let of_repr ({ Repr.Stable.V2.max_width; wrap_index = c } : R.Stable.V2.t) - : t = + let of_repr + ({ Repr.Stable.V2.max_proofs_verified; wrap_index = c } : + R.Stable.V2.t ) : t = let d = - (Common.wrap_domains ~proofs_verified:(Width.to_int max_width)).h + (Common.wrap_domains + ~proofs_verified: + (Pickles_base.Proofs_verified.to_int max_proofs_verified) ) + .h in let log2_size = Import.Domain.log2_size d in let max_quot_size = Common.max_quot_size_int (Import.Domain.size d) in @@ -231,7 +239,7 @@ module Stable = struct ; lookup_index = None } ) in - { Poly.max_width; wrap_index = c; wrap_vk } + { Poly.max_proofs_verified; wrap_index = c; wrap_vk } (* Proxy derivers to [R.t]'s, ignoring [wrap_vk] *) @@ -277,7 +285,7 @@ Stable.Latest. , compare )] let dummy : t = - { max_width = Width.zero + { max_proofs_verified = N2 ; wrap_index = (let g = Backend.Tock.Curve.(to_affine_exn one) in { sigma_comm = Vector.init Plonk_types.Permuts.n ~f:(fun _ -> g) @@ -297,8 +305,8 @@ module Checked = struct open Impl type t = - { (* TODO: Not sure this is used *) - max_width : Width.Checked.t + { max_proofs_verified : + Impl.field Pickles_base.Proofs_verified.One_hot.Checked.t (** The maximum of all of the [step_widths]. *) ; wrap_index : Inner_curve.t Plonk_verification_key_evals.t (** The plonk verification key for the 'wrapping' proof that this key @@ -312,10 +320,13 @@ module Checked = struct let to_input = let open Random_oracle_input.Chunked in - fun { max_width; wrap_index } : _ Random_oracle_input.Chunked.t -> - let width w = (Width.Checked.to_field w, width_size) in + fun { max_proofs_verified; wrap_index } : _ Random_oracle_input.Chunked.t -> + let max_proofs_verified = + Pickles_base.Proofs_verified.One_hot.Checked.to_input + max_proofs_verified + in List.reduce_exn ~f:append - [ packed (width max_width) + [ max_proofs_verified ; wrap_index_to_input (Fn.compose Array.of_list Inner_curve.to_field_elements) wrap_index @@ -339,9 +350,11 @@ let typ : (Checked.t, t) Impls.Step.Typ.t = let open Step_main_inputs in let open Impl in Typ.of_hlistable - [ Width.typ; Plonk_verification_key_evals.typ Inner_curve.typ ] + [ Pickles_base.Proofs_verified.One_hot.typ (module Impls.Step) + ; Plonk_verification_key_evals.typ Inner_curve.typ + ] ~var_to_hlist:Checked.to_hlist ~var_of_hlist:Checked.of_hlist ~value_of_hlist:(fun _ -> failwith "Side_loaded_verification_key: value_of_hlist" ) - ~value_to_hlist:(fun { Poly.wrap_index; max_width; _ } -> - [ max_width; wrap_index ] ) + ~value_to_hlist:(fun { Poly.wrap_index; max_proofs_verified; _ } -> + [ max_proofs_verified; wrap_index ] ) From eb9f88b910d1523a4b992fe1a5d0e698f1653aa9 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:23:20 -0700 Subject: [PATCH 07/15] make domain dynamic for public input commitment for circuit for verifying wrap proofs --- src/lib/pickles/step_verifier.ml | 153 +++++++++++++++++++++++++++---- 1 file changed, 133 insertions(+), 20 deletions(-) diff --git a/src/lib/pickles/step_verifier.ml b/src/lib/pickles/step_verifier.ml index b459f43b4bb..0fa812eb116 100644 --- a/src/lib/pickles/step_verifier.ml +++ b/src/lib/pickles/step_verifier.ml @@ -107,25 +107,27 @@ struct let ( + ) = Ops.add_fast end + module Public_input_scalar = struct + type t = Field.t + + let typ = Field.typ + + module Constant = struct + include Field.Constant + + let to_bigint = Impl.Bigint.of_field + end + end + let multiscale_known (ts : ( [ `Field of Field.t | `Packed_bits of Field.t * int ] * Inner_curve.Constant.t ) array ) = + let module F = Public_input_scalar in let rec pow2pow x i = if i = 0 then x else pow2pow Inner_curve.Constant.(x + x) (i - 1) in - let module F = struct - type t = Field.t - - let typ = Field.typ - - module Constant = struct - include Field.Constant - - let to_bigint = Impl.Bigint.of_field - end - end in with_label __LOC__ (fun () -> let correction, acc = Array.mapi ts ~f:(fun i (s, x) -> @@ -339,9 +341,108 @@ struct | _ -> assert false + module O = One_hot_vector.Make (Impl) + open Tuple_lib + + let public_input_commitment_dynamic (type n) (which : n O.t) + (domains : (Domains.t, n) Vector.t) ~public_input = + (* + let domains : (Domains.t, Nat.N3.n) Vector.t = + Vector.map ~f:(fun proofs_verified -> Common.wrap_domains ~proofs_verified) + [ 0; 1 ; 2 ] + in *) + let precomputations = Precomputed.Lagrange_precomputations.pallas in + let lagrange_commitment (d : Domains.t) (i : int) : Inner_curve.Constant.t = + let d = + Precomputed.Lagrange_precomputations.index_of_domain_log2 + (Domain.log2_size d.h) + in + match precomputations.(d).(i) with + | [| g |] -> + Inner_curve.Constant.of_affine g + | _ -> + assert false + in + let select_curve_points (type k) + ~(points_for_domain : Domains.t -> (Inner_curve.Constant.t, k) Vector.t) + : (Inner_curve.t, k) Vector.t = + match domains with + | [] -> + assert false + | d :: ds -> + if Vector.for_all ds ~f:(fun d' -> Domain.equal d.h d'.h) then + Vector.map ~f:Inner_curve.constant (points_for_domain d) + else + Vector.map2 + (which :> (Boolean.var, n) Vector.t) + domains + ~f:(fun b d -> + let points = points_for_domain d in + Vector.map points ~f:(fun g -> + let x, y = Inner_curve.constant g in + Field.((b :> t) * x, (b :> t) * y) ) ) + |> Vector.reduce_exn + ~f:(Vector.map2 ~f:(Double.map2 ~f:Field.( + ))) + |> Vector.map ~f:(Double.map ~f:(Util.seal (module Impl))) + in + let lagrange i = + select_curve_points ~points_for_domain:(fun d -> + [ lagrange_commitment d i ] ) + |> Vector.unsingleton + in + let lagrange_with_correction (type n) ~input_length i : + (Inner_curve.t, Nat.N2.n) Vector.t = + let actual_shift = + (* TODO: num_bits should maybe be input_length - 1. *) + Ops.bits_per_chunk * Ops.chunks_needed ~num_bits:input_length + in + let rec pow2pow x i = + if i = 0 then x else pow2pow Inner_curve.Constant.(x + x) (i - 1) + in + select_curve_points ~points_for_domain:(fun d -> + let g = lagrange_commitment d i in + let open Inner_curve.Constant in + [ g; negate (pow2pow g actual_shift) ] ) + in + let x_hat = + let terms = + Array.mapi public_input ~f:(fun i x -> + match x with + | b, 1 -> + assert_ (Constraint.boolean (b :> Field.t)) ; + `Cond_add (Boolean.Unsafe.of_cvar b, lagrange i) + | x, n -> + `Add_with_correction + ((x, n), lagrange_with_correction ~input_length:n i) ) + in + let correction = + Array.reduce_exn + (Array.filter_map terms ~f:(function + | `Cond_add _ -> + None + | `Add_with_correction (_, [ _; corr ]) -> + Some corr ) ) + ~f:Ops.add_fast + in + Array.foldi terms ~init:correction ~f:(fun i acc term -> + match term with + | `Cond_add (b, g) -> + with_label __LOC__ (fun () -> + Inner_curve.if_ b ~then_:(Ops.add_fast g acc) ~else_:acc ) + | `Add_with_correction ((x, num_bits), [ g; _ ]) -> + Ops.add_fast acc + (Ops.scale_fast2' (module Public_input_scalar) g x ~num_bits) ) + |> Inner_curve.negate + in + x_hat + let incrementally_verify_proof (type b) - (module Proofs_verified : Nat.Add.Intf with type n = b) ~domain - ~verification_key:(m : _ Plonk_verification_key_evals.t) ~xi ~sponge + (module Proofs_verified : Nat.Add.Intf with type n = b) + ~(domain : + [ `Known of Domain.t + | `Side_loaded of + _ Composition_types.Branch_data.Proofs_verified.One_hot.Checked.t ] + ) ~verification_key:(m : _ Plonk_verification_key_evals.t) ~xi ~sponge ~(public_input : [ `Field of Field.t | `Packed_bits of Field.t * int ] array ) ~(sg_old : (_, Proofs_verified.n) Vector.t) ~advice @@ -362,10 +463,24 @@ struct let open Plonk_types.Messages in let x_hat = with_label "x_hat" (fun () -> - multiscale_known - (Array.mapi public_input ~f:(fun i x -> - (x, lagrange_commitment ~domain i) ) ) - |> Inner_curve.negate ) + match domain with + | `Known domain -> + multiscale_known + (Array.mapi public_input ~f:(fun i x -> + (x, lagrange_commitment ~domain i) ) ) + |> Inner_curve.negate + | `Side_loaded which -> + public_input_commitment_dynamic which + (Vector.map + ~f:(fun proofs_verified -> + Common.wrap_domains ~proofs_verified ) + [ 0; 1; 2 ] ) + ~public_input: + (Array.map public_input ~f:(function + | `Field x -> + (x, Public_input_scalar.Constant.size_in_bits) + | `Packed_bits (b, n) -> + (b, n) ) ) ) in let without = Type.Without_degree_bound in let absorb_g gs = absorb sponge without gs in @@ -487,8 +602,6 @@ struct let domain_generator ~log2_size = Backend.Tick.Field.domain_generator ~log2_size |> Impl.Field.constant - module O = One_hot_vector.Make (Impl) - let side_loaded_domain (type branches) = let open Side_loaded_verification_key in fun ~(log2_size : Field.t) -> @@ -700,7 +813,7 @@ struct (* TODO: This needs to handle the fact of variable length evaluations. Meaning it needs opt sponge. *) let finalize_other_proof (type b branches) - (module Proofs_verified : Nat.Add.Intf with type n = b) ~max_width + (module Proofs_verified : Nat.Add.Intf with type n = b) ~(step_domains : [ `Known of (Domains.t, branches) Vector.t | `Side_loaded ] ) ~(* TODO: Add "actual proofs verified" so that proofs don't From 1c377817f64557db28807c5b7a72b6128a385a21 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:23:39 -0700 Subject: [PATCH 08/15] propagate change to rest of circuit --- src/lib/pickles/step_main.ml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/lib/pickles/step_main.ml b/src/lib/pickles/step_main.ml index 5a21c2e0fe0..cb46eb0554f 100644 --- a/src/lib/pickles/step_main.ml +++ b/src/lib/pickles/step_main.ml @@ -45,9 +45,8 @@ let verify_one sponge in (* TODO: Refactor args into an "unfinalized proof" struct *) - finalize_other_proof d.max_proofs_verified ~max_width:d.max_width - ~step_domains:d.step_domains ~sponge ~prev_challenges - proof_state.deferred_values prev_proof_evals ) + finalize_other_proof d.max_proofs_verified ~step_domains:d.step_domains + ~sponge ~prev_challenges proof_state.deferred_values prev_proof_evals ) in let branch_data = proof_state.deferred_values.branch_data in let statement = @@ -78,8 +77,7 @@ let verify_one in let verified = with_label __LOC__ (fun () -> - verify ~proofs_verified:d.max_proofs_verified - ~wrap_domain:d.wrap_domains.h + verify ~proofs_verified:d.max_proofs_verified ~wrap_domain:d.wrap_domain ~is_base_case:(Boolean.not should_verify) ~sg_old:prev_challenge_polynomial_commitments ~proof:wrap_proof ~wrap_verification_key:d.wrap_key statement unfinalized ) @@ -295,11 +293,10 @@ let step_main : `Known (Vector.map basic.proofs_verifieds ~f:Field.of_int) ; max_proofs_verified = (module Max_proofs_verified) - ; max_width = None ; typ = basic.typ ; var_to_field_elements = basic.var_to_field_elements ; value_to_field_elements = basic.value_to_field_elements - ; wrap_domains = basic.wrap_domains + ; wrap_domain = `Known basic.wrap_domains.h ; step_domains = `Known basic.step_domains ; wrap_key = dlog_plonk_index } From 64782d50a153f6531a88467a4073935ccb00bd23 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:23:57 -0700 Subject: [PATCH 09/15] refactor branch_data --- src/lib/pickles/types_map.ml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/lib/pickles/types_map.ml b/src/lib/pickles/types_map.ml index 1177761cd39..5159f0a445a 100644 --- a/src/lib/pickles/types_map.ml +++ b/src/lib/pickles/types_map.ml @@ -152,9 +152,11 @@ module For_step = struct ; value_to_field_elements : 'a_value -> Tick.Field.t array ; var_to_field_elements : 'a_var -> Impls.Step.Field.t array ; wrap_key : inner_curve_var Plonk_verification_key_evals.t - ; wrap_domains : Domains.t + ; wrap_domain : + [ `Known of Domain.t + | `Side_loaded of + Impls.Step.field Pickles_base.Proofs_verified.One_hot.Checked.t ] ; step_domains : [ `Known of (Domains.t, 'branches) Vector.t | `Side_loaded ] - ; max_width : Side_loaded_verification_key.Width.Checked.t option } let of_side_loaded (type a b c d) @@ -183,11 +185,8 @@ module For_step = struct ; value_to_field_elements ; var_to_field_elements ; wrap_key = index.wrap_index - ; wrap_domains = - Common.wrap_domains - ~proofs_verified:(Nat.to_int (Nat.Add.n max_proofs_verified)) + ; wrap_domain = `Side_loaded index.max_proofs_verified ; step_domains = `Side_loaded - ; max_width = Some index.max_width } let of_compiled @@ -203,7 +202,6 @@ module For_step = struct } : _ Compiled.t ) = { branches - ; max_width = None ; max_proofs_verified ; proofs_verifieds = `Known (Vector.map proofs_verifieds ~f:Impls.Step.Field.of_int) @@ -213,7 +211,7 @@ module For_step = struct ; wrap_key = Plonk_verification_key_evals.map (Lazy.force wrap_key) ~f:Step_main_inputs.Inner_curve.constant - ; wrap_domains + ; wrap_domain = `Known wrap_domains.h ; step_domains = `Known step_domains } end From 40d2cee4c70b8b6c51d1c1ec95b7fd9ca2067810 Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:24:22 -0700 Subject: [PATCH 10/15] add side loaded verification unit test and precomputation for computing necessary lagrange bases --- src/lib/pickles/pickles.ml | 46 +++++++++++++++++++++++++++++++++++-- src/lib/pickles/pickles.mli | 2 ++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/lib/pickles/pickles.ml b/src/lib/pickles/pickles.ml index 452ac1ea945..bbc253ebb8e 100644 --- a/src/lib/pickles/pickles.ml +++ b/src/lib/pickles/pickles.ml @@ -818,8 +818,8 @@ module Side_loaded = struct let d = Types_map.lookup_compiled tag.Tag.id in { wrap_vk = Some (Lazy.force d.wrap_vk) ; wrap_index = Lazy.force d.wrap_key - ; max_width = - Width.of_int_exn (Nat.to_int (Nat.Add.n d.max_proofs_verified)) + ; max_proofs_verified = + Pickles_base.Proofs_verified.of_nat (Nat.Add.n d.max_proofs_verified) } module Max_width = Width.Max @@ -880,6 +880,12 @@ module Side_loaded = struct let verify ~value_to_field_elements ts = verify_promise ~value_to_field_elements ts |> Promise.to_deferred + + let srs_precomputation () : unit = + let srs = Tock.Keypair.load_urs () in + List.iter [ 0; 1; 2 ] ~f:(fun i -> + Kimchi_bindings.Protocol.SRS.Fq.add_lagrange_basis srs + (Domain.log2_size (Common.wrap_domains ~proofs_verified:i).h) ) end let compile_promise : @@ -994,6 +1000,42 @@ let%test_module "test no side-loaded" = let () = Tick.Keypair.set_urs_info [] + let%test_unit "test deserialization and verification for side-loaded keys" = + Side_loaded.srs_precomputation () ; + let pi = + match + "KChzdGF0ZW1lbnQoKHByb29mX3N0YXRlKChkZWZlcnJlZF92YWx1ZXMoKHBsb25rKChhbHBoYSgoaW5uZXIoNTI4Y2RiZjE2NzA4YTUzYSAxZjkwYTdlZWEyZTA2ZjZhKSkpKShiZXRhKDYxN2U1YTdmZDZiZTM2NmEgZGUxOTcxMjJhNDQxNTE3NSkpKGdhbW1hKDNjYTM1ZDQ0NTIxODFjOTkgMTBmMDg1NDBiYTYxYjBlYykpKHpldGEoKGlubmVyKDliOWNiM2ViODlmOTk4NjAgZmMzZjJhNTU2YjNkYTNiOCkpKSkpKShjb21iaW5lZF9pbm5lcl9wcm9kdWN0KFNoaWZ0ZWRfdmFsdWUgMHgwODIzRTU2NzkzQjU1OTI2MTRBREJBNEQwRTVGRTcxODJDMzYwNTlFRkE2N0I2MkZGMzQ4QzI5ODAyNUVEM0IxKSkoYihTaGlmdGVkX3ZhbHVlIDB4MTVFNkU1ODMwODhGMzgzOUEwQTI0QkEwOTYwNThEMzExRjgwRTYzREM3QzVGOTY5NjFFREYwRTg0MzFCM0E4OSkpKHhpKChpbm5lcig1Yzc4YjUxMDZkYzkxOTZiIGRkOTIzNjA4ZjNhMmQ3YzcpKSkpKGJ1bGxldHByb29mX2NoYWxsZW5nZXMoKChwcmVjaGFsbGVuZ2UoKGlubmVyKDAyNzdmNmFhZDlkODM1YTUgZDdjZTY0NGFmMWUwYTYyMykpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDcxNTVjOGNhMjcwODkwYTkgODgyMTBlZjUwNWQ3NDYzYSkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDY2ZGQwOWNmOGM3NjdjYTggNDlhMWYzZjBkMDJjMjdkMSkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGIzYWY1YjdmZmY3N2QzZGQgN2UzZDUzYjJkNjk5ZDIxMCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDFhNzAzNDcyMmYzOWM2ODAgZGFjMGI5MjA3MTBhM2JhZikpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDMxYTM5MTk2M2ExZWRhMjIgMTc2OGY5NjNmZGEzMGRiZCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGNhNjk3N2JjMmNkMDhmMDIgOGNjYTA4MGEzZWVhOTFkZSkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGNhMWM0NDU5YzZkYjkwZTAgNWRjOTc0NDQyMjQ2OTJiOCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDVhODY5MWZlOTM4ZDc3NjYgZmZhN2I3NmQ1MDU0NTMwMCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGUyOGE2YmQ3ODg1ZTJkY2UgY2ZmYzcxMGZkMDIzZmNmMikpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDY3YzljYWNkYmVjMTAxNTIgZGJiYmIxNzQ0NjUxNGNkYykpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGI5NjI2OTBkNGM2MTQ3ZmUgMDQ3ZWQyYjY0MzJhZTlhOCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKDI0N2EzYzAyNmZkNDJhMWYgMzBmZmQzZWIyZTkyZjZlMCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGZiMDQwYTVmN2FlMTY4MmEgNjdlODhjMDNiNDY0MjlmYikpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGRhN2FhZWI5OTE0MmQ0OTAgZTZkZjFlZjJhMjdiZDVkZCkpKSkpKChwcmVjaGFsbGVuZ2UoKGlubmVyKGM5NTkwYmEyZDY1ZTc3NGMgNjUxM2JlOTc2ZGJiZDAxNCkpKSkpKSkoYnJhbmNoX2RhdGEoKHByb29mc192ZXJpZmllZCBOMCkoZG9tYWluX2xvZzIiXG4iKSkpKSkoc3BvbmdlX2RpZ2VzdF9iZWZvcmVfZXZhbHVhdGlvbnMoMzQ1YmNhODlhMThiZTZlYiAzMmIzMmJlYTk4NTNjZTUxIGU0Yjc4YmQwOWJiYjY4YTUgMGM2NzkxZmIwOGUwY2E1NykpKG1lX29ubHkoKGNoYWxsZW5nZV9wb2x5bm9taWFsX2NvbW1pdG1lbnQoMHgwRjY5QjY1QTU4NTVGM0EzOThEMERGRDBDMTMxQjk2MTJDOUYyMDYxRDJGMDZFNjc2RjYxMkM0OEQ4MjdFMUU2IDB4MENDQUYzRjAzRjlEMkMzQzNENDRFMDlBMTIxMDY5MTFGQTY5OURGOTM0RjcwNkU2MjEzMUJBRDYzOUYzMDE1NSkpKG9sZF9idWxsZXRwcm9vZl9jaGFsbGVuZ2VzKCgoKHByZWNoYWxsZW5nZSgoaW5uZXIoMzM4MmIzYzlhY2U2YmY2ZiA3OTk3NDM1OGY5NzYxODYzKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoZGQzYTJiMDZlOTg4ODc5NyBkZDdhZTY0MDI5NDRhMWM3KSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoYzZlOGU1MzBmNDljOWZjYiAwN2RkYmI2NWNkYTA5Y2RkKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNTMyYzU5YTI4NzY5MWExMyBhOTIxYmNiMDJhNjU2ZjdiKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoZTI5Yzc3YjE4ZjEwMDc4YiBmODVjNWYwMGRmNmIwY2VlKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoMWRiZGE3MmQwN2IwOWM4NyA0ZDFiOTdlMmU5NWYyNmEwKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoOWM3NTc0N2M1NjgwNWYxMSBhMWZlNjM2OWZhY2VmMWU4KSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNWMyYjhhZGZkYmU5NjA0ZCA1YThjNzE4Y2YyMTBmNzliKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoMjJjMGIzNWM1MWUwNmI0OCBhNjg4OGI3MzQwYTk2ZGVkKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoOTAwN2Q3YjU1ZTc2NjQ2ZSBjMWM2OGIzOWRiNGU4ZTEyKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNDQ0NWUzNWUzNzNmMmJjOSA5ZDQwYzcxNWZjOGNjZGU1KSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNDI5ODgyODQ0YmJjYWE0ZSA5N2E5MjdkN2QwYWZiN2JjKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoOTljYTNkNWJmZmZkNmU3NyBlZmU2NmE1NTE1NWM0Mjk0KSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoNGI3ZGIyNzEyMTk3OTk1NCA5NTFmYTJlMDYxOTNjODQwKSkpKSkoKHByZWNoYWxsZW5nZSgoaW5uZXIoMmNkMWNjYmViMjA3NDdiMyA1YmQxZGUzY2YyNjQwMjFkKSkpKSkpKCgocHJlY2hhbGxlbmdlKChpbm5lcigzMzgyYjNjOWFjZTZiZjZmIDc5OTc0MzU4Zjk3NjE4NjMpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcihkZDNhMmIwNmU5ODg4Nzk3IGRkN2FlNjQwMjk0NGExYzcpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcihjNmU4ZTUzMGY0OWM5ZmNiIDA3ZGRiYjY1Y2RhMDljZGQpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcig1MzJjNTlhMjg3NjkxYTEzIGE5MjFiY2IwMmE2NTZmN2IpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcihlMjljNzdiMThmMTAwNzhiIGY4NWM1ZjAwZGY2YjBjZWUpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcigxZGJkYTcyZDA3YjA5Yzg3IDRkMWI5N2UyZTk1ZjI2YTApKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcig5Yzc1NzQ3YzU2ODA1ZjExIGExZmU2MzY5ZmFjZWYxZTgpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcig1YzJiOGFkZmRiZTk2MDRkIDVhOGM3MThjZjIxMGY3OWIpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcigyMmMwYjM1YzUxZTA2YjQ4IGE2ODg4YjczNDBhOTZkZWQpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcig5MDA3ZDdiNTVlNzY2NDZlIGMxYzY4YjM5ZGI0ZThlMTIpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcig0NDQ1ZTM1ZTM3M2YyYmM5IDlkNDBjNzE1ZmM4Y2NkZTUpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcig0Mjk4ODI4NDRiYmNhYTRlIDk3YTkyN2Q3ZDBhZmI3YmMpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcig5OWNhM2Q1YmZmZmQ2ZTc3IGVmZTY2YTU1MTU1YzQyOTQpKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcig0YjdkYjI3MTIxOTc5OTU0IDk1MWZhMmUwNjE5M2M4NDApKSkpKSgocHJlY2hhbGxlbmdlKChpbm5lcigyY2QxY2NiZWIyMDc0N2IzIDViZDFkZTNjZjI2NDAyMWQpKSkpKSkpKSkpKSkocGFzc190aHJvdWdoKChhcHBfc3RhdGUoKSkoY2hhbGxlbmdlX3BvbHlub21pYWxfY29tbWl0bWVudHMoKSkob2xkX2J1bGxldHByb29mX2NoYWxsZW5nZXMoKSkpKSkpKHByZXZfZXZhbHMoKGV2YWxzKCgocHVibGljX2lucHV0IDB4MUQ1MDUwQUJDMTkzRkQ4Mjg4RkU4QjA5REE5QTJBQThDNEE5NUU3OTZDMzNERkI3MTJFOENDQUQ3MzY3MjY2QSkoZXZhbHMoKHcoKDB4MkMzM0MxNzNCREU5MzQwQkU5NDFFQ0QyMDlBQjZFOTlFQ0E4QkRDQTFDQThCREE4REFDM0U0MEMzMzE1RjY5NikoMHgwMkFFOTI5NjgzNDREMUY1OTYwM0JBMDE1QzI5RDc4MDE4OTdGNkI1OUU1RUQ0M0EzQkVFMzE2RDZBODc2QzNCKSgweDNENEZERDI0MDI4NEYwOTZCMEQ5Q0U0MDVDMjAxNkU3Q0FFNDk5MzFEMDU3MUYyN0RBN0EzRERCMjAyRkM0MzcpKDB4MUQ4QTlBMTdBQkRGRjU5NzU4MzJCMkVBNEFFQjk0QkFERTYzNDZBNTU0RUIyNEE1MUIzRUNGRjU2MEQzMzc0OCkoMHgzNkY4MDZGMDQzRDhGMzNGN0ZEODk3MzBGQjY5RTVEQUYzMjNFODYzN0QyM0Q5NTY5NDY2NUFCMUIyOUFEMTk0KSgweDIxQ0U2NzdFOTQxNjc4M0RCQTczMTBFMjgxM0QyMDAxMDRBMDMyOERDQTVDRjJDMEU2MzJCRkQ3MTk5NTFDQkQpKDB4MEEzNDY0RDVBQkJERjFDMUZBNkMzQ0Y1QzUzMjhDQkVEN0QxNDAyQUQ0OTkwQUYyRDA3Q0Y2OTU4NzAwRTA3OSkoMHgzMDY3OTIzQUY5M0M4NUJDNjc3NzE1Rjc4RUZFRTJCNzY1RjQ3MTJEOTJBMThERDY5MUIyRDYxNzI0NUQyODM3KSgweDFENzVFMUNDRTQxNjVGRDE5QkJGMUQ4MzRGMDM2NkUzMzMwQTkxNkYyNTI4MDFBQ0MyQTlGQ0NGRTE5QkIwM0YpKDB4Mjk3OTNDM0QzMTEzNTM0NDRDNEZDRjJCRjYyMjk5ODkzRjY5RkNFRjBBREY3MzQ1MzEwREI3RTczNkMyMTc1OCkoMHgzRjkwRTI0NDhDQUIyNjM5Nzg4RUVGMEVEQkQ0Rjg3NDYzMDgyRUFFMEM1MkY3MTBFMEE1N0I0MjM4NTc3QzA5KSgweDNFMTlFOUU0NUM2Q0ZDRjBGNzAzNkQzQTU5OEUyNkJDNEMyNTBBQjQ1MDQ5RTE5QTgxRUYzRjlDNjhFN0IwOUUpKDB4MzFDRjJGQzQ1QzU5RTQ1RTVCMTZBOUZBMzU3OTcyQUVGMUY3NDQzODhDODFDODg2QjI4QkRCQzU1ODE1Q0U0NSkoMHgyNEIzMTBBNDE4Q0I1ODE1NTEzRENDNUI0REJGNEIyQzY0QkQ5NEEyRDQ3NjQyOTRFRUJERjRDN0RFMUIxQjA4KSgweDNFNzQ4QjhCRjdGM0Y2MzIzNUI2NTBEQjg3M0JENjUyQkM1OERCMUM2N0M5NEFGMDNCMjE4REI1OENBMEVBODYpKSkoeigweDNGQTY3NDFEODRFMTE0MzRENzkxOEE0NTlBRDFCNjk4QjhGMzYxNkUyQTkwMUIzQjE3RTlFMEJBOEMyMjlBOTUpKShzKCgweDIxNjAyODVBNzg4MDMxQzQ1QjBFMDQxQzBDM0UxMzIyRTEzMzBDNzE4QjcwOTk5OUU2NzdFNEM4MkMxQThERUMpKDB4MkNDMUVFMTE1NEY1MjdCMzNBMDExQTVGODE2QUZDM0MyMTk4OTJEMENDM0EyNTUwMUE5MDE4M0EyMjIxQjg0NykoMHgyOTkzNjZEN0JEQjUwQ0QyNzhCREI0M0ZGQ0MxQUY2NkNGRDZDODIxMjAzRjk4MEFDMjJBOUUwMTc4NjEyRkNDKSgweDA0MjA0NzU5RTdEOEU4NEMxMTIyQkNGNjUwMDhBQkFDMDE3REU3REFFNDRCN0U0NzlEMzA3NzM5NjZFQjZCMEEpKDB4MDhENUFCREIzOENFRUE2RDUwRkMzNzhGQ0NFQTY1MTE2QzI5OEVFMDMwN0Q4MjdGRjY3NDQ3NTAyQzVDNUEyMykoMHgwQUIxQjE2MDVDMDdGQjA1NTQxNDMwOEZEOUQzODcyRDExODRBQzQzNkJGNjJCRTA2QkY2OEE0MjlFQjgwNkM4KSkpKGdlbmVyaWNfc2VsZWN0b3IoMHgyMDczRTU3RUNBMDk3Q0RCNDM0OUY1NkE5NkREODcwRUY0MjMyRjU0NzYyNEJGREQ3QUZGREY4NDA3ODI2MDAwKSkocG9zZWlkb25fc2VsZWN0b3IoMHgxNDEyNjQxRjM3OEI3QjRBQTJERjFCMjk1NzNFM0JCQTJFMDkyRTc0RDQ4Q0M4Q0EwM0JGQkQ4ODc1NUY1REQ1KSkpKSkoKHB1YmxpY19pbnB1dCAweDBFRkMwQ0M0RTg2MDRDQjRCMzM3QjIzN0JCNDY5MTYxMTBGNTYwNDA0MTY2OUUzOEVCMTcxMkM3OEE4NjUzOUQpKGV2YWxzKCh3KCgweDMwQzgxMjQ1NUQ4NDBGMDlCMUExMEQ3M0U2MDdGMUNEMjNGMDk3N0UyMDU5NDZERDcyNTIxNDlDM0M4RUIyRUIpKDB4MDMwMTA4MkZDODVBODVBNUM1RTQ4NDgzQ0IyMzFGNjRCRTRFNDJBREI3QUI3M0I5NzMwMzRGOTJDMjAwODI0MykoMHgxQUMyNjNDMjkzQjU0OEU3ODYyMjM0NDgxODY1QTZDNDI1NTE4MEYzM0Q1RkNCMUUzMDM2MERDNUFBNEE4MTY0KSgweDI2NzlCMDM5MDFBQTJBMjg2REYxRTJBOTBCQzcyQTNBRjU3QzEzREQ2NUI5QkIxMTEwNERCOTE4OUFEQkI5NzApKDB4MzlGMENGRTUxMzNENENDM0I1OThGMUY2RUExNjAwNDY2MURGN0JBNkQxMzE2QzM4RTEyNEM2NUVGNEYyMUM5NSkoMHgxNjQ1N0RGRDZCRjMyM0JFMTMxNjI3NzlFQjBGNDhDQUQzQUQ4RDQ5NzBFOUU2NDMzRjI3NUIyMjI2Q0Y5OUQ5KSgweDJBRjQzNkZFMEZBRjBDQjkwNUREODIwMkREQzQyQzA5RDE1NjVDRTQxNUZENDRGMzMxNzhEOTRCMUJGNzYxMjcpKDB4MjZBOTE0RjdENTVBQzMxMjkxOEQ0MUZEQTUxNjM0MkU5MjkwMzRDMDZEMTk3MDc5NEMxMTU2RkY4NjkwQjBFNikoMHgwQkREREIyNzZCOUNERjRCMkM5QjRDNkI0M0YyRjMwMkQ0NkUyQTAxMDQ3MjRENzc3OUI3MTRDQzFDMTNEMTBDKSgweDA1N0MwNDVGNERBNzIwMjMxN0U0QTQ3OTUyQkVGMTlEMTA5NDc1NzQ5RkM4QkYwRUQ5MjQ0RkQ2QkRCMjBDQzMpKDB4M0FEOTgwNUJFODYzNDVCM0ZFOTgzNjdEMkFEQUFBRjZBM0IyQTUxMUI3MDExRDM1NENDMDc0QkIwRjBCNjE4QykoMHgwODY0QkIyREY2MEYyOUJFQkM4RDU1REVDMkI2RjE5OURGNTNDQjY1MEJENzk3RDhDODFBQTdEMzlGN0E0OTRDKSgweDM3NUYyMTUzNkI2NkU4MTZEQ0ZDRTgyOTQ5NUE3QjQyOUNBMUVCNjU4MTIzREU4ODU4Qjc2NURCMjZEMURDNjgpKDB4MzREMUI1OUEzMzM2OTM1MDg2N0VFMEU1MzhDNjhENjkzRTE5QkQ1RjhGMDVGQkRFNTI4MjhBNkFFMzk2NjZDQSkoMHgzODFBRDI4NTMzNEE3ODg0NjkwRjNBQjg0MTIyOTFGQ0IwRDMzNTcxNjlDMEYxNzZEMkE2REI4RDJCM0ZDMDJCKSkpKHooMHgyRkI0MTUzNkU0NjU1QzExOUJFNUYwREVEOTAzOTFBODE3MUMxOTFCM0E5NzY0Rjc2NUZCQjZFQkYyQUFCQUM5KSkocygoMHgzRjU1MjJBMUQ4QTBBQkZBODg3NkI0MTg1RTlDQTFGODg1NjYzRjU1NTc5QzM5RjczNTJGOTgxQ0IzMDRDQ0VGKSgweDJFMDcwMEQ2RjhBMDJDMDRCMURGRTYzMDg5NkI1OTYxNUYyMUM0QjNCNTQxRTI2RUU2M0RCQ0ZERkU1OUQ2NTgpKDB4MTBGNzMyN0M4MzNFQjM1QjQ0OTlBRDRBMUVGMEJDQjY2ODYxODIyMzgxREVCMENDNjc5OUU3MTgyODkyQkQyNikoMHgyOUFCOEY0QzdFMjU2RDJENzcwM0UzNjhGOTEwMUJFRDAyMTVFMDhDRUM4N0FBNTQ5OUNGQTdEMUU5RTExNjU3KSgweDE2NTIzRERGNDM4QUNGMkMwNzJEQzdGMDBDNDFGMUUzQTUyMTQ3NjFDNzdEMjUzMzk3MEE5MzgyQjVCNDhEMzApKDB4MEQ2ODRBNDYwQjM0ODA4MkY1RUZCMDNGN0E2MzVCNTM1OEU1MjIzNTgyMUQzNjI1MUQ2NzY0NENFNjk0QUJDNCkpKShnZW5lcmljX3NlbGVjdG9yKDB4MkIyMDRCODU5NTI5OUQyMkNDODNERTZFMkE3OEQ0QUYzOUFBRTg1MjdGQjRCMjk3QTM1MDUxRjM3NkFFMTBDNikpKHBvc2VpZG9uX3NlbGVjdG9yKDB4MzcwQzdEQUM1OERCMURBQjExNDdEQUE4QkJGN0VFMUYxRTJDMkVBQjY0QkVFRDg4NUNBMTRGQzg2RDc4NjQ1OSkpKSkpKSkoZnRfZXZhbDEgMHgwNDU5REU5RUE3NEI4Q0IzOEI1NDQ1NEZBMEY1OUQzNzUzMDdCMTIxMEY3NDAzNTI2MTUzRDVDQzEyODhERTYzKSkpKHByb29mKChtZXNzYWdlcygod19jb21tKCgoMHgzRTJDRjhGREI3RjI1Q0MzRDUyM0U4ODczNUNDOEIwMDY4QTQzNkExMDdEOTI2OTc3QjQ0MDg5NTVBRkI1QTdEIDB4MzJDRUU5NTVFQzVCRkNGMjY5QTA1MEM1MEM5RUQ4Njg2NjRGMjZBRURCNEZDQzk2QTJFQjIyQzRFOTAzMUFDQykpKCgweDIwMjlGNTRDRTNGRTEyNTUwMDVEQzZFMEQ1NkY0NUVENDZEOTI5NEEyMDIxQUQ3QzREOUVDQjlBMkZDMzVEREMgMHgyMDA5OEU5RUI0Mzc0MTRGODYxQzhCQjVGREYzMTExRUIzQzY3MDdEQzE1NkZGRUUzRjNCNzEyRkI2N0Y0QTJFKSkoKDB4MTExMEFFM0YwNUEzREYyRkU0MTQ5RUI3MTI1QjdDRjMxNUQwMUQ2QkZCREM0RTFFQkVBMDVBREQ2MzM0NzBGRCAweDMwQkFFRjA5MUMxNjVCOEZDRkFGQUE5NkMwRkI5RUI1OUE2RkQ5ODE3Njg5NzQyMzA0MzYyM0FGQjhEQ0IwODQpKSgoMHgzMzk1RDI5OTNDQ0JCOUMwQTIyQkUzMjFENzBGNUYwMUYzOUI4M0Q3OEQ3RDM2ODRERTdFRkVGNzFDOUVFRDk0IDB4M0E5OUEwNzhEQTcwNkYzQzQzQjZDMDgxREU1QTA5QTY5RDJEMzA4QkE1MEI5NjFDQUM2QTY2NEUzRDRFOEUzRSkpKCgweDI1OEM1NkZBMzJCNTU1QkZDMzI4OEY2RUVBQTExMzQ0RTQ0MzBDNTFGM0VENkE1OUYzNUY3NDlGOUZBRjA4NEUgMHgxRDQ3QUMzNDFFRjdBQTc2RjE1RjAyMzlBNDk0QTU0MUUwMThDMTEzQUNENjJFODdGQUE3NzY0RTIzMjUxOTQ0KSkoKDB4MkMwNDMxMUI4MUVEMjkyNDBERTlEQTYyMkM4OTQzMjMyMzZERDYyMzg0NkU4M0MwODMwOUQxQzU1MkIwNjUwMyAweDI0MzgwMzZFRTdFRjJFQUVCOTIxNkE4NDM2OTJBMkZBNDVGOEI1OTUxMDdEOUVBNkMwNTUyM0M4Mjc0RENERkUpKSgoMHgxOUMxREUxMzk4MjU4M0EyMkZBRDA0NTUzMDgyNDk5Qzg4MDU1QzBENzA3QzA5REM3NzY1MEVCQzE0NzE4RjZDIDB4MjYxMUIxRkM3MjFCOEI3M0IxMDk4ODZFNUEyOTYwQUJCQzVBNDcxNDcyRjJERTI3RjBCNzA5ODlCMEU2NDBCRikpKCgweDEzNjU1MDMxNUE0NDQwRTIyREIzMjkwNkUzQzdDOTU1Qjk2QzczNUU0MDU4RjFBRkY4QkRDRjc1QkUyMzI0QzggMHgzNEFCODdBNTkwQ0I0Qjk2NzRGMjhBNzVGNkNGOTI3NTdFODRFMTY0OUYzMkNBQkNCRTBCNzZBRUQxQTYwRThEKSkoKDB4MkVFOEQ1QkVBNEQ0NjAzMjFCOUJEMUI1OEJENUY5RUY3NkRGM0QwREVCQjAxNTE5MEQzMTdDNjFDNzM1ODRBQyAweDNEMzMwNDAzRTU0QkQxODlDNTU0NDgxNzBENTlENkY5RDNFRjQ4QzgwOTUyODFGNDU1ODhCOTJCNjEwNzUzNUYpKSgoMHgzNzBFMjMzNzU3MDdCNEU3NDQ4NjQxNUExNTNDQjFGMDExMUMyQjk1MEM4NzE3OEZBODU4OTFDQ0FCMEQzRDhBIDB4MEU3NUM1OThFNjM2ODgyMTdCRUZCQjVEQ0EwMjA0MzNDRTE1OEQ0RjgwNzBDNjM5ODIyNzVGODI2MUEzQ0U5NSkpKCgweDJFRkExNjAzNTBDQzQyODJFRTA2QUY0NjNFQzhDQTY5ODBBRjA3OTgzQTQyQjYyNzVFNDJGQzRBQTZFNjg1QzggMHgwRUVDQTlFREI1MTI2NTE4MkNCRUMxMEVGM0IwQUFGODFFRkI1M0U5QjkxOTk0MDE5NEMyNzI2QjlBNzg1RDFDKSkoKDB4MjdGRTY5RkY0QTcxNkUyREYxMzg5Q0ZDRDRDNDI1QjA1MEMwMDkzMUNERDEyM0MwQzVCRUE3REZGREQzRDYwMyAweDEyMkUwNTkzMTIwNjM1NUFBQjYwREJBRTA3N0Q0OTA4ODdERDFDQUE1OTlCQUMwNTQ1OEJDM0Y0MTQyOENCQjYpKSgoMHgzNjYzRTFDMUMyN0M2RjE2M0FCNTUyRTgzQjIxRkREQzVFQkFBM0I3MzVFRkZGRTM4QkFFOTlCMDFENzFEMDM3IDB4MkM0NkM5MTMzNkNFMzgxRjM5MDBCRDJBODBDMkIzNkE2QkM5MEM1RDUzQTU3OUUwMjI0MEJCQUJCMjAxOEU2MCkpKCgweDI2NjY3RTIzQTAwODVGRERBOTcwRDRDREM3OEQ2QTREOUM5RjAwMzA2MUY0MEY1QUU4RjgxOTg2QzBENkQyNjAgMHgyQjA1QTlGMTIwREFBQTM1NUY1NEU4RDBCOTZBNzhBNjc0ODk4RkIxODMwQTRFQjcxMzU2MTM3Qzg5ODRCREE1KSkoKDB4MTA1RDI0OTFFRUFFMDNEMUFBNEFEODkwODQxMkYzRUQwQjk4OEE0M0M0RjMzQzgxNTgxQzNBNjBGRUU5NzIxRiAweDJEQkFBRDU2QkZBMURDRERFNUNGRTQwNDgwQzhFOEU1N0UwMDkzRkVCMTUzRDlENEY5ODM0MDdCM0VBOTE0MTIpKSkpKHpfY29tbSgoMHgwMjlFRTdGNjREM0ZGRjFGNjkyMEQ2RjAwOTMwNEMyQzhGOUFCRjJCNzY5QUNENjlGN0Y3ODIwMUEwOUYxMEJCIDB4MzAxNDQ5NDgzQkYzQTY4ODU1MjE5MjkzNEUxMDM5MUQ3QkU5N0U1NEJFQjI2RjdBM0YzQjFBMjQ0M0NBMDdFQykpKSh0X2NvbW0oKDB4MjdFRDA1NkUyODg2NDY5M0FCMTY1M0Y2MkFERjVDNkY0N0RDQ0QwNzBFRjE2QTJFOTExMjgzMjI0MDE1OTIxRSAweDEwNzcyODRERDE1Rjk5MTQzRUZBQ0JBODVEM0RENjM2MDhGMjIyQ0Q2RDdDRjdBNzkzREZDNjQzOTBCN0RCRDgpKDB4MDdBMTBGOTVBNEY1NTU5N0Y2NkMzQzkyQkJGOUQ2OUEyM0M2RUU4NkNFMkM4NjRGQzBBMzVGQjE5OTk4MEI4OSAweDJCQzU2NEVDMDZCOEI3MDUyRjQ2OUMzRUM3NEFERDMyQzFDNzEzRUZBMTlGMjYxMDJFN0M3MzUyMEY5MEVEMkMpKDB4M0YzMEU5NkMzRDVBMjMxNzBGOTQ4OTU1NjU0MjJDNkQ1NEI4Qzg1OTREMTU0Q0I0OTVCRDgwODk0MTg0OEMyMSAweDE3Rjg1M0QzQzU4NjkwNDJDNjAwQzcxNzIwNjEwQTIxREQwNTdENjg5QTM0Q0YwOEU2QTcwNTRCMUJEREQ3MEMpKDB4MEMyN0ZBOEQyODI5QkNCREQ5MEUyNDU2NzczOTRERjcxNTFGN0M0RTk0RDk1ODMyOTYyRDcxODdGRUIzMzQzMiAweDA0NDJDNzNCQzdDMzc3OTFEQTlDRTBCRTYzMzJGNjkxNjZFRjZFNkY2NTFFMjNEODU5MjA3QjFGQURGOUUxQTkpKDB4MDM5QjkyMDA2N0Y1OUIzNDU4RjhDRkE2NjBCQzU4NUI3MDU4MjY5MDZCODg4OTNCODhDQURFMTk5MzA2MDRDNCAweDMzQUFBNjIyMTEzQTE0QkIxNDA4NTM4QjM4Q0E1MTU3QkNDODM1NTQ2QkMwODFCQTJEMzlFNUE2MzZGNzg1NEIpKDB4MEU3NkFFRTQ3NDg1MDczQURCNjZFODgyN0I3RjExQzk5Qjc0RjVEMzYwQUYxMkMzMjZERUJGRjQ1N0FCQjI5OCAweDE1RDdGNTlCRDZCRDBFNDlCMzZCQUUxQThFMTcwNzNGQUQzNDQyQjgyNjhENTBEMzI3RTg3Q0Q0Mzc0QzlFMkUpKDB4MjRCMTdDNDI3NThDRDk3N0RBMzFBNUQ2MTlEMEIwQ0M4ODVBMDc0RjEzREYxQjBEOTAzNkE1QkU5NjJGQUE2NiAweDMzQUJGNzU5NjRENDMxOEYyMUFBN0YzQzg4OUVBODhDNDk1RTEzMjJCMjlDODE2NDZDOTAxOTA2MjZBRjkzQTApKSkpKShvcGVuaW5ncygocHJvb2YoKGxyKCgoMHgwMThFODJCODVGNDMzODBFMzJDRURBRDU3MTg4NkRDREI2NTFGRDE2QzU0QUZBQ0M4QTVGMEZDQTFBMzVENzdBIDB4MDc1NThDOERFOTM2MjgyNkY1MkVEMUZDOUYzRkFDM0U2MEJFNkJGOUE2OTNGMUE5NjBDQjJGNTRCRjlBRDMwOCkoMHgyREQzNEFERjczMjM0MENFMTY2QTM5ODlDMjg2M0UwMEFBMjBFRThERDM2ODFBNkZDNDc5NDhEREMyMjkxOTE5IDB4MzlFRkIzNTkyOTI0Q0Y0OUY0NUQ1QjQ3MUFDRDY2QkQ2QTlENzJDN0YwMzRFQzc1NzAzNzQwNzM3RTA2OEZGOSkpKCgweDA1REQ3ODQ1QjBEMTkyMTJBQ0RGNjY2REQ5MEYzMDk5OTlCRjI4NzE5QjJBMUY3MEIyMjhBRjVEM0U1OUE2MzMgMHgyMDc3OTlBQjQyMDE1NUM2RkZFQ0RCMzUzOEIwRUYyMjU5RUVGNzc2QTMzQTc4MUFDNEYzRUY2QkNFRTYwNzAwKSgweDNBQUZDNEUyNEEyNUQyQUZGNzE0RjAwMDhGMjQ2NTQ5NkM2MkVCNkMxRjc1NjJFNjA1QzM4RUM1OURCREJDNjcgMHgzNzhGNUJBQ0NFNUM0QkQ2RkVGODYzMEY2OEM0MzlGOEZFOTg2RjIxOEE1NjJCMUVDMDU0RTA3RkM1ODI0QjU5KSkoKDB4MzhFNjA4RTZDODY2QUQxQzYxQkM2RjI1MEEwQUQ3NzYxQjcxQzZFNUUwRjdBMDY1RjAxQjdCMkY0RjQ4NUQxOCAweDJGMUNGQ0VFOTY1ODRGNTkyQ0RFMDVCMEIzRjkzNkE4RDFGQjYwM0EyOTg0RUVDQjFEQjA0MkJBNkQ4MUE2RDkpKDB4MDdBRDYxODFBOEUzMkMzODk4QjA2QkYwOTJFMjhEMUM4RTkyODI5MzEyNTYwOTAzMzk3OUFFRERCOTExNkJDRSAweDM1Mjg3RjdBQTIzMDBFQ0ExQ0M1OEFFODE0MUFCOTc0MTFFMDBGNjFDNjVGNUIxQTk4QTU4RUY1OTE4QzM2M0IpKSgoMHgzNDYxRkFDRTFCRUI4NUY2MDVFNzJGQUY5QTNDODA0Q0MzQkY4MkZDMjA5NDU4MzUyOEYwQzdFQkE3NERGQjQ4IDB4MjIxMjAxNUU4Q0EyOTY1RkUwRThBNEEwNjgzOENFRERFRDFFQTUzMUExMzlGNUNGRDE1ODhEQjU3MzYzODFDMykoMHgwREUxNDM5NzdCQThCM0ZDOTNEMjU0MzRFRURBNDkyMUU4QkRFNUFENTlFMTE4MUU2QjQ1NkI0MzA5MDU3RjA4IDB4MjRCMDk0RDRBQzQ1NkVDM0Y1NUQ0NjgzMEY0RTgyQkYwNzMxMkExRkFBOTdEOTEzOEJGNDFGMTZGN0UyM0E5QSkpKCgweDIxRTU2NDUzMzBEQzczRjZGNjgxOTE3NkY4RTkwQTA4MjcxMTc2NjRBOTNCNEQ5NkUxOURFOEIyODE5Njg5RjIgMHgxQUM2MzFENjA4RkRFQjFFRUZGQjZDMThBNzIwRTQwQ0YxNDA4QjBCRTI2NkE2MkJFOEI3RDBCNDZEQUYwRkQzKSgweDAwRDczQkU5QzMxOTMxOUU0QzEyQThGOTYxMEM0NzZEMTZGMDg3OEYwMzJERTZENjY2NEU3N0RBQUE0NDYzODcgMHgxMjgxNEY4NjM4ODI2RUE2MDk5RTA2OTE3NzBGRkU1MEY4MTdDRkIzQzQ1QzFGMDY1RUIwRjg1RDZFRTdCQThCKSkoKDB4MjdEMDVENUNFOTJGODM3NUQxNUM3RTI4QTRGNkEwMkUxQzI0MEJCQTE4OTc4MzI5RENBMDcyNDM2Q0RCM0I3QiAweDFDOTk0ODQzQkUzNzk3RTlBNkYyQUM2RkNDQUIxQzlCMTc0NUU4MTkxNDNGMjkxOEEzODNEM0QzMzZDNTg0NkMpKDB4MUQ4QUJDNTk0RURFMzExQTc0QTNDRUU3REUzNkU0MDY1ODUxQzBFRDAzQTQxNDhGMUExM0FGOEE0RTFDRThCMiAweDJDMzIwN0I2N0VFMDA1QzdGQzVCMUMwNzJFOTgwQURGOTY5NUYwMTVBRTI2QkYxNkFFMzJFODNDMDZGQ0M2MTEpKSgoMHgxMzVEQzBGOTg0NjVFMzZBRUZDNEFGQUYwODJGNDU5NDQzNEI0QTQzNzQzMDlDQkQzMzQ3NTA5ODNBNzgxMUE0IDB4MTEwNTdDMERGNkJEMkNDN0E1MDVBNkIzOTk2OTA3MDY1NkNCMzlFNEVDNDc5RENGRTQyRTAxRTcwQkEzOTExNCkoMHgxRTI1NEQ5QjdFNkJFREZFMTQyMjY0RTFCOTNCMUNBOTJCOTQzMjY0RTQ4QzhFMjc2QUFCQkMwNjNFNzlDMDJCIDB4MkE2MTcyMjlGNEQxOTRGM0JFM0QxNUQzOEI3NzdFQTRBQkJBMjhGMzY0MUIyNjlGN0EyNTFGQkZDNTExQjI1QSkpKCgweDFFOUUzRkE0NkE1MEVDN0E0MkYzNzBFOUE0MjlDMjE5ODRGQ0Y3MzBGQUFDODkxM0VDNkU1MEI5REJBMDM5MEMgMHgxOUE3Q0Q3QTg0QzNFOTk4QUJGQ0FCMUQxQUI4REYxRTlGNTdENTg3OEVDQjEyNjM2QThDMEQwMDhFNDY2OTM0KSgweDNGMkMyQjczN0NENzM2NThBQ0UzQ0M5MjQyREQ5QTUyRTM5ODM2QjEzOEJDREI3MTY1OEIxMDUyQzdGRTlDODMgMHgyMThFOEVBQjFGNjU3RUZFRjFBMjgxRkU2MUE2QjFDREQ5MzAzMzEzMEZDNjY0NDAzRUIxNjEwQUUyMEVGQjNCKSkoKDB4MDYzRThCNTBBOTBFN0FGQUE0NUI0QUUyQkI0RjQ4NTM3RjE0Q0ZFODJCRUYzMUExMTAwOTM5OTlGMEFCNTMzMyAweDEwMjgxQzhDMEUwMTc0RkEyMTIxRjQzNUYzNUQ5RTgwNTA2MzdBQTNGNThFMkEzNDJERUI5QzkxNzk4QzQ3QUMpKDB4MEQ0M0FCMDg1M0M2QzIwMkEyQ0UzQzM5RTlEMUNEQTYxNDQ5QThBMTZBOTEwMTJGRkU1OEFGQ0JGNjc1RDNENiAweDNCNURBREFBQUU1N0NGNkZCOTcyQzUyMUZFRDFBQzAzQjk2MDg1MUMwRDQ0QjYxMjJFQkI3MkEyMjU4QTQ2MDQpKSgoMHgxOEFFMzg4NUFDOEFGMEU2QkQ5QzBFNzc4NUQ4MzQ3N0VENkY1RkU4QTIzOUFFMjUyNjE0MTkzMUQ4MUVBQjU2IDB4MjlGQkIwODREOEZCRTcwM0QwMDhFOUNENzBCNzAyQjMxMTNCNDlGODU5QzJBMTlCNDQwNkFEMTMwRDM3MzFBMikoMHgwNEFGOTlFNzIwMjU0QjIyRThERjM2OEFFNkZDMjczQUM3NUE0NjM5QTZGMzAwNzM2OUZENDA1NTMyOTY0Q0JFIDB4MTI0NTI1RTM3RUM2MTVCMUY1N0Q1NDAwMjgzNkUzNTM4MDU0ODI3NkM2MUQ2QjI1MzlFQTUxQzkwMTVFRUQ5QykpKCgweDMyQTRFQ0E3Mjg2NEVFRkZDRjJEODNCODQzQjlCRTRBREJDRDQ1Qjk3MjYyNDgxMUM4OTRGOTE2RTRDODFBMzAgMHgzRTZGNTdBQjlDRjUzNjE4NjY0QTdBRDk4NjJGNjVCRjE2NEVGRkI0MkI3NDk3QjY0QTg4NDQzMzkzMThDMzY1KSgweDJGN0VFQ0M2M0YzRURGNTE5QTgzRTIwRDY0RTg4MjEzMTc5MjY0RjkzQTI0MzhBMjJBMTYzMzVFQjI4NTNFNkEgMHgxRDAzQzQwODc1MTZFRTAxQzEzOTgyNTA1OTk3Q0Y1RTEzQThFNEMyMjhCNDM0NkRFRkRDQjExMDFFNjU2NDk0KSkoKDB4Mzk0QzNGNDc2RjhERkFFNjhFNUI0NjEwRTczMjM5RjdBQ0Q4QzVBRTEyRTZGMDk0QjJEMTk5RDM5MzA4RDg3RCAweDFBMzhENDFDNjhDN0JEM0M2MTc2RDI0Rjc3NDY0MTEzNkQ2QzkyOTgxMUQ4NkFFNzJFNTQ1OThCQjdEQjI3RjQpKDB4MTYwQ0I0NEIyRkFGOTNCMDM3NUQ0MEU3N0Q1NjAwOTFGMDY2Qzg2MTZCNjkyRkY4NDJGOTBCNkZFQkM5QkFCMiAweDE2QzRFNUFEQTY1MzRCNUVBMDQwNjkxOEFEMkQ2NEJDNDE0RUFGRkJDNzIzRjI3QjM1OUM1MjRGRjVGQ0UzOUMpKSgoMHgzRkIxOTExNEU5NDdGRkRDNTQwRkI0Mjg0ODM1Q0I3NDI3OURBQjFDRjMxNTRGMDg3NEIwQTBBNUU2M0EzRUVCIDB4M0Q2NUQ1QjE3MkNFRjhEMzFGMzRBNDlBQjA4ODlGN0ExMEEyMjM4ODQ2QjZCMjQ1NjlENjhBQTc5MUY5NENCNikoMHgwRjAyNjk5RDgwMERCODY4QTA2RTNFRTRBMEMxNThDOTBCQzQ4QTY5MUU4MTc0NEZGQkNGREEzMkZGMjREQ0Y0IDB4MjcxNDY3MTI0M0ZEODIzN0QzMzlFMEFDMkM5NDFFRTlBNjQyRkRGNkZDQkJFMDMxQjQyNjk2RkQ2OUU4MzFBQikpKCgweDA1MjFGNkIwNTIxMkRDOTc1QUYwMDA3Q0QyNEQzMjhCMkVDRUQxQzgyNzkxRDJFNjA2MDU5QjY1QkNCRTU1NEUgMHgzNkJFNkRBQzRCNzczNDk0MTIxRjdERDVGODUwN0QzNkFFNkFDQzFEQzk5RkE4NjBERUQxQ0E3QUU4QTNFRDAxKSgweDM4QjUxQjU5MEJGNTBDQzZBMjRBQjgwNDc0RUIxNDdBMzBDNEFGM0REMTlBNTY1NEMxQjEwNTU1OUJEMTRENEQgMHgzRTExREU4QjFCNDYzOEZCRDhDNEQ2ODM2QTc0N0MwQTgxNTc4QTREMjJCODRBQzU4RUMwNjFGRUI2OEIzMTc3KSkoKDB4MkQ1MzI4RTBCQTU4OTk1QzcwNjY3NzRBNDYzRjhBOTAyRDdDMkI5N0JENDVDMTBCOUQ4QjREODIzREYxMDZBQyAweDI2OTMzQTlDMjE3NzI3QzlDREM0QTQ0OTREM0UzMzJCMzZCQjk5NzM5NkZDQTcwNjA5OUZGRDM0MzlCQjQ4MzYpKDB4MEJCMTE2QkE4MDdEMTJENERGNzk1NTdGRkI3RjYwQjQ4ODU4NjAxOTEyNTMwRTNGNDlDODkwQTM0QUVEMzFDQiAweDI0NjJFMDM5NkVEMzAyREQxMEE2RUY0M0FFNTMyMzMzNTQzRjRBODc1NTk5RTgzRkJFNDEwNjY0NERERDNGOEUpKSkpKHpfMSAweDA2QTYxNkMzQTYyNUY5MkVENjVCNUNBOTlEOUExREFBQTQ3NjQ4MUI5QzQ1RTQ1NTNFN0E4RTQzNkIxM0Q1NzApKHpfMiAweDMxMEFFNDBDQkNFMjFGQTBEQzkyRDFERkU3REY0OUQ5MzlBNTc5RkYwMjlGODY5MTE4MDM2QkY4QjM3MDQzOEMpKGRlbHRhKDB4MzY2NDE0RjRGRTlDM0REQjI3REE1QTg1NDUyQ0VEQkM2NUFGRDEwNEQxRjVDMjQxQkUyRTU5NEY2MTVBQkJCQyAweDBCNDE5MEQ1OUVFQTZFQkY4QjkzMTYwNTQ0MzlFOTJCNUJGREM4Q0Q5QkIwQzg2NDc4M0Q1RjFENzg1REY4N0UpKShjaGFsbGVuZ2VfcG9seW5vbWlhbF9jb21taXRtZW50KDB4MTM0MEMxMEIzMEFEMDdGNDkxM0MzQ0RENTg4QzNFOEE1QTZFNkRBQzk5NDczNzhGQTk3RDExRjUyQ0NENEFFMSAweDBCMTEwQUFEMkQxOTU3QzlDNjk0NDQzOURFRDgwQzlDRTlBMEVBRDM1Qzk2OTAzQUMxRUFEQkM5NEFFQjVEMjkpKSkpKGV2YWxzKCgodygoMHgxQkYxQ0U0OTREMjQzRkVGOTI1M0NCNjZDQzNENjMwMEEzN0VENEEyMzBDMTU0NDUxNzc5RkExNkY2QUFFREQ3KSgweDJBOUFCNDE3OEY5NUVBRTZBM0Q2MDgyNzZBNEJDRDM5MEE4OERBRjhDMzUxOTYwNjFFRDc5REFEQjc0N0NBNjIpKDB4MkYyNzJGRDhERjM1MkMwMzVFODFGQzFBNUM4NjY0QUFCRUY0RjYyOTYyQjdFM0QwM0Y2QkY1M0MxMEMyQjM5OCkoMHgwOTY3QjBGN0Y3NEU2NTU4QUI4NkQ4MTNFQUI4NDkwQzQzQzU2OUJBQjlFNzI3NjFDOEQ0MDg2ODEwQTYyMUIyKSgweDNCRTU4RTdFM0M4REZGRTgzMTdFNjhFNTA3MjlGRkJENkUyMkUzRkU0M0YzRkQwQzQ2OUY0Njc2ODA2ODU1MEIpKDB4MjQxN0NCNTM4MERBRDc5NzgwRDYyNDI4Q0MwOTE3NUZCRTJEQkM0NDNFMDc2NzE1NzU4OUE3RDU4MTQ1OEQzMykoMHgyMDZGQTE3NzlDNTA1N0NEMDYzOTY2NkQyNTgxQTE3MEI4M0NFNjU0QzY1NDU0NEM3M0Y3REZEMDIyRkYxNTk3KSgweDNFQzg1NzM3ODM4RUQ4QzRDQjkwRDU0NTIzMjMxQzk1MEZDNjQxREFBODM5MEFDNjYxMjk5NUFEQkJGQzI5NDcpKDB4MUEyNEMzMzk3RDJGMzlGMURGRUVDQ0NCNjZDNzhCRTYxMjc5RDVDMjJBRDY5MkMyM0RENTI2ODEzMzc5M0YzOCkoMHgxODEzQzU5MTMzRjQyMDRGMTU1NTREODkxRjk0RDgwMkQyNkUyRjE4MzQzRDUxM0UxNjQ3MDY2MzZDRDdENkU0KSgweDA1MzRERjY3OTU0QjdBQUE5MERCREZBODE0NjhCODNGNDE4MkI5MjdENUI0MThFNTMxNzk1OTk4Qjk4MjVCRTMpKDB4MEY3RkMyQ0VBMTk5ODQ5NzJFRTU3MzI3NDNBQ0RBNEM2QzQwNkYwM0E4NTI1NTUwMTlGMjEzRTQzMzI2QjYxQSkoMHgzNjdBREE1MzcwMzNBMDU0QTY1RjBFMTQ1RTZFNzlCNTZGMDU0RUVCODAxMUYxRUVFMTYzRTEzN0Q2MzY2Qjg5KSgweDFCMzIzMkRGQTMxNjk5N0Y0NTNEN0E2RjIwMDVFNkUwOTZCNTRCMzg0N0Y2RkU4RDU4MTE2NTg4N0Y4NUZENzEpKDB4MEVEQzFCQ0Q4Qjc4MjMzRjJDNUUyMzZENkQwNTI2NUE1ODY1ODdBQjBCMUMwRjVFRTNBMjZFM0VDNDVDODU1OSkpKSh6KDB4MkQ0NjcyN0NBQkQxQUQyMEU0NzZFN0VEOEQ2NjQ2NDBEMDU2NUQzRjAxQ0JCRjdDNjI1OEUyRjQzNkUwRkI2NCkpKHMoKDB4MTZDMUQxN0Y4OEMyNjdDNDNENERGRDE5NzY4NTgzQTJFOUFCN0FFQzY5NzVCMDlGMTM5REYxQUI1QzQxQzgxNSkoMHgyNTBFQTY3QUQyMkUyNjYxMjA4QjA1RTcyQjEwNTRGNjA3OThGRDU4RERGRTMzMzNGQUE5QjVBQjU0N0M2NzQ1KSgweDI1OEE4QzkxODI4MEMyNjVGODI1RUI3MkMwQjhDNjI1NjY1QzJGQUY2MDY5N0Q1ODhFQzZBQUNBQzczRDBCODYpKDB4MDcyRUZBQUZDOTY3RUZFNDVCRkYyRUVDMUE4Q0JGOEEwQjJDQzFGNDRCMjUyOTZEQTMzRjczQjNFNDg4NjJEMikoMHgzQTIzQThBQTJBM0QwREM4NTI5OURFNDk3NUM4NDg1NDczQzlDMUQwRDBEODRBMEJFQ0ZGRDMxMzUxQTYwNzFEKSgweDBEQkM1MUM5REY5MjNBQ0I0NDI3NDc0MjA5NTc2MUU1OTlFRDFEOEY5NEVGOEY0MTRDMTUxRENDNTIyM0ExM0YpKSkoZ2VuZXJpY19zZWxlY3RvcigweDFBQjlDODhCNTNDOUNGRDBBNjU4MjMzMTE3MTFBQkYxRTEzRTVCMzUyREMyRDM1QzZEMzRBNDUwOEVGNDJDMUQpKShwb3NlaWRvbl9zZWxlY3RvcigweDBENERCOTY5NDk4NzNCOTBGMzY1QkNCQzczQjJBMUFBRTY5NTUzMzc0MkY2NDcyRTA1MEQwMjRDNDdFRjA1MUYpKSkoKHcoKDB4MDQ0RTI0ODZEMjJCNTczNzczM0M0OTMzOTQ0ODY1MDc5QzFEMjRDQjFCNjJENUE1RDk5RkI0QTg0RDFBNzgwNikoMHgyQjdENkY4RkNBN0EwMTc3MDYyNjQ4OEFEODU0MEJEQkFEMTMzN0M2MjdDRDhBOUU2MzIxMkEyQTA1ODMxNDEwKSgweDJEOTI2NzNFQkM2N0ZCODhEQzMwNTNGMDIxQUE0NEY1RUNDMTBGRTU2RTlEODE2OUVCMjhCNjNDODZBRTU3NjYpKDB4MTFCRDE3OTE3RDY4QTJFNjhGNEUxNjk5OEE4OUYxNUY1M0JDRUU4NTI0MDQyRTg3MzE2QTkxN0JFMTE4QjU3MykoMHgxOTc4RUY3MzYyNzc0NkEwNTBERkZGQjk4MUFDQ0FGREUxRUQ1MTY5MDkyMTk5NERCQ0VFNjlFNDQ4OTJDMDdBKSgweDIwQjI0Q0RERDAyRjlFM0UzODY0QjkwNUEwRTM0QzE5MTA5MTRBMzk5MDQ5NzIwOEI0NEQ5QjdEMkY5QzA0RDgpKDB4MDc0MzQ3REUzOURCQjczOTE2M0VDMTZGNEFDNjEwQkFGRTkzMjhDNzY3N0E1OUFEQjBFNDk0OUJFQTcyMTM5RikoMHgyOUYzMzQyODNBMDk3QkVGNTQ1RUQ0QkQyNUZFOTA1Mzg1NjVBRkIxRUNDRkJGMTJCQjYzNkY1MzY5NTBBQUU1KSgweDFEOTU2RjI3QTJDMkIzMkY1MTA4RjkyNjFCRjA4MzM2Q0FCRjNGNDNBMzRENzY1NDk3NDdDNTg5QUIyNjhFMjYpKDB4MEY2N0Y4MjJCNTAwNTEyOUZEREZBMTk4MDZCNjNFMkY5MjkzNjUxMzE5RTAyNEY0NzBBNEUzQzA5M0M5NTNGQSkoMHgwN0ZFMTczNzM2MDUwMjZEMDYxMUVBOEM1NkQ1QTVFMDEyNzM3QTY1MUI5REI0RjJCNkQzNjQzRTY2QUU4MDU1KSgweDA1MENBMjE3N0U3NjhEMTkwREIxQjhFRjM2QkZDOTI5NTc5NjQ0N0MwRjAwRjFDMzBENEVBRDJDNENDRjI1NzYpKDB4MDA4QjEzMkI4REQ5NzFFOEJENzEwRTIxNzZCQTFBMTQ4NkU5ODI2ODI2MDNEN0M5OTM1NEZGRERENDJFRDBERikoMHgzODZFMDRBODQ1NUFDQjg3RDBFNzM3Mjc3NDBFQ0Q3RkQyMTYwN0JCRTcwQ0U0MTNBQUEyRUQ1MjkzRkEyMDNCKSgweDI5MjI1QkQ5MkYwMENDNzEyRTlGM0ZGQ0E3NjYwNTkyQjgwOTg3QkU4QjM1RERGRjgzMTk0RjA3OTlEQzNCNDQpKSkoeigweDIzNDVBMUE3RkIwMDRGRjRCOTMzRTQ3RTkxNEJDNzYyRDMzMjFBQzc0QTFFQjgwN0YyMkY3NUY3MTZBMjk3NDUpKShzKCgweDM4NEY5RENDNTBGRkNDQ0QxN0ZFNTMwOTRGREQ2QzZFM0ExODk5MzdFRjIyMDIwNTVBOUU4NDIwN0QxRjk5MEYpKDB4M0UzQzczRjM0OEMzNkI2MUQ1MkQ1RERGRjM2RDc2NjM1N0I1OEE5MTQ4NzU1NDk0NzEzNTFCRUFCMzU5NTJDQikoMHgxOTNBNDYyQjk3MzFFNzNDODYyMkU2NThCQUQwREI1QTkzMjIxMzk3OERCMzkyNURCQjVBQ0YwN0Y4QUIyQjRDKSgweDJCNkU3MUEzNUY4QTZDMTYxQTIyRDZDQTQ1Q0E1NzY2Mzc4ODkwQzMwRUE2MUFGMEExNzlDQjZCNTQ5NkUxNzcpKDB4MDNBN0JGNDFDRjQ2MjE1ODcxREMzODVGMUM0QUIwM0E4QzNERDY3RUMzRjc4OUU0MjVCQUVDOEVEMkI0QTY1RikoMHgyM0MzNzU4QzUyRkUyNDNBNUU2M0ZENkFFQzIyMThDQzJBMDAxQTZGNjU1RjJFNDRGMUExM0UzOTFGRkE0QkI4KSkpKGdlbmVyaWNfc2VsZWN0b3IoMHgyQ0M0M0YwQTlEOThDQkU4RTVCNkZDMzU0RTlCMDkwQjkxMDc1NDE4MTE2NURCRTQ3NUU4OEEwQTAyRjVBNzg2KSkocG9zZWlkb25fc2VsZWN0b3IoMHgyMkE4MUM1MENCQkU2MDhDQjZGOEE4MDc0NzE0MjRFQjBBNTE2N0IzOTI0NDZGMzJFMTkyRTMzRUZEQkZDRTc1KSkpKSkoZnRfZXZhbDEgMHgzNEFENUZBOEFEMzhEOUZCODM1MzRGODUxRjA5MjRCQTNCOUI0M0UxQzQ1NzAzRjE1MUExOUJDQ0U3MUY0RTdEKSkpKSkp" + |> Side_loaded.Proof.of_base64 + with + | Error e -> + failwith e + | Ok pi -> + pi + in + let statement = + let transaction = + Backend.Tick.Field.t_of_sexp + (Atom + "0x2340A5795E22C7C923991D225400D0052B3A995C35BCCDC612E6205287419EC1" + ) + in + let at_party = + Backend.Tick.Field.t_of_sexp + (Atom + "0x2340A5795E22C7C923991D225400D0052B3A995C35BCCDC612E6205287419EC1" + ) + in + [| transaction; at_party |] + in + let vk = + Side_loaded.Verification_key.of_base58_check_exn + "VVA53aPgmCXemUiPjxo1dhgdNUSWbJarTh9Xhaki6b1AjVE31nk6wnSKcPa6JSJ8KDTDMryCozStCeisLTXLoYxBo3fjFhgPJn25EnuJMggPrVocSW3SfQBY7dgpPqQVccsqSPcFGJptarG6dRrLcx65M4SqudGDWbzpKd2oLyeTVifRTREq2BibC3rWMpUDuLwXEnp61FfFaktb4WKu3hfHyYBt5vL3Xndi9kynUWuhznijLG2yP7eX7o5M3nbjfkg7NdWaGReZH1yt4ewtrmHEMF5qTdK2UPgNzpScaK7ix8wZV5qECT483DsuY6Wpx3s2FfdmRDYwdr2YejhW4ZnJLNAxMgUkV3xkid5esqnk5TuQrdHMYvLZXju3RrZrvqhmbTFXpANKskZnuH1BUvkeoPvpQeYdoeYDJ6bgM6NFB3oWsPTU3vSMg3Wjsqx6Ekc8MuZHuaziGax9WNxbM3H6HscZFRs4npttEiwj1gSvZNaVc9FfRdCa3CMMWJNR1CkA1zKtCb8Sie1yiHc89hDA7K5mufV1yaX88xmAQrhZpTLCE8Ch62Zp3P1Vy6QVDACZCKSiz3bhikYEXFKZaJfRYVZVPeEBgjnUDrB4SD61KKnvWWESV8a3uGudeBLnJqoPJuBC8bZTUfskxqzkXmz2XTv4HMARJRTg21tFB8mZmLgVuaSWpc6inGxTZeWmE9ECSFzHuazEPNQ6yn1xo7G72ixrmLZrZqhbhPfnqSL5SWnmFWaWTihNNdHac8FDwb8JKvneC5yUur3WAZ8tTULiiNVvQhjhKVUrym2wTWFwhDAy6GqZcYeWRig9gpgdaxEuA7YnDc8XZZ5JS643PBfAWZZ3mZR4NxXPnVfn1xAUD2VFXmA8pzkqRwQ8DSpSPpKuwzwuJQUW6QSGtBheKFSxrXt6qekFX2azueedJZrhnwPW78dM7v3Qd2zTWo8iD2wfBB1Yot8BfUqAk7FYyi9hajKT1qZWQMg3kUVBywX93KBht2RFDJeVwiuE2hHaAzobxnnwsPJKPHaU8SM1EXQ4cFP2zJ2acPig52MNht3Z34fMeZ65bA3eEbcDbJw3pk2YS1pHtEr818b5TisPu6gshwkRGghbnTsQzHCjZVf61rpT4WphBsv6ob6foLwdc5ZSxq2BFzAWUv5j5nrtU9fqnQCx1DooZxAc8BnjxCXQ5TnE4Rpj82JwUR59QFNza2RwK2vZLvrNPt1LK5eCkZV8fBWuYD9J4AnxGA8icQbWBAfsSk9xXJBynEKymAsw6eTFPWCAMjQgJLhJP8MJR3NyNbqMfT1nR924EyZged7US9ogU8CLV5GcMBTSzAyCSFwFN8LGL1uT9sStzwQNbUvKvXYRwWNMYpb7Mxcjz1NjBaMbiWUryMcJc3D19yXt8VNt5g3L3Ty4GtL3WWV2aXRRXcuzYZai6wV8ESPGd3R6o4NJS5Ct5Z98fx25sNtswb77Q18pU379m4wsk8ck872oMZTPp9bDHTVpLoEBHd1gkC6j7pP8dx3cNTWc1NoewCGLi6zLDNfPZDrRXZESnaDRgVGEDinXS5SeAihMcQxvriHyskPW4SidcZsZtPvLnoQz7HQRpDnXfg4j6b8P5EX6sSJbkU9is3k6e8puQirFzLLgh2uC4oZH8EzLRZcGkonQPP5sLTmfwX4s5DJYdS4NLAVYSXndVZ4fazLfqPLukdWQkxZihUq4NtFkfzpNB8MPUBe6T72zhnvqVPegeEhgVvUokcn2DRJUc93DSYSGEJ3eZNFTruCgbM7xMXq83K6eraFRvxGqAgsQcTcQKwEfF9XvuppFDBbEHjdg84w1XiRkZ7xPKDdF6Hvi5G8V6rr6q1T7qypKiFqNrwM6frbJqgjedLpAY6RkPchip2WsZTpEX3EY1ryyGnJxZvb2fjCooQ9u1R6zNArVCV383KNJQZAaWFgzd58F7ZJ1fGU8zeFzDuhqSwqPyDE299sVYMSfbvp7xjWygxrbjApRE2FkjQtjuxaiXzsuemvrrSedVCGrktCHNqPKkJxbLcpz97rRBvwnKSd26x8LKHn2Zjzp2qeyxsY8HN7WVPATxPE4xXqi9dw41o8LBQ3GDGe1ASjphdp4bxj1guHhSZbMKTJDj7hJKyuvBMdG1YKQo3uv2qu5MiB3Afu5SZbZStNKBnxc2DRoDyF45yrQNeoBJogcSLAqWG624ZAdU4BWrqRJNjoAu6GxxE6E8TvFtvyDW1R9Nv7tXzmWE7RarrAL9YUD6uqe7gAanAv1cdAJRcPcdr2YvUL7zeB5d1daPfwJW4PYDvMwnnqDFSXgNqPreh8nFaiReDYjiHkwCojPcCgdcK5gJwpQTasjkWQBk2RmFQdfaLCpiPZGroZ6hTvRBHq2MwdUtkQHZjjCvY9fUtnniMVdUgkAZ9oLj8evpeoDEwyEHE1upmZZN84CMPP32NpHDtH3PwgGR3" + in + assert ( + Promise.block_on_async_exn (fun () -> + Side_loaded.verify_promise ~value_to_field_elements:Fn.id + [ (vk, statement, pi) ] ) ) + open Impls.Step let () = Snarky_backendless.Snark0.set_eval_constraints true diff --git a/src/lib/pickles/pickles.mli b/src/lib/pickles/pickles.mli index 0b3d838019c..76c5cf9160f 100644 --- a/src/lib/pickles/pickles.mli +++ b/src/lib/pickles/pickles.mli @@ -223,6 +223,8 @@ module Side_loaded : sig (* Must be called immediately before calling the prover for the inductive rule for which this tag is used as a predecessor. *) val in_prover : ('var, 'value, 'n1, 'n2) Tag.t -> Verification_key.t -> unit + + val srs_precomputation : unit -> unit end (** This compiles a series of inductive rules defining a set into a proof From 780979952b17b6834fd6b319443a83a658114d0b Mon Sep 17 00:00:00 2001 From: Izaak Meckler Date: Fri, 3 Jun 2022 12:24:42 -0700 Subject: [PATCH 11/15] call srs_precomputation in verifier process --- src/lib/verifier/prod.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/verifier/prod.ml b/src/lib/verifier/prod.ml index 783202b4f94..2060362fd05 100644 --- a/src/lib/verifier/prod.ml +++ b/src/lib/verifier/prod.ml @@ -50,6 +50,7 @@ module Worker_state = struct Memory_stats.log_memory_stats logger ~process:"verifier" ; match proof_level with | Full -> + Pickles.Side_loaded.srs_precomputation () ; Deferred.return (let module M = struct module T = Transaction_snark.Make (struct From 9a2e2db866787b14b9637a7b44ae8d03c29baacb Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Mon, 6 Jun 2022 17:50:07 +0100 Subject: [PATCH 12/15] Dump all failing replays for hash change --- buildkite/scripts/replayer-test.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/buildkite/scripts/replayer-test.sh b/buildkite/scripts/replayer-test.sh index ddcb7d78e51..a0da43cb848 100755 --- a/buildkite/scripts/replayer-test.sh +++ b/buildkite/scripts/replayer-test.sh @@ -38,4 +38,6 @@ cd /workdir echo "Running replayer" mina-replayer --archive-uri postgres://postgres:$PGPASSWORD@localhost:5432/archive \ - --input-file $TEST_DIR/input.json --output-file /dev/null + --input-file $TEST_DIR/input.json --output-file /dev/null \ + --continue-on-error # DO NOT MERGE +exit 1 From ef7f158f7068f18bc37faf39d8ac72f02e005f8b Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Mon, 6 Jun 2022 19:04:01 +0100 Subject: [PATCH 13/15] Fixup expected hash in replayer test --- src/app/replayer/test/archive_db.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/replayer/test/archive_db.sql b/src/app/replayer/test/archive_db.sql index a7877ab51fb..27e5dd46360 100644 --- a/src/app/replayer/test/archive_db.sql +++ b/src/app/replayer/test/archive_db.sql @@ -1860,7 +1860,7 @@ COPY public.accounts_created (block_id, account_identifier_id, creation_fee) FRO -- COPY public.blocks (id, state_hash, parent_id, parent_hash, creator_id, block_winner_id, snarked_ledger_hash_id, staking_epoch_data_id, next_epoch_data_id, min_window_density, total_currency, ledger_hash, height, global_slot_since_hard_fork, global_slot_since_genesis, "timestamp", chain_status) FROM stdin; -1 3NLDRvvByy2GMYivoFg7FADtNzjqZZ71jX2AZDVdxqtYLusb7AcV \N 3NKBXHShSYqxwWuxRiFXCSCNKmDVJaEgTHh4FRSYWzMTJ9MRseTC 5 5 1 1 2 77 11616000000065089 jxgSP5L3y7YLoG37xJ5XQmCGb7676TDuek4j3qZVs9LjUF6Gbd4 1 0 0 1651708749987 canonical +1 3NLDRvvByy2GMYivoFg7FADtNzjqZZ71jX2AZDVdxqtYLusb7AcV \N 3NKBXHShSYqxwWuxRiFXCSCNKmDVJaEgTHh4FRSYWzMTJ9MRseTC 5 5 1 1 2 77 11616000000065089 jxjJQUU4K3u2yXpLPCFqcYbqA3ZfTrEgn3Gqw96V5rwWRgkGZtr 1 0 0 1651708749987 canonical 2 3NKHwN7Hg65eGsM2fY3kn3m5KvVsK6GVFgAgVNoXDRnCRhr1UVWJ 1 3NLDRvvByy2GMYivoFg7FADtNzjqZZ71jX2AZDVdxqtYLusb7AcV 4 3 1 1 3 77 11616000000065089 jxHbatt2mrbNoNwFJJbZdWVqSHGejMcwbRkS85yJBy91eHZixxt 2 1 1 1651708944856 pending 3 3NLejSrDnJMo1BRVSq6fqzZMAupQRiA1eB98yEbh6SNxyiGXWYRJ 2 3NKHwN7Hg65eGsM2fY3kn3m5KvVsK6GVFgAgVNoXDRnCRhr1UVWJ 4 3 1 1 4 77 11616000000065089 jxiLr7L1xNDGtqRAh4UpA3bJC1C1Tvk8UoVboR3hkhNUppHP9Z1 3 2 2 1651709109987 pending 4 3NKRDvPbuYVUX23tMVCMA33U5Qrua6A4EFpTfyu4UJzsa27x97aY 3 3NLejSrDnJMo1BRVSq6fqzZMAupQRiA1eB98yEbh6SNxyiGXWYRJ 4 3 1 1 5 77 11616000000065089 jw7bVbZrKcaxZKEvm1xj2rqrmb42FNVuLQ6yyhJE9CmycZ9wzVY 4 3 3 1651709289987 pending @@ -2099,7 +2099,7 @@ COPY public.public_keys (id, value) FROM stdin; -- COPY public.snarked_ledger_hashes (id, value) FROM stdin; -1 jxgSP5L3y7YLoG37xJ5XQmCGb7676TDuek4j3qZVs9LjUF6Gbd4 +1 jxjJQUU4K3u2yXpLPCFqcYbqA3ZfTrEgn3Gqw96V5rwWRgkGZtr \. From 1b080db55fdc7c2cfc21c8f23e219c3e7618ca88 Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Mon, 6 Jun 2022 19:15:49 +0100 Subject: [PATCH 14/15] Revert "Dump all failing replays for hash change" This reverts commit 9a2e2db866787b14b9637a7b44ae8d03c29baacb. NOTE: fixing this programmatically from the log involves (from vim) :tabe src/app/replayer/test/archive_db.sql :vsplit ~/Downloads/mina_build_XXXXX_replayer-test.log You can set up a (slow) macro to perform all of the replacements with qq/expected_ledger_hash 2f""ayi"4f""syi"h:%s/a/s/g lq Then, for N the number of replacements, we run N@q and go grab a coffee. This *WILL* lock up your vim instance for some time. --- buildkite/scripts/replayer-test.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/buildkite/scripts/replayer-test.sh b/buildkite/scripts/replayer-test.sh index a0da43cb848..ddcb7d78e51 100755 --- a/buildkite/scripts/replayer-test.sh +++ b/buildkite/scripts/replayer-test.sh @@ -38,6 +38,4 @@ cd /workdir echo "Running replayer" mina-replayer --archive-uri postgres://postgres:$PGPASSWORD@localhost:5432/archive \ - --input-file $TEST_DIR/input.json --output-file /dev/null \ - --continue-on-error # DO NOT MERGE -exit 1 + --input-file $TEST_DIR/input.json --output-file /dev/null From 3909b8c6c42ffaafc67979c087dfba7cf5dfa000 Mon Sep 17 00:00:00 2001 From: mrmr1993 Date: Mon, 6 Jun 2022 21:33:01 +0100 Subject: [PATCH 15/15] Update all expected replayer hashes --- src/app/replayer/test/archive_db.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/replayer/test/archive_db.sql b/src/app/replayer/test/archive_db.sql index 27e5dd46360..c570b1d140d 100644 --- a/src/app/replayer/test/archive_db.sql +++ b/src/app/replayer/test/archive_db.sql @@ -1861,10 +1861,10 @@ COPY public.accounts_created (block_id, account_identifier_id, creation_fee) FRO COPY public.blocks (id, state_hash, parent_id, parent_hash, creator_id, block_winner_id, snarked_ledger_hash_id, staking_epoch_data_id, next_epoch_data_id, min_window_density, total_currency, ledger_hash, height, global_slot_since_hard_fork, global_slot_since_genesis, "timestamp", chain_status) FROM stdin; 1 3NLDRvvByy2GMYivoFg7FADtNzjqZZ71jX2AZDVdxqtYLusb7AcV \N 3NKBXHShSYqxwWuxRiFXCSCNKmDVJaEgTHh4FRSYWzMTJ9MRseTC 5 5 1 1 2 77 11616000000065089 jxjJQUU4K3u2yXpLPCFqcYbqA3ZfTrEgn3Gqw96V5rwWRgkGZtr 1 0 0 1651708749987 canonical -2 3NKHwN7Hg65eGsM2fY3kn3m5KvVsK6GVFgAgVNoXDRnCRhr1UVWJ 1 3NLDRvvByy2GMYivoFg7FADtNzjqZZ71jX2AZDVdxqtYLusb7AcV 4 3 1 1 3 77 11616000000065089 jxHbatt2mrbNoNwFJJbZdWVqSHGejMcwbRkS85yJBy91eHZixxt 2 1 1 1651708944856 pending -3 3NLejSrDnJMo1BRVSq6fqzZMAupQRiA1eB98yEbh6SNxyiGXWYRJ 2 3NKHwN7Hg65eGsM2fY3kn3m5KvVsK6GVFgAgVNoXDRnCRhr1UVWJ 4 3 1 1 4 77 11616000000065089 jxiLr7L1xNDGtqRAh4UpA3bJC1C1Tvk8UoVboR3hkhNUppHP9Z1 3 2 2 1651709109987 pending -4 3NKRDvPbuYVUX23tMVCMA33U5Qrua6A4EFpTfyu4UJzsa27x97aY 3 3NLejSrDnJMo1BRVSq6fqzZMAupQRiA1eB98yEbh6SNxyiGXWYRJ 4 3 1 1 5 77 11616000000065089 jw7bVbZrKcaxZKEvm1xj2rqrmb42FNVuLQ6yyhJE9CmycZ9wzVY 4 3 3 1651709289987 pending -5 3NLuycS3WqnYZBxgLTYBXv8a1qYaS6CSt2ZQ4cG3gi4rvD64yDsg 4 3NKRDvPbuYVUX23tMVCMA33U5Qrua6A4EFpTfyu4UJzsa27x97aY 4 3 1 1 6 77 11616000000065089 jx5XboJuVNJ4u8m7KpQi3SwtG4gE9cxE5toj8xDWYBBBY4urWh3 5 4 4 1651709469987 pending +2 3NKHwN7Hg65eGsM2fY3kn3m5KvVsK6GVFgAgVNoXDRnCRhr1UVWJ 1 3NLDRvvByy2GMYivoFg7FADtNzjqZZ71jX2AZDVdxqtYLusb7AcV 4 3 1 1 3 77 11616000000065089 jwtggYGqA1qVbLqrpuXoEksBi3ddT7GM1qVAbUMvDg2jz3nMj64 2 1 1 1651708944856 pending +3 3NLejSrDnJMo1BRVSq6fqzZMAupQRiA1eB98yEbh6SNxyiGXWYRJ 2 3NKHwN7Hg65eGsM2fY3kn3m5KvVsK6GVFgAgVNoXDRnCRhr1UVWJ 4 3 1 1 4 77 11616000000065089 jxcKVgdor95eAatRgWe3TZWrweSjxvAwvcZJ1g6TMHb4dcDVU7n 3 2 2 1651709109987 pending +4 3NKRDvPbuYVUX23tMVCMA33U5Qrua6A4EFpTfyu4UJzsa27x97aY 3 3NLejSrDnJMo1BRVSq6fqzZMAupQRiA1eB98yEbh6SNxyiGXWYRJ 4 3 1 1 5 77 11616000000065089 jwztmpmEC61N5QYkaMb1tbiNCDeNVhoyhj8bU7eKV65syUDEqT6 4 3 3 1651709289987 pending +5 3NLuycS3WqnYZBxgLTYBXv8a1qYaS6CSt2ZQ4cG3gi4rvD64yDsg 4 3NKRDvPbuYVUX23tMVCMA33U5Qrua6A4EFpTfyu4UJzsa27x97aY 4 3 1 1 6 77 11616000000065089 jwexhTzbjxHmy7wrbEPLzt3aQKsszzwoq9vYqkgdV43dCh6iq6A 5 4 4 1651709469987 pending \.