vyper icon indicating copy to clipboard operation
vyper copied to clipboard

VIP: witness storage

Open charles-cooper opened this issue 3 years ago • 6 comments

Simple Summary

Provide a witness() keyword which only stores the hash of a complex type. To "read" the struct from storage, it must be provided as a calldata "proof" and the hash of the proof must match the stored value.

Motivation

A common pattern is to hash a struct the first time it's seen, then after that, require it to be provided in calldata, and check that its hash matches the stored value. This keyword would abstract the pattern. This is an alternative optimization to struct packing and would generally perform better when "most" of the struct is accessed, but may perform less well when no-more-than-one-word of the packed struct is accessed.

Specification

specification by example:

my_struct: witness(MyStruct)  # stored as bytes32 value
some_other_struct: witness(MyStruct)

def foo(proof: self.my_struct.Proof):
    # compiler inserts test that keccak256(proof) == self.my_struct
    foo: MyStruct = proof  # proof has type MyStruct

    baz: MyStruct = self.some_other_struct  # banned - the proof is not in the calldata

    foo.bar = ...  # modify foo
    self.my_struct = foo  # generated code: self.my_struct = keccak256(foo)

Backwards Compatibility

New feature, backwards compatible

Copyright

Copyright and related rights waived via CC0

charles-cooper avatar Jun 05 '22 03:06 charles-cooper

This proposal is pretty unclear in it's current format. Is it a set of MyStruct objects stored? Just one? What is the size of the type stored in storage? how does foo: MyStruct = proof: witness[MyStruct] work? Can I configure the hash function used? Can I read values from self.my_struct without a proof?

fubuloubu avatar Jun 05 '22 03:06 fubuloubu

Is it a set of MyStruct objects stored? Just one?

in this example - just one. witness() is a keyword used like immutable() or public() - so, ex. an array of MyStruct would be witness(MyStruct[10])

What is the size of the type stored in storage?

32 bytes - the output of the keccak256 hash function

how does foo: MyStruct = proof: witness[MyStruct] work?

proof has physical data in it. so foo: MyStruct = proof would just be a bytes copy.

Can I configure the hash function used?

no

Can I read values from self.my_struct without a proof?

no, will clarify

charles-cooper avatar Jun 05 '22 03:06 charles-cooper

i guess one open question is, do we assign from the proof or the witness?

# only allowed if a proof is allowed in calldata; bytes are copied from calldata
foo: MyStruct = self.my_struct

vs

foo: MyStruct = proof

only one should be allowed.

charles-cooper avatar Jun 05 '22 04:06 charles-cooper

i guess one open question is, do we assign from the proof or the witness?

# only allowed if a proof is allowed in calldata; bytes are copied from calldata
foo: MyStruct = self.my_struct

vs

foo: MyStruct = proof

only one should be allowed.

Tbh, both of these are pretty confusing. Should probably be something more like:

foo: MyStruct = self.my_struct.validate(proof)

fubuloubu avatar Jun 05 '22 04:06 fubuloubu

Tbh, both of these are pretty confusing. Should probably be something more like:

foo: MyStruct = self.my_struct.validate(proof)

Whoa, that's pretty slick

charles-cooper avatar Jun 05 '22 04:06 charles-cooper

meeting notes: should be generic type, punt until module system is more fleshed out

charles-cooper avatar Jun 06 '22 16:06 charles-cooper