Skip to content
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

Optimize MerkleTree.contains from O(n) to O(1) #775

Merged
merged 8 commits into from Dec 22, 2021

Conversation

leanthebean
Copy link
Contributor

Summary

In the merkle tree file, we have a contained method. Currently, there's a linear scan that goes through all of the existing nullifiers when determining if a transaction is valid (e.g. not a double spend). As we get more transactions, this linear scan becomes more and more inefficient (e.g. beefy later blocks would take several seconds).

This change adds another db store that lets you check in O(1) if an element exists in a merkle tree.

Testing Plan

Tested it locally and avg block time is about 20ms per transaction per block

Breaking Change

This requires a db upgrade.

  • Yes
  • No

@leanthebean leanthebean requested a review from a team as a code owner December 17, 2021 04:53
@leanthebean leanthebean changed the base branch from master to staging December 17, 2021 04:54
Copy link
Member

@dguenther dguenther left a comment

Choose a reason for hiding this comment

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

From a code review standpoint, looks good -- I think @NullSoldier wants to add a perf test to this before it merges, and it'd be nice to run it on a laptop node for a bit to see if any issues happen to pop up.

@@ -167,10 +167,8 @@ export class Verifier {
tx?: IDatabaseTransaction,
): Promise<VerificationResult> {
return this.chain.db.withTransaction(tx, async (tx) => {
const noteSize = await this.chain.notes.size(tx)
Copy link
Member

Choose a reason for hiding this comment

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

Ah, looks like this might have been a bug, good spot! If possible it might be nice to pull this out into a separate PR so any issues with it aren't conflated with the LeavesIndex cache.

Copy link
Contributor

Choose a reason for hiding this comment

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

Moved that fix here, #798

@dguenther
Copy link
Member

It looks like there are some test failures in here as well

@dguenther dguenther force-pushed the staging branch 4 times, most recently from a342cae to 8474295 Compare December 18, 2021 04:19
@NullSoldier NullSoldier changed the title Merkle tree contains Optimize MerkleTree.contains to not linear scan Dec 21, 2021
@NullSoldier NullSoldier changed the title Optimize MerkleTree.contains to not linear scan Optimize MerkleTree.contains from O(n) to O(1) Dec 21, 2021
@NullSoldier
Copy link
Contributor

@@ -296,16 +294,15 @@ export class Verifier {
async hasValidSpends(block: Block, tx?: IDatabaseTransaction): Promise<VerificationResult> {
Copy link
Contributor

Choose a reason for hiding this comment

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

The removed code that was buggy here was intending to check for the note being spent really existed in the tree at the time it was spent but was not. Should this still check for that some how? The spend size is the size of the nullifier tree when the commitment was created, not the note tree right? @dguenther

leanthebean and others added 4 commits December 21, 2021 22:51
had a horribly inefficient linear scan which brought up
avg time to add a block with multiple transactions
to multiple seconds making syncing painfully slow.
This change introduces another db store to keep track
of merkle tree elements for O(1) retrieval if the element
is in the tree or not.
@NullSoldier NullSoldier marked this pull request as draft December 22, 2021 07:21
@NullSoldier
Copy link
Contributor

We still need to clean up the index in the MerkleTree.truncate function.

@@ -327,22 +331,21 @@ export class Verifier {
* spend's spend root.
*
* @param spend the spend to be verified
* @param size the size of the nullifiers tree at which the spend must not exist
Copy link
Member

Choose a reason for hiding this comment

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

This should probably be added back, or we should kill the whole comment so it's at least somewhat consistent

Copy link
Contributor

Choose a reason for hiding this comment

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

Fixed

@NullSoldier NullSoldier marked this pull request as ready for review December 22, 2021 21:28
@NullSoldier NullSoldier merged commit 427bfe8 into staging Dec 22, 2021
@NullSoldier NullSoldier deleted the merkle_tree_contains branch December 22, 2021 21:29
NullSoldier added a commit that referenced this pull request Dec 22, 2021
* In merkletree's contained & contains method we
had a horribly inefficient linear scan which brought up
avg time to add a block with multiple transactions
to multiple seconds making syncing painfully slow.
This change introduces another db store to keep track
of merkle tree elements for O(1) retrieval if the element
is in the tree or not.

* removing pastSize from the contained method

* Use existing merkle hash

* Fix dead lock

* Minor cleanup inside of verifier (#811)

* Rename hasValidSpends to verifyConnectedSpends (#809)

* Only verify spend root in hasValidSpends (#810)

* Truncate index during MerkleTree truncate (#820)

Co-authored-by: NullSoldier <nullprogrammer@gmail.com>
dguenther added a commit that referenced this pull request Jan 4, 2022
* Use nullifier tree size to verify spend (#798)

We were using the notes tree size to verifier the spend, which is not
correct but shouldn't generally cause an issue because the note
tree is bigger than the nullifier tree.

* Fix use of clearLine in miners:mined (#816)

* chore: add graffiti logging on start (#818)

* Set default expiration delta to 15 from 450 (#822)

* Use @napi-rs/blake-hash instead of blake3 (#815)

The blake3 package doesn't seem to ship a native addon that
works properly in node 16, so let's try blake-hash instead.

* Optimize MerkleTree.contains from O(n) to O(1) (#775)

* In merkletree's contained & contains method we
had a horribly inefficient linear scan which brought up
avg time to add a block with multiple transactions
to multiple seconds making syncing painfully slow.
This change introduces another db store to keep track
of merkle tree elements for O(1) retrieval if the element
is in the tree or not.

* removing pastSize from the contained method

* Use existing merkle hash

* Fix dead lock

* Minor cleanup inside of verifier (#811)

* Rename hasValidSpends to verifyConnectedSpends (#809)

* Only verify spend root in hasValidSpends (#810)

* Truncate index during MerkleTree truncate (#820)

Co-authored-by: NullSoldier <nullprogrammer@gmail.com>

* Bump version to 0.1.15

* Update docker-compose configurations

* Fix path and change services name

* Fix readme

* Add volume to miner service

* Proofreading Docker.md

Co-authored-by: Jason Spafford <nullprogrammer@gmail.com>
Co-authored-by: Derek Guenther <derek@ironfish.network>
Co-authored-by: jintao <cdv1013@gmail.com>
Co-authored-by: Elena Nadolinski <elena@ironfish.network>
Co-authored-by: Rohan Jadvani <5459049+rohanjadvani@users.noreply.github.com>
Co-authored-by: Derek Guenther <dguenther9@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants