Skip to content

Commit

Permalink
Refactorings in Receipt_chain_database
Browse files Browse the repository at this point in the history
- Changed transactions to payments to be aligned to our terminologies
- Change name of module to receipt_chain_database_lib. We will
instantiate it later in Coda_main.
Renaming is to avoid name shadowing when insantiating
  • Loading branch information
wu-s-john committed Nov 9, 2018
1 parent 547a6d5 commit a12c231
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 100 deletions.
3 changes: 2 additions & 1 deletion src/lib/coda_base/jbuild
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
bignum_bigint
coda_numbers
debug_assert
banlist_lib))
banlist_lib
receipt_chain_database_lib))
(preprocessor_deps ("../../config.mlh"))
(preprocess (pps (ppx_jane ppx_deriving.eq ppx_deriving.ord)))
(synopsis "Snarks and friends necessary for keypair generation")))
Expand Down
14 changes: 0 additions & 14 deletions src/lib/receipt_chain_database/database.mli

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
open Core_kernel

module Make
(Transaction : Intf.Transaction)
(Payment : Intf.Payment)
(Receipt_chain_hash : Intf.Receipt_chain_hash
with type transaction_payload := Transaction.payload)
with type payment_payload := Payment.payload)
(Key_value_db : Key_value_database.S
with type key := Receipt_chain_hash.t
and type value :=
( Receipt_chain_hash.t
, Transaction.t )
Tree_node.t) :
(Receipt_chain_hash.t, Payment.t) Tree_node.t) :
Intf.Test.S
with type receipt_chain_hash := Receipt_chain_hash.t
and type transaction := Transaction.t
and type payment := Payment.t
and type database := Key_value_db.t = struct
type t = Key_value_db.t

Expand All @@ -21,7 +19,7 @@ module Make
let prove t ~proving_receipt ~resulting_receipt =
let open Or_error.Let_syntax in
let rec parent_traversal start_receipt last_receipt :
(Receipt_chain_hash.t * Transaction.t) list Or_error.t =
(Receipt_chain_hash.t * Payment.t) list Or_error.t =
match%bind
Key_value_db.get t ~key:last_receipt
|> Result.of_option
Expand All @@ -32,8 +30,8 @@ module Make
with
| Root ->
Or_error.errorf
!"The transaction of root hash %{sexp: Receipt_chain_hash.t} is \
not recorded"
!"The payment of root hash %{sexp: Receipt_chain_hash.t} is not \
recorded"
last_receipt
| Child {value; parent; _} ->
if Receipt_chain_hash.equal start_receipt last_receipt then
Expand All @@ -45,28 +43,28 @@ module Make
let%map result = parent_traversal proving_receipt resulting_receipt in
List.rev result

let get_transaction t ~receipt =
let get_payment t ~receipt =
Key_value_db.get t ~key:receipt
|> Option.bind ~f:(function Root -> None | Child {value; _} -> Some value)

