From 7c213ce161cb929659c9cd96c7bfa08bcd8ef820 Mon Sep 17 00:00:00 2001 From: Rupam Dey <117000803+rupam-04@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:34:35 +0530 Subject: [PATCH] feat: implement `PayloadProof` function (#14356) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: implement function `PayloadProof` to calculate proof of execution payload * remove comments * feat: implement function to compute field roots of * feat: implement function to compute `BeaconBlock` field roots and add tests * fix dependencies * check if interface implements the assserted type * fix: lint * replace `ok != true` with `!ok` * remove unused parameter from `PayloadProof` * remove test and move `PayloadProof` to `blocks/proofs.go` * remove `PayloadProof` from `fieldtrie` * replace `fieldtrie.ProofFromMerkleLayers` with `trie.ProofFromMerkleLayers` * Update container/trie/sparse_merkle.go * update dependencies --------- Co-authored-by: Radosław Kapka Co-authored-by: Radosław Kapka --- .../state/fieldtrie/field_trie_helpers.go | 15 ---- beacon-chain/state/state-native/BUILD.bazel | 1 + beacon-chain/state/state-native/proofs.go | 8 +- consensus-types/blocks/BUILD.bazel | 1 + consensus-types/blocks/proofs.go | 73 +++++++++++++++++++ container/trie/sparse_merkle.go | 15 ++++ 6 files changed, 94 insertions(+), 19 deletions(-) diff --git a/beacon-chain/state/fieldtrie/field_trie_helpers.go b/beacon-chain/state/fieldtrie/field_trie_helpers.go index bef64470976b..9d6585005d00 100644 --- a/beacon-chain/state/fieldtrie/field_trie_helpers.go +++ b/beacon-chain/state/fieldtrie/field_trie_helpers.go @@ -14,21 +14,6 @@ import ( ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ) -// ProofFromMerkleLayers creates a proof starting at the leaf index of the state Merkle layers. -func ProofFromMerkleLayers(layers [][][]byte, startingLeafIndex int) [][]byte { - // The merkle tree structure looks as follows: - // [[r1, r2, r3, r4], [parent1, parent2], [root]] - proof := make([][]byte, 0) - currentIndex := startingLeafIndex - for i := 0; i < len(layers)-1; i++ { - neighborIdx := currentIndex ^ 1 - neighbor := layers[i][neighborIdx] - proof = append(proof, neighbor) - currentIndex = currentIndex / 2 - } - return proof -} - func (f *FieldTrie) validateIndices(idxs []uint64) error { length := f.length if f.dataType == types.CompressedArray { diff --git a/beacon-chain/state/state-native/BUILD.bazel b/beacon-chain/state/state-native/BUILD.bazel index 85de3ebd0aeb..9e24c64e914f 100644 --- a/beacon-chain/state/state-native/BUILD.bazel +++ b/beacon-chain/state/state-native/BUILD.bazel @@ -67,6 +67,7 @@ go_library( "//consensus-types/primitives:go_default_library", "//container/multi-value-slice:go_default_library", "//container/slice:go_default_library", + "//container/trie:go_default_library", "//crypto/bls:go_default_library", "//crypto/hash:go_default_library", "//encoding/bytesutil:go_default_library", diff --git a/beacon-chain/state/state-native/proofs.go b/beacon-chain/state/state-native/proofs.go index c08fcd8b29ed..c1b12c4e388f 100644 --- a/beacon-chain/state/state-native/proofs.go +++ b/beacon-chain/state/state-native/proofs.go @@ -4,8 +4,8 @@ import ( "context" "encoding/binary" - "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/fieldtrie" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native/types" + "github.com/prysmaticlabs/prysm/v5/container/trie" "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/runtime/version" ) @@ -56,7 +56,7 @@ func (b *BeaconState) CurrentSyncCommitteeProof(ctx context.Context) ([][]byte, if err := b.recomputeDirtyFields(ctx); err != nil { return nil, err } - return fieldtrie.ProofFromMerkleLayers(b.merkleLayers, types.CurrentSyncCommittee.RealPosition()), nil + return trie.ProofFromMerkleLayers(b.merkleLayers, types.CurrentSyncCommittee.RealPosition()), nil } // NextSyncCommitteeProof from the state's Merkle trie representation. @@ -74,7 +74,7 @@ func (b *BeaconState) NextSyncCommitteeProof(ctx context.Context) ([][]byte, err if err := b.recomputeDirtyFields(ctx); err != nil { return nil, err } - return fieldtrie.ProofFromMerkleLayers(b.merkleLayers, types.NextSyncCommittee.RealPosition()), nil + return trie.ProofFromMerkleLayers(b.merkleLayers, types.NextSyncCommittee.RealPosition()), nil } // FinalizedRootProof crafts a Merkle proof for the finalized root @@ -102,7 +102,7 @@ func (b *BeaconState) FinalizedRootProof(ctx context.Context) ([][]byte, error) epochRoot := bytesutil.ToBytes32(epochBuf) proof := make([][]byte, 0) proof = append(proof, epochRoot[:]) - branch := fieldtrie.ProofFromMerkleLayers(b.merkleLayers, types.FinalizedCheckpoint.RealPosition()) + branch := trie.ProofFromMerkleLayers(b.merkleLayers, types.FinalizedCheckpoint.RealPosition()) proof = append(proof, branch...) return proof, nil } diff --git a/consensus-types/blocks/BUILD.bazel b/consensus-types/blocks/BUILD.bazel index 1cf938c92152..30b4ff0cc1f9 100644 --- a/consensus-types/blocks/BUILD.bazel +++ b/consensus-types/blocks/BUILD.bazel @@ -18,6 +18,7 @@ go_library( importpath = "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks", visibility = ["//visibility:public"], deps = [ + "//beacon-chain/state/stateutil:go_default_library", "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types:go_default_library", diff --git a/consensus-types/blocks/proofs.go b/consensus-types/blocks/proofs.go index b3181da579ce..34e0f394a055 100644 --- a/consensus-types/blocks/proofs.go +++ b/consensus-types/blocks/proofs.go @@ -3,15 +3,23 @@ package blocks import ( "context" "encoding/binary" + "errors" "fmt" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stateutil" "github.com/prysmaticlabs/prysm/v5/config/params" + "github.com/prysmaticlabs/prysm/v5/container/trie" "github.com/prysmaticlabs/prysm/v5/crypto/hash/htr" "github.com/prysmaticlabs/prysm/v5/encoding/ssz" "github.com/prysmaticlabs/prysm/v5/runtime/version" "go.opencensus.io/trace" ) +const ( + payloadFieldIndex = 9 + bodyFieldIndex = 4 +) + func ComputeBlockBodyFieldRoots(ctx context.Context, blockBody *BeaconBlockBody) ([][]byte, error) { _, span := trace.StartSpan(ctx, "blocks.ComputeBlockBodyFieldRoots") defer span.End() @@ -172,3 +180,68 @@ func ComputeBlockBodyFieldRoots(ctx context.Context, blockBody *BeaconBlockBody) return fieldRoots, nil } + +func ComputeBlockFieldRoots(ctx context.Context, block *BeaconBlock) ([][]byte, error) { + _, span := trace.StartSpan(ctx, "blocks.ComputeBlockFieldRoots") + defer span.End() + + if block == nil { + return nil, errNilBlock + } + + fieldRoots := make([][]byte, 5) + for i := range fieldRoots { + fieldRoots[i] = make([]byte, 32) + } + + // Slot + slotRoot := ssz.Uint64Root(uint64(block.slot)) + copy(fieldRoots[0], slotRoot[:]) + + // Proposer Index + proposerRoot := ssz.Uint64Root(uint64(block.proposerIndex)) + copy(fieldRoots[1], proposerRoot[:]) + + // Parent Root + copy(fieldRoots[2], block.parentRoot[:]) + + // State Root + copy(fieldRoots[3], block.stateRoot[:]) + + // block body Root + blockBodyRoot, err := block.body.HashTreeRoot() + if err != nil { + return nil, err + } + copy(fieldRoots[4], blockBodyRoot[:]) + + return fieldRoots, nil +} + +func PayloadProof(ctx context.Context, block *BeaconBlock) ([][]byte, error) { + i := block.Body() + blockBody, ok := i.(*BeaconBlockBody) + if !ok { + return nil, errors.New("failed to cast block body") + } + + blockBodyFieldRoots, err := ComputeBlockBodyFieldRoots(ctx, blockBody) + if err != nil { + return nil, err + } + + blockBodyFieldRootsTrie := stateutil.Merkleize(blockBodyFieldRoots) + blockBodyProof := trie.ProofFromMerkleLayers(blockBodyFieldRootsTrie, payloadFieldIndex) + + beaconBlockFieldRoots, err := ComputeBlockFieldRoots(ctx, block) + if err != nil { + return nil, err + } + + beaconBlockFieldRootsTrie := stateutil.Merkleize(beaconBlockFieldRoots) + beaconBlockProof := trie.ProofFromMerkleLayers(beaconBlockFieldRootsTrie, bodyFieldIndex) + + finalProof := append(blockBodyProof, beaconBlockProof...) + + return finalProof, nil +} diff --git a/container/trie/sparse_merkle.go b/container/trie/sparse_merkle.go index 6fbb3dc3a9ee..4aea824fba3a 100644 --- a/container/trie/sparse_merkle.go +++ b/container/trie/sparse_merkle.go @@ -259,3 +259,18 @@ func (m *SparseMerkleTrie) NumOfItems() int { } return len(m.originalItems) } + +// ProofFromMerkleLayers creates a proof starting at the leaf index of the merkle layers. +func ProofFromMerkleLayers(layers [][][]byte, startingLeafIndex int) [][]byte { + // The merkle tree structure looks as follows: + // [[r1, r2, r3, r4], [parent1, parent2], [root]] + proof := make([][]byte, 0) + currentIndex := startingLeafIndex + for i := 0; i < len(layers)-1; i++ { + neighborIdx := currentIndex ^ 1 + neighbor := layers[i][neighborIdx] + proof = append(proof, neighbor) + currentIndex = currentIndex / 2 + } + return proof +}