-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
prefixStore
does not handle iteration correctly for nil endpoints
#2243
Comments
Thanks for pointing out this.
If we use
The keys those start with We can either skip multiple elements until the element starts with the prefix, or modify I'll make a PR that addresses this issue for SDK but it looks good to check on |
@mossid looks like you are right about the The correct thing to do is to take In your example
|
@mossid please change the label if you believe this should be addressed before game of steaks. |
The
KVStore
interface defines iteration as over half-open interval of keys with inclusive start key and exclusive start key. Except when a nil end point is given which means iterate the entire range inclusively. When your domain is a prefix then these facts interact with each other to make reverse iteration a little more complex. In this case in order to capture all possible keys you need to pass the underlying iterator a range that will capture keys that do not start with the prefix.In Tendermint there is an implementation of
DB
,prefixDB
that gets this right, see: https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db.go#L129-L161Three parts of the
prefixStore
implementation are wrong currently.The
prefixIterator
Domain
function: https://github.com/cosmos/cosmos-sdk/blob/develop/store/prefixstore.go#L94-L99. You need to use the end points passed toprefixStore.Iterator
you cannot just strip the prefix from the underlyingIterator
's domain.The
ReverseIterator
function is unfortunately not symmetrical WRT theIterator
function - it needs to potential skip the first element and also to become invalid if it iterates past the end of the prefix. The current endpoint is also wrong being greater than prefix rather than less than.ReverseIterator
also has what looks like just a typo: https://github.com/cosmos/cosmos-sdk/blob/develop/store/prefixstore.go#L83 - I'm sure the author meant to prefixstart
To illustrate 2, suppose we have a prefix
1101
, then consider the descending sequence for a reverse iterator:So the endpoints we need to pass to underlying iterator's
ReverseIterator
function are1110
(inclusive) and1100
(exclusive) in order to be sure to catch everything in the range, but it means we may need to skip first key if1110
is stored and as soon as we iterate past1100
we need to invalidate the iterator.I was hoping I could simplify the logic of
prefixDB
but didn't find a way to do so meaningfully. UnfortunatelyprefixDB
does a defensive, but unnecessary copy on every key so I have implemented a version that doesn't: https://github.com/silasdavis/burrow/blob/state/storage/prefix_db.go#L67-L93. I have made some stylistic tweaks but it is otherwise the same. I also extracted the logic around the prefix into aPrefix
type: https://github.com/silasdavis/burrow/blob/state/storage/prefix.go.I feel like since there are shared semantics between
DB
(necessarily, really) andKVStore
there ought to be shared code - seeing as this stuff is a little bit finicky. I also have a PR into IAVL that introduces aKeyFormat
type: https://github.com/tendermint/iavl/pull/107/files. It seems to me that we could extract the logic for iterating, formatting, and scanning over keys with a possible prefix into a single type that could be shared amonst these implementations. ThoughtKeyFormat
andPrefix
might work better as separate concerns.The text was updated successfully, but these errors were encountered: