-
Notifications
You must be signed in to change notification settings - Fork 38
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
refactoring the ics23 spec #975
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First set of remarks. I've only made it through hashes.qnt
so far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow 🤯
Second set of comments below:
examples/cosmos/ics23/ics23.qnt
Outdated
// ICS23 IavlSpec has: | ||
// MinPrefixLength = 4 | ||
// MaxPrefixLength = 12 | ||
// ChildSize = 33 | ||
// MinPrefixLength = 1 | ||
// MaxPrefixLength = 1 | ||
// ChildSize = 32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks to be the tendermint spec, but the comment says IavlSpec
.
Can we confirm which one it is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, I always thought it Iavl, but seems to be Tendermint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems to be Tendermint
Can we ask someone to confirm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Co-authored-by: Thomas Pani <[email protected]>
I thought this was good to merge, but something in 60a2df4 broke the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks super useful for testing. I guess now with the sum types, we could turn this spec more readable. I will reach out to the team to see how we can coordinate around merging it.
examples/cosmos/ics23/hashes.qnt
Outdated
/// A word is a map from a path to the term node. | ||
/// The first root's child is [ 0 ], the second root's child is [ 1 ], | ||
/// the first child of [ 0 ] is [ 0, 0], etc. | ||
type Term_t = Bytes_t -> TERM_NODE_T |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a binary tree, This would also be encoded today as sum type, e.g., a list of LeftNode
, RightNode
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have to note that Quint sum types are not inductive/recursive, for a reason of not admitting unbounded data structures. So it would not be possible to encode a tree with Quint sum types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But now it is a list of integers, where only 0 and 1 is used. Why cannot it be a list of a sum type instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try to write it in any language that has complete support for sum types, e.g., in OCaml. You will see that you need recursion. Basically, you have to say that a node refers to the children nodes (of the same type). If you use a mathematical function instead, e.g.,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A list of a sum type would not give you much, as you would not want a leaf to appear in the middle of the list.
// the roots' children | ||
pure val top = | ||
keys(term).filter(p => length(p) == 1).map(p => term.get(p)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Root's children is checked with length(p) == 1
. If this would be a defined predicate, perhaps it would be more readable.
} else { | ||
// Merge the arguments as trees representing terms. | ||
// The number of root's children in the left term: | ||
pure val lwidth = size(keys(left).filter(p => length(p) == 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure I understand what is going on here. Does this only work if left and right don't contain an entry for [1]
? Otherwise the expression lwidth + p[0]
below would suddenly introduce keys whose value is greater than 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I remember, every node is encoded as the path to this node. For instance, [0, 1, 0, 1]
in a binary tree would mean that you first go left, then right, then left, then right. So if we count the number of paths of length 1, then we get the number of root's children.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, but if we have 2 root's children, we get lwidth = 2
, but then two lines below we have
keys(right).map(p => [ lwidth + p[0] ].concat(p.slice(1, length(p))))
So it seems that I could get a 2 and a 3 as first list entry, and this is what I don't understand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That definition merges two trees. Say, if you have root's children [0, 1]
in tree 1 and root's children [0, 1]
in tree 2, then you would like to have root's children [0, 1, 2, 3]
. This is what addition does for you: It simply renames the immediate root's children in tree 2, for all paths in that tree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. I just thought we would stay with binary trees...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clearing my name from the review requests here.
Hope all is well! <3
@josef-widder I realize now that the reason why we can't merge this is that a test is failing.
I've inspected the commits since the last time it was passing, and I suspect that this is the problematic bit: 2335cf9. Maybe this test needed to be updated according to the new representation including the length marker byte, but wasn't - I'm not sure, as I don't really understand what is going on here. Do you have a sense of the problem? |
Notes, mostly for my future self: My fix from 1115a51 was enough to make the existing invariants hold on simulations, but even after playing with the I noticed that the invariants had a lot of non-determinism, so I wrote a stronger invariant that checks Upon debugging, I found that |
This is a refactoring of the ICS23 spec, which took more effort than I expected (about 8hrs in total):
hashes.qnt
treeGen.qnt
.Reviewing this specification is probably a big effort. So I would appreciate at least a quick glance, to check that nothing weird stands out.