AztecProtocol / aztec-packages

Apache License 2.0
157 stars 160 forks source link

feat(StateVariables): Extend SharedMutable to work with multi-field values #5491

Open nventuro opened 3 months ago

nventuro commented 3 months ago

https://github.com/AztecProtocol/aztec-packages/pull/5490 introduced a version of SharedMutable<T> where T is required to implement FromField, meaning it must fit inside a Field wrapper value. While this is fine for values such as AztecAddress, we'll eventually want to store multi-field structs, and so this constraint will need to be relaxed.

We may implement this by relying solely on Serialize and Deserialize, but are likely going to run into trouble until https://github.com/noir-lang/noir/issues/4633 is fixed, since creating an array out of multiple single-field elements is the core issue there.

nventuro commented 2 months ago

This will likely result in ScheduledValueChange requiring that T implements the serde traits (instead of to/from field).

LHerskind commented 1 week ago

Napkin math of saving on the Token::transfer function with this.

We need to lookup 2 users keys.

One lookup should cost ~17K Currently a lookup of a key is 27K, but we are really doing two lookups of 10k + 6.5K for the case where it is in the address).

The cost should therefore be ~34K.

What is our current cost:

Meaning that we have 5 lookups of 27K -> ~135K spent on key rotation.

We should save 101K constraints by having this in.

@iAmMichaelConnor, not sure where you would ideally have some of these numbers 🤷

iAmMichaelConnor commented 1 week ago

Can a sharedMutable be hashed into a single field?

I.e. store the following in a single storage slot:

(I forget the naming and mechanics of how sharedMutable has been implemented, but hopefully you get the gist).

If that's all stored in a single slot, for a particular user address, then to read that slot from the archive tree, it's ~80 constraints for poseidon2 of 2 fields * 40 height = 3200 constraints. To unpack all that data listed above, something like a poseidon2 of 10 fields - maybe ~400 constraints. To unpack the signs, a few constraints. To recompute the y-coords: 4 or 5 constraints per point. Although there might need to be a range check, to check the 'negative-ness', in which case all this messing around with signs might be unnecessary and similar in cost to poseidon-hashing the y-coords directly into the slot's hash value. Meh, for an estimate, let's say the coordinates aren't compressed, then instead of ~400 constraints, it's more like a poseidon2 of 16 fields - maybe ~640 constraints. So 3200 + 640 = 4000 constraints to read the keys from the registry for 1 address. 8000 for two addresses. And if we're in the case where the address isn't-yet registered in the registry, we should be re-purposing the poseidon hashes that were used for unpacking above. I.e. we read the registry, find that the value is 0 (or maybe some known hash of 0's if we're reading a hash of some empty shared mutable slot), and then pass the address's preimage data to the existing poseidon hashes. So this case should add approx 0 extra gates.

So I reckon 8000 to read all keys for sender & recipient.

LHerskind commented 1 week ago

Can a sharedMutable be hashed into a single field?

Yes, that is done as part of #7169. So we have the logic. But you still need the values to be there such that you can actually read them and open (using oracles). This issue is just to fix the latter, but is blocked by noir atm.