Summary
The semantics of a "write once memory" are exactly what it says on the tin. You cannot write more than once to the same address in a WOM. Attempting to do this should result in a machine panic. But, currently, the ZkC interpreter does not prevent this and allows invalid executions to continue.
Example
The following ZkC program is invalid and should result in a machine panic:
output out(address:u16) -> (byte:u8)
fn main(){
out[0] = 1
out[0] = 2 // <-- INVALID
}
With the following json input file:
Then, currently, running this program gives the following:
> zkc exec test.json test.zkc
out = 0x02
Clearly, this violates the intended semantics of a "write once memory".
Approach
Let's consider two example programs:
Program A:
output out(address:u16) -> (byte:u8)
fn main(){
out[0] = 1
out[1] = 2
}
Program B:
output out(address:u16) -> (byte:u8)
fn main(){
out[1] = 2
out[0] = 1
The implementation for write-only memory lives in pkg/zkc/vm/internal/memory/wom.go. There are two different ways this implementation could be updated to enforce a write-once semantics:
- (strict) This approach requires every write to an output memory is for the next consecutive address. This would mean that "Program A" above is valid, but "Program B" is not.
- (non-strict) This approach is more relaxed and simply requires that writes to the same address with different values result are not permitted.
The difference between these two approaches comes down to the complexity of enforcing their semantics. Basically, option (1) is easier to implement than option (2).
Implementation for Option (1)
In this option, its pretty straightforward. Within our implementation of memory.WriteOnce we simply check that the write address matches the current size of the data array and, if not, cause a machine panic.
Implementation for Option (2)
This is harder to implement, but of course gives rise to more flexible implementations for constructing output. Its not clear whether this flexibility is needed or not. The rough idea would be this: alongside the existing data array we additionally maintain an written []bool array of the same size. This identifies addresses which have already been written. Thus, when a write occurs at address, we check whether written[address]==true or not. If it does, we issue a machine panic.
Summary
The semantics of a "write once memory" are exactly what it says on the tin. You cannot write more than once to the same address in a WOM. Attempting to do this should result in a machine panic. But, currently, the ZkC interpreter does not prevent this and allows invalid executions to continue.
Example
The following ZkC program is invalid and should result in a machine panic:
With the following
jsoninput file:Then, currently, running this program gives the following:
Clearly, this violates the intended semantics of a "write once memory".
Approach
Let's consider two example programs:
Program A:
Program B:
The implementation for write-only memory lives in
pkg/zkc/vm/internal/memory/wom.go. There are two different ways this implementation could be updated to enforce a write-once semantics:The difference between these two approaches comes down to the complexity of enforcing their semantics. Basically, option (1) is easier to implement than option (2).
Implementation for Option (1)
In this option, its pretty straightforward. Within our implementation of
memory.WriteOncewe simply check that the write address matches the current size of thedataarray and, if not, cause a machine panic.Implementation for Option (2)
This is harder to implement, but of course gives rise to more flexible implementations for constructing output. Its not clear whether this flexibility is needed or not. The rough idea would be this: alongside the existing
dataarray we additionally maintain anwritten []boolarray of the same size. This identifies addresses which have already been written. Thus, when a write occurs ataddress, we check whetherwritten[address]==trueor not. If it does, we issue a machine panic.