Skip to content
This repository has been archived by the owner on Feb 27, 2023. It is now read-only.

Implement compute-optimised Merkle tree #5

Merged
merged 84 commits into from
Aug 13, 2020
Merged

Conversation

musalbas
Copy link
Member

@musalbas musalbas commented Jul 3, 2020

smt.go Outdated Show resolved Hide resolved
treehasher.go Outdated Show resolved Hide resolved
@musalbas
Copy link
Member Author

musalbas commented Aug 4, 2020

TODO: investigate why this case is not being hit by test. https://coveralls.io/builds/32535000/source?filename=proofs.go#L80

Edit: done.

@liamsi
Copy link
Member

liamsi commented Aug 4, 2020

It's probably a good idea to run sth like golangci-lint run locally or in CI, too (will likely not help with the above problem though).

@musalbas
Copy link
Member Author

Looks like the DeepSparseMerkleSubTree.AddBranches needs to be reimplemented and cannot simply call the updateWithSideNodes function in an optimised tree, as this does more than simply add branches from the proof.

@musalbas
Copy link
Member Author

I think this is ready to be merged.

@musalbas musalbas marked this pull request as ready for review August 12, 2020 10:34
@musalbas
Copy link
Member Author

Speedtest to update 10,000 keys in a fresh tree:
Before optimisations: 0m3.355s
After optimisations: 0m0.327s

This confers to up to a ~10x speed-up.

Copy link
Member

@liamsi liamsi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some minor comments. This looks like excellent work 👍

README.md Show resolved Hide resolved
proofs.go Outdated
Comment on lines 19 to 26
// BitMask, in the case of a compact proof, is a bit mask of the sidenodes
// of the proof where an on-bit indicates that the sidenode at the bit's
// index is a placeholder. This is only set if the proof is compact.
BitMask []byte

// NumSideNodes, in the case of a compact proof, indicates the number of
// sidenodes in the proof when decompacted. This is only set if the proof is compact.
NumSideNodes int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully understand how that would make the API fiddly and complicated (imho it could make it clearer). Let's not bother with this now. Happy to look into it after this gets merged.

mapstore_test.go Show resolved Hide resolved
@musalbas
Copy link
Member Author

Ok, I'm going to split compact proofs to a different type.

@musalbas
Copy link
Member Author

Done

Comment on lines +14 to +18
type keyAlreadyEmptyError struct{}

func (e *keyAlreadyEmptyError) Error() string {
return "key already empty"
}
Copy link
Member

@liamsi liamsi Aug 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this error useful if not public? If it is public users can switch over it and handle it accordingly.
Ah OK, I see: this is only used internally (and switched over).

This can be simplified to var keyAlreadyEmptyError = errors.New("key already empty") , then you can do
if err == keyAlreadyEmptyError instead of if _, ok := err.(*keyAlreadyEmptyError); ok {.

Or, use the errors.Is function. See "Errors in Go 1.13" in https://blog.golang.org/go1.13-errors

Also, name should be errKeyAlreadyEmpty.

Comment on lines +90 to +92
value, err := smt.ms.Get(valueHash)
if err != nil {
return nil, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Gets won't err in tests because the in-memory store never errs. In the future we might want to test these too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but if someone changes the in-memory store implementation in the future then this ensures that any errors returned are caught in tests.

smt.go Outdated
Comment on lines 364 to 366
if err != nil {
return SparseMerkleProof{}, err
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this err checked here? That is confusing, as it would occur several lines up in:
sideNodes, leafHash, leafData, err := smt.sideNodesForRoot(path, root)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why I did that. Fixed.

Comment on lines +7 to +12
// BadProofError is returned when an invalid Merkle proof is supplied.
type BadProofError struct{}

func (e *BadProofError) Error() string {
return "bad proof"
}
Copy link
Member

@liamsi liamsi Aug 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the other place, this is equivalent to a sentinel error:

Suggested change
// BadProofError is returned when an invalid Merkle proof is supplied.
type BadProofError struct{}
func (e *BadProofError) Error() string {
return "bad proof"
}
// ErrBadProof is returned when an invalid Merkle proof is supplied.
var ErrBadProof = errors.New("bad proof")

Also, it is more common / go-idiomatic to have a Err prefix.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it better to have a special type for the error, so that users of the API can differentiate between error types?

Copy link
Member

@liamsi liamsi Aug 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In go it is very common to use sentinel values for cases like this (this custom error is just returns a string with no further fields or context). See for instance the io package: https://golang.org/pkg/io/#pkg-variables

The custom error doesn't provide any additional value compared to a sentinel error (not to say that in other cases it could make more sense to use a custom error).

Also, worth looking at: https://blog.golang.org/go1.13-errors

@musalbas musalbas merged commit 3a1770d into master Aug 13, 2020
@adlerjohn adlerjohn deleted the optimised_compute branch March 11, 2021 22:30
musalbas pushed a commit that referenced this pull request Sep 16, 2022
Lazy tree implementation
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants