-
Notifications
You must be signed in to change notification settings - Fork 14
feat(ffi): add proposal root retrieval #910
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
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.
I would have thought that you would just change the code so that propose always returns the hash along with the proposal ID, rather than having to look things up again and dealing with errors.
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 would have thought that you would just change the code so that propose always returns the hash along with the proposal ID, rather than having to look things up again and dealing with errors.
// Get the root hash of the new proposal. | ||
let mut root_hash: Value = match new_proposal.root_hash_sync().map_err(|e| e.to_string())? { | ||
Some(root) => Value::from(root.as_slice()), | ||
None => String::new().into(), |
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.
@rkuris This shouldn't occur in the ethhash case, but it does. Honestly I'd prefer that db.root_hash_sync()
also returns nil in the no hash, but definitely out of scope. However, this must return a root for no data (the failing ethhash test)
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.
Should be addressed in #918
@@ -204,13 +204,19 @@ func kvForTest(i int) KeyValue { | |||
// 1. By calling [Database.Propose] and then [Proposal.Commit]. | |||
// 2. By calling [Database.Update] directly - no proposal storage is needed. | |||
func TestInsert100(t *testing.T) { | |||
type dbView interface { | |||
Get(key []byte) ([]byte, error) |
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.
@aaronbuchwald I think this conflicts with something you're doing, so I can get rid of this or edit if you like
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.
No need to worry about a conflict here, will be easy to fix when we get to it
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.
Rust code is fine, recommended some rewording of comments.
Would be good to get Aaron to look at the go code better than me.
ffi/firewood.h
Outdated
* A `Value` containing {id, root} if creating the proposal succeeded. | ||
* The root will always be 32 bytes, and the id will be non-zero. |
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 had to read this a few times; the second sentence is only true if the proposal succeeds.
* A `Value` containing {id, root} if creating the proposal succeeded. | |
* The root will always be 32 bytes, and the id will be non-zero. | |
* On success, a `Value` containing {len=id, data=hash}. In this case, the | |
* hash will always be 32 bytes, and the id will be non-zero. |
ffi/firewood.h
Outdated
@@ -318,7 +319,8 @@ struct Value fwd_propose_on_db(const struct DatabaseHandle *db, | |||
* | |||
* # Returns | |||
* | |||
* A `Value` containing {id, nil} if creating the proposal succeeded. | |||
* A `Value` containing {id, root} if creating the proposal succeeded. |
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.
same as above
// If the hash is empty, return the empty root hash. | ||
if p.root == nil { | ||
return make([]byte, RootLength), nil | ||
} |
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.
What's the rationale to do this lazily rather than ensure it's populated with the empty root hash if needed?
This may save a memory allocation, but seems that it will likely be simpler if we just guarantee proposals are always fully populated.
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.
There is an issue #918 for this in the ethhash front, but it might be better to enforce this on "normal" firewood as well.
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.
Hmm I think #918 is a separate issue.
We have two choices - what to do when there's an empty root in firewood/ethhash mode and separately how to populate the golang proposal struct. My point only applies to the latter ie. if a nil root indicates that it must actually be represented by this zeroed out byte slice, then should we just populate it when creating the proposal instance?
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.
Ahhh ok that makes sense. I'll address that.
// TODO: Check proposal root | ||
// Requires #918 | ||
// hash, err := proposal.Root() | ||
// require.NoError(t, err, "%T.Root() after commit", proposal) | ||
// require.Equalf(t, expectedHash, hash, "%T.Root() of empty trie", db) |
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.
Do we need to include this TODO here? If this requires 918, would it make more sense to include the added check in 918?
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.
#918 is an issue, but the code is trivial so we could remove that and fill it in when addressing the issue.
2a69cc4
to
531b32a
Compare
ffi/src/lib.rs
Outdated
/// This is used in several different ways, and should be interpreted based on the | ||
/// function that returned it. |
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.
Is this the best way to handle this comment? Maybe it just depends on the context and it is, but not sure as I'm reading it
// Pin the returned value to prevent it from being garbage collected. | ||
defer runtime.KeepAlive(v) | ||
|
||
if v == nil { | ||
return errNilBuffer | ||
return nil, 0, errNilBuffer |
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.
is this case 1 as well?
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 should never happen, since the FFI layer is always returned an instance of the C Value, not a pointer to it. However, I thought it would be best to keep this case to avoid panics, but I can add it to the comment
// case | data | len | meaning | ||
// | ||
// 1. | nil | 0 | invalid | ||
// 2. | nil | non-0 | proposal deleted everything |
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.
to make sure I understand - the proposal ID is non-zero because it's a real proposal, but the root is empty because the trie ends up being empty as well?
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.
Yes. The proposal ID is created from a counter initialized to 1, so we are guaranteed that it's nonzero.
// If the hash is empty, return the empty root hash. | ||
if p.root == nil { | ||
return make([]byte, RootLength), nil | ||
} |
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.
Hmm I think #918 is a separate issue.
We have two choices - what to do when there's an empty root in firewood/ethhash mode and separately how to populate the golang proposal struct. My point only applies to the latter ie. if a nil root indicates that it must actually be represented by this zeroed out byte slice, then should we just populate it when creating the proposal instance?
This is needed to enable support of Trie wrapping in geth.