From 4292c45629188031f617dc099bf1dfaf6c2fc973 Mon Sep 17 00:00:00 2001 From: Justin Traglia <95511699+jtraglia@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:26:32 -0600 Subject: [PATCH 1/3] Use 16-bit random value in validator filter (#8916) --------- Signed-off-by: Paul Harris Co-authored-by: Paul Harris Co-authored-by: Mehdi AOUADI Co-authored-by: Enrico Del Fante --- .../helpers/BeaconStateAccessorsElectra.java | 48 ++++++++++++++++ .../electra/helpers/MiscHelpersElectra.java | 52 +++++++++++++++--- .../helpers/MiscHelpersElectraTest.java | 2 +- .../tech/pegasys/teku/fuzz/FuzzUtilTest.java | 35 ++++++------ .../attestation.ssz_snappy | Bin 150 -> 150 bytes .../one_basic_attestation/post.ssz_snappy | Bin 6813 -> 6497 bytes .../one_basic_attestation/pre.ssz_snappy | Bin 6789 -> 6473 bytes 7 files changed, 111 insertions(+), 26 deletions(-) diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateAccessorsElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateAccessorsElectra.java index 10685789ef5..f1b29f6eb22 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateAccessorsElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateAccessorsElectra.java @@ -14,13 +14,24 @@ package tech.pegasys.teku.spec.logic.versions.electra.helpers; import static com.google.common.base.Preconditions.checkArgument; +import static tech.pegasys.teku.infrastructure.crypto.Hash.getSha256Instance; +import static tech.pegasys.teku.spec.logic.common.helpers.MathHelpers.bytesToUInt64; +import static tech.pegasys.teku.spec.logic.common.helpers.MathHelpers.uint64ToBytes; +import static tech.pegasys.teku.spec.logic.versions.electra.helpers.MiscHelpersElectra.MAX_RANDOM_VALUE; +import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import java.util.List; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.infrastructure.crypto.Sha256; +import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.config.SpecConfig; import tech.pegasys.teku.spec.config.SpecConfigDeneb; import tech.pegasys.teku.spec.config.SpecConfigElectra; +import tech.pegasys.teku.spec.constants.Domain; +import tech.pegasys.teku.spec.datastructures.state.Validator; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; @@ -105,4 +116,41 @@ public static BeaconStateAccessorsElectra required( public IntList getNextSyncCommitteeIndices(final BeaconState state) { return getNextSyncCommitteeIndices(state, configElectra.getMaxEffectiveBalanceElectra()); } + + @Override + protected IntList getNextSyncCommitteeIndices( + final BeaconState state, final UInt64 maxEffectiveBalance) { + final UInt64 epoch = getCurrentEpoch(state).plus(1); + final IntList activeValidatorIndices = getActiveValidatorIndices(state, epoch); + final int activeValidatorCount = activeValidatorIndices.size(); + checkArgument(activeValidatorCount > 0, "Provided state has no active validators"); + + final Bytes32 seed = getSeed(state, epoch, Domain.SYNC_COMMITTEE); + final SszList validators = state.getValidators(); + final IntList syncCommitteeIndices = new IntArrayList(); + final int syncCommitteeSize = configElectra.getSyncCommitteeSize(); + final Sha256 sha256 = getSha256Instance(); + + int i = 0; + Bytes randomBytes = null; + while (syncCommitteeIndices.size() < syncCommitteeSize) { + if (i % 16 == 0) { + randomBytes = Bytes.wrap(sha256.digest(seed, uint64ToBytes(Math.floorDiv(i, 16L)))); + } + final int shuffledIndex = + miscHelpers.computeShuffledIndex(i % activeValidatorCount, activeValidatorCount, seed); + final int candidateIndex = activeValidatorIndices.getInt(shuffledIndex); + final int offset = (i % 16) * 2; + final UInt64 randomValue = bytesToUInt64(randomBytes.slice(offset, 2)); + final UInt64 effectiveBalance = validators.get(candidateIndex).getEffectiveBalance(); + if (effectiveBalance + .times(MAX_RANDOM_VALUE) + .isGreaterThanOrEqualTo(maxEffectiveBalance.times(randomValue))) { + syncCommitteeIndices.add(candidateIndex); + } + i++; + } + + return syncCommitteeIndices; + } } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectra.java index e5c8869e9ad..387f1ecacb7 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectra.java @@ -13,13 +13,19 @@ package tech.pegasys.teku.spec.logic.versions.electra.helpers; +import static com.google.common.base.Preconditions.checkArgument; +import static tech.pegasys.teku.infrastructure.crypto.Hash.getSha256Instance; import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ZERO; import static tech.pegasys.teku.spec.config.SpecConfig.FAR_FUTURE_EPOCH; +import static tech.pegasys.teku.spec.logic.common.helpers.MathHelpers.bytesToUInt64; +import static tech.pegasys.teku.spec.logic.common.helpers.MathHelpers.uint64ToBytes; import it.unimi.dsi.fastutil.ints.IntList; import java.util.Optional; +import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.bls.BLSPublicKey; +import tech.pegasys.teku.infrastructure.crypto.Sha256; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.config.SpecConfigDeneb; import tech.pegasys.teku.spec.config.SpecConfigElectra; @@ -33,6 +39,7 @@ import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; public class MiscHelpersElectra extends MiscHelpersDeneb { + public static final UInt64 MAX_RANDOM_VALUE = UInt64.valueOf(65535); private final SpecConfigElectra specConfigElectra; private final PredicatesElectra predicatesElectra; @@ -68,6 +75,44 @@ public int computeProposerIndex( SpecConfigElectra.required(specConfig).getMaxEffectiveBalanceElectra()); } + @Override + protected int computeProposerIndex( + final BeaconState state, + final IntList indices, + final Bytes32 seed, + final UInt64 maxEffectiveBalance) { + checkArgument(!indices.isEmpty(), "compute_proposer_index indices must not be empty"); + + final Sha256 sha256 = getSha256Instance(); + + int i = 0; + final int total = indices.size(); + Bytes randomBytes = null; + while (true) { + final int candidateIndex = indices.getInt(computeShuffledIndex(i % total, total, seed)); + if (i % 16 == 0) { + randomBytes = Bytes.wrap(sha256.digest(seed, uint64ToBytes(Math.floorDiv(i, 16L)))); + } + final int offset = (i % 16) * 2; + final UInt64 randomValue = bytesToUInt64(randomBytes.slice(offset, 2)); + final UInt64 validatorEffectiveBalance = + state.getValidators().get(candidateIndex).getEffectiveBalance(); + if (validatorEffectiveBalance + .times(MAX_RANDOM_VALUE) + .isGreaterThanOrEqualTo(maxEffectiveBalance.times(randomValue))) { + return candidateIndex; + } + i++; + } + } + + @Override + public UInt64 getMaxEffectiveBalance(final Validator validator) { + return predicatesElectra.hasCompoundingWithdrawalCredential(validator) + ? specConfigElectra.getMaxEffectiveBalanceElectra() + : specConfigElectra.getMinActivationBalance(); + } + @Override public Validator getValidatorFromDeposit( final BLSPublicKey pubkey, final Bytes32 withdrawalCredentials, final UInt64 amount) { @@ -91,13 +136,6 @@ public Validator getValidatorFromDeposit( return validator.withEffectiveBalance(validatorEffectiveBalance); } - @Override - public UInt64 getMaxEffectiveBalance(final Validator validator) { - return predicatesElectra.hasCompoundingWithdrawalCredential(validator) - ? specConfigElectra.getMaxEffectiveBalanceElectra() - : specConfigElectra.getMinActivationBalance(); - } - @Override public boolean isFormerDepositMechanismDisabled(final BeaconState state) { // if the next deposit to be processed by Eth1Data poll has the index of the first deposit diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectraTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectraTest.java index 86acdd78d47..a88afadbc15 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectraTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectraTest.java @@ -129,7 +129,7 @@ void consolidatedValidatorsMoreLikelyToPropose() { proposerIndexCount++; } } - assertThat(proposerIndexCount).isEqualTo(4); + assertThat(proposerIndexCount).isEqualTo(5); } private BeaconState randomStateWithConsolidatedValidator(final int consolidationAmount) { diff --git a/fuzz/src/test/java/tech/pegasys/teku/fuzz/FuzzUtilTest.java b/fuzz/src/test/java/tech/pegasys/teku/fuzz/FuzzUtilTest.java index 796c56e94fd..3e69f57fc11 100644 --- a/fuzz/src/test/java/tech/pegasys/teku/fuzz/FuzzUtilTest.java +++ b/fuzz/src/test/java/tech/pegasys/teku/fuzz/FuzzUtilTest.java @@ -15,6 +15,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.fasterxml.jackson.core.JsonProcessingException; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; @@ -22,7 +23,6 @@ import java.util.Optional; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.junit.BouncyCastleExtension; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.xerial.snappy.Snappy; @@ -39,6 +39,8 @@ import tech.pegasys.teku.fuzz.input.SyncAggregateFuzzInput; import tech.pegasys.teku.fuzz.input.VoluntaryExitFuzzInput; import tech.pegasys.teku.fuzz.input.WithdrawalRequestFuzzInput; +import tech.pegasys.teku.infrastructure.json.JsonUtil; +import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition; import tech.pegasys.teku.infrastructure.ssz.SszData; import tech.pegasys.teku.infrastructure.ssz.schema.SszSchema; import tech.pegasys.teku.spec.Spec; @@ -63,6 +65,7 @@ import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; import tech.pegasys.teku.spec.datastructures.operations.SignedVoluntaryExit; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateSchemaElectra; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; @@ -92,7 +95,8 @@ class FuzzUtilTest { // https://github.com/ethereum/consensus-specs/tree/dev/tests/generators @Test - public void fuzzAttestation_minimal() { + @SuppressWarnings("unchecked") + public void fuzzAttestation_minimal() throws JsonProcessingException { final FuzzUtil fuzzUtil = new FuzzUtil(false, true); final Path testCaseDir = @@ -102,21 +106,24 @@ public void fuzzAttestation_minimal() { testCaseDir.resolve("attestation.ssz_snappy"), spec.forMilestone(SpecMilestone.ELECTRA).getSchemaDefinitions().getAttestationSchema()); final BeaconState preState = loadSsz(testCaseDir.resolve("pre.ssz_snappy"), beaconStateSchema); - final BeaconState postState = + final BeaconStateElectra postState = loadSsz(testCaseDir.resolve("post.ssz_snappy"), beaconStateSchema); - AttestationFuzzInput input = new AttestationFuzzInput(spec, preState, data); - byte[] rawInput = input.sszSerialize().toArrayUnsafe(); - Optional result = fuzzUtil.fuzzAttestation(rawInput).map(Bytes::wrap); + final AttestationFuzzInput input = new AttestationFuzzInput(spec, preState, data); + final byte[] rawInput = input.sszSerialize().toArrayUnsafe(); + final Optional result = fuzzUtil.fuzzAttestation(rawInput).map(Bytes::wrap); - Bytes expected = postState.sszSerialize(); assertThat(result).isNotEmpty(); - assertThat(result.get()).isEqualTo(expected); + final BeaconStateElectra resultState = + BeaconStateElectra.required(spec.deserializeBeaconState(result.get())); + DeserializableTypeDefinition t = + (DeserializableTypeDefinition) + resultState.getSchema().getJsonTypeDefinition(); + assertThat(JsonUtil.prettySerialize(resultState, t)) + .isEqualTo(JsonUtil.prettySerialize(postState, t)); } @Test - // TODO: re-enable when we merge #8916 - @Disabled("requires Use 16-bit random value in validator filter #8916") public void fuzzAttesterSlashing_minimal() { final FuzzUtil fuzzUtil = new FuzzUtil(false, true); @@ -142,8 +149,6 @@ public void fuzzAttesterSlashing_minimal() { } @Test - // TODO: re-enable when we merge #8916 - @Disabled("requires Use 16-bit random value in validator filter #8916") public void fuzzBlock_minimal() { final FuzzUtil fuzzUtil = new FuzzUtil(false, true); @@ -171,8 +176,6 @@ public void fuzzBlock_minimal() { } @Test - // TODO: re-enable when we merge #8916 - @Disabled("requires Use 16-bit random value in validator filter #8916") public void fuzzBlockHeader_minimal() { final FuzzUtil fuzzUtil = new FuzzUtil(false, true); @@ -213,8 +216,6 @@ public void fuzzDeposit_minimal() { } @Test - // TODO: re-enable when we merge #8916 - @Disabled("requires Use 16-bit random value in validator filter #8916") public void fuzzProposerSlashing_minimal() { final FuzzUtil fuzzUtil = new FuzzUtil(false, true); @@ -385,8 +386,6 @@ public void fuzzWithdrawalRequest_minimal() { assertThat(result.get()).isEqualTo(expected); } - // TODO fix as part of https://github.com/Consensys/teku/pull/8876 - @Disabled("Disabling until we have a fix for this") @Test public void fuzzConsolidationRequest_minimal() { final FuzzUtil fuzzUtil = new FuzzUtil(false, true); diff --git a/fuzz/src/test/resources/minimal/operations/attestation/pyspec_tests/one_basic_attestation/attestation.ssz_snappy b/fuzz/src/test/resources/minimal/operations/attestation/pyspec_tests/one_basic_attestation/attestation.ssz_snappy index 32c9853830a3cc5f25f45110f8a0b680c74aa5b6..4bc6673add4e792dc41db7cda9a68ef09df0f189 100644 GIT binary patch literal 150 zcmV;H0BQf`0R-g$MgahP#+8qNt_v?CCT+;b%7lG0Zg`dza;9=@d6G9evMjUlMlt|) z0RVnb0PtaeD3Hg6A>5EI$6H6!_vY}rrMTXA<}22T&xS_<%D2@O+kUhv&mgWW1!Kzg zbyYA8sIe@srT%DfHIDSvLx~Z(Slm_4E}$;H`UGAwyFVvuM#A@uS6WP9$5->a>B+6#s5}*WKI4`hM-n+FG^=^8=ANRKg6@09s}?8)ZnCFkAuyoG{yRR~pMq z3}ziWZnOcGb&gKLSY}C!sZ!?*>>&d*B)%fDj6l?F>89YOv-4|ba{o9v_xC!#d(S>K zzPe@Ip4srukEF~UF?IY!-$O-PO?Txq+i)(ciPzHF8~W>qJ(>HzEj3f)|5&?jR}Za> z9PK*QwNWq`{{2{kC>Z!SCv>gN>+TahVKmnb504&ieDZ+3U0oln-|uKGSr=MyX$GM| zI2m~LlSeX25FcSTBLsCk4D%WcRbmVd7K?fc%6zVE!2uK>goC=}?5V)D{K?)0>sMga zVz|j*=-^|<(=im&80Lgn;E8VF#YaG;$1tP7kgLMbEW=WNEWZ6h;5W;$ zuiFtnz8U((IM+Y+hx;wd{bpLJ8in!1UBALKS#&rSdau^zrUsxxfYXCS?avAdEJ_S# zh}?G~3`QY_7;!VP7+z3dwwbno??)o?|MS2vxJ?XWSQUDnOsYPhxO+1$J@t6Zkh zPj~n4n_Bo#Q)lojJaBvUzg#Rk`QyZ*w7ayY0LqxPP|hf_BUnJQTP@>a*~>2>v?!r0 zxyVK#YK@$ZL!Dq11enL9Xp0?5y44hYu0Ha!h=WDOC{n#YIl;_-6Wq&iP{|t8>E6>3 zuY`m1tX+VD(N#hYDuhN}d>gpZFt~(d>yLge=Ab}iPj1%>FoZ;e=h+k~A-r^P3T!3d zwPLdN$07~|2PecvIyNZf;O^4<8-%8kq@c~DkLM*Ru`wRLbX9VlN(%o=zN=44SYbk+ zA_KY*b%8?W6selj4<2QA1_v}8#AGSrPNK11ZsfFrAaC?(JqOp5uIlq2pGE3Idc$j)xts)poD5=~8`_LB~!A!7+{cie}WOG@+Vd7y@ z@yq!k{jn2A1&f`V_I+O;ofkj#>ui?heM#^yhUAkACimsHAH1e}#xFnFo6-IF>9we; zhENQ<4wa3G-#P>g)a!0!&PJF zey)wK%4aO-arl5q0S{Hh2q?IX@NGT?W)1~=-Caz5VhTDuC?MDrB*-ZE5dAlxTnfcZ zESJhbl)s`{kLvFsWiiS@JdxG$1McsR;y1@7J)bx-l!+rFQ|XhBK2Kp*W-MG7Pns{M zCc<+IyrNEbswVPESc+HNy?CLt6}G~0mM`S9xUYO{h7QVDKK#Ji`H|MJ+I!XOY7B1@ zdiagCmaWdkJewtqzxG2oa^gvls}R(pMz*=&S>!E^(5ysPqo|!A38F{!)bbC9hRtke zd>Iv5*%~h@|K*99TOI+a=fiTZ43(_$q~VB%lW!VbYl#QRl@-ZLI0eT0O&Fhshpw{> zB*5l4({PkVaY*6%)*U^SW~HsWdNj*cKEtD-h(mJ3m+Iy<&-WXfg*1$EG#s(zxFR-a z(DkgDEloH0%EXCo2lDXvN6?s>=va==3GIxPtxv=~<0`%a(+c6iH~#t2VH z_KsuyjbUBANr7e@f)1Qnc1zvYamKN^BGTxC#YSH&L?*bEzMY2l@7B?9 zN*18fVv^uB&;6GO?Ej@BFk<`QQE!EONyTujFcsX9rG*^0oT*#%J>3>+o|y|9vN;sZ cWrqSb%|7@zJc8s-?8!>dF)(#JmdU{W1G_iQ{{R30 diff --git a/fuzz/src/test/resources/minimal/operations/attestation/pyspec_tests/one_basic_attestation/pre.ssz_snappy b/fuzz/src/test/resources/minimal/operations/attestation/pyspec_tests/one_basic_attestation/pre.ssz_snappy index 7e2480031de461b2849a0544b52afe5e90de5a62..4a116a875d1458db49c4c1a79f8f53cc8907b521 100644 GIT binary patch delta 1025 zcmX9+du&rx7(chY>+bD?zF&Kl_13a2Fb{~#p&^Vw4WMOavr&d5Q$Q0K%n7p{ccrn^ z#9-F3&y6;cWu4XrQ43A9)Xh?iTWEp`d+tO7*rjvQv{rBa&zt{IWH}Lf6 zs`jgWrh2b#{^eYG=e^QruRsJ&w#`u9(}^7egSX{RRsb@tw@zq}%T zxc5ZwM#)s{{*h*x5c#w)dZ{z$8=jo_e8(n^G7NHS1 z9(m=nhbl^v8f7;l1Pvk#a|R4`att08%lb>ILf&n{FiMTU0n;+}MC4NO)WE#+8*myi zTxT$Ji!tN(1fxDG)ehyk7|Ll3kEB@QPplWEMnPxBFss2(q{Gmr!pd+mwf#)w566*j zyO21w8HVJzC_MhB?`_B3Hd?D2gNett!y4OE*}-J={kL2`Y8bjDI9DKQf7MXn&|)}A zjHju};m2lXdy}EPcOJ5KCzE|j90++QliZs%FI2k+aF$(%Z9-O*yak9q(m3mJm$ZI4> zT)%iK`Pm*Uj<-lbc0+bsM8;}E_Kp<9?izT#I6z`<^`HCqv4KMU(sToA+MIe=4p#(VyA-=*gAn znx-%ey9(9KN#8gmIJsDf^Y>sF*u7bpP_Q6e#X_7L3kgE>7O{{aXJJsrO3m~%K3X&J z)%E(AnnHY0Pr&;;7DT8kR>Hz9gl~yiFbi1Nzg1N7a5xGbuf zNSDh2#9wi3ME!S=GmSV%C#p7K$o=hc;>M)B?;}U1I%#a|LB{mMPg8l-nTzKplNT!4 zsffHHujo_V!BYigr0Ruq&z`Am#qDs6^o2rF_`<`-^Pz(D5eL>UjJ1c?->F?+XLyy; z!>{Bzz9tXzY?b4G?t=)F#FJuHA*e%xYj(NC^Ft{!NeTgcm2ym z27G~&Mj#q1V5A$`cl1@8)wbT+@oZb=tcZgW0VAL>X55MP zVC%m=uuc+45}jk#?^knBAYqidYh!j6*ec)ouQ7D!@t|q?6IoV9P8jMLl{kksA$5(^ zY1D^e=eb}ya%ghFQdAw5GB4AHsO@1XCZpYt!$N(2Q!h39Ox4eH6EM?aSm?Xvjc)Yvr~?4lWw>gyYGeOKG=&Zxi0_2=Monjwj<*Q^~X4(4gC zP+4{P+@aeF|CTO>g9f>CkVteDSLFWg1t2w}Bvx1saf^&Fg3 z2Ds@kdB|GmQXUQ6e=#e6$@f%8n0a Date: Tue, 17 Dec 2024 18:12:17 +0100 Subject: [PATCH 2/3] Fix single attestation publication (#8929) --- .../teku/spec/util/DataStructureUtil.java | 6 +- .../AggregatingAttestationPoolTest.java | 4 +- .../eth2/gossip/AttestationGossipManager.java | 2 +- .../gossip/AttestationGossipManagerTest.java | 126 +++++++++++++----- 4 files changed, 99 insertions(+), 39 deletions(-) diff --git a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java index 6cea4f93c38..7e312c58cf7 100644 --- a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java +++ b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java @@ -821,6 +821,10 @@ public Attestation randomAttestation() { } public SingleAttestation randomSingleAttestation() { + return randomSingleAttestation(randomUInt64()); + } + + public SingleAttestation randomSingleAttestation(final UInt64 slot) { return spec.getGenesisSchemaDefinitions() .toVersionElectra() .orElseThrow() @@ -828,7 +832,7 @@ public SingleAttestation randomSingleAttestation() { .create( randomUInt64(Integer.MAX_VALUE), randomUInt64(Integer.MAX_VALUE), - randomAttestationData(), + randomAttestationData(slot), randomSignature()); } diff --git a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPoolTest.java b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPoolTest.java index d5f7acae9ad..22d2277ff45 100644 --- a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPoolTest.java +++ b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPoolTest.java @@ -46,7 +46,7 @@ import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.TestSpecContext; import tech.pegasys.teku.spec.TestSpecFactory; -import tech.pegasys.teku.spec.TestSpecInvocationContextProvider; +import tech.pegasys.teku.spec.TestSpecInvocationContextProvider.SpecContext; import tech.pegasys.teku.spec.datastructures.attestation.ValidatableAttestation; import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; @@ -84,7 +84,7 @@ class AggregatingAttestationPoolTest { private Int2IntMap committeeSizes; @BeforeEach - public void setUp(final TestSpecInvocationContextProvider.SpecContext specContext) { + public void setUp(final SpecContext specContext) { spec = specContext.getSpec(); specMilestone = specContext.getSpecMilestone(); dataStructureUtil = specContext.getDataStructureUtil(); diff --git a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/AttestationGossipManager.java b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/AttestationGossipManager.java index 3e9dfa6ba60..e2a02b11450 100644 --- a/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/AttestationGossipManager.java +++ b/networking/eth2/src/main/java/tech/pegasys/teku/networking/eth2/gossip/AttestationGossipManager.java @@ -53,7 +53,7 @@ public void onNewAttestation(final ValidatableAttestation validatableAttestation if (validatableAttestation.isAggregate() || !validatableAttestation.markGossiped()) { return; } - final Attestation attestation = validatableAttestation.getAttestation(); + final Attestation attestation = validatableAttestation.getUnconvertedAttestation(); subnetSubscriptions .gossip(attestation) .finish( diff --git a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/AttestationGossipManagerTest.java b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/AttestationGossipManagerTest.java index 94e562b650c..0a1f7bff35d 100644 --- a/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/AttestationGossipManagerTest.java +++ b/networking/eth2/src/test/java/tech/pegasys/teku/networking/eth2/gossip/AttestationGossipManagerTest.java @@ -19,10 +19,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static tech.pegasys.teku.infrastructure.async.SafeFutureAssert.safeJoin; +import static tech.pegasys.teku.spec.SpecMilestone.ELECTRA; +import static tech.pegasys.teku.spec.SpecMilestone.PHASE0; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestTemplate; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.async.StubAsyncRunner; import tech.pegasys.teku.infrastructure.metrics.StubCounter; @@ -35,7 +39,9 @@ import tech.pegasys.teku.networking.eth2.gossip.topics.OperationProcessor; import tech.pegasys.teku.networking.p2p.gossip.GossipNetwork; import tech.pegasys.teku.spec.Spec; -import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.TestSpecContext; +import tech.pegasys.teku.spec.TestSpecInvocationContextProvider.SpecContext; import tech.pegasys.teku.spec.datastructures.attestation.ValidatableAttestation; import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.state.ForkInfo; @@ -45,43 +51,56 @@ import tech.pegasys.teku.storage.client.MemoryOnlyRecentChainData; import tech.pegasys.teku.storage.client.RecentChainData; +@TestSpecContext(milestone = {PHASE0, ELECTRA}) public class AttestationGossipManagerTest { - private final Spec spec = TestSpecFactory.createMinimalPhase0(); - private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); + private Spec spec; + private SpecMilestone specMilestone; + private DataStructureUtil dataStructureUtil; + private RecentChainData recentChainData; + private ForkInfo forkInfo; + private AttestationGossipManager attestationGossipManager; @SuppressWarnings("unchecked") private final OperationProcessor gossipedAttestationProcessor = mock(OperationProcessor.class); - private final RecentChainData recentChainData = MemoryOnlyRecentChainData.create(spec); private final GossipNetwork gossipNetwork = mock(GossipNetwork.class); private final GossipEncoding gossipEncoding = GossipEncoding.SSZ_SNAPPY; - private AttestationGossipManager attestationGossipManager; + private final StubMetricsSystem metricsSystem = new StubMetricsSystem(); private final StubAsyncRunner asyncRunner = new StubAsyncRunner(); - private final ForkInfo forkInfo = - new ForkInfo(spec.fork(UInt64.ZERO), dataStructureUtil.randomBytes32()); - private final AttestationSubnetSubscriptions attestationSubnetSubscriptions = - new AttestationSubnetSubscriptions( - spec, - asyncRunner, - gossipNetwork, - gossipEncoding, - recentChainData, - gossipedAttestationProcessor, - forkInfo, - DebugDataDumper.NOOP); + + private final Int2IntMap committeeSizes = new Int2IntOpenHashMap(); @BeforeEach - public void setup() { + public void setup(final SpecContext specContext) { + spec = specContext.getSpec(); + specMilestone = specContext.getSpecMilestone(); + dataStructureUtil = specContext.getDataStructureUtil(); + recentChainData = MemoryOnlyRecentChainData.create(spec); + forkInfo = new ForkInfo(spec.fork(UInt64.ZERO), dataStructureUtil.randomBytes32()); + + final AttestationSubnetSubscriptions attestationSubnetSubscriptions = + new AttestationSubnetSubscriptions( + spec, + asyncRunner, + gossipNetwork, + gossipEncoding, + recentChainData, + gossipedAttestationProcessor, + forkInfo, + DebugDataDumper.NOOP); + BeaconChainUtil.create(spec, 0, recentChainData).initializeStorage(); + attestationGossipManager = new AttestationGossipManager(metricsSystem, attestationSubnetSubscriptions); } - @Test - public void onNewAttestation_afterMatchingAssignment() { + @TestTemplate + public void onNewAttestation_afterMatchingAssignment(final SpecContext specContext) { + specContext.assumeIsOneOf(PHASE0); // Create a new DataStructureUtil so that generated attestations are not subject to change // when access to the global DataStructureUtil changes final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); @@ -113,23 +132,30 @@ public void onNewAttestation_afterMatchingAssignment() { verify(gossipNetwork).gossip(getSubnetTopic(subnetId), serialized2); } - @Test + @TestTemplate public void onNewAttestation_noMatchingAssignment() { - final Attestation attestation = dataStructureUtil.randomAttestation(2); + final ValidatableAttestation validatableAttestation = createAttestation(2); + final Attestation attestation = + getExpectedAttestationFromValidatableAttestation(validatableAttestation); + final int subnetId = computeSubnetId(attestation); // Subscribed to different subnet attestationGossipManager.subscribeToSubnetId(subnetId + 1); // Post new attestation - attestationGossipManager.onNewAttestation(ValidatableAttestation.from(spec, attestation)); + attestationGossipManager.onNewAttestation(validatableAttestation); verify(gossipNetwork).gossip(getSubnetTopic(subnetId), gossipEncoding.encode(attestation)); } - @Test + @TestTemplate public void onNewAttestation_afterDismissal() { - final Attestation attestation = dataStructureUtil.randomAttestation(1); - final Attestation attestation2 = dataStructureUtil.randomAttestation(1); + final ValidatableAttestation validatableAttestation = createAttestation(1); + final ValidatableAttestation validatableAttestation2 = createAttestation(1); + final Attestation attestation = + getExpectedAttestationFromValidatableAttestation(validatableAttestation); + final Attestation attestation2 = + getExpectedAttestationFromValidatableAttestation(validatableAttestation2); // Setup committee assignment final int subnetId = computeSubnetId(attestation2); final int dismissedSubnetId = computeSubnetId(attestation); @@ -140,42 +166,72 @@ public void onNewAttestation_afterDismissal() { // Attestation for dismissed assignment should be ignored final Bytes serialized = gossipEncoding.encode(attestation); - attestationGossipManager.onNewAttestation(ValidatableAttestation.from(spec, attestation)); + attestationGossipManager.onNewAttestation(validatableAttestation); verify(gossipNetwork).gossip(getSubnetTopic(dismissedSubnetId), serialized); // Attestation for remaining assignment should be processed final Bytes serialized2 = gossipEncoding.encode(attestation2); - attestationGossipManager.onNewAttestation(ValidatableAttestation.from(spec, attestation2)); + attestationGossipManager.onNewAttestation(validatableAttestation2); verify(gossipNetwork).gossip(getSubnetTopic(subnetId), serialized2); } - @Test + @TestTemplate void onNewAttestation_incrementSuccessCount() { - final Attestation attestation = dataStructureUtil.randomAttestation(3); + final ValidatableAttestation validatableAttestation = createAttestation(3); + when(gossipNetwork.gossip(any(), any())).thenReturn(SafeFuture.completedFuture(null)); // Attestation for dismissed assignment should be ignored - attestationGossipManager.onNewAttestation(ValidatableAttestation.from(spec, attestation)); + attestationGossipManager.onNewAttestation(validatableAttestation); assertThat(getPublishSuccessCounterValue()).isEqualTo(1); assertThat(getPublishFailureCounterValue()).isZero(); } - @Test + @TestTemplate void onNewAttestation_incrementFailureCount() { - final Attestation attestation = dataStructureUtil.randomAttestation(); + final ValidatableAttestation validatableAttestation = createAttestation(3); when(gossipNetwork.gossip(any(), any())) .thenReturn(SafeFuture.failedFuture(new RuntimeException("Ooops"))); // Attestation for dismissed assignment should be ignored - attestationGossipManager.onNewAttestation(ValidatableAttestation.from(spec, attestation)); + attestationGossipManager.onNewAttestation(validatableAttestation); assertThat(getPublishSuccessCounterValue()).isZero(); assertThat(getPublishFailureCounterValue()).isEqualTo(1); } + private ValidatableAttestation createAttestation(final int slot) { + final Attestation attestationFromValidators; + + if (specMilestone.isGreaterThanOrEqualTo(ELECTRA)) { + attestationFromValidators = dataStructureUtil.randomSingleAttestation(UInt64.valueOf(slot)); + } else { + attestationFromValidators = dataStructureUtil.randomAttestation(slot); + } + + final ValidatableAttestation validatableAttestation = + ValidatableAttestation.from(spec, attestationFromValidators, committeeSizes); + + if (attestationFromValidators.isSingleAttestation()) { + validatableAttestation.convertToAggregatedFormatFromSingleAttestation( + dataStructureUtil.randomAttestation(slot)); + } + + return validatableAttestation; + } + + private Attestation getExpectedAttestationFromValidatableAttestation( + final ValidatableAttestation validatableAttestation) { + if (specMilestone.isGreaterThanOrEqualTo(ELECTRA)) { + return validatableAttestation.getUnconvertedAttestation(); + } else { + return validatableAttestation.getAttestation(); + } + } + private long getPublishSuccessCounterValue() { return getPublishCounter().getValue("success"); } From 4dbdd0856b83294ef633e8ba1c8d278384429c91 Mon Sep 17 00:00:00 2001 From: Enrico Del Fante Date: Wed, 18 Dec 2024 12:36:55 +0100 Subject: [PATCH 3/3] 24-12-1-clear-changelog (#8932) --- CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5215b6faff..3d7628c3eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,4 @@ ### Additions and Improvements - Optimized blobs validation pipeline -### Bug Fixes -- Updated the gas change check for block building so that warnings only get raised if the change is off spec. -- Fixed an issue with the `/eth/v1/config/spec` API not returning all previously included configuration parameters. -- Increase the maximum size of a compressed message for libp2p to ensure uncompressed blocks can grow to max size. +### Bug Fixes \ No newline at end of file