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

Implement Bitcoin Merkle trees #1386

Merged
merged 8 commits into from
Dec 1, 2020
Merged

Implement Bitcoin Merkle trees #1386

merged 8 commits into from
Dec 1, 2020

Conversation

hdevalence
Copy link
Contributor

@hdevalence hdevalence commented Nov 25, 2020

Motivation

Close #906, #1385.

Solution

As a side effect of computing Merkle roots, we build a list of
transaction hashes. Instead of discarding these, add them to
PreparedBlock and FinalizedBlock so that they can be reused rather than
recomputed.

This PR adds Merkle root validation to:

  1. the block verifier;
  2. the checkpoint verifier.

In the first case, Bitcoin Merkle tree malleability has no effect,
because only a single Merkle tree in each malleablity set is valid (the
others have duplicate transactions).

In the second case, we need to check that the Merkle tree does not contain any
duplicate transactions.

Closes #1385
Closes #906

The code in this pull request has:

Review

This should be done before the first alpha, but does not block anything.

Follow Up Work

We should have tests that exercise rejection behavior for both verifiers, but for the checkpoint verifier in particular. These are not done yet, and #1392 tracks them.

Copy link
Contributor

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

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

This looks good.

I'd be happy to merge this PR with the block test vector tests, and open a follow-up ticket for more testing.

I have a few suggestions for improvement:

  • test mainnet and testnet block test vectors
  • do the merkle root check immediately after the block difficulty check

And a question:

Would a transaction uniqueness check in the block verifier improve security until the full implementation is in place?

zebra-chain/src/block/merkle.rs Outdated Show resolved Hide resolved
zebra-consensus/src/block.rs Show resolved Hide resolved
zebra-consensus/src/block.rs Show resolved Hide resolved
@hdevalence hdevalence dismissed teor2345’s stale review November 25, 2020 18:48

included changes in fixup commits

@hdevalence
Copy link
Contributor Author

Updated the PR to test on all block test vectors, not just mainnet, and to include a comment explaining the HashSet deduplication.

@hdevalence
Copy link
Contributor Author

Updated issue description to reference #1392.

@hdevalence hdevalence marked this pull request as draft November 25, 2020 20:01
@hdevalence hdevalence marked this pull request as ready for review November 25, 2020 20:19
@hdevalence
Copy link
Contributor Author

Moved the checkpointer's Merkle checks into its existing check_block function and added duplicate transaction checks to the block verifier per @teor2345's request.

@hdevalence
Copy link
Contributor Author

Looks like the Merkle root computation is incorrect on blocks with a single transaction.

@dconnolly
Copy link
Contributor

image

@teor2345

This comment has been minimized.

@teor2345

This comment has been minimized.

@teor2345
Copy link
Contributor

Ok, right, that was all pretty obvious stuff, I won't clutter the ticket with it.

@teor2345
Copy link
Contributor

I pushed a commit that shows the transactions, block height, and block hash when the merkle root test fails.

hdevalence and others added 6 commits November 30, 2020 15:18
As a side effect of computing Merkle roots, we build a list of
transaction hashes.  Instead of discarding these, add them to
PreparedBlock and FinalizedBlock so that they can be reused rather than
recomputed.

This commit adds Merkle root validation to:

1. the block verifier;
2. the checkpoint verifier.

In the first case, Bitcoin Merkle tree malleability has no effect,
because only a single Merkle tree in each malleablity set is valid (the
others have duplicate transactions).

In the second case, we need to check that the Merkle tree does not contain any
duplicate transactions.

Closes #1385
Closes #906
This moves it in with the existing `check_block` method and expands that
method's contract to cover general block validation checks.
Change the Merkle root validation logic to also check that a block does not
contain duplicate transactions.  This check is redundant with later
double-spend checks, but is a useful defense-in-depth.
Also show the block height and block hash.
The `CoinbaseData` parses the block height separately from the rest of the
free-form coinbase data.  However, it had two bugs:

1. It did not require that the height was canonically encoded;
2. Its canonical encoding was incorrect relative to the BIP34-inherited encoding.

This meant that we computed some transaction hashes incorrectly, because when
we re-serialized the coinbase transaction, we would canonically serialize the
coinbase transaction (using the incorrect definition of canonical, bug 2).  And
we didn't notice that the wrong definition of canonical encoding was being used
because we accepted what we thought were non-canonically encoded heights.

The relevant rules are here: https://github.com/zcash/zcash/blob/877212414ad40e37cf8e49884a90767c54d59ba2/src/script/script.h#L307-L346

This commit changes the encoding to reject non-canonically encoded heights, and
to match the correct encoding rules.  We check that at least one
non-canonically encoded height is correctly rejected using a new test vector.

The database format increments because we saved a bunch of wrongly encoded blocks.

This discrepancy was originally noticed by @teor2345, who pointed out that a
previous version of the block 202 test vector (now preserved as "bad block
202") did not match the block from zcashd.
@hdevalence
Copy link
Contributor Author

Rebased onto main and fixed a consensus-critical bug identified by @teor2345 in d4ddd6d

Copy link
Contributor

@teor2345 teor2345 left a comment

Choose a reason for hiding this comment

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

Looks good to me.

I'll be adding some more block test vectors so I can implement some difficulty contextual validation tests. We'll automatically pick up those blocks in these tests as well.

@teor2345 teor2345 merged commit 4fd9203 into main Dec 1, 2020
@teor2345 teor2345 deleted the block-merkle-tree branch December 1, 2020 00:14
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.

Validate the transaction merkle root and transaction uniqueness Implement Bitcoin Merkle tree logic
4 participants