Skip to content

Commit

Permalink
Add BlobSidecar by root RPC verification (Consensys#7645)
Browse files Browse the repository at this point in the history
  • Loading branch information
zilm13 authored Nov 2, 2023
1 parent fbfd906 commit ae5974d
Show file tree
Hide file tree
Showing 9 changed files with 406 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import org.apache.logging.log4j.LogManager;
Expand All @@ -34,7 +35,9 @@
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.kzg.KZG;
import tech.pegasys.teku.networking.eth2.rpc.beaconchain.BeaconChainMethods;
import tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarByRootValidator;
import tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsByRangeListenerValidatingProxy;
import tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsByRootListenerValidatingProxy;
import tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlocksByRangeListenerWrapper;
import tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.MetadataMessagesFactory;
import tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.StatusMessageFactory;
Expand Down Expand Up @@ -246,7 +249,8 @@ public SafeFuture<Void> requestBlobSidecarsByRoot(
method,
new BlobSidecarsByRootRequestMessage(
blobSidecarsByRootRequestMessageSchema.get(), blobIdentifiers),
listener))
new BlobSidecarsByRootListenerValidatingProxy(
this, spec, listener, kzg, blobIdentifiers)))
.orElse(failWithUnsupportedMethodException("BlobSidecarsByRoot"));
}

