Skip to content

Commit

Permalink
optimize indexed attestation validation
Browse files Browse the repository at this point in the history
  • Loading branch information
tbenr committed Dec 19, 2024
1 parent 7551460 commit 4fb038d
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright Consensys Software Inc., 2024
*
* 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.benchmarks;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import org.apache.tuweni.bytes.Bytes;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import tech.pegasys.teku.benchmarks.util.CustomRunner;
import tech.pegasys.teku.bls.BLSSignatureVerifier;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.TestSpecFactory;
import tech.pegasys.teku.spec.datastructures.operations.IndexedAttestation;
import tech.pegasys.teku.spec.datastructures.state.Fork;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.logic.common.util.AsyncBLSSignatureVerifier;

@State(Scope.Thread)
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 2, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class IndexedAttestationValidationBenchmark {
Spec spec;
BeaconState beaconState;
IndexedAttestation indexedAttestation;
Fork fork;
AsyncBLSSignatureVerifier asyncBLSSignatureVerifier;

@Setup(Level.Trial)
public void init() throws Exception {
spec = TestSpecFactory.createMainnetDeneb();

final byte[] stateBytes = Files.readAllBytes(Path.of("/Users/tbenr/state.ssz"));
final byte[] indexedAttestationBytes =
Files.readAllBytes(Path.of("/Users/tbenr/attestation.ssz"));

beaconState = spec.deserializeBeaconState(Bytes.of(stateBytes));
indexedAttestation =
spec.atSlot(beaconState.getSlot())
.getSchemaDefinitions()
.getIndexedAttestationSchema()
.sszDeserialize(Bytes.of(indexedAttestationBytes));

fork = spec.getForkSchedule().getFork(spec.computeEpochAtSlot(beaconState.getSlot()));
asyncBLSSignatureVerifier = AsyncBLSSignatureVerifier.wrap(BLSSignatureVerifier.NO_OP);
}

@Benchmark
public void validateIndexedAttestation(Blackhole bh) {
bh.consume(
spec.atSlot(beaconState.getSlot())
.getAttestationUtil()
.isValidIndexedAttestationAsync(
fork, beaconState, indexedAttestation, asyncBLSSignatureVerifier)
.join());
}

public static void main(String[] args) throws Exception {
IndexedAttestationValidationBenchmark benchmark = new IndexedAttestationValidationBenchmark();
benchmark.init();
new CustomRunner(2, 10000).withBench(benchmark::validateIndexedAttestation).run();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
import static com.google.common.base.Preconditions.checkArgument;
import static tech.pegasys.teku.infrastructure.async.SafeFuture.completedFuture;

import com.google.common.collect.Comparators;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.Comparator;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -236,20 +235,28 @@ public SafeFuture<AttestationProcessingResult> isValidIndexedAttestationAsync(
final AsyncBLSSignatureVerifier signatureVerifier) {
final SszUInt64List indices = indexedAttestation.getAttestingIndices();

if (indices.isEmpty()
|| !Comparators.isInStrictOrder(indices.asListUnboxed(), Comparator.naturalOrder())) {
if (indices.isEmpty()) {
return completedFuture(
AttestationProcessingResult.invalid("Attesting indices are not sorted"));
AttestationProcessingResult.invalid("Attesting indices must not be empty"));
}

final List<BLSPublicKey> pubkeys =
indices
.streamUnboxed()
.flatMap(i -> beaconStateAccessors.getValidatorPubKey(state, i).stream())
.toList();
if (pubkeys.size() < indices.size()) {
return completedFuture(
AttestationProcessingResult.invalid("Attesting indices include non-existent validator"));
UInt64 lastIndex = null;
final List<BLSPublicKey> pubkeys = new ArrayList<>(indices.size());

for (final UInt64 index : indices.asListUnboxed()) {
if (lastIndex != null && index.isLessThanOrEqualTo(lastIndex)) {
return completedFuture(
AttestationProcessingResult.invalid("Attesting indices are not sorted"));
}
lastIndex = index;
final Optional<BLSPublicKey> validatorPubKey =
beaconStateAccessors.getValidatorPubKey(state, index);
if (validatorPubKey.isEmpty()) {
return completedFuture(
AttestationProcessingResult.invalid(
"Attesting indices include non-existent validator"));
}
pubkeys.add(validatorPubKey.get());
}

return validateAttestationDataSignature(
Expand Down

0 comments on commit 4fb038d

Please sign in to comment.