-
Notifications
You must be signed in to change notification settings - Fork 579
Add an implementation of a Converting_merkle_tree
#16853
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
fca917b
Add an implementation of a Converting_merkle_tree
mrmr1993 6c675fd
merkle_ledger: make the converting_ledger field non-optional for the …
martyall 3e4b74b
merkle_ledger_test: constrain all modules with canonical module types…
martyall 36f03a2
mina_base/account.ml: Add Unstable account type as a trivial example …
martyall 8fb20d2
test_database.ml: parameterize the test by account type
martyall 0a8e523
test_stubs, test_database: remove the constraining intf on Hash becau…
martyall 363ca1b
converting_merkle_tree: re-export the conversion function for ease of…
martyall 9083ea9
test_converting: add module and tests
martyall 47c3225
added changelog entry
martyall 6d09ac6
fix broken upstream test
martyall File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# PR 16853: Add an implementation of a Converting_merkle_tree | ||
|
||
## Summary | ||
|
||
Added an implementation of `Converting_merkle_tree`, a module which muxes | ||
write requests to a `primary_ledger` to also target a `converting_ledger`. | ||
The intention is to use this in the leadup to a hardfork, where we can run | ||
the post-hardfork schema alongside the existing schema. | ||
|
||
## Changes | ||
- Add an implementation of a Converting_merkle_tree | ||
- Added tests using a temporary `Account.Unstable` type. Subsequent work | ||
will change this to a type which is more reflective of the upcoming changes | ||
to the `Account` type. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
open Core_kernel | ||
|
||
module Make (Inputs : sig | ||
include Intf.Inputs.Intf | ||
|
||
type converted_account | ||
|
||
val convert : Account.t -> converted_account | ||
end) | ||
(Primary_ledger : Intf.Ledger.S | ||
with module Location = Inputs.Location | ||
and module Addr = Inputs.Location.Addr | ||
and type key := Inputs.Key.t | ||
and type token_id := Inputs.Token_id.t | ||
and type token_id_set := Inputs.Token_id.Set.t | ||
and type account := Inputs.Account.t | ||
and type root_hash := Inputs.Hash.t | ||
and type hash := Inputs.Hash.t | ||
and type account_id := Inputs.Account_id.t | ||
and type account_id_set := Inputs.Account_id.Set.t) | ||
(Converting_ledger : Intf.Ledger.S | ||
with module Location = Inputs.Location | ||
and module Addr = Inputs.Location.Addr | ||
and type key := Inputs.Key.t | ||
and type token_id := Inputs.Token_id.t | ||
and type token_id_set := Inputs.Token_id.Set.t | ||
and type account := Inputs.converted_account | ||
and type root_hash := Inputs.Hash.t | ||
and type hash := Inputs.Hash.t | ||
and type account_id := Inputs.Account_id.t | ||
and type account_id_set := Inputs.Account_id.Set.t) : sig | ||
include | ||
Intf.Ledger.S | ||
with module Location = Inputs.Location | ||
and module Addr = Inputs.Location.Addr | ||
and type key := Inputs.Key.t | ||
and type token_id := Inputs.Token_id.t | ||
and type token_id_set := Inputs.Token_id.Set.t | ||
and type account := Inputs.Account.t | ||
and type root_hash := Inputs.Hash.t | ||
and type hash := Inputs.Hash.t | ||
and type account_id := Inputs.Account_id.t | ||
and type account_id_set := Inputs.Account_id.Set.t | ||
|
||
val create : Primary_ledger.t -> Converting_ledger.t -> t | ||
|
||
val primary_ledger : t -> Primary_ledger.t | ||
|
||
val converting_ledger : t -> Converting_ledger.t | ||
|
||
val convert : Inputs.Account.t -> Inputs.converted_account | ||
end = struct | ||
let convert = Inputs.convert | ||
|
||
module Location = Inputs.Location | ||
module Addr = Inputs.Location.Addr | ||
module Path = Primary_ledger.Path | ||
|
||
type path = Primary_ledger.path | ||
|
||
type index = int | ||
|
||
type t = | ||
{ primary_ledger : Primary_ledger.t | ||
; converting_ledger : Converting_ledger.t | ||
} | ||
|
||
let create primary_ledger converting_ledger = | ||
{ primary_ledger; converting_ledger } | ||
|
||
let primary_ledger { primary_ledger; _ } = primary_ledger | ||
|
||
let converting_ledger { converting_ledger; _ } = converting_ledger | ||
|
||
let depth t = Primary_ledger.depth t.primary_ledger | ||
|
||
let num_accounts t = Primary_ledger.num_accounts t.primary_ledger | ||
|
||
let merkle_path_at_addr_exn t addr = | ||
Primary_ledger.merkle_path_at_addr_exn t.primary_ledger addr | ||
|
||
let get_inner_hash_at_addr_exn t addr = | ||
Primary_ledger.get_inner_hash_at_addr_exn t.primary_ledger addr | ||
|
||
let set_all_accounts_rooted_at_exn t addr accounts = | ||
Primary_ledger.set_all_accounts_rooted_at_exn t.primary_ledger addr accounts ; | ||
Converting_ledger.set_all_accounts_rooted_at_exn t.converting_ledger addr | ||
(List.map ~f:convert accounts) | ||
|
||
let set_batch_accounts t addressed_accounts = | ||
Primary_ledger.set_batch_accounts t.primary_ledger addressed_accounts ; | ||
Converting_ledger.set_batch_accounts t.converting_ledger | ||
(List.map | ||
~f:(fun (addr, account) -> (addr, convert account)) | ||
addressed_accounts ) | ||
|
||
let get_all_accounts_rooted_at_exn t addr = | ||
Primary_ledger.get_all_accounts_rooted_at_exn t.primary_ledger addr | ||
|
||
let merkle_root t = Primary_ledger.merkle_root t.primary_ledger | ||
|
||
let to_list t = Primary_ledger.to_list t.primary_ledger | ||
|
||
let to_list_sequential t = Primary_ledger.to_list_sequential t.primary_ledger | ||
|
||
let iteri t ~f = Primary_ledger.iteri t.primary_ledger ~f | ||
|
||
let foldi t ~init ~f = Primary_ledger.foldi t.primary_ledger ~init ~f | ||
|
||
let foldi_with_ignored_accounts t account_ids ~init ~f = | ||
Primary_ledger.foldi_with_ignored_accounts t.primary_ledger account_ids | ||
~init ~f | ||
|
||
let fold_until t ~init ~f ~finish = | ||
Primary_ledger.fold_until t.primary_ledger ~init ~f ~finish | ||
|
||
let accounts t = Primary_ledger.accounts t.primary_ledger | ||
|
||
let tokens t = Primary_ledger.tokens t.primary_ledger | ||
|
||
let token_owner t token_id = | ||
Primary_ledger.token_owner t.primary_ledger token_id | ||
|
||
let token_owners t = Primary_ledger.token_owners t.primary_ledger | ||
|
||
let location_of_account t account_id = | ||
Primary_ledger.location_of_account t.primary_ledger account_id | ||
|
||
let location_of_account_batch t account_ids = | ||
Primary_ledger.location_of_account_batch t.primary_ledger account_ids | ||
|
||
let get_or_create_account t account_id account = | ||
let open Or_error.Let_syntax in | ||
let%bind res = | ||
Primary_ledger.get_or_create_account t.primary_ledger account_id account | ||
in | ||
let%map converting_res = | ||
Converting_ledger.get_or_create_account t.converting_ledger account_id | ||
(convert account) | ||
in | ||
let () = | ||
match (res, converting_res) with | ||
| (`Added, _), (`Existed, _) | (`Existed, _), (`Added, _) -> | ||
failwith "Inconsistent account state in converting ledger" | ||
| (_, loc_res), (_, loc_conv) -> | ||
if not (Location.equal loc_res loc_conv) then | ||
failwith "Inconsistent location in converting ledger" | ||
in | ||
res | ||
|
||
let close t = | ||
Primary_ledger.close t.primary_ledger ; | ||
Converting_ledger.close t.converting_ledger | ||
|
||
let last_filled t = Primary_ledger.last_filled t.primary_ledger | ||
|
||
let get_uuid t = Primary_ledger.get_uuid t.primary_ledger | ||
|
||
let get_directory t = Primary_ledger.get_directory t.primary_ledger | ||
|
||
let get t location = Primary_ledger.get t.primary_ledger location | ||
|
||
let get_batch t locations = | ||
Primary_ledger.get_batch t.primary_ledger locations | ||
|
||
let set t location account = | ||
Primary_ledger.set t.primary_ledger location account ; | ||
Converting_ledger.set t.converting_ledger location (convert account) | ||
|
||
let set_batch ?hash_cache t located_accounts = | ||
Primary_ledger.set_batch ?hash_cache t.primary_ledger located_accounts ; | ||
Converting_ledger.set_batch t.converting_ledger | ||
(List.map | ||
~f:(fun (loc, account) -> (loc, convert account)) | ||
located_accounts ) | ||
|
||
let get_at_index_exn t idx = | ||
Primary_ledger.get_at_index_exn t.primary_ledger idx | ||
|
||
let set_at_index_exn t idx account = | ||
Primary_ledger.set_at_index_exn t.primary_ledger idx account ; | ||
Converting_ledger.set_at_index_exn t.converting_ledger idx (convert account) | ||
|
||
let index_of_account_exn t account_id = | ||
Primary_ledger.index_of_account_exn t.primary_ledger account_id | ||
|
||
let merkle_path t location = | ||
Primary_ledger.merkle_path t.primary_ledger location | ||
|
||
let merkle_path_at_index_exn t idx = | ||
Primary_ledger.merkle_path_at_index_exn t.primary_ledger idx | ||
|
||
let merkle_path_batch t locations = | ||
Primary_ledger.merkle_path_batch t.primary_ledger locations | ||
|
||
let wide_merkle_path_batch t locations = | ||
Primary_ledger.wide_merkle_path_batch t.primary_ledger locations | ||
|
||
let get_hash_batch_exn t locations = | ||
Primary_ledger.get_hash_batch_exn t.primary_ledger locations | ||
|
||
let detached_signal t = Primary_ledger.detached_signal t.primary_ledger | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
(** Create a merkle tree implementation that proxies through to | ||
[Primary_ledger] for all reads and writes, but also updates the | ||
[Converting_ledger] on every mutation. | ||
|
||
The goal of this is to make it easy to upgrade ledgers for breaking changes | ||
to the merkle tree. A running daemon can use this to keep track of ledgers | ||
as normal, but can also retrieve the [Converting_ledger] so that it may be | ||
used for an automated switch-over at upgrade time. | ||
*) | ||
module Make (Inputs : sig | ||
include Intf.Inputs.Intf | ||
|
||
type converted_account | ||
|
||
val convert : Account.t -> converted_account | ||
end) | ||
(Primary_ledger : Intf.Ledger.S | ||
with module Location = Inputs.Location | ||
and module Addr = Inputs.Location.Addr | ||
and type key := Inputs.Key.t | ||
and type token_id := Inputs.Token_id.t | ||
and type token_id_set := Inputs.Token_id.Set.t | ||
and type account := Inputs.Account.t | ||
and type root_hash := Inputs.Hash.t | ||
and type hash := Inputs.Hash.t | ||
and type account_id := Inputs.Account_id.t | ||
and type account_id_set := Inputs.Account_id.Set.t) | ||
(Converting_ledger : Intf.Ledger.S | ||
with module Location = Inputs.Location | ||
and module Addr = Inputs.Location.Addr | ||
and type key := Inputs.Key.t | ||
and type token_id := Inputs.Token_id.t | ||
and type token_id_set := Inputs.Token_id.Set.t | ||
and type account := Inputs.converted_account | ||
and type root_hash := Inputs.Hash.t | ||
and type hash := Inputs.Hash.t | ||
and type account_id := Inputs.Account_id.t | ||
and type account_id_set := Inputs.Account_id.Set.t) : sig | ||
include | ||
Intf.Ledger.S | ||
with module Location = Inputs.Location | ||
and module Addr = Inputs.Location.Addr | ||
and type key := Inputs.Key.t | ||
and type token_id := Inputs.Token_id.t | ||
and type token_id_set := Inputs.Token_id.Set.t | ||
and type account := Inputs.Account.t | ||
and type root_hash := Inputs.Hash.t | ||
and type hash := Inputs.Hash.t | ||
and type account_id := Inputs.Account_id.t | ||
and type account_id_set := Inputs.Account_id.Set.t | ||
|
||
val create : Primary_ledger.t -> Converting_ledger.t -> t | ||
|
||
val primary_ledger : t -> Primary_ledger.t | ||
|
||
val converting_ledger : t -> Converting_ledger.t | ||
|
||
val convert : Inputs.Account.t -> Inputs.converted_account | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this do?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So every time you see a diff of this form
It was a hack in order to be able to assign module signatures to things in the file
src/lib/merkle_ledger_tests/test_stubs.ml
that previously had only module defs with no signature. (The reason I added signatures was because personally I was having a hard time understanding what role different modules were playing in the ledger/db creation -- imagine a giant haskell file with no type signatures).Since some of those signature's declarations were injected via an
include
statement and have opaque typet
, I used this hack in order to get the call sites to compile. In this particular case it was because of this module signatureI am not an ocaml expert, but I could not find another way to get this to compile. I was also trying things like
include Account_id with type t = blah
but I wasn't getting that to work. Maybe @glyh or @georgeee has a better idea of how to get around this.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a type witness, you bring them into context, so the compiler knows some types under current context are equal. They are purely compile time, and in runtime they're just a wrapper for
Fn.id
.I'll take a look at the code to see if we could get rid of them, as in general I hate this pattern.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case, it's of type
(Mina_base.Account_id.t, Account_id.t) Type_equal.t
, so it's instructing the compiler these 2 types are equal.I personally think it's fine if we just expose the internal of the opaque
t
in the signature: We already exposed it via wire type, which means this should be a public-available interface. Hence it can't be of more harm doing so.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@glyh any luck on what it would take to remove this pattern?