Commit 06d66f12 authored by clabby's avatar clabby Committed by GitHub

feat(ctb): `PreimageOracle` LPP Diff Testing (#9147)

* feat(ctb): merkle tree diff testing

* Fix `go-ffi` merkle diff tester ++ add more tests

* fix(op-challenger): merkle tree signed integer conversion

---------
Co-authored-by: default avatarrefcell <abigger87@gmail.com>
parent 15615479
...@@ -56,7 +56,7 @@ func (m *merkleNode) IsRightChild(o *merkleNode) bool { ...@@ -56,7 +56,7 @@ func (m *merkleNode) IsRightChild(o *merkleNode) bool {
// It is an append-only tree, where leaves are added from left to right. // It is an append-only tree, where leaves are added from left to right.
type BinaryMerkleTree struct { type BinaryMerkleTree struct {
Root *merkleNode Root *merkleNode
LeafCount int LeafCount uint64
} }
func NewBinaryMerkleTree() *BinaryMerkleTree { func NewBinaryMerkleTree() *BinaryMerkleTree {
...@@ -72,11 +72,11 @@ func (m *BinaryMerkleTree) RootHash() (rootHash common.Hash) { ...@@ -72,11 +72,11 @@ func (m *BinaryMerkleTree) RootHash() (rootHash common.Hash) {
} }
// walkDownToMaxLeaf walks down the tree to the max leaf node. // walkDownToMaxLeaf walks down the tree to the max leaf node.
func (m *BinaryMerkleTree) walkDownToLeafCount(subtreeLeafCount int) *merkleNode { func (m *BinaryMerkleTree) walkDownToLeafCount(subtreeLeafCount uint64) *merkleNode {
maxSubtreeLeafCount := MaxLeafCount + 1 maxSubtreeLeafCount := uint64(MaxLeafCount) + 1
levelNode := m.Root levelNode := m.Root
for height := 0; height < BinaryMerkleTreeDepth; height++ { for height := 0; height < BinaryMerkleTreeDepth; height++ {
if subtreeLeafCount*2 <= maxSubtreeLeafCount { if subtreeLeafCount*2 <= uint64(maxSubtreeLeafCount) {
if levelNode.Left == nil { if levelNode.Left == nil {
levelNode.Left = &merkleNode{ levelNode.Left = &merkleNode{
Label: zeroHashes[height], Label: zeroHashes[height],
...@@ -101,12 +101,17 @@ func (m *BinaryMerkleTree) walkDownToLeafCount(subtreeLeafCount int) *merkleNode ...@@ -101,12 +101,17 @@ func (m *BinaryMerkleTree) walkDownToLeafCount(subtreeLeafCount int) *merkleNode
// AddLeaf adds a leaf to the binary merkle tree. // AddLeaf adds a leaf to the binary merkle tree.
func (m *BinaryMerkleTree) AddLeaf(leaf types.Leaf) { func (m *BinaryMerkleTree) AddLeaf(leaf types.Leaf) {
m.AddRawLeaf(leaf.Hash())
}
// AddRawLeaf adds a raw 32 byte leaf to the binary merkle tree.
func (m *BinaryMerkleTree) AddRawLeaf(hash common.Hash) {
// Walk down to the new max leaf node. // Walk down to the new max leaf node.
m.LeafCount += 1 m.LeafCount += 1
levelNode := m.walkDownToLeafCount(m.LeafCount) levelNode := m.walkDownToLeafCount(m.LeafCount)
// Set the leaf node data. // Set the leaf node data.
levelNode.Label = leaf.Hash() levelNode.Label = hash
// Walk back up the tree, updating the hashes with its sibling hash. // Walk back up the tree, updating the hashes with its sibling hash.
for height := 0; height < BinaryMerkleTreeDepth; height++ { for height := 0; height < BinaryMerkleTreeDepth; height++ {
...@@ -137,7 +142,7 @@ func (m *BinaryMerkleTree) ProofAtIndex(index uint64) (proof Proof, err error) { ...@@ -137,7 +142,7 @@ func (m *BinaryMerkleTree) ProofAtIndex(index uint64) (proof Proof, err error) {
return proof, IndexOutOfBoundsError return proof, IndexOutOfBoundsError
} }
levelNode := m.walkDownToLeafCount(int(index) + 1) levelNode := m.walkDownToLeafCount(index + 1)
for height := 0; height < BinaryMerkleTreeDepth; height++ { for height := 0; height < BinaryMerkleTreeDepth; height++ {
if levelNode.Parent.IsLeftChild(levelNode) { if levelNode.Parent.IsLeftChild(levelNode) {
if levelNode.Parent.Right == nil { if levelNode.Parent.Right == nil {
......
...@@ -11,6 +11,8 @@ func main() { ...@@ -11,6 +11,8 @@ func main() {
DiffTestUtils() DiffTestUtils()
case "trie": case "trie":
FuzzTrie() FuzzTrie()
case "merkle":
DiffMerkle()
default: default:
log.Fatal("Must pass a subcommand") log.Fatal("Must pass a subcommand")
} }
......
package main
import (
"fmt"
"log"
"os"
"strconv"
"github.com/ethereum-optimism/optimism/op-challenger/game/keccak/merkle"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
// VerifyMerkleProof verifies a merkle proof against the root hash and the leaf hash.
// Reference: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#is_valid_merkle_branch
func VerifyMerkleProof(root, leaf common.Hash, index uint64, proof [merkle.BinaryMerkleTreeDepth]common.Hash) bool {
value := leaf
for i := 0; i < merkle.BinaryMerkleTreeDepth; i++ {
if ((index >> i) & 1) == 1 {
value = crypto.Keccak256Hash(proof[i][:], value[:])
} else {
value = crypto.Keccak256Hash(value[:], proof[i][:])
}
}
return value == root
}
const (
// GenProof generates a merkle proof for a given leaf index by reconstructing the merkle tree from the passed
// leaves.
genProof = "gen_proof"
)
var (
rootAndProof, _ = abi.NewType("tuple", "", []abi.ArgumentMarshaling{
{Name: "root", Type: "bytes32"},
{Name: "proof", Type: "bytes32[]"},
})
merkleEncoder = abi.Arguments{
{Type: rootAndProof},
}
)
// DiffMerkle generates an abi-encoded `merkleTestCase` of a specified variant.
func DiffMerkle() {
variant := os.Args[2]
if len(variant) == 0 {
log.Fatal("Must pass a variant to the merkle diff tester!")
}
switch variant {
case genProof:
if len(os.Args) < 5 {
log.Fatal("Invalid arguments to `gen_proof` variant.")
}
rawLeaves, err := hexutil.Decode(os.Args[3])
if err != nil {
log.Fatal("Failed to decode leaves: ", err)
}
index, err := strconv.ParseInt(os.Args[4], 10, 64)
if err != nil {
log.Fatal("Failed to parse leaf index: ", err)
}
merkleTree := merkle.NewBinaryMerkleTree()
// Append all leaves to the merkle tree.
for i := 0; i < len(rawLeaves)/32; i++ {
leaf := common.BytesToHash(rawLeaves[i<<5 : (i+1)<<5])
merkleTree.AddRawLeaf(leaf)
}
// Generate the proof for the given index.
proof, err := merkleTree.ProofAtIndex(uint64(index))
if err != nil {
log.Fatal("Failed to generate proof: ", err)
}
// Generate the merkle root.
root := merkleTree.RootHash()
// Return "abi.encode(root, proof)"
packed, err := merkleEncoder.Pack(struct {
Root common.Hash
Proof [merkle.BinaryMerkleTreeDepth]common.Hash
}{
Root: root,
Proof: proof,
})
if err != nil {
log.Fatal("Failed to ABI encode root and proof: ", err)
}
fmt.Print(hexutil.Encode(packed[32:]))
default:
log.Fatal("Invalid variant passed to merkle diff tester!")
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment