-
Notifications
You must be signed in to change notification settings - Fork 578
Cache account update action hashes in a new aux field in Account_update #17120
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
base: compatible
Are you sure you want to change the base?
Cache account update action hashes in a new aux field in Account_update #17120
Conversation
src/lib/mina_base/user_command.ml
Outdated
account_update_digest_r forest_digest_r ) | ||
(t1 : (proof_l, account_update_digest_l, forest_digest_l) with_forest) | ||
(t2 : (proof_r, account_update_digest_r, forest_digest_r) with_forest) = | ||
(type account_update_digest_l forest_digest_l account_update_digest_r |
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.
I'd rollback the explicit variable, as it ensured that the function is abstract in proof type too
src/lib/mina_base/zkapp_command.ml
Outdated
({ fee_payer; memo; account_updates } : (_, _, _) with_forest) : | ||
(unit, unit, unit) with_forest = | ||
({ fee_payer; memo; account_updates } : | ||
((_, (_, _) Control.Poly.t) Account_update.Poly.t, _, _) with_forest ) : |
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.
I think it's fine to remove type signature here
I haven't been able to have the wire and stable types be of different shapes just yet, but I did fix the derived |
6591b58
to
b56c266
Compare
I've merged the branch from #17147 and added a commit to restore the old wire types version of |
1927b90
to
4d8b7c3
Compare
The |
!ci-build-me |
| User_command.Poly.Signed_command tx -> | ||
Helper.to_yojson ~proof_to_yojson:Proof.to_yojson (Signed_command tx) | ||
| User_command.Poly.Signed_command _ as tx -> | ||
Helper.to_yojson ~proof_to_yojson:Nothing.unreachable_code tx |
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.
Is the change from Proof.to_yojson
necessary? Could we find a more elegant solution?
Or at least document this usage of unreachable_code
.
Seeing this code makes me worry that we could have an unexpected runtime error because of it, although it feels like we shouldn't I'd consider rewriting something or leaving a comment in code to explain the "why" and "how".
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 unreachable_code
function takes a value of the uninhabited Never.t
type. (I think that's its name). It's more like type level documentation that the proof_to_yojson
argument is unused in the body of Helper.to_yojson
during this call. I'll document it.
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.
Added a comment
src/lib/mina_base/account_update.ml
Outdated
|
||
let to_latest = Fn.id | ||
end | ||
end] | ||
|
||
let with_no_aux ~body ~authorization : t = { body; authorization } |
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.
Fee payer doesn't have aux
.
So I suggest to rename the method to simply make
, and then we can derive it with make
added to [@@deriving ...]
.
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.
I have renamed that method
|
||
let to_latest = Fn.id | ||
|
||
let to_yojson : _ -> Yojson.Safe.t = Nothing.unreachable_code |
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.
Do we need these?
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.
Yes, at least with how deriving yojson
and sexp
currently work. When you invoke that on types like t = (A.t, B.t) Poly.t
it will always generate an instance that looks something like
let to_yojson = Poly.to_yojson A.to_yojson B.to_yojson
So the yojson
and sexp
functions for this No_aux
type must exist if that type will appear in those kinds of type definitions, to satisfy the ppx code generator.
let events_elements events = | ||
List.fold events ~init:0 ~f:(fun acc event -> acc + Array.length event) | ||
in | ||
let all_updates, num_event_elements, num_action_elements = | ||
let statement_tuple = ((), (), ()) in |
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.
I don't understand this change, please explain
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.
The old code collected all the updates in the command into a list, then mapped over the list with const ((),(),())
, basically. It never used the values in the list, so this new code should be equivalent to the old code. (The only thing that Update_group.group_by_zkapp_command_rev
requires of the statement list is that it have the correct length, really.)
The reason why I had to change it is that I need this to work with a command with updates with any aux
parameter, and the fee payer Account_update.t
gets computed in this function here with of_fee_payer
, so to keep the old code structure I would have needed to unify the fee payer update aux parameter with the aux parameter in the t.account_updates
themselves. It seemed better to just skip that work than to, say, go through the trouble of mapping the aux to unit
in the t.account_updates
.
@@ -247,9 +247,6 @@ module Mina_base = struct | |||
(O.Zkapp_command.Call_forest.Stable) | |||
(W.Zkapp_command.Call_forest) | |||
include Assert_equal0V2 (O.Control.Stable) (W.Control) | |||
include Assert_equal0V1 (O.Account_update.Stable) (W.Account_update) |
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.
Changes in this file need to be reverted?
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.
I'll double check, but I believe these need to be deleted because the wire types versions of these types are no longer equal in representation to the stable types.
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.
I'm hoping this does not cause any issues with the o1js bindings
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.
Yes, O.Account_update.Stable
is the stable account update type, which is not equal to the wire types W.Account_update
. If I were to add a type Account_update.Wire.V1.t = (Body.Stable.V1.t, Control.Stable.V2.t) Account_update.Poly.Wire.V1.t
, then I'm pretty sure it would instead be true that
include Assert_equal0V1 (O.Account_update.Wire.Stable) (W.Account_update)
I'd have to add stable wire types to keep the other assertions.
This does make me worry a bit that the stable/wire split is going to mess up the o1js bindings - I'd imagine that the Account_update
type is actively used in those.
end] | ||
end | ||
|
||
[%%versioned_binable |
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.
why is it versioned_binable
? Seems like normal versioned to me.
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.
The correct binable
instance for this type is the one derived through the Poly.Wire
type, I think, so I don't think it should be regular versioned
. You can see in the Without_aux
type below that a correct binable
instance is derived with Wire
there. I will check again to see why I didn't derive it through Poly.Wire
here. I vaguely recall that it might have been to work around some quirks in the ppx-generated code? Either that or the binable instance is never used and the compiler didn't complain about the missing instance anywhere, so the omission was never caught.
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.
I checked - I can in fact use
include
Binable.Of_binable2_without_uuid
(Wire.Stable.V1)
(struct
type nonrec ('x, 'y) t = ('x, 'y, unit) t
let of_binable t = of_wire t
let to_binable = to_wire
end)
to give Poly.Stable.V1.t
a binable instance. The only issue is that the generated instance is not compatible with the ppx deriving mechanism for binable, so things defined in terms of Poly.Stable.V1.t
would still need to use an explicit Binable.Of_binable*
to get their own binable instance.
The reason why the generated instance is not compatible is because of some things I'm forced to do:
- I need to use
Of_binable2
forPoly.Stable.V1.t
, sincePoly.Wire.Stable.V1.t
has arity 2 ('x, 'y) Poly.Wire.Stable.V1.t
needs to have arity 2 because we want it to be definitionally equal toMina_wire_types.Mina_base.Account_update.Poly.V1.t
, which has arity 2('x, 'y, 'z) Poly.V1.t
needs to have arity 3 because it needs to have theaux
field present- The ppx
deriving binable
mechanism always assumes that an arity 3 type hasbin_shape
, etc., functions that take 3 functions, one for each type parameter. However, the functions generated by (1) only take two functions, so the code generated byderiving binable
for types defined in terms ofPoly.Stable.V1.t
is ill-formed as a result.
I also tried giving Mina_wire_types.Mina_base.Account_update.Poly.V1.t
a phantom 'aux
parameter so that Poly.Wire.Stable.V1.t
could have the same phantom parameter, making everything arity 3 and allowing me to use Of_binable3
for Poly.Stable.V1.t
. That still doesn't work, because Account_update.Poly.V1.of_wire
must have a type equivalent to
val of_wire : ('x, 'y, 'aux) Account_update.Poly.Wire.V1.t -> ('x, 'y, unit) Account_update.Poly.V1.t
since we have to materialize a concrete value for aux
out of thin air. This signature seems to be incompatible with what the Of_binable*
functors expect.
(** The cached hash of the actions in an account update body *) | ||
} | ||
|
||
let to_yojson : _ -> Yojson.Safe.t = Nothing.unreachable_code |
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.
Do we need these?
~use_full_commitment:true ~balance_change:(int_to_amount 1) | ||
~token_id:owned_token_id | ||
} | ||
@@ Account_update.with_aux |
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.
TODO: worth making a simple follow-up PR which would define a function in the file which would take parameters ~may_use_token
, ~(balance_change : int)
and ~token_id
.
Otherwise I see a lot of code duplication.
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.
I can do that in a follow-up PR, sure.
4d8b7c3
to
db9c6c2
Compare
!ci-build-me |
db9c6c2
to
faae69d
Compare
!ci-build-me |
I think I've gotten to all the review comments. Just to summarize the outstanding issues:
I also had to change this bit of code in response to a failed test: I need to provide the |
This field (in Account_update.Poly.t and Account_update.Fee_payer.t) is intended to contain auxiliary data related to the Account_update. For now, the only type this field is used with is the No_aux.t type, which indicates that no auxiliary data is present. In future, the non-stable types will cache the hash of the actions in their body in this field. Since the aux field is not intended for serialization, the yojson/sexp instances for Account_update.Poly.t have been modified to preserve the existing serialization structure. In particular, during serialization this field is ignored, and during deserialization this field cannot be present, and can only take on the type No_aux.t.
This Aux_data will hold the cached value of the hash of the actions in the account update body. For now, it only takes on the value Field.zero. Various functions have been adapted to improve their polymorphism, so they can work with both the stable and unstable versions of the account update types.
The actions_hash is now properly cached on Account_update creation
faae69d
to
3bf7044
Compare
!ci-build-me |
Had to fix the conflicts from #17299 |
Explain your changes:
A new
aux
field has been added to theAccount_update
types. For the stable versions of these types it holds nothing, but for the unstable versions of these types it is used to cache the hash of the actions of the body of the account update. This new field is not intended for serialization, so theyojson
andsexp
definitions inAccount_update
were modified to ensure that the serialization format for these types is unchanged.This PR is prompted by the observation in #16968 that the
--transfer-parties-get-actions-events
flag in themina ledger test apply
benchmark causes the final application time to increase by roughly 7 seconds. (Benchmarking was done on my laptop). This time would also increase by a further 7 seconds for every 100 actions added to the max actions in the genesis constants. By caching theactions_hash
onAccount_update
creation, this increase is eliminated, and the final application time is now constant with respect to the number of actions in an account update.Explain how you tested your changes:
I ran
mina ledger test apply
with and without the--transfer-parties-get-actions-events
flag, and with various edited values of the max actions in the genesis constants. In all cases the final application time in that benchmark was roughly 3 seconds:actions=events=0
:actions=events=1000
andmax_action_elements=max_event_elements=1000
:Increasing the actions and events still causes the
real
time of the benchmark to increase, but this is independent of the number of actions and events in the other parties in the ledger - it goes from about1m
with 100 max, to7m
with 1000 max.Checklist: