Compare commits

...

9 Commits

9 changed files with 262 additions and 119 deletions

View File

@@ -74,7 +74,7 @@ func FetchDataColumnSidecars(
// set as we retrieve them.
incompleteRoots := make(map[[fieldparams.RootLength]byte]bool, blockCount)
slotsWithCommitments := make(map[primitives.Slot]bool, blockCount)
slotByRoot := make(map[[fieldparams.RootLength]byte]primitives.Slot, blockCount)
roBlockByRoot := make(map[[fieldparams.RootLength]byte]blocks.ROBlock, blockCount)
storedIndicesByRoot := make(map[[fieldparams.RootLength]byte]map[uint64]bool, blockCount)
for _, roBlock := range roBlocks {
@@ -93,7 +93,7 @@ func FetchDataColumnSidecars(
slot := block.Slot()
incompleteRoots[root] = true
slotByRoot[root] = slot
roBlockByRoot[root] = roBlock
slotsWithCommitments[slot] = true
storedIndices := params.Storage.Summary(root).Stored()
@@ -118,7 +118,7 @@ func FetchDataColumnSidecars(
}
// Request direct sidecars from peers.
directSidecarsByRoot, err := requestDirectSidecarsFromPeers(params, slotByRoot, requestedIndices, slotsWithCommitments, storedIndicesByRoot, incompleteRoots)
directSidecarsByRoot, err := requestDirectSidecarsFromPeers(params, roBlockByRoot, requestedIndices, slotsWithCommitments, storedIndicesByRoot, incompleteRoots)
if err != nil {
return nil, nil, errors.Wrap(err, "request direct sidecars from peers")
}
@@ -139,7 +139,7 @@ func FetchDataColumnSidecars(
}
// Request all possible indirect sidecars from peers which are neither stored nor in `directSidecarsByRoot`
indirectSidecarsByRoot, err := requestIndirectSidecarsFromPeers(params, slotByRoot, slotsWithCommitments, storedIndicesByRoot, directSidecarsByRoot, requestedIndices, incompleteRoots)
indirectSidecarsByRoot, err := requestIndirectSidecarsFromPeers(params, roBlockByRoot, slotsWithCommitments, storedIndicesByRoot, directSidecarsByRoot, requestedIndices, incompleteRoots)
if err != nil {
return nil, nil, errors.Wrap(err, "request all sidecars from peers")
}
@@ -229,7 +229,7 @@ func requestSidecarsFromStorage(
// It returns a map from each root to its successfully retrieved sidecars.
func requestDirectSidecarsFromPeers(
params DataColumnSidecarsParams,
slotByRoot map[[fieldparams.RootLength]byte]primitives.Slot,
roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock,
requestedIndices map[uint64]bool,
slotsWithCommitments map[primitives.Slot]bool,
storedIndicesByRoot map[[fieldparams.RootLength]byte]map[uint64]bool,
@@ -266,7 +266,7 @@ func requestDirectSidecarsFromPeers(
initialMissingCount := computeTotalCount(missingIndicesByRoot)
indicesByRootByPeer, err := computeIndicesByRootByPeer(params.P2P, slotByRoot, missingIndicesByRoot, connectedPeers)
indicesByRootByPeer, err := computeIndicesByRootByPeer(params.P2P, roBlockByRoot, missingIndicesByRoot, connectedPeers)
if err != nil {
return nil, errors.Wrap(err, "explore peers")
}
@@ -285,10 +285,10 @@ func requestDirectSidecarsFromPeers(
}
// Fetch the sidecars from the chosen peers.
roDataColumnsByPeer := fetchDataColumnSidecarsFromPeers(params, slotByRoot, slotsWithCommitments, indicesByRootByPeerToQuery)
roDataColumnsByPeer := fetchDataColumnSidecarsFromPeers(params, roBlockByRoot, slotsWithCommitments, indicesByRootByPeerToQuery)
// Verify the received data column sidecars.
verifiedRoDataColumnSidecars, err := verifyDataColumnSidecarsByPeer(params.P2P, params.NewVerifier, roDataColumnsByPeer)
verifiedRoDataColumnSidecars, err := verifyDataColumnSidecarsByPeer(params.P2P, params.NewVerifier, roBlockByRoot, roDataColumnsByPeer)
if err != nil {
return nil, errors.Wrap(err, "verify data columns sidecars by peer")
}
@@ -300,7 +300,7 @@ func requestDirectSidecarsFromPeers(
}
// Compute indices by root by peers with the updated missing indices and connected peers.
indicesByRootByPeer, err = computeIndicesByRootByPeer(params.P2P, slotByRoot, missingIndicesByRoot, connectedPeers)
indicesByRootByPeer, err = computeIndicesByRootByPeer(params.P2P, roBlockByRoot, missingIndicesByRoot, connectedPeers)
if err != nil {
return nil, errors.Wrap(err, "explore peers")
}
@@ -323,7 +323,7 @@ func requestDirectSidecarsFromPeers(
// - all peers are exhausted.
func requestIndirectSidecarsFromPeers(
p DataColumnSidecarsParams,
slotByRoot map[[fieldparams.RootLength]byte]primitives.Slot,
roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock,
slotsWithCommitments map[primitives.Slot]bool,
storedIndicesByRoot map[[fieldparams.RootLength]byte]map[uint64]bool,
alreadyAvailableByRoot map[[fieldparams.RootLength]byte][]blocks.VerifiedRODataColumn,
@@ -370,7 +370,7 @@ func requestIndirectSidecarsFromPeers(
}
// Compute which peers have which of the missing indices.
indicesByRootByPeer, err := computeIndicesByRootByPeer(p.P2P, slotByRoot, indicesToRetrieveByRoot, connectedPeers)
indicesByRootByPeer, err := computeIndicesByRootByPeer(p.P2P, roBlockByRoot, indicesToRetrieveByRoot, connectedPeers)
if err != nil {
return nil, errors.Wrap(err, "explore peers")
}
@@ -395,10 +395,10 @@ func requestIndirectSidecarsFromPeers(
}
// Fetch the sidecars from the chosen peers.
roDataColumnsByPeer := fetchDataColumnSidecarsFromPeers(p, slotByRoot, slotsWithCommitments, indicesByRootByPeerToQuery)
roDataColumnsByPeer := fetchDataColumnSidecarsFromPeers(p, roBlockByRoot, slotsWithCommitments, indicesByRootByPeerToQuery)
// Verify the received data column sidecars.
verifiedRoDataColumnSidecars, err := verifyDataColumnSidecarsByPeer(p.P2P, p.NewVerifier, roDataColumnsByPeer)
verifiedRoDataColumnSidecars, err := verifyDataColumnSidecarsByPeer(p.P2P, p.NewVerifier, roBlockByRoot, roDataColumnsByPeer)
if err != nil {
return nil, errors.Wrap(err, "verify data columns sidecars by peer")
}
@@ -436,7 +436,7 @@ func requestIndirectSidecarsFromPeers(
}
// Compute indices by root by peers with the updated missing indices and connected peers.
indicesByRootByPeer, err = computeIndicesByRootByPeer(p.P2P, slotByRoot, indicesToRetrieveByRoot, connectedPeers)
indicesByRootByPeer, err = computeIndicesByRootByPeer(p.P2P, roBlockByRoot, indicesToRetrieveByRoot, connectedPeers)
if err != nil {
return nil, errors.Wrap(err, "explore peers")
}
@@ -709,7 +709,7 @@ func updateResults(
// fetchDataColumnSidecarsFromPeers retrieves data column sidecars from peers.
func fetchDataColumnSidecarsFromPeers(
params DataColumnSidecarsParams,
slotByRoot map[[fieldparams.RootLength]byte]primitives.Slot,
roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock,
slotsWithCommitments map[primitives.Slot]bool,
indicesByRootByPeer map[goPeer.ID]map[[fieldparams.RootLength]byte]map[uint64]bool,
) map[goPeer.ID][]blocks.RODataColumn {
@@ -736,7 +736,7 @@ func fetchDataColumnSidecarsFromPeers(
"totalRequestedCount": requestedCount,
})
roDataColumns, err := sendDataColumnSidecarsRequest(params, slotByRoot, slotsWithCommitments, peerID, indicesByRoot)
roDataColumns, err := sendDataColumnSidecarsRequest(params, roBlockByRoot, slotsWithCommitments, peerID, indicesByRoot)
if err != nil {
log.WithError(err).Debug("Failed to send data column sidecars request")
return
@@ -755,7 +755,7 @@ func fetchDataColumnSidecarsFromPeers(
func sendDataColumnSidecarsRequest(
params DataColumnSidecarsParams,
slotByRoot map[[fieldparams.RootLength]byte]primitives.Slot,
roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock,
slotsWithCommitments map[primitives.Slot]bool,
peerID goPeer.ID,
indicesByRoot map[[fieldparams.RootLength]byte]map[uint64]bool,
@@ -775,7 +775,7 @@ func sendDataColumnSidecarsRequest(
})
// Try to build a by range byRangeRequest first.
byRangeRequests, err := buildByRangeRequests(slotByRoot, slotsWithCommitments, indicesByRoot, batchSize)
byRangeRequests, err := buildByRangeRequests(roBlockByRoot, slotsWithCommitments, indicesByRoot, batchSize)
if err != nil {
return nil, errors.Wrap(err, "craft by range request")
}
@@ -854,7 +854,7 @@ func sendDataColumnSidecarsRequest(
// (Missing blocks or blocks without commitments do count as contiguous)
// If one of this condition is not met, returns nil.
func buildByRangeRequests(
slotByRoot map[[fieldparams.RootLength]byte]primitives.Slot,
roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock,
slotsWithCommitments map[primitives.Slot]bool,
indicesByRoot map[[fieldparams.RootLength]byte]map[uint64]bool,
batchSize uint64,
@@ -864,7 +864,7 @@ func buildByRangeRequests(
}
var reference map[uint64]bool
slots := make([]primitives.Slot, 0, len(slotByRoot))
slots := make([]primitives.Slot, 0, len(roBlockByRoot))
for root, indices := range indicesByRoot {
if reference == nil {
reference = indices
@@ -874,12 +874,12 @@ func buildByRangeRequests(
return nil, nil
}
slot, ok := slotByRoot[root]
roBlock, ok := roBlockByRoot[root]
if !ok {
return nil, errors.Errorf("slot not found for block root %#x", root)
}
slots = append(slots, slot)
slots = append(slots, roBlock.Block().Slot())
}
slices.Sort(slots)
@@ -944,6 +944,7 @@ func buildByRootRequest(indicesByRoot map[[fieldparams.RootLength]byte]map[uint6
func verifyDataColumnSidecarsByPeer(
p2p prysmP2P.P2P,
newVerifier verification.NewDataColumnsVerifier,
roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock,
roDataColumnsByPeer map[goPeer.ID][]blocks.RODataColumn,
) ([]blocks.VerifiedRODataColumn, error) {
// First optimistically verify all received data columns in a single batch.
@@ -957,7 +958,7 @@ func verifyDataColumnSidecarsByPeer(
roDataColumnSidecars = append(roDataColumnSidecars, columns...)
}
verifiedRoDataColumnSidecars, err := verifyByRootDataColumnSidecars(newVerifier, roDataColumnSidecars)
verifiedRoDataColumnSidecars, err := verifyRPCDataColumnSidecars(newVerifier, roBlockByRoot, roDataColumnSidecars)
if err == nil {
// This is the happy path where all sidecars are verified.
return verifiedRoDataColumnSidecars, nil
@@ -967,7 +968,7 @@ func verifyDataColumnSidecarsByPeer(
// Reverify peer by peer to identify faulty peer(s), reject all its sidecars, and downscore it.
verifiedRoDataColumnSidecars = make([]blocks.VerifiedRODataColumn, 0, count)
for peer, columns := range roDataColumnsByPeer {
peerVerifiedRoDataColumnSidecars, err := verifyByRootDataColumnSidecars(newVerifier, columns)
peerVerifiedRoDataColumnSidecars, err := verifyRPCDataColumnSidecars(newVerifier, roBlockByRoot, columns)
if err != nil {
// This peer has invalid sidecars.
log := log.WithError(err).WithField("peerID", peer)
@@ -982,15 +983,23 @@ func verifyDataColumnSidecarsByPeer(
return verifiedRoDataColumnSidecars, nil
}
// verifyByRootDataColumnSidecars verifies the provided read-only data columns against the
// verifyRPCDataColumnSidecars verifies the provided read-only data columns against the
// requirements for data column sidecars received via the by root request.
func verifyByRootDataColumnSidecars(newVerifier verification.NewDataColumnsVerifier, roDataColumns []blocks.RODataColumn) ([]blocks.VerifiedRODataColumn, error) {
verifier := newVerifier(roDataColumns, verification.ByRootRequestDataColumnSidecarRequirements)
func verifyRPCDataColumnSidecars(
newVerifier verification.NewDataColumnsVerifier,
roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock,
roDataColumns []blocks.RODataColumn,
) ([]blocks.VerifiedRODataColumn, error) {
verifier := newVerifier(roDataColumns, verification.RPCDataColumnSidecarRequirements)
if err := verifier.ValidFields(); err != nil {
return nil, errors.Wrap(err, "valid fields")
}
if err := verifier.SidecarRootAndSignatureAligned(roBlockByRoot); err != nil {
return nil, errors.Wrap(err, "sidecar root and signature aligned")
}
if err := verifier.SidecarInclusionProven(); err != nil {
return nil, errors.Wrap(err, "sidecar inclusion proven")
}
@@ -1008,12 +1017,12 @@ func verifyByRootDataColumnSidecars(newVerifier verification.NewDataColumnsVerif
}
// computeIndicesByRootByPeer returns a peers->root->indices map only for
// root and indices given in `indicesByBlockRoot`. It also only selects peers
// root and indices given in `indicesByRoot`. It also only selects peers
// for a given root only if its head state is higher than the block slot.
func computeIndicesByRootByPeer(
p2p prysmP2P.P2P,
slotByBlockRoot map[[fieldparams.RootLength]byte]primitives.Slot,
indicesByBlockRoot map[[fieldparams.RootLength]byte]map[uint64]bool,
roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock,
indicesByRoot map[[fieldparams.RootLength]byte]map[uint64]bool,
peers map[goPeer.ID]bool,
) (map[goPeer.ID]map[[fieldparams.RootLength]byte]map[uint64]bool, error) {
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
@@ -1065,10 +1074,10 @@ func computeIndicesByRootByPeer(
// For each block root and its indices, find suitable peers
indicesByRootByPeer := make(map[goPeer.ID]map[[fieldparams.RootLength]byte]map[uint64]bool)
for blockRoot, indices := range indicesByBlockRoot {
blockSlot, ok := slotByBlockRoot[blockRoot]
for root, indices := range indicesByRoot {
roBlock, ok := roBlockByRoot[root]
if !ok {
return nil, errors.Errorf("slot not found for block root %#x", blockRoot)
return nil, errors.Errorf("slot not found for block root %#x", root)
}
for index := range indices {
@@ -1079,7 +1088,7 @@ func computeIndicesByRootByPeer(
return nil, errors.Errorf("head slot not found for peer %s", peer)
}
if peerHeadSlot < blockSlot {
if peerHeadSlot < roBlock.Block().Slot() {
continue
}
@@ -1087,10 +1096,10 @@ func computeIndicesByRootByPeer(
if _, exists := indicesByRootByPeer[peer]; !exists {
indicesByRootByPeer[peer] = make(map[[fieldparams.RootLength]byte]map[uint64]bool)
}
if _, exists := indicesByRootByPeer[peer][blockRoot]; !exists {
indicesByRootByPeer[peer][blockRoot] = make(map[uint64]bool)
if _, exists := indicesByRootByPeer[peer][root]; !exists {
indicesByRootByPeer[peer][root] = make(map[uint64]bool)
}
indicesByRootByPeer[peer][blockRoot][index] = true
indicesByRootByPeer[peer][root][index] = true
}
}
}

View File

@@ -400,12 +400,12 @@ func TestFetchDataColumnSidecarsFromPeers(t *testing.T) {
expectedResponseSidecar, err := blocks.NewRODataColumn(expectedResponseSidecarPb)
require.NoError(t, err)
slotByRoot := map[[fieldparams.RootLength]byte]primitives.Slot{
roBlockByRoot := createRoBlockByRoot(t, map[[fieldparams.RootLength]byte]primitives.Slot{
{1}: 1,
{3}: 3,
{4}: 4,
{7}: 7,
}
})
slotsWithCommitments := map[primitives.Slot]bool{
1: true,
@@ -458,7 +458,7 @@ func TestFetchDataColumnSidecarsFromPeers(t *testing.T) {
other.PeerID(): {expectedResponseSidecar},
}
actualResponse := fetchDataColumnSidecarsFromPeers(params, slotByRoot, slotsWithCommitments, indicesByRootByPeer)
actualResponse := fetchDataColumnSidecarsFromPeers(params, roBlockByRoot, slotsWithCommitments, indicesByRootByPeer)
require.Equal(t, len(expectedResponse), len(actualResponse))
for peerID := range expectedResponse {
@@ -508,12 +508,12 @@ func TestSendDataColumnSidecarsRequest(t *testing.T) {
{7}: {1: true, 2: true},
}
slotByRoot := map[[fieldparams.RootLength]byte]primitives.Slot{
roBlockByRoot := createRoBlockByRoot(t, map[[fieldparams.RootLength]byte]primitives.Slot{
{1}: 1,
{3}: 3,
{4}: 4,
{7}: 7,
}
})
slotsWithCommitments := map[primitives.Slot]bool{
1: true,
@@ -553,7 +553,7 @@ func TestSendDataColumnSidecarsRequest(t *testing.T) {
RateLimiter: leakybucket.NewCollector(1., 1, time.Second, false /* deleteEmptyBuckets */),
}
actualResponse, err := sendDataColumnSidecarsRequest(params, slotByRoot, slotsWithCommitments, other.PeerID(), indicesByRoot)
actualResponse, err := sendDataColumnSidecarsRequest(params, roBlockByRoot, slotsWithCommitments, other.PeerID(), indicesByRoot)
require.NoError(t, err)
require.DeepEqual(t, expectedResponse, actualResponse[0])
})
@@ -565,11 +565,11 @@ func TestSendDataColumnSidecarsRequest(t *testing.T) {
{7}: {1: true, 2: true},
}
slotByRoot := map[[fieldparams.RootLength]byte]primitives.Slot{
roBlockByRoot := createRoBlockByRoot(t, map[[fieldparams.RootLength]byte]primitives.Slot{
expectedResponse.BlockRoot(): 1,
{4}: 4,
{7}: 7,
}
})
slotsWithCommitments := map[primitives.Slot]bool{
1: true,
@@ -620,7 +620,7 @@ func TestSendDataColumnSidecarsRequest(t *testing.T) {
RateLimiter: leakybucket.NewCollector(1., 1, time.Second, false /* deleteEmptyBuckets */),
}
actualResponse, err := sendDataColumnSidecarsRequest(params, slotByRoot, slotsWithCommitments, other.PeerID(), indicesByRoot)
actualResponse, err := sendDataColumnSidecarsRequest(params, roBlockByRoot, slotsWithCommitments, other.PeerID(), indicesByRoot)
require.NoError(t, err)
require.DeepEqual(t, expectedResponse, actualResponse[0])
})
@@ -652,13 +652,13 @@ func TestBuildByRangeRequests(t *testing.T) {
{3}: {2: true, 3: true},
}
slotByRoot := map[[fieldparams.RootLength]byte]primitives.Slot{
roBlockByRoot := createRoBlockByRoot(t, map[[fieldparams.RootLength]byte]primitives.Slot{
{1}: 1,
{2}: 2,
{3}: 3,
}
})
actual, err := buildByRangeRequests(slotByRoot, nil, indicesByRoot, nullBatchSize)
actual, err := buildByRangeRequests(roBlockByRoot, nil, indicesByRoot, nullBatchSize)
require.NoError(t, err)
require.Equal(t, 0, len(actual))
})
@@ -669,10 +669,10 @@ func TestBuildByRangeRequests(t *testing.T) {
{2}: {1: true, 2: true},
}
slotByRoot := map[[fieldparams.RootLength]byte]primitives.Slot{
roBlockByRoot := createRoBlockByRoot(t, map[[fieldparams.RootLength]byte]primitives.Slot{
{1}: 1,
{2}: 3,
}
})
slotsWithCommitments := map[primitives.Slot]bool{
1: true,
@@ -680,7 +680,7 @@ func TestBuildByRangeRequests(t *testing.T) {
3: true,
}
actual, err := buildByRangeRequests(slotByRoot, slotsWithCommitments, indicesByRoot, nullBatchSize)
actual, err := buildByRangeRequests(roBlockByRoot, slotsWithCommitments, indicesByRoot, nullBatchSize)
require.NoError(t, err)
require.Equal(t, 0, len(actual))
})
@@ -695,12 +695,12 @@ func TestBuildByRangeRequests(t *testing.T) {
{7}: {1: true, 2: true},
}
slotByRoot := map[[fieldparams.RootLength]byte]primitives.Slot{
roBlockByRoot := createRoBlockByRoot(t, map[[fieldparams.RootLength]byte]primitives.Slot{
{1}: 1,
{3}: 3,
{4}: 4,
{7}: 7,
}
})
slotsWithCommitments := map[primitives.Slot]bool{
1: true,
@@ -727,7 +727,7 @@ func TestBuildByRangeRequests(t *testing.T) {
},
}
actual, err := buildByRangeRequests(slotByRoot, slotsWithCommitments, indicesByRoot, batchSize)
actual, err := buildByRangeRequests(roBlockByRoot, slotsWithCommitments, indicesByRoot, batchSize)
require.NoError(t, err)
require.DeepEqual(t, expected, actual)
})
@@ -792,7 +792,12 @@ func TestVerifyDataColumnSidecarsByPeer(t *testing.T) {
require.NoError(t, err)
newDataColumnsVerifier := newDataColumnsVerifierFromInitializer(initializer)
actual, err := verifyDataColumnSidecarsByPeer(p2p, newDataColumnsVerifier, roDataColumnsByPeer)
roBlockByRoot := createRoBlockByRoot(t, map[[fieldparams.RootLength]byte]primitives.Slot{
roDataColumnSidecars[0].BlockRoot(): roDataColumnSidecars[0].Slot(),
})
actual, err := verifyDataColumnSidecarsByPeer(p2p, newDataColumnsVerifier, roBlockByRoot, roDataColumnsByPeer)
require.NoError(t, err)
require.Equal(t, stop-start, len(actual))
@@ -837,7 +842,12 @@ func TestVerifyDataColumnSidecarsByPeer(t *testing.T) {
require.NoError(t, err)
newDataColumnsVerifier := newDataColumnsVerifierFromInitializer(initializer)
actual, err := verifyDataColumnSidecarsByPeer(p2p, newDataColumnsVerifier, roDataColumnsByPeer)
roBlockByRoot := createRoBlockByRoot(t, map[[fieldparams.RootLength]byte]primitives.Slot{
roDataColumnSidecars[0].BlockRoot(): roDataColumnSidecars[0].Slot(),
})
actual, err := verifyDataColumnSidecarsByPeer(p2p, newDataColumnsVerifier, roBlockByRoot, roDataColumnsByPeer)
require.NoError(t, err)
require.Equal(t, middle-start, len(actual))
@@ -881,34 +891,34 @@ func TestComputeIndicesByRootByPeer(t *testing.T) {
peerIDs = append(peerIDs, peerID)
}
slotByBlockRoot := map[[fieldparams.RootLength]byte]primitives.Slot{
[fieldparams.RootLength]byte{1}: 8,
[fieldparams.RootLength]byte{2}: 10,
[fieldparams.RootLength]byte{3}: 9,
[fieldparams.RootLength]byte{4}: 50,
}
roBlockByRoot := createRoBlockByRoot(t, map[[fieldparams.RootLength]byte]primitives.Slot{
{1}: 8,
{2}: 10,
{3}: 9,
{4}: 50,
})
indicesByBlockRoot := map[[fieldparams.RootLength]byte]map[uint64]bool{
[fieldparams.RootLength]byte{1}: {3: true, 4: true, 5: true},
[fieldparams.RootLength]byte{2}: {1: true, 10: true, 37: true, 80: true},
[fieldparams.RootLength]byte{3}: {10: true, 38: true, 39: true, 40: true},
[fieldparams.RootLength]byte{4}: {89: true, 108: true, 122: true},
{1}: {3: true, 4: true, 5: true},
{2}: {1: true, 10: true, 37: true, 80: true},
{3}: {10: true, 38: true, 39: true, 40: true},
{4}: {89: true, 108: true, 122: true},
}
expected := map[peer.ID]map[[fieldparams.RootLength]byte]map[uint64]bool{
peerIDs[0]: {
[fieldparams.RootLength]byte{4}: {89: true, 122: true},
{4}: {89: true, 122: true},
},
peerIDs[1]: {
[fieldparams.RootLength]byte{2}: {1: true, 37: true},
{2}: {1: true, 37: true},
},
peerIDs[2]: {
[fieldparams.RootLength]byte{2}: {37: true},
[fieldparams.RootLength]byte{3}: {38: true},
{2}: {37: true},
{3}: {38: true},
},
peerIDs[3]: {
[fieldparams.RootLength]byte{2}: {10: true},
[fieldparams.RootLength]byte{3}: {10: true},
{2}: {10: true},
{3}: {10: true},
},
}
@@ -917,7 +927,7 @@ func TestComputeIndicesByRootByPeer(t *testing.T) {
peerIDsMap[id] = true
}
actual, err := computeIndicesByRootByPeer(p2p, slotByBlockRoot, indicesByBlockRoot, peerIDsMap)
actual, err := computeIndicesByRootByPeer(p2p, roBlockByRoot, indicesByBlockRoot, peerIDsMap)
require.NoError(t, err)
require.Equal(t, len(expected), len(actual))
@@ -1023,3 +1033,20 @@ func TestComputeTotalCount(t *testing.T) {
actual := computeTotalCount(input)
require.Equal(t, expected, actual)
}
func createRoBlockByRoot(t *testing.T, slotByRoot map[[fieldparams.RootLength]byte]primitives.Slot) map[[fieldparams.RootLength]byte]blocks.ROBlock {
roBlockByRoot := make(map[[fieldparams.RootLength]byte]blocks.ROBlock, len(slotByRoot))
for root, slot := range slotByRoot {
signedBeaconBlockPb := util.NewBeaconBlock()
signedBeaconBlockPb.Block.Slot = slot
signedBeaconBlock, err := blocks.NewSignedBeaconBlock(signedBeaconBlockPb)
require.NoError(t, err)
roBlock, err := blocks.NewROBlockWithRoot(signedBeaconBlock, root)
require.NoError(t, err)
roBlockByRoot[root] = roBlock
}
return roBlockByRoot
}

View File

@@ -12,8 +12,6 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p"
p2ptest "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/testing"
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/types"
p2pTypes "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/types"
p2ptypes "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/types"
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v6/beacon-chain/verification"
fieldparams "github.com/OffchainLabs/prysm/v6/config/fieldparams"
@@ -80,7 +78,7 @@ func TestSendRequest_SendBeaconBlocksByRangeRequest(t *testing.T) {
}
_, err := stream.Write([]byte{0x01})
assert.NoError(t, err)
msg := p2pTypes.ErrorMessage(processorErr.Error())
msg := types.ErrorMessage(processorErr.Error())
_, err = p2pProvider.Encoding().EncodeWithMaxLength(stream, &msg)
assert.NoError(t, err)
return
@@ -323,7 +321,7 @@ func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) {
bogusPeer := p2ptest.NewTestP2P(t)
p1.Connect(bogusPeer)
req := &p2pTypes.BeaconBlockByRootsReq{}
req := &types.BeaconBlockByRootsReq{}
_, err := SendBeaconBlocksByRootRequest(ctx, startup.NewClock(time.Now(), [32]byte{}), p1, bogusPeer.PeerID(), req, nil)
assert.ErrorContains(t, "protocols not supported", err)
})
@@ -334,7 +332,7 @@ func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) {
assert.NoError(t, stream.Close())
}()
req := new(p2pTypes.BeaconBlockByRootsReq)
req := new(types.BeaconBlockByRootsReq)
assert.NoError(t, p2pProvider.Encoding().DecodeWithMaxLength(stream, req))
if len(*req) == 0 {
return
@@ -351,7 +349,7 @@ func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) {
}
_, err := stream.Write([]byte{0x01})
assert.NoError(t, err)
msg := p2pTypes.ErrorMessage(processorErr.Error())
msg := types.ErrorMessage(processorErr.Error())
_, err = p2pProvider.Encoding().EncodeWithMaxLength(stream, &msg)
assert.NoError(t, err)
return
@@ -372,7 +370,7 @@ func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) {
p1.Connect(p2)
p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil))
req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1]}
req := &types.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1]}
blocks, err := SendBeaconBlocksByRootRequest(ctx, startup.NewClock(time.Now(), [32]byte{}), p1, p2.PeerID(), req, nil)
assert.NoError(t, err)
assert.Equal(t, 2, len(blocks))
@@ -385,7 +383,7 @@ func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) {
p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil))
// No error from block processor.
req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1]}
req := &types.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1]}
blocksFromProcessor := make([]interfaces.ReadOnlySignedBeaconBlock, 0)
blocks, err := SendBeaconBlocksByRootRequest(ctx, startup.NewClock(time.Now(), [32]byte{}), p1, p2.PeerID(), req, func(block interfaces.ReadOnlySignedBeaconBlock) error {
blocksFromProcessor = append(blocksFromProcessor, block)
@@ -403,7 +401,7 @@ func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) {
p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil))
// Send error from block processor.
req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1]}
req := &types.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1]}
errFromProcessor := errors.New("processor error")
_, err := SendBeaconBlocksByRootRequest(ctx, startup.NewClock(time.Now(), [32]byte{}), p1, p2.PeerID(), req, func(block interfaces.ReadOnlySignedBeaconBlock) error {
return errFromProcessor
@@ -418,7 +416,7 @@ func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) {
p2.SetStreamHandler(pcl, knownBlocksProvider(p2, nil))
// No cap on max roots.
req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1], knownRoots[2], knownRoots[3]}
req := &types.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1], knownRoots[2], knownRoots[3]}
clock := startup.NewClock(time.Now(), [32]byte{})
blocks, err := SendBeaconBlocksByRootRequest(ctx, clock, p1, p2.PeerID(), req, nil)
assert.NoError(t, err)
@@ -457,7 +455,7 @@ func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) {
return nil
}))
req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1], knownRoots[2], knownRoots[3]}
req := &types.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1], knownRoots[2], knownRoots[3]}
blocks, err := SendBeaconBlocksByRootRequest(ctx, startup.NewClock(time.Now(), [32]byte{}), p1, p2.PeerID(), req, nil)
assert.ErrorContains(t, expectedErr.Error(), err)
assert.Equal(t, 0, len(blocks))
@@ -477,7 +475,7 @@ func TestSendRequest_SendBeaconBlocksByRootRequest(t *testing.T) {
return nil
}))
req := &p2pTypes.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1], knownRoots[2], knownRoots[3]}
req := &types.BeaconBlockByRootsReq{knownRoots[0], knownRoots[1], knownRoots[2], knownRoots[3]}
blocks, err := SendBeaconBlocksByRootRequest(ctx, startup.NewClock(time.Now(), [32]byte{}), p1, p2.PeerID(), req, nil)
assert.NoError(t, err)
assert.Equal(t, 3, len(blocks))
@@ -520,7 +518,7 @@ func TestBlobValidatorFromRootReq(t *testing.T) {
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
r := p2pTypes.BlobSidecarsByRootReq(c.ids)
r := types.BlobSidecarsByRootReq(c.ids)
vf := blobValidatorFromRootReq(&r)
for _, sc := range c.response {
err := vf(sc)
@@ -1201,7 +1199,7 @@ func TestSendDataColumnSidecarsByRootRequest(t *testing.T) {
require.NoError(t, err)
nilTestCases := []struct {
name string
request p2ptypes.DataColumnsByRootIdentifiers
request types.DataColumnsByRootIdentifiers
}{
{
name: "nil request",
@@ -1209,7 +1207,7 @@ func TestSendDataColumnSidecarsByRootRequest(t *testing.T) {
},
{
name: "count is 0",
request: p2ptypes.DataColumnsByRootIdentifiers{{}, {}},
request: types.DataColumnsByRootIdentifiers{{}, {}},
},
}
@@ -1227,7 +1225,7 @@ func TestSendDataColumnSidecarsByRootRequest(t *testing.T) {
cfg.MaxRequestDataColumnSidecars = 4
params.OverrideBeaconConfig(cfg)
request := p2ptypes.DataColumnsByRootIdentifiers{
request := types.DataColumnsByRootIdentifiers{
{Columns: []uint64{1, 2, 3}},
{Columns: []uint64{4, 5, 6}},
}
@@ -1324,7 +1322,7 @@ func TestSendDataColumnSidecarsByRootRequest(t *testing.T) {
blockRoot1, blockRoot2 := expected[0].BlockRoot(), expected[3].BlockRoot()
sentRequest := p2ptypes.DataColumnsByRootIdentifiers{
sentRequest := types.DataColumnsByRootIdentifiers{
{BlockRoot: blockRoot1[:], Columns: []uint64{1, 2, 3}},
{BlockRoot: blockRoot2[:], Columns: []uint64{1, 2, 3}},
}
@@ -1335,7 +1333,7 @@ func TestSendDataColumnSidecarsByRootRequest(t *testing.T) {
p2.SetStreamHandler(protocol, func(stream network.Stream) {
wg.Done()
requestReceived := new(p2ptypes.DataColumnsByRootIdentifiers)
requestReceived := new(types.DataColumnsByRootIdentifiers)
err := p2.Encoding().DecodeWithMaxLength(stream, requestReceived)
assert.NoError(t, err)

View File

@@ -30,6 +30,7 @@ const (
// Data columns specific.
RequireValidFields
RequireCorrectSubnet
RequireSidecarRootAndSignatureAligned
)
var allBlobSidecarRequirements = []Requirement{

View File

@@ -39,22 +39,15 @@ var (
RequireSidecarProposerExpected,
}
// ByRangeRequestDataColumnSidecarRequirements defines the set of requirements that DataColumnSidecars received
// via the by range request must satisfy in order to upgrade an RODataColumn to a VerifiedRODataColumn.
// https://github.com/ethereum/consensus-specs/blob/master/specs/fulu/p2p-interface.md#datacolumnsidecarsbyrange-v1
ByRangeRequestDataColumnSidecarRequirements = []Requirement{
RequireValidFields,
RequireSidecarInclusionProven,
RequireSidecarKzgProofVerified,
}
// ByRootRequestDataColumnSidecarRequirements defines the set of requirements that DataColumnSidecars received
// via the by root request must satisfy in order to upgrade an RODataColumn to a VerifiedRODataColumn.
// RPCDataColumnSidecarRequirements defines the set of requirements that DataColumnSidecars received
// via RPC request must satisfy in order to upgrade an RODataColumn to a VerifiedRODataColumn.
// https://github.com/ethereum/consensus-specs/blob/master/specs/fulu/p2p-interface.md#datacolumnsidecarsbyroot-v1
ByRootRequestDataColumnSidecarRequirements = []Requirement{
// https://github.com/ethereum/consensus-specs/blob/master/specs/fulu/p2p-interface.md#datacolumnsidecarsbyrange-v1
RPCDataColumnSidecarRequirements = []Requirement{
RequireValidFields,
RequireSidecarInclusionProven,
RequireSidecarKzgProofVerified,
RequireSidecarRootAndSignatureAligned,
}
// SpectestDataColumnSidecarRequirements is used by the forkchoice spectests when verifying data columns used in the on_block tests.
@@ -508,20 +501,54 @@ func (dv *RODataColumnsVerifier) SidecarProposerExpected(ctx context.Context) (e
return nil
}
func (dv *RODataColumnsVerifier) SidecarRootAndSignatureAligned(roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock) (err error) {
if ok, err := dv.results.cached(RequireSidecarRootAndSignatureAligned); ok {
return err
}
defer dv.recordResult(RequireSidecarRootAndSignatureAligned, &err)
for _, dataColumn := range dv.dataColumns {
// Check if we know the corresponding RO block.
root := dataColumn.BlockRoot()
roBlock, ok := roBlockByRoot[root]
if !ok {
return columnErrBuilder(errors.Errorf("no ro block found for data column with root %#x", root))
}
// Check if the signature aligns.
sidecarSignature := bytesutil.ToBytes96(dataColumn.SignedBlockHeader.Signature)
blockSignature := roBlock.Signature()
if sidecarSignature != blockSignature {
return columnErrBuilder(errUnalignedRootAndSignature)
}
// Check if the root aligns.
// Note, this should always be true since we used the root to lookup the RO block.
blockRoot := roBlock.Root()
if root != blockRoot {
return columnErrBuilder(errUnalignedRootAndSignature)
}
}
return nil
}
// state retrieves the state of the corresponding root from the cache if possible, else retrieves it from the state by rooter.
func (dv *RODataColumnsVerifier) state(ctx context.Context, root [fieldparams.RootLength]byte) (state.BeaconState, error) {
// If the parent root is already in the cache, return it.
// If the root is already in the cache, return it.
if st, ok := dv.stateByRoot[root]; ok {
return st, nil
}
// Retrieve the parent state from the state by rooter.
// Retrieve the state from the state by rooter.
st, err := dv.sr.StateByRoot(ctx, root)
if err != nil {
return nil, errors.Wrap(err, "state by root")
}
// Store the parent state in the cache.
// Store the state in the cache.
dv.stateByRoot[root] = st
return st, nil

View File

@@ -933,6 +933,77 @@ func TestDataColumnsSidecarProposerExpected(t *testing.T) {
}
}
func TestSidecarRootAndSignatureAligned(t *testing.T) {
const (
columnSlot = 1
blobCount = 1
)
parentRoot := [fieldparams.RootLength]byte{}
columns := GenerateTestDataColumns(t, parentRoot, columnSlot, blobCount)
firstColumn := columns[0]
t.Run("unknown block", func(t *testing.T) {
verifier := new(Initializer).NewDataColumnsVerifier(columns, RPCDataColumnSidecarRequirements)
err := verifier.SidecarRootAndSignatureAligned(nil)
require.NotNil(t, err)
})
t.Run("signatures mismatch", func(t *testing.T) {
signedBeaconBlockPb := util.NewBeaconBlock()
originalSignature := firstColumn.SignedBlockHeader.Signature
alteredSignature := make([]byte, len(originalSignature))
copy(alteredSignature, originalSignature)
alteredSignature[0] ^= 0xFF // Alter the signature
signedBeaconBlockPb.Signature = alteredSignature
signedBeaconBlock, err := blocks.NewSignedBeaconBlock(signedBeaconBlockPb)
require.NoError(t, err)
roBlock, err := blocks.NewROBlock(signedBeaconBlock)
require.NoError(t, err)
verifier := new(Initializer).NewDataColumnsVerifier(columns, RPCDataColumnSidecarRequirements)
err = verifier.SidecarRootAndSignatureAligned(map[[fieldparams.RootLength]byte]blocks.ROBlock{
firstColumn.BlockRoot(): roBlock,
})
require.NotNil(t, err)
})
t.Run("roots mismatch", func(t *testing.T) {
signedBeaconBlockPb := util.NewBeaconBlock()
signedBeaconBlock, err := blocks.NewSignedBeaconBlock(signedBeaconBlockPb)
require.NoError(t, err)
roBlock, err := blocks.NewROBlock(signedBeaconBlock)
require.NoError(t, err)
verifier := new(Initializer).NewDataColumnsVerifier(columns, RPCDataColumnSidecarRequirements)
err = verifier.SidecarRootAndSignatureAligned(map[[fieldparams.RootLength]byte]blocks.ROBlock{
firstColumn.BlockRoot(): roBlock,
})
require.NotNil(t, err)
})
t.Run("nominal", func(t *testing.T) {
signedBeaconBlockPb := util.NewBeaconBlock()
signedBeaconBlock, err := blocks.NewSignedBeaconBlock(signedBeaconBlockPb)
require.NoError(t, err)
roBlock, err := blocks.NewROBlockWithRoot(signedBeaconBlock, firstColumn.BlockRoot())
require.NoError(t, err)
verifier := new(Initializer).NewDataColumnsVerifier(columns, RPCDataColumnSidecarRequirements)
err = verifier.SidecarRootAndSignatureAligned(map[[fieldparams.RootLength]byte]blocks.ROBlock{
roBlock.Root(): roBlock,
})
require.NoError(t, err)
})
}
func TestColumnRequirementSatisfaction(t *testing.T) {
const (
columnSlot = 1

View File

@@ -69,6 +69,10 @@ var (
// errBatchBlockRootMismatch is returned by VerifiedROBlobs in the scenario where the root of the given signed block
// does not match the block header in one of the corresponding sidecars.
errBatchBlockRootMismatch = errors.Join(ErrBlobInvalid, errors.New("sidecar block header root does not match signed block"))
// errUnalignedRootAndSignature is returned by VerifiedRODataColumns when none of the provided RO blocks
// align with the sidecar block header root and signature.
errUnalignedRootAndSignature = errors.Join(ErrBlobInvalid, errors.New("sidecar block header root and signature do not align with any provided ro blocks"))
)
var (

View File

@@ -49,6 +49,7 @@ type DataColumnsVerifier interface {
SidecarInclusionProven() error
SidecarKzgProofVerified() error
SidecarProposerExpected(ctx context.Context) error
SidecarRootAndSignatureAligned(roBlockByRoot map[[fieldparams.RootLength]byte]blocks.ROBlock) error
}
// NewDataColumnsVerifier is a function signature that can be used to mock a setup where a

View File

@@ -79,18 +79,19 @@ func (*MockBlobVerifier) SatisfyRequirement(_ Requirement) {}
// --------------------
type MockDataColumnsVerifier struct {
ErrValidFields error
ErrCorrectSubnet error
ErrNotFromFutureSlot error
ErrSlotAboveFinalized error
ErrSidecarParentSeen error
ErrSidecarParentValid error
ErrValidProposerSignature error
ErrSidecarParentSlotLower error
ErrSidecarDescendsFromFinalized error
ErrSidecarInclusionProven error
ErrSidecarKzgProofVerified error
ErrSidecarProposerExpected error
ErrValidFields error
ErrCorrectSubnet error
ErrNotFromFutureSlot error
ErrSlotAboveFinalized error
ErrSidecarParentSeen error
ErrSidecarParentValid error
ErrValidProposerSignature error
ErrSidecarParentSlotLower error
ErrSidecarDescendsFromFinalized error
ErrSidecarInclusionProven error
ErrSidecarKzgProofVerified error
ErrSidecarProposerExpected error
ErrSidecarRootAndSignatureAligned error
}
var _ DataColumnsVerifier = &MockDataColumnsVerifier{}
@@ -148,3 +149,7 @@ func (m *MockDataColumnsVerifier) SidecarKzgProofVerified() error {
func (m *MockDataColumnsVerifier) SidecarProposerExpected(_ context.Context) error {
return m.ErrSidecarProposerExpected
}
func (m *MockDataColumnsVerifier) SidecarRootAndSignatureAligned(_ map[[fieldparams.RootLength]byte]blocks.ROBlock) error {
return m.ErrSidecarRootAndSignatureAligned
}