You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We use SnapshotMap a lot over at DAO DAO, and there's one aspect of it that has always bothered me. I think it leads to pretty confusing behavior and is likely to cause bugs, potentially security issues, if not deeply understood by the developers using it.
In my understanding, when a new value V_new is saved at height n, a snapshot is created at height n that contains the current value V_old in a field called old, and the value in the primary map is overridden with V_new. load and may_load read directly from the primary map, finding V_new, behaving as expected. But may_load_at_height actually returns V_old when called with the height at which V_new was saved.
Thus, may_load differs from may_load_at_height if used in the same block right after a value is stored. IMO, one would assume that calling save at the current height followed by may_load_at_height at the current height would retrieve the value just saved, but that is not the case.
The only justification I could see for this behavior is that SnapshotMap intends to be deterministic for the whole block, always returning the value that existed at the very beginning of the block. Thus, if another contract queries a contract that uses a SnapshotMap during a block where the map is being updated, it will always get the same value, regardless of the ordering of transactions in the block. Though I'm not totally convinced by this argument as one already cannot expect deterministic ordering when interacting with other contracts, and other state storage exists besides SnapshotMap. And I'm not sure if this benefit is worth the tradeoff of less intuitive security critical code.
Is there another reason it was designed this way? Please convince me this is necessary 😁
There are a few ways I think this could be fixed pretty easily, though I don't have full knowledge of this monorepo, and maybe these would break other things. Let me know what you think.
use height + 1 when calling self.snapshots.may_load_at_height, since it will try to find the next change after the requested height, whose old value would be the correct value for height.
We use
SnapshotMap
a lot over at DAO DAO, and there's one aspect of it that has always bothered me. I think it leads to pretty confusing behavior and is likely to cause bugs, potentially security issues, if not deeply understood by the developers using it.In my understanding, when a new value
V_new
is saved at heightn
, a snapshot is created at heightn
that contains the current valueV_old
in a field calledold
, and the value in theprimary
map is overridden withV_new
.load
andmay_load
read directly from theprimary
map, findingV_new
, behaving as expected. Butmay_load_at_height
actually returnsV_old
when called with the height at whichV_new
was saved.Thus,
may_load
differs frommay_load_at_height
if used in the same block right after a value is stored. IMO, one would assume that callingsave
at the current height followed bymay_load_at_height
at the current height would retrieve the value just saved, but that is not the case.The only justification I could see for this behavior is that
SnapshotMap
intends to be deterministic for the whole block, always returning the value that existed at the very beginning of the block. Thus, if another contract queries a contract that uses aSnapshotMap
during a block where the map is being updated, it will always get the same value, regardless of the ordering of transactions in the block. Though I'm not totally convinced by this argument as one already cannot expect deterministic ordering when interacting with other contracts, and other state storage exists besidesSnapshotMap
. And I'm not sure if this benefit is worth the tradeoff of less intuitive security critical code.Is there another reason it was designed this way? Please convince me this is necessary 😁
There are a few ways I think this could be fixed pretty easily, though I don't have full knowledge of this monorepo, and maybe these would break other things. Let me know what you think.
write_change
cw-storage-plus/src/snapshot/map.rs
Lines 117 to 126 in cac9687
height - 1
instead ofheight
, since that is the last block at which theold
value is accurate.may_load_at_height
cw-storage-plus/src/snapshot/map.rs
Lines 154 to 170 in cac9687
height + 1
when callingself.snapshots.may_load_at_height
, since it will try to find the next change after the requested height, whoseold
value would be the correct value forheight
.Snapshot
cw-storage-plus/src/snapshot/mod.rs
Lines 151 to 180 in cac9687
inclusive
toexclusive
when looking in the changelog for theold
value.As far as I can tell, 2 and 3 above are identical, except 3 may fix the problem in other places if
Snapshot
is used the same way.The text was updated successfully, but these errors were encountered: