Add lib_sapling for privacy-preserving transactions

This OCaml library implements the Sapling protocol for privacy-preserving
transactions as described in its
[specification](https://github.com/zcash/zips/blob/master/protocol/sapling.pdf),
version 2020.1.2.
Co-authored-by: Marc Beunardeau's avatarMarc beunardeau <[email protected]>
Co-authored-by: Mehdi Bouaziz's avatarMehdi Bouaziz <[email protected]>
parent db30bef8
......@@ -577,6 +577,11 @@ opam:pbkdf:
variables:
package: pbkdf
opam:sapling:
<<: *opam_definition
variables:
package: sapling
opam:secp256k1-internal:
<<: *opam_definition
variables:
......
......@@ -12,10 +12,13 @@ export OPAMYES=${OPAMYES:=true}
## In another ideal world, this list should be extracted from the pinned
## packages and filter only conf-* packages
opam depext conf-gmp conf-libev conf-m4 conf-perl conf-pkg-config conf-hidapi
opam depext conf-gmp conf-libev conf-m4 conf-perl conf-pkg-config conf-hidapi conf-rust
## In an ideal world, `--with-test` should be present only when using
## `--dev`. But this would probably break the CI, so we postponed this
## change until someone have some spare time. (@pirbo, @hnrgrgr)
opam install $opams --deps-only --with-test --criteria="-notuptodate,-changed,-removed"
# install librustzcash.{a,h} and zcash_params
src/lib_sapling/build-deps.sh
wrap-fun-args=false
let-binding-spacing=compact
field-space=loose
break-separators=after-and-docked
sequence-style=separator
doc-comments=before
margin=80
module-item-spacing=sparse
parens-tuple=always
parens-tuple-patterns=always
break-string-literals=newlines-and-wrap
\ No newline at end of file
This OCaml library implements the Sapling protocol for privacy-preserving
transactions as described in its
[specification](https://github.com/zcash/zips/blob/master/protocol/sapling.pdf),
version 2020.1.2.
A large part of the functionalities are implemented by the
[librustzcash library](https://github.com/zcash/librustzcash) from the ZCash
project.
This library provides a binding of `librustzcash` and implements the
needed data structures to use the library.
Additionally it provides some facilities to forge transactions.
# Rustzcash
The Rust library exports a C compatible interface in
`librustzcash/src/rustzcash.rs` and
`librustzcash/include/librustzcash.h` that is used by the ZCash C++
node and client.
The files `rustzcash.ml{,i}` simply bind this C interface.
We assume that the library is present in the system and it can easily
be compiled using the script ``build-deps.sh``, which will also
download the parameters necessary to create proofs and verify them.
The script is called as part of the usual `make build-deps`.
The binding is simply converting functions and types from Rust to
OCaml and it can't be used alone to test the library as a number of
data structures are missing.
# Core
The file `core.ml` contains a more high level presentation of the
Sapling protocol with respect to the low level binding.
Core is organized in several modules that are exposed through a
limited signature for bakers and a more complete signature for
clients.
# Storage
All the data structures are implemented in `storage.ml`, including the
incremental Merkle tree, the nullifier set, the root bounded list and
the ciphertexts list.
# Example
The file `test/example.ml` contains a simplified implementation of a
client and baker using the library.
#!/bin/bash
set -e
# This script installs librustzcash.{a,h} and the zcash parameters.
# It assumes that cargo is installed in the system, use your package
# manager or https://rustup.rs
# install librustzcash.{a,h}
BUILD_DIR="/tmp/tezos-zcash"
function build_librustzcash () {
#directory where this script lives
local dir_script=$(cd $(dirname $0) && pwd)
local commit="a57dc7f47807ea50cb0a5deec9b84b3e7da11bc0"
local repository="https://github.com/zcash/librustzcash"
local install_dir="${OPAM_SWITCH_PREFIX}/lib/librustzcash"
if ! [ -d ${install_dir} ] ; then
echo "Installing librustzcash in ${install_dir}"
command -v cargo > /dev/null 2>&1 || { echo >&2 "Install cargo. Aborting."; exit 1; }
mkdir -p ${BUILD_DIR}
cd ${BUILD_DIR}
echo "Downloading librustzcash from ${repository}, commit ${commit}"
curl -L ${repository}/archive/${commit}.zip --output ${commit}.zip
unzip ${commit}.zip >> /dev/null # do not print the resulting file
cd librustzcash-${commit}
export RUST_BACKTRACE=1
cargo build --release -p librustzcash
rm -rf ${install_dir}
mkdir ${install_dir}
cp target/release/librustzcash.a librustzcash/include/librustzcash.h ${install_dir}
cd ${dir_script}
rm -rf ${BUILD_DIR}
fi
}
function install_zcash_params () {
local zcash_params="${OPAM_SWITCH_PREFIX}/share/zcash-params"
if ! [ -d ${zcash_params} ] ; then
echo "Installing zcash parameters in ${zcash_params}"
mkdir ${zcash_params}
wget -O ${zcash_params}/sapling-spend.params https://z.cash/downloads/sapling-spend.params
wget -O ${zcash_params}/sapling-output.params https://z.cash/downloads/sapling-output.params
fi
}
function cleanup () {
echo "Cleaning up build directory ${BUILD_DIR}"
rm -rf ${BUILD_DIR}
}
trap cleanup EXIT INT
build_librustzcash
echo -n ""
install_zcash_params
module C = Configurator.V1
let () =
let libdir = "-L" ^ Sys.getenv "OPAM_SWITCH_PREFIX" ^ "/lib/librustzcash" in
C.Flags.write_sexp "flags.sexp" ["-lpthread"; libdir; "-lrustzcash"]
(executable
(name discover)
(libraries dune.configurator))
(alias
(name runtest_lint)
(deps (glob_files *.ml{,i}))
(action (run %{lib:tezos-tooling:lint.sh} %{deps})))
This diff is collapsed.
This diff is collapsed.
;; This binding assumes that librustzcash.{a,h} are installed under
;; $OPAM_SWITCH_PREFIX/lib/librustzcash
;; dune should be able to find it using
;; (c_library_flags -lpthread -Llibrustzcash -lrustzcash)
;; It does not work, so we go through the complication of a discover.ml
;; simply to generate that.
(rule
(targets flags.sexp)
(deps (:discover config/discover.exe))
(action (run %{discover})))
(library
(name sapling)
(public_name sapling)
(library_flags :standard -linkall -ccopt -pthread)
(c_names rustzcash_stubs)
(c_library_flags (:include flags.sexp))
(libraries hex data-encoding tezos-crypto tezos-stdlib tezos-error-monad)
(flags (:standard -open Tezos_stdlib
-open Tezos_crypto
-open Tezos_error_monad)))
(alias
(name runtest_lint)
(deps (glob_files *.ml{,i}))
(action (run %{lib:tezos-tooling:lint.sh} %{deps})))
(lang dune 1.11)
(name sapling)
This diff is collapsed.
module Core = Core.Client
module S = Storage
module Input : sig
type t
val encoding : t Data_encoding.t
val pos : t -> int64
val amount : t -> int64
val address : t -> Core.Viewing_key.address
val compare : t -> t -> int
val is_spent :
t -> S.state -> Core.Viewing_key.t -> bool Error_monad.tzresult Lwt.t
val get :
S.state ->
int64 ->
Core.Viewing_key.t ->
(Bytes.t * t) option Error_monad.tzresult Lwt.t
val get_out :
S.state ->
int64 ->
Core.Spending_key.ovk ->
(Bytes.t * t) option Error_monad.tzresult Lwt.t
val mem : S.state -> int64 -> bool Error_monad.tzresult Lwt.t
end
type output
val make_output : Core.Viewing_key.address -> int64 -> Bytes.t -> output
val forge_transaction :
?number_dummy_inputs:int ->
?number_dummy_outputs:int ->
Input.t list ->
output list ->
Core.Spending_key.t ->
string ->
S.state ->
Core.UTXO.transaction Error_monad.tzresult Lwt.t
val forge_shield_transaction :
?number_dummy_inputs:int ->
?number_dummy_outputs:int ->
output list ->
int64 ->
string ->
S.state ->
Core.UTXO.transaction Error_monad.tzresult Lwt.t
This diff is collapsed.
(* The MIT License (MIT)
*
* Copyright (c) 2019 Nomadic Labs <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. *)
include Rustzcash_sig.T
val max_amount : int64
val check_amount : int64 -> unit
(** Loads the parameters for our instance of Groth16.
The parameters are search in:
- /usr/share/zcash-params
- ${OPAM_SWITCH_PREFIX}/share/zcash-params
- ${HOME}/.zcash-params
Only sapling parameters are loaded. *)
val init_params : unit -> unit
(** Derives the nullifer pk corresponding to a nullifier sk *)
val nsk_to_nk : nsk -> nk
(** Derives the spending/signing pk corresponding to a secret spending/signing sk *)
val ask_to_ak : ask -> ak
(** Derives the incoming viewing key *)
val crh_ivk : ak -> nk -> ivk
(** Checks that a potential diversifier respects the needed properties *)
val check_diversifier : diversifier -> bool
(** Computes a diversified pk that the payee gives to the payer offline *)
val ivk_to_pkd : ivk -> diversifier -> pkd option
(** Gives a random scalar *)
val generate_r : unit -> Bytes.t
(** Computes a nullifier. The first int64 is the amount of the note, the second
is the position you want it inserted in.
The rcm should be the same as the one to compute cm and the spend or output
proof, and should be generated using generate_r. *)
val compute_nf :
diversifier ->
pkd ->
amount:int64 ->
rcm ->
ak ->
nk ->
position:int64 ->
nullifier option
(** Computes a commitment. The int64 is the amount, and the rcm is the same than
for the nullifier and output or spend proof. It should be generated at
random using generate_r. *)
val compute_cm : diversifier -> pkd -> amount:int64 -> rcm -> commitment option
(** Computes the shared secret of a Diffie Hellman key exchange (on the JubJub
curve) with base depending on the diversifier.
For the sender the epk is the pkd of the receiver, the esk was generated by
him using generate_r.
For the receiver the epk is the one published by the sender, and the secret is
his ivk. *)
val ka_agree_sender : pkd -> esk -> symkey option
val ka_agree_receiver : epk -> ivk -> symkey option
(** Computes the epheremal pk from the epheremal sk for a Diffie Hellman key
exchange. This is used by the sender. The esk should be generated using
generate_r *)
val ka_derivepublic : diversifier -> esk -> epk option
(** Creates the spend sig for an input. The sighash argument is the hash of
the input ie. cv,cm,...
The has to be generated using generate_r *)
val spend_sig : ask -> ar -> sighash -> spend_sig option
(** Creates and frees a proving context. The proving context has to be created
before creating proofs for inputs and outputs.
It is then used to create the binding sig, and freed.
It is a rust pointer to a scalar and an elliptic curve point *)
val proving_ctx_init : unit -> proving_ctx
val proving_ctx_free : proving_ctx -> unit
(** Creates the binding signature for a transaction. It is effectively
a zk proof that the sum of the amounts of a list of inputs and
outputs is the same as the given balance. The necessary information
is stored in the proving context when creating the proofs for
inputs and ouputs. The proving context has to be freed after
calling this function. *)
val make_binding_sig :
proving_ctx -> balance:int64 -> sighash -> binding_sig option
(** Creates proof and sig for an output *)
val output_proof :
proving_ctx ->
esk ->
diversifier ->
pkd ->
rcm ->
amount:int64 ->
(cv * output_proof) option
(** Creates the zk proof and sig for an input.
The first is the same as the one for the commitement and nullifier.
The second one is the same as for the binding sig.
The bigstring is the witness created by get_witness. *)
val spend_proof :
proving_ctx ->
ak ->
nsk ->
diversifier ->
rcm ->
ar ->
amount:int64 ->
root:hash ->
witness:Bytes.t ->
(cv * rk * spend_proof) option
(** Reduces mod r_j, takes a 64 bytes input*)
val to_scalar : Bytes.t -> Bytes.t
(** Creates and frees a verifying context. The proving context has to be created
before verifying proofs the inputs and outputs.
It is then used to verify the binding sig, and freed.
It is a rust pointer to an elliptic curve point *)
val verification_ctx_init : unit -> verification_ctx
val verification_ctx_free : verification_ctx -> unit
val check_output :
verification_ctx -> cv -> commitment -> epk -> output_proof -> bool
val check_spend :
verification_ctx ->
cv ->
hash ->
nullifier ->
rk ->
spend_proof ->
spend_sig ->
sighash ->
bool
val merkle_hash : height:int -> hash -> hash -> hash
val tree_uncommitted : hash
val final_check : verification_ctx -> int64 -> binding_sig -> sighash -> bool
val zip32_xsk_master : Bytes.t -> zip32_expanded_spending_key
val zip32_xfvk_address :
zip32_full_viewing_key ->
diversifier_index ->
(diversifier_index * diversifier * pkd) option
val zip32_xsk_derive :
zip32_expanded_spending_key -> Int32.t -> zip32_expanded_spending_key
val zip32_xfvk_derive :
zip32_full_viewing_key -> Int32.t -> zip32_full_viewing_key
(* Signature that hides the Bytes.t implementation *)
module type T = sig
type ask (* 32 bytes *)
type ak (* 32 *)
type nsk (* 32 *)
type nk (* 32 *)
type ovk (* 32 *)
type diversifier (* 11 *)
type pkd (* 32 *)
type nullifier (* 32 *)
type commitment (* 32 *)
type epk (* 32 *)
type symkey (* 32 *)
type sighash (* 32 *)
type spend_sig (* 64 *)
type proving_ctx
type verification_ctx
type hash (* 32 *)
type cv (* 32 *)
type rk (* 32 *)
type spend_proof (* 48 + 48 + 96 *)
type binding_sig (* 64 *)
type output_proof (* 48 + 48 + 96 *)
type ivk (* 32 *)
type ar (* 32 *)
type rcm (* 32 *)
type esk (* 32 *)
type diversifier_index (* 11 *)
(*96 bytes*)
type expanded_spending_key = {ask : ask; nsk : nsk; ovk : ovk}
(* 169 bytes *)
(* this is an extended_spending_key that can be used to derive more
keys using zip32*)
type zip32_expanded_spending_key = {
depth : Bytes.t;
(* 1 byte *)
parent_fvk_tag : Bytes.t;
(* 4 bytes *)
child_index : Bytes.t;
(* 4 bytes *)
chain_code : Bytes.t;
(* 32 bytes *)
expsk : expanded_spending_key;
(* 96 bytes *)
dk : Bytes.t; (* 32 bytes *)
}
(* 96 bytes*)
type full_viewing_key = {ak : ak; nk : nk; ovk : ovk}
(* 169 bytes *)
(* this is an extended_full_viewing_key that can be used to derive more
keys using zip32 *)
type zip32_full_viewing_key = {
depth : Bytes.t;
(* 1 byte *)
parent_fvk_tag : Bytes.t;
(* 4 bytes *)
child_index : Bytes.t;
(* 4 bytes *)
chain_code : Bytes.t;
(* 32 bytes *)
fvk : full_viewing_key;
(* 96 bytes *)
dk : Bytes.t; (* 32 bytes *)
}
val to_nk : Bytes.t -> nk
val to_ak : Bytes.t -> ak
val to_ask : Bytes.t -> ask
val to_nsk : Bytes.t -> nsk
val to_pkd : Bytes.t -> pkd
val to_ovk : Bytes.t -> ovk
val to_nullifier : Bytes.t -> nullifier
val to_commitment : Bytes.t -> commitment
val to_symkey : Bytes.t -> symkey
val to_epk : Bytes.t -> epk
val to_spend_sig : Bytes.t -> spend_sig
val to_hash : Bytes.t -> hash
val to_cv : Bytes.t -> cv
val to_rk : Bytes.t -> rk
val to_spend_proof : Bytes.t -> spend_proof
val to_output_proof : Bytes.t -> output_proof
val to_sighash : Bytes.t -> sighash
val to_binding_sig : Bytes.t -> binding_sig
val to_diversifier : Bytes.t -> diversifier
val to_diversifier_index : Bytes.t -> diversifier_index
val to_ar : Bytes.t -> ar
val to_rcm : Bytes.t -> rcm
val to_esk : Bytes.t -> esk
val to_ivk : Bytes.t -> ivk
val to_expanded_spending_key : Bytes.t -> expanded_spending_key
val to_zip32_expanded_spending_key : Bytes.t -> zip32_expanded_spending_key
val to_full_viewing_key : Bytes.t -> full_viewing_key
val to_zip32_full_viewing_key : Bytes.t -> zip32_full_viewing_key
val of_nk : nk -> Bytes.t
val of_ak : ak -> Bytes.t
val of_ask : ask -> Bytes.t
val of_nsk : nsk -> Bytes.t
val of_pkd : pkd -> Bytes.t
val of_ovk : ovk -> Bytes.t
val of_nullifier : nullifier -> Bytes.t
val of_commitment : commitment -> Bytes.t
val of_symkey : symkey -> Bytes.t
val of_epk : epk -> Bytes.t
val of_spend_sig : spend_sig -> Bytes.t
val of_hash : hash -> Bytes.t
val of_cv : cv -> Bytes.t
val of_rk : rk -> Bytes.t
val of_spend_proof : spend_proof -> Bytes.t
val of_output_proof : output_proof -> Bytes.t
val of_sighash : sighash -> Bytes.t
val of_binding_sig : binding_sig -> Bytes.t
val of_diversifier : diversifier -> Bytes.t
val of_diversifier_index : diversifier_index -> Bytes.t
val of_ar : ar -> Bytes.t
val of_rcm : rcm -> Bytes.t
val of_esk : esk -> Bytes.t
val of_ivk : ivk -> Bytes.t
val of_expanded_spending_key : expanded_spending_key -> Bytes.t
val of_zip32_expanded_spending_key : zip32_expanded_spending_key -> Bytes.t
val of_full_viewing_key : full_viewing_key -> Bytes.t
val of_zip32_full_viewing_key : zip32_full_viewing_key -> Bytes.t
val hash_compare : hash -> hash -> int
val hash_of_commitment : commitment -> hash
val commitment_of_hash : hash -> commitment
end
This diff is collapsed.
opam-version: "2.0"
name: "ocaml-sapling"
authors: [ "Nomadic Labs <[email protected]>" ]
maintainer: "Nomadic Labs <[email protected]>"
synopsis: "OCaml library for the Sapling protocol, using librustzcash"
homepage: "https://gitlab.com/nomadic-labs/tezos"
bug-reports: "https://gitlab.com/tezos/nomadic-labs/issues"
dev-repo: "git+https://gitlab.com/nomadic-labs/tezos.git"
license: "MIT"
depends: [
"conf-rust" {build}
"dune" {build & >= "1.7"}
"hex"
]
build: [[ "dune" "build" "-j" jobs "-p" name "@install" ]]
run-test: [[ "dune" "runtest" "-p" name "-j" jobs ]]
This diff is collapsed.
(* The MIT License (MIT)
*
* Copyright (c) 2019 Nomadic Labs <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. *)
module type STORAGE = sig
type 'a tzresult
type state
val empty : state
val size : state -> (int64 * int64) tzresult Lwt.t
val get_root : state -> Core.Baker.Hash.t tzresult Lwt.t
val mem_root : state -> Core.Baker.Hash.t -> bool tzresult Lwt.t
val mem : state -> int64 -> bool tzresult Lwt.t
val get :
state ->