Expand Down Expand Up @@ -278,10 +282,19 @@ public SafeFuture<Optional<BlobSidecar>> requestBlobSidecarByRoot(
.map(
method ->
requestOptionalItem(
method,
new BlobSidecarsByRootRequestMessage(
blobSidecarsByRootRequestMessageSchema.get(),
Collections.singletonList(blobIdentifier))))
method,
new BlobSidecarsByRootRequestMessage(
blobSidecarsByRootRequestMessageSchema.get(),
Collections.singletonList(blobIdentifier)))
.thenPeek(
maybeBlobSidecar ->
maybeBlobSidecar.ifPresent(
blobSidecar -> {
final BlobSidecarByRootValidator blobSidecarByRootValidator =
new BlobSidecarByRootValidator(
this, spec, kzg, Set.of(blobIdentifier));
blobSidecarByRootValidator.validateBlobSidecar(blobSidecar);
})))
.orElse(failWithUnsupportedMethodException("BlobSidecarsByRoot"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,19 @@
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;

public class AbstractBlobSidecarsValidatingProxy {
public class AbstractBlobSidecarsValidator {

private static final Logger LOG = LogManager.getLogger();

protected final Spec spec;
protected final KZG kzg;

public AbstractBlobSidecarsValidatingProxy(final Spec spec, final KZG kzg) {
public AbstractBlobSidecarsValidator(final Spec spec, final KZG kzg) {
this.spec = spec;
this.kzg = kzg;
}

protected boolean verifyBlobSidecar(final BlobSidecar blobSidecar) {
boolean verifyBlobSidecarKzg(final BlobSidecar blobSidecar) {
try {
return spec.atSlot(blobSidecar.getSlot()).miscHelpers().verifyBlobKzgProof(kzg, blobSidecar);
} catch (final KZGException ex) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright Consensys Software Inc., 2022
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods;

import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_KZG_VERIFICATION_FAILED;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_UNEXPECTED_IDENTIFIER;

import java.util.Set;
import tech.pegasys.teku.kzg.KZG;
import tech.pegasys.teku.networking.p2p.peer.Peer;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobIdentifier;

public class BlobSidecarByRootValidator extends AbstractBlobSidecarsValidator {

private final Peer peer;
private final Set<BlobIdentifier> expectedBlobIdentifiers;

public BlobSidecarByRootValidator(
final Peer peer,
final Spec spec,
final KZG kzg,
final Set<BlobIdentifier> expectedBlobIdentifiers) {
super(spec, kzg);
this.peer = peer;
this.expectedBlobIdentifiers = expectedBlobIdentifiers;
}

public void validateBlobSidecar(final BlobSidecar blobSidecar) {
final BlobIdentifier blobIdentifier =
new BlobIdentifier(blobSidecar.getBlockRoot(), blobSidecar.getIndex());
if (!expectedBlobIdentifiers.contains(blobIdentifier)) {
throw new BlobSidecarsResponseInvalidResponseException(
peer, BLOB_SIDECAR_UNEXPECTED_IDENTIFIER);
}

if (!verifyBlobSidecarKzg(blobSidecar)) {
throw new BlobSidecarsResponseInvalidResponseException(
peer, BLOB_SIDECAR_KZG_VERIFICATION_FAILED);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@

package tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods;

import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsByRangeResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_KZG_VERIFICATION_FAILED;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsByRangeResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_SLOT_NOT_IN_RANGE;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsByRangeResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_UNEXPECTED_INDEX;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsByRangeResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_UNEXPECTED_SLOT;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsByRangeResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_UNKNOWN_PARENT;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_KZG_VERIFICATION_FAILED;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_SLOT_NOT_IN_RANGE;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_UNEXPECTED_INDEX;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_UNEXPECTED_SLOT;
import static tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods.BlobSidecarsResponseInvalidResponseException.InvalidResponseType.BLOB_SIDECAR_UNKNOWN_PARENT;

import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
Expand All @@ -29,7 +29,7 @@
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;

public class BlobSidecarsByRangeListenerValidatingProxy extends AbstractBlobSidecarsValidatingProxy
public class BlobSidecarsByRangeListenerValidatingProxy extends AbstractBlobSidecarsValidator
implements RpcResponseListener<BlobSidecar> {

private final Peer peer;
Expand Down Expand Up @@ -62,20 +62,20 @@ public SafeFuture<?> onResponse(final BlobSidecar blobSidecar) {
() -> {
final UInt64 blobSidecarSlot = blobSidecar.getSlot();
if (!blobSidecarSlotIsInRange(blobSidecarSlot)) {
throw new BlobSidecarsByRangeResponseInvalidResponseException(
throw new BlobSidecarsResponseInvalidResponseException(
peer, BLOB_SIDECAR_SLOT_NOT_IN_RANGE);
}

if (blobSidecar.getIndex().isGreaterThanOrEqualTo(maxBlobsPerBlock)) {
throw new BlobSidecarsByRangeResponseInvalidResponseException(
throw new BlobSidecarsResponseInvalidResponseException(
peer, BLOB_SIDECAR_UNEXPECTED_INDEX);
}

final BlobSidecarSummary blobSidecarSummary = BlobSidecarSummary.create(blobSidecar);
verifyBlobSidecarIsAfterLast(blobSidecarSummary);

if (!verifyBlobSidecar(blobSidecar)) {
throw new BlobSidecarsByRangeResponseInvalidResponseException(
if (!verifyBlobSidecarKzg(blobSidecar)) {
throw new BlobSidecarsResponseInvalidResponseException(
peer, BLOB_SIDECAR_KZG_VERIFICATION_FAILED);
}

Expand All @@ -92,34 +92,29 @@ private boolean blobSidecarSlotIsInRange(final UInt64 blobSidecarSlot) {
private void verifyBlobSidecarIsAfterLast(final BlobSidecarSummary blobSidecarSummary) {
if (maybeLastBlobSidecarSummary.isEmpty()) {
if (!blobSidecarSummary.index().equals(UInt64.ZERO)) {
throw new BlobSidecarsByRangeResponseInvalidResponseException(
peer, BLOB_SIDECAR_UNEXPECTED_INDEX);
throw new BlobSidecarsResponseInvalidResponseException(peer, BLOB_SIDECAR_UNEXPECTED_INDEX);
}
return;
}

if (blobSidecarSummary.inTheSameBlock(maybeLastBlobSidecarSummary.get())) {
if (!blobSidecarSummary.index().equals(maybeLastBlobSidecarSummary.get().index().plus(1))) {
throw new BlobSidecarsByRangeResponseInvalidResponseException(
peer, BLOB_SIDECAR_UNEXPECTED_INDEX);
throw new BlobSidecarsResponseInvalidResponseException(peer, BLOB_SIDECAR_UNEXPECTED_INDEX);
}
} else {

if (!blobSidecarSummary.index().equals(UInt64.ZERO)) {
throw new BlobSidecarsByRangeResponseInvalidResponseException(
peer, BLOB_SIDECAR_UNEXPECTED_INDEX);
throw new BlobSidecarsResponseInvalidResponseException(peer, BLOB_SIDECAR_UNEXPECTED_INDEX);
}

if (!blobSidecarSummary
.blockParentRoot()
.equals(maybeLastBlobSidecarSummary.get().blockRoot())) {
throw new BlobSidecarsByRangeResponseInvalidResponseException(
peer, BLOB_SIDECAR_UNKNOWN_PARENT);
throw new BlobSidecarsResponseInvalidResponseException(peer, BLOB_SIDECAR_UNKNOWN_PARENT);
}

if (!blobSidecarSummary.slot().isGreaterThan(maybeLastBlobSidecarSummary.get().slot())) {
throw new BlobSidecarsByRangeResponseInvalidResponseException(
peer, BLOB_SIDECAR_UNEXPECTED_SLOT);
throw new BlobSidecarsResponseInvalidResponseException(peer, BLOB_SIDECAR_UNEXPECTED_SLOT);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright Consensys Software Inc., 2022
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.networking.eth2.rpc.beaconchain.methods;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.kzg.KZG;
import tech.pegasys.teku.networking.p2p.peer.Peer;
import tech.pegasys.teku.networking.p2p.rpc.RpcResponseListener;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.BlobIdentifier;

public class BlobSidecarsByRootListenerValidatingProxy implements RpcResponseListener<BlobSidecar> {

private final Peer peer;
private final Spec spec;
private final RpcResponseListener<BlobSidecar> blobSidecarResponseListener;
private final Set<BlobIdentifier> expectedBlobIdentifiers;
private final KZG kzg;

public BlobSidecarsByRootListenerValidatingProxy(
final Peer peer,
final Spec spec,
final RpcResponseListener<BlobSidecar> blobSidecarResponseListener,
final KZG kzg,
final List<BlobIdentifier> expectedBlobIdentifiers) {
this.peer = peer;
this.spec = spec;
this.blobSidecarResponseListener = blobSidecarResponseListener;
this.kzg = kzg;
this.expectedBlobIdentifiers = new HashSet<>(expectedBlobIdentifiers);
}

@Override
public SafeFuture<?> onResponse(final BlobSidecar blobSidecar) {
return SafeFuture.of(
() -> {
final BlobSidecarByRootValidator blobSidecarByRootValidator =
new BlobSidecarByRootValidator(peer, spec, kzg, expectedBlobIdentifiers);
blobSidecarByRootValidator.validateBlobSidecar(blobSidecar);
return blobSidecarResponseListener.onResponse(blobSidecar);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@
import tech.pegasys.teku.networking.eth2.rpc.core.InvalidResponseException;
import tech.pegasys.teku.networking.p2p.peer.Peer;

public class BlobSidecarsByRangeResponseInvalidResponseException extends InvalidResponseException {
public class BlobSidecarsResponseInvalidResponseException extends InvalidResponseException {

public BlobSidecarsByRangeResponseInvalidResponseException(
public BlobSidecarsResponseInvalidResponseException(
Peer peer, InvalidResponseType invalidResponseType) {
super(
String.format(
"Received invalid response from peer %s: %s", peer, invalidResponseType.describe()));
}

public BlobSidecarsByRangeResponseInvalidResponseException(
InvalidResponseType invalidResponseType) {
public BlobSidecarsResponseInvalidResponseException(InvalidResponseType invalidResponseType) {
super("Received invalid response: " + invalidResponseType.describe());
}

Expand All @@ -36,7 +35,8 @@ public enum InvalidResponseType {
BLOB_SIDECAR_UNEXPECTED_INDEX("BlobSidecar with unexpected index"),
BLOB_SIDECAR_UNKNOWN_PARENT(
"BlobSidecar parent blockRoot doesn't match previous blobSidecar blockRoot"),
BLOB_SIDECAR_UNEXPECTED_SLOT("BlobSidecar slot is from block with incorrect slot");
BLOB_SIDECAR_UNEXPECTED_SLOT("BlobSidecar slot is from block with incorrect slot"),
BLOB_SIDECAR_UNEXPECTED_IDENTIFIER("BlobSidecar is not within requested identifiers");

private final String description;

Expand Down
Loading

0 comments on commit ae5974d

Please sign in to comment.