Skip to content

Commit fb21b1a

Browse files
Dump blob sidecars which failed data availability (#8376)
1 parent 795a011 commit fb21b1a

File tree

6 files changed

+104
-8
lines changed

6 files changed

+104
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ the [releases page](https://github.com/Consensys/teku/releases).
1717

1818
### Breaking Changes
1919

20+
- Renamed `--Xp2p-dumps-to-file-enabled` hidden CLI option to `--Xdebug-data-dumping-enabled`
21+
2022
### Additions and Improvements
2123

2224
- Added metadata fields to `/eth/v1/beacon/blob_sidecars/{block_id}` Beacon API response as per https://github.com/ethereum/beacon-APIs/pull/441

ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/forkchoice/ForkChoice.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import static tech.pegasys.teku.infrastructure.logging.P2PLogger.P2P_LOG;
1818
import static tech.pegasys.teku.infrastructure.time.TimeUtilities.secondsToMillis;
1919
import static tech.pegasys.teku.spec.constants.NetworkConstants.INTERVALS_PER_SLOT;
20+
import static tech.pegasys.teku.spec.logic.versions.deneb.blobs.BlobSidecarsValidationResult.INVALID;
2021
import static tech.pegasys.teku.statetransition.forkchoice.StateRootCollector.addParentStateRoots;
2122

2223
import com.google.common.annotations.VisibleForTesting;
@@ -540,21 +541,20 @@ private BlockImportResult importBlockAndState(
540541
payloadResult.getFailureCause().orElseThrow());
541542
}
542543

544+
LOG.debug("blobSidecars validation result: {}", blobSidecarsAndValidationResult::toLogString);
545+
543546
switch (blobSidecarsAndValidationResult.getValidationResult()) {
544-
case VALID, NOT_REQUIRED -> LOG.debug(
545-
"blobSidecars validation result: {}", blobSidecarsAndValidationResult::toLogString);
546547
case NOT_AVAILABLE -> {
547-
LOG.debug(
548-
"blobSidecars validation result: {}", blobSidecarsAndValidationResult::toLogString);
549548
return BlockImportResult.failedDataAvailabilityCheckNotAvailable(
550549
blobSidecarsAndValidationResult.getCause());
551550
}
552551
case INVALID -> {
553-
LOG.error(
554-
"blobSidecars validation result: {}", blobSidecarsAndValidationResult::toLogString);
552+
debugDataDumper.saveInvalidBlobSidecars(
553+
blobSidecarsAndValidationResult.getBlobSidecars(), block);
555554
return BlockImportResult.failedDataAvailabilityCheckInvalid(
556555
blobSidecarsAndValidationResult.getCause());
557556
}
557+
default -> {}
558558
}
559559

560560
final ForkChoiceStrategy forkChoiceStrategy = getForkChoiceStrategy();

ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/DebugDataDumper.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
package tech.pegasys.teku.statetransition.util;
1515

16+
import java.util.List;
1617
import java.util.Optional;
1718
import java.util.function.Supplier;
1819
import org.apache.tuweni.bytes.Bytes;
1920
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
21+
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
2022
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
2123

2224
public interface DebugDataDumper {
@@ -42,6 +44,10 @@ public void saveInvalidBlock(
4244
final SignedBeaconBlock block,
4345
final String failureReason,
4446
final Optional<Throwable> failureCause) {}
47+
48+
@Override
49+
public void saveInvalidBlobSidecars(
50+
final List<BlobSidecar> blobSidecars, final SignedBeaconBlock block) {}
4551
};
4652

4753
void saveGossipMessageDecodingError(
@@ -58,4 +64,6 @@ void saveGossipRejectedMessage(
5864

5965
void saveInvalidBlock(
6066
SignedBeaconBlock block, String failureReason, Optional<Throwable> failureCause);
67+
68+
void saveInvalidBlobSidecars(List<BlobSidecar> blobSidecars, SignedBeaconBlock block);
6169
}

ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/DebugDataFileDumper.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.sql.Date;
2424
import java.text.DateFormat;
2525
import java.text.SimpleDateFormat;
26+
import java.util.List;
2627
import java.util.Optional;
2728
import java.util.function.Supplier;
2829
import org.apache.logging.log4j.LogManager;
@@ -31,6 +32,7 @@
3132
import org.apache.tuweni.bytes.Bytes32;
3233
import tech.pegasys.teku.infrastructure.time.TimeProvider;
3334
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
35+
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
3436
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
3537

3638
public class DebugDataFileDumper implements DebugDataDumper {
@@ -41,6 +43,7 @@ public class DebugDataFileDumper implements DebugDataDumper {
4143
private static final String DECODING_ERROR_SUB_DIR = "decoding_error";
4244
private static final String REJECTED_SUB_DIR = "rejected";
4345
private static final String INVALID_BLOCK_DIR = "invalid_blocks";
46+
private static final String INVALID_BLOB_SIDECARS_DIR = "invalid_blob_sidecars";
4447

4548
private boolean enabled;
4649
private final Path directory;
@@ -125,14 +128,41 @@ public void saveInvalidBlock(
125128
"invalid block", Path.of(INVALID_BLOCK_DIR).resolve(fileName), block.sszSerialize());
126129
if (success) {
127130
LOG.warn(
128-
"Rejecting invalid block at slot {} with root {} because {}",
131+
"Rejecting invalid block at slot {} with root {}, reason: {}, cause: {}",
129132
slot,
130133
blockRoot,
131134
failureReason,
132135
failureCause.orElse(null));
133136
}
134137
}
135138

139+
@Override
140+
public void saveInvalidBlobSidecars(
141+
final List<BlobSidecar> blobSidecars, final SignedBeaconBlock block) {
142+
if (!enabled) {
143+
return;
144+
}
145+
final String kzgCommitmentsFileName =
146+
String.format(
147+
"%s_%s_kzg_commitments.ssz", block.getSlot(), block.getRoot().toUnprefixedHexString());
148+
saveBytesToFile(
149+
"kzg commitments",
150+
Path.of(INVALID_BLOB_SIDECARS_DIR).resolve(kzgCommitmentsFileName),
151+
block.getMessage().getBody().getOptionalBlobKzgCommitments().orElseThrow().sszSerialize());
152+
blobSidecars.forEach(
153+
blobSidecar -> {
154+
final UInt64 slot = blobSidecar.getSlot();
155+
final Bytes32 blockRoot = blobSidecar.getBlockRoot();
156+
final UInt64 index = blobSidecar.getIndex();
157+
final String fileName =
158+
String.format("%s_%s_%s.ssz", slot, blockRoot.toUnprefixedHexString(), index);
159+
saveBytesToFile(
160+
"blob sidecar",
161+
Path.of(INVALID_BLOB_SIDECARS_DIR).resolve(fileName),
162+
blobSidecar.sszSerialize());
163+
});
164+
}
165+
136166
@VisibleForTesting
137167
boolean saveBytesToFile(
138168
final String description, final Path relativeFilePath, final Bytes bytes) {

ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/forkchoice/ForkChoiceTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,31 @@ void onBlock_shouldFailIfBlobsAreNotAvailable() {
247247
verify(blobSidecarsAvailabilityChecker).getAvailabilityCheckResult();
248248
}
249249

250+
@Test
251+
void onBlock_shouldFailIfBlobsAreInvalid() {
252+
setupWithSpec(TestSpecFactory.createMinimalDeneb());
253+
final SignedBlockAndState blockAndState = chainBuilder.generateBlockAtSlot(ONE);
254+
storageSystem.chainUpdater().advanceCurrentSlotToAtLeast(blockAndState.getSlot());
255+
final List<BlobSidecar> blobSidecars =
256+
storageSystem
257+
.chainStorage()
258+
.getBlobSidecarsBySlotAndBlockRoot(blockAndState.getSlotAndBlockRoot())
259+
.join();
260+
261+
when(blobSidecarsAvailabilityChecker.getAvailabilityCheckResult())
262+
.thenReturn(
263+
SafeFuture.completedFuture(
264+
BlobSidecarsAndValidationResult.invalidResult(blobSidecars)));
265+
266+
importBlockAndAssertFailure(
267+
blockAndState, FailureReason.FAILED_DATA_AVAILABILITY_CHECK_INVALID);
268+
269+
verify(blobSidecarManager).createAvailabilityChecker(blockAndState.getBlock());
270+
verify(blobSidecarsAvailabilityChecker).initiateDataAvailabilityCheck();
271+
verify(blobSidecarsAvailabilityChecker).getAvailabilityCheckResult();
272+
verify(debugDataDumper).saveInvalidBlobSidecars(blobSidecars, blockAndState.getBlock());
273+
}
274+
250275
@Test
251276
void onBlock_consensusValidationShouldNotResolveWhenDataAvailabilityFails() {
252277
setupWithSpec(TestSpecFactory.createMinimalDeneb());

ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/DebugDataFileDumperTest.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.sql.Date;
2525
import java.text.DateFormat;
2626
import java.text.SimpleDateFormat;
27+
import java.util.List;
2728
import java.util.Optional;
2829
import org.apache.tuweni.bytes.Bytes;
2930
import org.junit.jupiter.api.Test;
@@ -33,12 +34,13 @@
3334
import tech.pegasys.teku.infrastructure.time.StubTimeProvider;
3435
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
3536
import tech.pegasys.teku.spec.TestSpecFactory;
37+
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
3638
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
3739
import tech.pegasys.teku.spec.util.DataStructureUtil;
3840

3941
class DebugDataFileDumperTest {
4042
final DataStructureUtil dataStructureUtil =
41-
new DataStructureUtil(TestSpecFactory.createDefault());
43+
new DataStructureUtil(TestSpecFactory.createMinimalDeneb());
4244
private final StubTimeProvider timeProvider = StubTimeProvider.withTimeInSeconds(10_000);
4345

4446
@Test
@@ -91,6 +93,35 @@ void saveInvalidBlockToFile_shouldSaveToFile(@TempDir final Path tempDir) {
9193
checkBytesSavedToFile(expectedFile, block.sszSerialize());
9294
}
9395

96+
@Test
97+
void saveInvalidBlobSidecars_shouldSaveToFiles(@TempDir final Path tempDir) {
98+
final DebugDataFileDumper dumper = new DebugDataFileDumper(tempDir);
99+
final SignedBeaconBlock block = dataStructureUtil.randomSignedBeaconBlock();
100+
final List<BlobSidecar> blobSidecars = dataStructureUtil.randomBlobSidecarsForBlock(block);
101+
dumper.saveInvalidBlobSidecars(blobSidecars, block);
102+
103+
final String kzgCommitmentsFileName =
104+
String.format(
105+
"%s_%s_kzg_commitments.ssz", block.getSlot(), block.getRoot().toUnprefixedHexString());
106+
final Path expectedKzgCommitmentsFileName =
107+
tempDir.resolve("invalid_blob_sidecars").resolve(kzgCommitmentsFileName);
108+
checkBytesSavedToFile(
109+
expectedKzgCommitmentsFileName,
110+
block.getMessage().getBody().getOptionalBlobKzgCommitments().orElseThrow().sszSerialize());
111+
112+
blobSidecars.forEach(
113+
blobSidecar -> {
114+
final String fileName =
115+
String.format(
116+
"%s_%s_%s.ssz",
117+
blobSidecar.getSlot(),
118+
blobSidecar.getBlockRoot().toUnprefixedHexString(),
119+
blobSidecar.getIndex());
120+
final Path expectedFile = tempDir.resolve("invalid_blob_sidecars").resolve(fileName);
121+
checkBytesSavedToFile(expectedFile, blobSidecar.sszSerialize());
122+
});
123+
}
124+
94125
@Test
95126
void saveBytesToFile_shouldNotThrowExceptionWhenNoDirectory(@TempDir final Path tempDir) {
96127
final DebugDataFileDumper dumper = new DebugDataFileDumper(tempDir);

0 commit comments

Comments
 (0)