let add t ~previous (transaction : Transaction.t) =
let payload = Transaction.payload transaction in
let add t ~previous (payment : Payment.t) =
let payload = Payment.payload payment in
let receipt_chain_hash = Receipt_chain_hash.cons payload previous in
let node, status =
Option.value_map
(Key_value_db.get t ~key:receipt_chain_hash)
~default:
( Some (Tree_node.Child {parent= previous; value= transaction})
( Some (Tree_node.Child {parent= previous; value= payment})
, `Ok receipt_chain_hash )
~f:(function
| Root ->
( Some (Child {parent= previous; value= transaction})
( Some (Child {parent= previous; value= payment})
, `Duplicate receipt_chain_hash )
| Child {parent= retrieved_parent; _} ->
if not (Receipt_chain_hash.equal previous retrieved_parent) then
(None, `Error_multiple_previous_receipts retrieved_parent)
else
( Some (Child {parent= previous; value= transaction})
( Some (Child {parent= previous; value= payment})
, `Duplicate receipt_chain_hash ))
in
Option.iter node ~f:(function node ->
Expand All @@ -78,7 +76,7 @@ end

let%test_module "receipt_database" =
( module struct
module Transaction = struct
module Payment = struct
include Char

type payload = t [@@deriving bin_io]
Expand All @@ -91,50 +89,47 @@ let%test_module "receipt_database" =

let empty = ""

let cons (payload : Transaction.t) t = String.of_char payload ^ t
let cons (payment : Payment.t) t = String.of_char payment ^ t
end

module Key_value_db =
Key_value_database.Make_mock
(Receipt_chain_hash)
(struct
type t = (Receipt_chain_hash.t, Transaction.t) Tree_node.t
type t = (Receipt_chain_hash.t, Payment.t) Tree_node.t
[@@deriving sexp]
end)

module Receipt_db = Make (Transaction) (Receipt_chain_hash) (Key_value_db)
module Verifier = Verifier.Make (Transaction) (Receipt_chain_hash)
module Receipt_db = Make (Payment) (Receipt_chain_hash) (Key_value_db)
module Verifier = Verifier.Make (Payment) (Receipt_chain_hash)

let populate_random_path ~db transactions initial_receipt_hash =
List.fold transactions ~init:[initial_receipt_hash]
~f:(fun current_leaves transaction ->
let populate_random_path ~db payments initial_receipt_hash =
List.fold payments ~init:[initial_receipt_hash]
~f:(fun current_leaves payment ->
let selected_forked_node = List.random_element_exn current_leaves in
match
Receipt_db.add db ~previous:selected_forked_node transaction
with
match Receipt_db.add db ~previous:selected_forked_node payment with
| `Ok checking_receipt -> checking_receipt :: current_leaves
| `Duplicate _ -> current_leaves
| `Error_multiple_previous_receipts _ ->
failwith "We should not have multiple previous receipts" )
|> ignore

let%test_unit "Recording a sequence of transactions can generate a valid \
merkle list from the first transaction to the last \
transaction" =
let%test_unit "Recording a sequence of payments can generate a valid \
merkle list from the first payment to the last payment" =
Quickcheck.test
~sexp_of:[%sexp_of: Receipt_chain_hash.t * Transaction.t list]
~sexp_of:[%sexp_of: Receipt_chain_hash.t * Payment.t list]
Quickcheck.Generator.(
tuple2 Receipt_chain_hash.gen (list_non_empty Transaction.gen))
~f:(fun (initial_receipt_chain, transactions) ->
tuple2 Receipt_chain_hash.gen (list_non_empty Payment.gen))
~f:(fun (initial_receipt_chain, payments) ->
let db = Receipt_db.create ~directory:"" in
let _, expected_merkle_path =
List.fold_map transactions ~init:initial_receipt_chain
~f:(fun prev_receipt_chain transaction ->
List.fold_map payments ~init:initial_receipt_chain
~f:(fun prev_receipt_chain payment ->
match
Receipt_db.add db ~previous:prev_receipt_chain transaction
Receipt_db.add db ~previous:prev_receipt_chain payment
with
| `Ok new_receipt_chain ->
(new_receipt_chain, (new_receipt_chain, transaction))
(new_receipt_chain, (new_receipt_chain, payment))
| `Duplicate _ ->
failwith
"Each receipt chain in a sequence should be unique"
Expand All @@ -145,38 +140,36 @@ let%test_module "receipt_database" =
( List.hd_exn expected_merkle_path
, List.last_exn expected_merkle_path )
in
[%test_result: (Receipt_chain_hash.t * Transaction.t) List.t]
[%test_result: (Receipt_chain_hash.t * Payment.t) List.t]
~message:"Merkle paths should be equal"
~expect:expected_merkle_path
( Receipt_db.prove db ~proving_receipt ~resulting_receipt
|> Or_error.ok_exn ) )

let%test_unit "There exists a valid merkle list if a path exists in a \
tree of transactions" =
tree of payments" =
Quickcheck.test
~sexp_of:
[%sexp_of: Receipt_chain_hash.t * Transaction.t * Transaction.t list]
~sexp_of:[%sexp_of: Receipt_chain_hash.t * Payment.t * Payment.t list]
Quickcheck.Generator.(
tuple3 Receipt_chain_hash.gen Transaction.gen
(list_non_empty Transaction.gen))
~f:(fun (prev_receipt_chain, initial_transaction, transactions) ->
tuple3 Receipt_chain_hash.gen Payment.gen
(list_non_empty Payment.gen))
~f:(fun (prev_receipt_chain, initial_payment, payments) ->
let db = Receipt_db.create ~directory:"" in
let initial_receipt_chain =
match
Receipt_db.add db ~previous:prev_receipt_chain
initial_transaction
Receipt_db.add db ~previous:prev_receipt_chain initial_payment
with
| `Ok receipt_chain -> receipt_chain
| `Duplicate _ ->
failwith
"There should be no duplicate inserts since the first \
transaction is only being inserted"
payment is only being inserted"
| `Error_multiple_previous_receipts _ ->
failwith
"There should be no errors with previous receipts since the \
first transaction is only being inserted"
first payment is only being inserted"
in
populate_random_path ~db transactions initial_receipt_chain ;
populate_random_path ~db payments initial_receipt_chain ;
let random_receipt_chain =
List.filter
(Hashtbl.keys (Receipt_db.database db))
Expand All @@ -195,24 +188,21 @@ let%test_module "receipt_database" =
let%test_unit "A merkle list should not exist if a proving receipt does \
not exist in the database" =
Quickcheck.test
~sexp_of:
[%sexp_of: Receipt_chain_hash.t * Transaction.t * Transaction.t list]
~sexp_of:[%sexp_of: Receipt_chain_hash.t * Payment.t * Payment.t list]
Quickcheck.Generator.(
tuple3 Receipt_chain_hash.gen Transaction.gen
(list_non_empty Transaction.gen))
~f:
(fun (initial_receipt_chain, unrecorded_transaction, transactions) ->
tuple3 Receipt_chain_hash.gen Payment.gen
(list_non_empty Payment.gen))
~f:(fun (initial_receipt_chain, unrecorded_payment, payments) ->
let db = Receipt_db.create ~directory:"" in
populate_random_path ~db transactions initial_receipt_chain ;
populate_random_path ~db payments initial_receipt_chain ;
let nonexisting_receipt_chain =
let receipt_chains = Hashtbl.keys (Receipt_db.database db) in
let largest_receipt_chain =
List.max_elt receipt_chains ~compare:(fun chain1 chain2 ->
Int.compare (String.length chain1) (String.length chain2) )
|> Option.value_exn
in
Receipt_chain_hash.cons unrecorded_transaction
largest_receipt_chain
Receipt_chain_hash.cons unrecorded_payment largest_receipt_chain
in
let random_receipt_chain =
List.filter
Expand Down
12 changes: 12 additions & 0 deletions src/lib/receipt_chain_database_lib/database.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Make
(Payment : Intf.Payment)
(Receipt_chain_hash : Intf.Receipt_chain_hash
with type payment_payload := Payment.payload)
(Key_value_db : Key_value_database.S
with type key := Receipt_chain_hash.t
and type value :=
(Receipt_chain_hash.t, Payment.t) Tree_node.t) :
Intf.Test.S
with type receipt_chain_hash := Receipt_chain_hash.t
and type payment := Payment.t
and type database := Key_value_db.t
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
(library
(name receipt_chain_database)
(public_name receipt_chain_database)
(name receipt_chain_database_lib)
(public_name receipt_chain_database_lib)
(library_flags (-linkall))
(libraries core key_value_database)
(inline_tests)
(preprocess (pps ppx_jane ppx_deriving.eq))
(synopsis "A library that contains a database that records sent transactions for an individual account and generates a merkle list of a transaction.
Also, the library contains a verifier that proves the correctness of a merkle list of transactions"))
(synopsis "A library that contains a database that records sent payments for an individual account and generates a merkle list of a payment.
Also, the library contains a verifier that proves the correctness of a merkle list of payments"))
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
open Core_kernel

(** Receipt_chain_database is a data structure that stores a client's
transactions and their corresponding receipt_chain_hash. A client
uses this database to prove that they sent a transaction by showing
payments and their corresponding receipt_chain_hash. A client
uses this database to prove that they sent a payment by showing
a verifier a Merkle list of their receipt chain from their latest
transaction to the transaction that they are trying to prove.
payment to the payment that they are trying to prove.
Each account has a receipt_chain_hash field, which is a certificate
of all the transactions that an account has send. If an account has
send transactions t_n ... t_1 and p_n ... p_1 is the hash of the
payload of these transactions, then the receipt_chain_hash, $r_n$,
of all the payments that an account has send. If an account has
send payments t_n ... t_1 and p_n ... p_1 is the hash of the
payload of these payments, then the receipt_chain_hash, $r_n$,
is equal to the following:
$$ r_n = h(p_n, h(p_{n - 1}, ...)) $$
where h is the hash function that determines the receipt_chain_hash
that takes as input the hash of a transaction payload and it's
that takes as input the hash of a payment payload and it's
preceding receipt_chain_hash.
The key of the database is a receipt_chain_hash.
The value of the database is the transaction corresponding to the
The value of the database is the payment corresponding to the
receipt_chain_hash *)
module type S = sig
type t

type receipt_chain_hash

type transaction
type payment

val create : directory:string -> t

val prove :
t
-> proving_receipt:receipt_chain_hash
-> resulting_receipt:receipt_chain_hash
-> (receipt_chain_hash * transaction) list Or_error.t
-> (receipt_chain_hash * payment) list Or_error.t
(** Prove will provide a merkle list of a proving receipt h_1 and
it's corresponding transaction t_1 to a resulting_receipt r_k
and it's corresponding transaction r_k, inclusively. Therefore,
it's corresponding payment t_1 to a resulting_receipt r_k
and it's corresponding payment r_k, inclusively. Therefore,
the output will be in the form of [(t_1, r_1), ... (t_k, r_k)],
where r_i = h(r_{i-1}, i_k) for i = 2...k *)

val get_transaction : t -> receipt:receipt_chain_hash -> transaction option
val get_payment : t -> receipt:receipt_chain_hash -> payment option

val add :
t
-> previous:receipt_chain_hash
-> transaction
-> payment
-> [ `Ok of receipt_chain_hash
| `Duplicate of receipt_chain_hash
| `Error_multiple_previous_receipts of receipt_chain_hash ]
(** Add stores a transaction into a client's database as a value.
The key is computed by using the transaction payload and the previous receipt_chain_hash.
(** Add stores a payment into a client's database as a value.
The key is computed by using the payment payload and the previous receipt_chain_hash.
This receipt_chain_hash is computed within the `add` function. As a result,
the computed receipt_chain_hash is returned *)
end
Expand All @@ -69,14 +69,14 @@ end
module type Receipt_chain_hash = sig
type t [@@deriving hash, bin_io, eq, sexp, compare]

type transaction_payload
type payment_payload

val empty : t

val cons : transaction_payload -> t -> t
val cons : payment_payload -> t -> t
end

module type Transaction = sig
module type Payment = sig
type t [@@deriving bin_io]

type payload [@@deriving bin_io]
Expand Down
File renamed without changes.
Loading

0 comments on commit a12c231

Please sign in to comment.