From 561e204925172e2e8643e0cef2d97ef5da413a61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20B=C3=A9gassat?= <38285177+OlivierBBB@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:14:52 +0200 Subject: [PATCH] feat: add PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS in spillings.toml and did some renaming (#819) Signed-off-by: Tsvetan Dimitrov --- .../linea/zktracer/CurveOperations.java | 10 +- .../linea/zktracer/module/hub/Hub.java | 24 +- .../hub/precompiles/PrecompileInvocation.java | 8 +- .../precompiles/EcPairingEffectiveCall.java | 168 -------- .../EcPairingFinalExponentiations.java | 360 ++++++++++++++++++ .../EcPairingG2MembershipCalls.java | 63 +++ ...lerLoop.java => EcPairingMillerLoops.java} | 6 +- ...airingLimit.java => EcPairingTallier.java} | 3 +- .../zktracer/runtime/callstack/CallFrame.java | 2 +- .../src/main/resources/spillings.toml | 3 +- 10 files changed, 450 insertions(+), 197 deletions(-) delete mode 100644 arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingEffectiveCall.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingFinalExponentiations.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingG2MembershipCalls.java rename arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/{EcPairingMillerLoop.java => EcPairingMillerLoops.java} (89%) rename arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/{EcPairingLimit.java => EcPairingTallier.java} (84%) diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/CurveOperations.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/CurveOperations.java index cdcfdf9b9c..6efbb06bca 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/CurveOperations.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/CurveOperations.java @@ -43,7 +43,7 @@ public class CurveOperations { public static BigInteger extractParameter(final Bytes input) { if (input.isEmpty()) { - throw new IllegalArgumentException("EC_DATA input can not be empty"); + throw new IllegalArgumentException("EC_DATA input cannot be empty"); } return new BigInteger(1, input.toArray()); } @@ -70,14 +70,8 @@ public static boolean isOnG2(Bytes xBytes) { final Fq2 pX = Fq2.create(pXRe, pXIm); final Fq2 pY = Fq2.create(pYRe, pYIm); - if (!pX.isValid() || !pY.isValid()) { - return false; - } - final AltBn128Fq2Point p2 = new AltBn128Fq2Point(pX, pY); - final AltBn128Fq2Point pPowQ = p2.multiply(Q); - - return pPowQ.isInfinity(); + return p2.isOnCurve() && p2.isInGroup(); } public static boolean ecRecoverSuccessful(final Bytes input) { diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/Hub.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/Hub.java index 8c7952eeba..cc5f109d48 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/Hub.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/Hub.java @@ -63,8 +63,9 @@ import net.consensys.linea.zktracer.module.limits.precompiles.BlakeRounds; import net.consensys.linea.zktracer.module.limits.precompiles.EcAddEffectiveCall; import net.consensys.linea.zktracer.module.limits.precompiles.EcMulEffectiveCall; -import net.consensys.linea.zktracer.module.limits.precompiles.EcPairingEffectiveCall; -import net.consensys.linea.zktracer.module.limits.precompiles.EcPairingMillerLoop; +import net.consensys.linea.zktracer.module.limits.precompiles.EcPairingFinalExponentiations; +import net.consensys.linea.zktracer.module.limits.precompiles.EcPairingG2MembershipCalls; +import net.consensys.linea.zktracer.module.limits.precompiles.EcPairingMillerLoops; import net.consensys.linea.zktracer.module.limits.precompiles.EcRecoverEffectiveCall; import net.consensys.linea.zktracer.module.limits.precompiles.ModexpEffectiveCall; import net.consensys.linea.zktracer.module.limits.precompiles.RipemdBlocks; @@ -285,7 +286,7 @@ public Hub(final Address l2l1ContractAddress, final Bytes l2l1Topic) { final EcRecoverEffectiveCall ecRec = new EcRecoverEffectiveCall(this); this.modexpEffectiveCall = new ModexpEffectiveCall(this, this.blakeModexpData); - final EcPairingEffectiveCall ecPairingCall = new EcPairingEffectiveCall(this); + final EcPairingFinalExponentiations ecPairingCall = new EcPairingFinalExponentiations(this); final L2Block l2Block = new L2Block(l2l1ContractAddress, LogTopic.of(l2l1Topic)); final BlakeRounds blakeRounds = new BlakeRounds(this, this.blakeModexpData); @@ -298,7 +299,8 @@ public Hub(final Address l2l1ContractAddress, final Bytes l2l1Topic) { new EcAddEffectiveCall(this), new EcMulEffectiveCall(this), ecPairingCall, - new EcPairingMillerLoop(ecPairingCall), + new EcPairingG2MembershipCalls(ecPairingCall), + new EcPairingMillerLoops(ecPairingCall), blakeRounds, new BlakeEffectiveCall(blakeRounds), // Block level limits @@ -1082,9 +1084,9 @@ private boolean requiresEvmExecution(final WorldView worldView, final Transactio public void traceContextReEnter(MessageFrame frame) { this.defers.runReEntry(this, frame); - if (this.currentFrame().needsUnlatchingAtReEntry() != null) { - this.unlatchStack(frame, this.currentFrame().needsUnlatchingAtReEntry()); - this.currentFrame().needsUnlatchingAtReEntry(null); + if (this.currentFrame().sectionToUnlatch() != null) { + this.unlatchStack(frame, this.currentFrame().sectionToUnlatch()); + this.currentFrame().sectionToUnlatch(null); } } @@ -1131,7 +1133,7 @@ public void tracePostExecution(MessageFrame frame, Operation.OperationResult ope this.defers.runPostExec(this, frame, operationResult); this.romLex.tracePostOpcode(frame); - if (this.currentFrame().needsUnlatchingAtReEntry() == null) { + if (this.currentFrame().sectionToUnlatch() == null) { this.unlatchStack(frame); } @@ -1457,7 +1459,7 @@ void traceOperation(MessageFrame frame) { CreateSection createSection = new CreateSection(this, myAccountSnapshot, createdAccountSnapshot); this.addTraceSection(createSection); - this.currentFrame().needsUnlatchingAtReEntry(createSection); + this.currentFrame().sectionToUnlatch(createSection); } case CALL -> { @@ -1537,7 +1539,7 @@ void traceOperation(MessageFrame frame) { new SmartContractCallSection( this, myAccountSnapshot, calledAccountSnapshot, rawCalledAddress, imcFragment); this.addTraceSection(section); - this.currentFrame().needsUnlatchingAtReEntry(section); + this.currentFrame().sectionToUnlatch(section); } else { // // CALL EXECUTED @@ -1566,7 +1568,7 @@ void traceOperation(MessageFrame frame) { rawCalledAddress, imcFragment); this.addTraceSection(section); - this.currentFrame().needsUnlatchingAtReEntry(section); + this.currentFrame().sectionToUnlatch(section); } } } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/precompiles/PrecompileInvocation.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/precompiles/PrecompileInvocation.java index b11a04fb7c..63129f09e6 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/precompiles/PrecompileInvocation.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/hub/precompiles/PrecompileInvocation.java @@ -21,7 +21,7 @@ import net.consensys.linea.zktracer.module.limits.precompiles.BlakeRounds; import net.consensys.linea.zktracer.module.limits.precompiles.EcAddEffectiveCall; import net.consensys.linea.zktracer.module.limits.precompiles.EcMulEffectiveCall; -import net.consensys.linea.zktracer.module.limits.precompiles.EcPairingEffectiveCall; +import net.consensys.linea.zktracer.module.limits.precompiles.EcPairingFinalExponentiations; import net.consensys.linea.zktracer.module.limits.precompiles.EcRecoverEffectiveCall; import net.consensys.linea.zktracer.module.limits.precompiles.ModexpEffectiveCall; import net.consensys.linea.zktracer.module.limits.precompiles.RipemdBlocks; @@ -96,7 +96,7 @@ public static PrecompileInvocation of(final Hub hub, Precompile p) { case MODEXP -> false; case EC_ADD -> hub.transients().op().gasAllowanceForCall() < 150; case EC_MUL -> hub.transients().op().gasAllowanceForCall() < 6000; - case EC_PAIRING -> EcPairingEffectiveCall.isHubFailure(hub); + case EC_PAIRING -> EcPairingFinalExponentiations.isHubFailure(hub); case BLAKE2F -> BlakeRounds.isHubFailure(hub); }; @@ -108,7 +108,7 @@ && switch (p) { > hub.transients().op().gasAllowanceForCall(); case EC_ADD -> EcAddEffectiveCall.isRamFailure(hub); case EC_MUL -> EcMulEffectiveCall.isRamFailure(hub); - case EC_PAIRING -> EcPairingEffectiveCall.isRamFailure(hub); + case EC_PAIRING -> EcPairingFinalExponentiations.isRamFailure(hub); case BLAKE2F -> BlakeRounds.isRamFailure(hub); }; @@ -137,7 +137,7 @@ && switch (p) { case MODEXP -> ModexpEffectiveCall.gasCost(hub); case EC_ADD -> EcAddEffectiveCall.gasCost(); case EC_MUL -> EcMulEffectiveCall.gasCost(); - case EC_PAIRING -> EcPairingEffectiveCall.gasCost(hub); + case EC_PAIRING -> EcPairingFinalExponentiations.gasCost(hub); case BLAKE2F -> BlakeRounds.gasCost(hub); }; diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingEffectiveCall.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingEffectiveCall.java deleted file mode 100644 index bb05b4f37c..0000000000 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingEffectiveCall.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright Consensys Software Inc. - * - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -package net.consensys.linea.zktracer.module.limits.precompiles; - -import static net.consensys.linea.zktracer.CurveOperations.isOnC1; -import static net.consensys.linea.zktracer.CurveOperations.isOnG2; - -import java.nio.MappedByteBuffer; -import java.util.List; -import java.util.Stack; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.experimental.Accessors; -import lombok.extern.slf4j.Slf4j; -import net.consensys.linea.zktracer.ColumnHeader; -import net.consensys.linea.zktracer.module.Module; -import net.consensys.linea.zktracer.module.hub.Hub; -import net.consensys.linea.zktracer.opcode.OpCode; -import org.apache.tuweni.bytes.Bytes; -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.evm.frame.MessageFrame; -import org.hyperledger.besu.evm.internal.Words; - -@Slf4j -@RequiredArgsConstructor -@Accessors(fluent = true) -public final class EcPairingEffectiveCall implements Module { - private final Hub hub; - @Getter private final Stack counts = new Stack<>(); - private static final int PRECOMPILE_BASE_GAS_FEE = 45_000; // cf EIP-1108 - private static final int PRECOMPILE_MILLER_LOOP_GAS_FEE = 34_000; // cf EIP-1108 - private static final int ECPAIRING_NB_BYTES_PER_MILLER_LOOP = 192; - - @Override - public String moduleKey() { - return "PRECOMPILE_ECPAIRING_EFFECTIVE_CALLS"; - } - - @Override - public void enterTransaction() { - counts.push(new EcPairingLimit(0, 0)); - } - - @Override - public void popTransaction() { - counts.pop(); - } - - public static boolean isHubFailure(final Hub hub) { - final OpCode opCode = hub.opCode(); - final MessageFrame frame = hub.messageFrame(); - - if (opCode.isCall()) { - final Address target = Words.toAddress(frame.getStackItem(1)); - if (target.equals(Address.ALTBN128_PAIRING)) { - long length = hub.transients().op().callDataSegment().length(); - if (length % 192 != 0) { - return true; - } - final long pairingCount = length / ECPAIRING_NB_BYTES_PER_MILLER_LOOP; - - return hub.transients().op().gasAllowanceForCall() - < PRECOMPILE_BASE_GAS_FEE + PRECOMPILE_MILLER_LOOP_GAS_FEE * pairingCount; - } - } - - return false; - } - - public static boolean isRamFailure(final Hub hub) { - final MessageFrame frame = hub.messageFrame(); - long length = hub.transients().op().callDataSegment().length(); - - if (length == 0) { - return true; - } - - for (int i = 0; i < length; i += 192) { - final Bytes coordinates = frame.shadowReadMemory(i, 192); - if (!isOnC1(coordinates.slice(0, 64)) || !isOnG2(coordinates.slice(64, 128))) { - return true; - } - } - - return false; - } - - public static long gasCost(final Hub hub) { - final OpCode opCode = hub.opCode(); - final MessageFrame frame = hub.messageFrame(); - - if (opCode.isCall()) { - final Address target = Words.toAddress(frame.getStackItem(1)); - if (target.equals(Address.ALTBN128_PAIRING)) { - final long length = hub.transients().op().callDataSegment().length(); - final long nMillerLoop = (length / ECPAIRING_NB_BYTES_PER_MILLER_LOOP); - if (nMillerLoop * ECPAIRING_NB_BYTES_PER_MILLER_LOOP != length) { - return 0; - } - - return PRECOMPILE_BASE_GAS_FEE + PRECOMPILE_MILLER_LOOP_GAS_FEE * nMillerLoop; - } - } - - return 0; - } - - @Override - public void tracePreOpcode(MessageFrame frame) { - final OpCode opCode = hub.opCode(); - - if (opCode.isCall()) { - final Address target = Words.toAddress(frame.getStackItem(1)); - if (target.equals(Address.ALTBN128_PAIRING)) { - long length = hub.transients().op().callDataSegment().length(); - - if (length % ECPAIRING_NB_BYTES_PER_MILLER_LOOP != 0) { - log.warn("[ECPairing] Argument is not a right size: " + length); - return; - } - - final long nMillerLoop = (length / ECPAIRING_NB_BYTES_PER_MILLER_LOOP); - - if (hub.transients().op().gasAllowanceForCall() - >= PRECOMPILE_BASE_GAS_FEE + PRECOMPILE_MILLER_LOOP_GAS_FEE * nMillerLoop) { - final EcPairingLimit lastEcpairingLimit = this.counts.pop(); - this.counts.push( - new EcPairingLimit( - lastEcpairingLimit.numberOfPrecompileCalls() + 1, - lastEcpairingLimit.numberOfMillerLoops() + nMillerLoop)); - } - } - } - } - - @Override - public int lineCount() { - int r = 0; - for (EcPairingLimit count : this.counts) { - r += count.numberOfPrecompileCalls(); - } - return r; - } - - @Override - public List columnsHeaders() { - throw new UnsupportedOperationException("should never be called"); - } - - @Override - public void commit(List buffers) { - throw new UnsupportedOperationException("should never be called"); - } -} diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingFinalExponentiations.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingFinalExponentiations.java new file mode 100644 index 0000000000..3f6c9d8a49 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingFinalExponentiations.java @@ -0,0 +1,360 @@ +/* + * Copyright Consensys Software Inc. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package net.consensys.linea.zktracer.module.limits.precompiles; + +import static net.consensys.linea.zktracer.CurveOperations.*; + +import java.math.BigInteger; +import java.nio.MappedByteBuffer; +import java.util.List; +import java.util.Stack; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.zktracer.ColumnHeader; +import net.consensys.linea.zktracer.module.Module; +import net.consensys.linea.zktracer.module.hub.Hub; +import net.consensys.linea.zktracer.opcode.OpCode; +import net.consensys.linea.zktracer.types.MemorySpan; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.crypto.altbn128.AltBn128Fq2Point; +import org.hyperledger.besu.crypto.altbn128.AltBn128Point; +import org.hyperledger.besu.crypto.altbn128.Fq; +import org.hyperledger.besu.crypto.altbn128.Fq2; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.internal.Words; + +@Slf4j +@RequiredArgsConstructor +@Accessors(fluent = true) +public final class EcPairingFinalExponentiations implements Module { + private final Hub hub; + @Getter private final Stack counts = new Stack<>(); + private static final int PRECOMPILE_BASE_GAS_FEE = 45_000; // cf EIP-1108 + private static final int PRECOMPILE_MILLER_LOOP_GAS_FEE = 34_000; // cf EIP-1108 + private static final int ECPAIRING_NB_BYTES_PER_MILLER_LOOP = 192; + private static final int ECPAIRING_NB_BYTES_PER_SMALL_POINT = 64; + private static final int ECPAIRING_NB_BYTES_PER_LARGE_POINT = 128; + + @Override + public String moduleKey() { + return "PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS"; + } + + @Override + public void enterTransaction() { + counts.push(new EcPairingTallier(0, 0, 0)); + } + + @Override + public void popTransaction() { + counts.pop(); + } + + @Override + public int lineCount() { + int r = 0; + for (EcPairingTallier count : this.counts) { + r += (int) count.numberOfFinalExponentiations(); + } + return r; + } + + @Override + public List columnsHeaders() { + throw new UnsupportedOperationException("should never be called"); + } + + @Override + public void commit(List buffers) { + throw new UnsupportedOperationException("should never be called"); + } + + @Override + public void tracePreOpcode(MessageFrame frame) { + + if (irrelevantOperation(frame)) { + return; + } + + final MemorySpan callDataSpan = hub.transients().op().callDataSegment(); + final long callDataSize = callDataSpan.length(); + final long callDataSizeMod192 = callDataSize % ECPAIRING_NB_BYTES_PER_MILLER_LOOP; + final long nMillerLoop = (callDataSize / ECPAIRING_NB_BYTES_PER_MILLER_LOOP); + if (callDataSizeMod192 != 0) { + log.warn( + "[ECPAIRING] faulty call data size: {} ≡ {} mod {}", + callDataSize, + callDataSizeMod192, + ECPAIRING_NB_BYTES_PER_MILLER_LOOP); + return; + } + + final long gasAllowance = hub.transients().op().gasAllowanceForCall(); + final long precompileCost = + PRECOMPILE_BASE_GAS_FEE + PRECOMPILE_MILLER_LOOP_GAS_FEE * nMillerLoop; + if (gasAllowance < precompileCost) { + log.warn( + "[ECPAIRING] insufficient gas: gas allowance = {}, precompile cost = {}", + gasAllowance, + precompileCost); + return; + } + + if (callDataSize == 0) { + return; + } + + /* + At this point: + - call data size is a positive multiple of 192 + - the precompile call is given sufficient gas + */ + + final EcPairingTallier currentEcpairingTallier = this.counts.pop(); + final long additionalRows = 12 * nMillerLoop + 2; + + if (!internalChecksPassed(frame, callDataSpan)) { + this.counts.push( + new EcPairingTallier( + currentEcpairingTallier.numberOfMillerLoops(), + currentEcpairingTallier.numberOfFinalExponentiations(), + currentEcpairingTallier.numberOfG2MembershipTests())); + return; + } + + if (callDataContainsMalformedLargePoint(frame, callDataSpan)) { + this.counts.push( + new EcPairingTallier( + currentEcpairingTallier.numberOfMillerLoops(), + currentEcpairingTallier.numberOfFinalExponentiations(), + currentEcpairingTallier.numberOfG2MembershipTests() + 1)); + return; + } + + EcpairingCounts preciseCount = preciseCount(frame, callDataSpan); + + this.counts.push( + new EcPairingTallier( + currentEcpairingTallier.numberOfMillerLoops() + preciseCount.nontrivialPairs(), + currentEcpairingTallier.numberOfFinalExponentiations() + preciseCount.nontrivialPairs() + == 0 + ? 0 + : 1, + currentEcpairingTallier.numberOfG2MembershipTests() + preciseCount.membershipTests())); + } + + public static boolean isHubFailure(final Hub hub) { + final OpCode opCode = hub.opCode(); + final MessageFrame frame = hub.messageFrame(); + + if (opCode.isCall()) { + final Address target = Words.toAddress(frame.getStackItem(1)); + if (target.equals(Address.ALTBN128_PAIRING)) { + long length = hub.transients().op().callDataSegment().length(); + if (length % 192 != 0) { + return true; + } + final long pairingCount = length / ECPAIRING_NB_BYTES_PER_MILLER_LOOP; + + return hub.transients().op().gasAllowanceForCall() + < PRECOMPILE_BASE_GAS_FEE + PRECOMPILE_MILLER_LOOP_GAS_FEE * pairingCount; + } + } + + return false; + } + + public static boolean isRamFailure(final Hub hub) { + final MessageFrame frame = hub.messageFrame(); + long length = hub.transients().op().callDataSegment().length(); + + if (length == 0) { + return true; + } + + for (int i = 0; i < length; i += 192) { + final Bytes coordinates = frame.shadowReadMemory(i, 192); + if (!isOnC1(coordinates.slice(0, 64)) || !isOnG2(coordinates.slice(64, 128))) { + return true; + } + } + + return false; + } + + public static long gasCost(final Hub hub) { + final OpCode opCode = hub.opCode(); + final MessageFrame frame = hub.messageFrame(); + + if (irrelevantOperation(hub.messageFrame())) { + return 0; + } + + if (opCode.isCall()) { + final Address target = Words.toAddress(frame.getStackItem(1)); + if (target.equals(Address.ALTBN128_PAIRING)) { + final long length = hub.transients().op().callDataSegment().length(); + final long nMillerLoop = (length / ECPAIRING_NB_BYTES_PER_MILLER_LOOP); + if (nMillerLoop * ECPAIRING_NB_BYTES_PER_MILLER_LOOP != length) { + return 0; + } + + return PRECOMPILE_BASE_GAS_FEE + PRECOMPILE_MILLER_LOOP_GAS_FEE * nMillerLoop; + } + } + + return 0; + } + + /** + * Specifies if an opcode is irrelevant to the tracing of ECPAIRING. + * + * @param frame + * @return true if the current operation is either not a call or would throw a stack exception; + * otherwise it returns true if the target of the CALL isn't the ECPAIRING precompile; + */ + static boolean irrelevantOperation(MessageFrame frame) { + final OpCode opCode = OpCode.of(frame.getCurrentOperation().getOpcode()); + + if (!opCode.isCall() || frame.stackSize() < 2) { + return true; + } + + final Address target = Words.toAddress(frame.getStackItem(1)); + return !target.equals(Address.ALTBN128_PAIRING); + } + + private boolean internalChecksPassed(MessageFrame frame, MemorySpan callData) { + long offset = callData.offset(); + long nPairsOfPoints = callData.length() / ECPAIRING_NB_BYTES_PER_MILLER_LOOP; + + for (long i = 0; i < nPairsOfPoints; i++) { + final Bytes coordinates = frame.shadowReadMemory(offset, ECPAIRING_NB_BYTES_PER_MILLER_LOOP); + + if (!isOnC1(coordinates.slice(0, ECPAIRING_NB_BYTES_PER_SMALL_POINT))) { + return false; + } + + final BigInteger pXIm = + extractParameter(coordinates.slice(ECPAIRING_NB_BYTES_PER_SMALL_POINT, 32)); + final BigInteger pXRe = + extractParameter(coordinates.slice(ECPAIRING_NB_BYTES_PER_SMALL_POINT + 32, 32)); + final BigInteger pYIm = + extractParameter(coordinates.slice(ECPAIRING_NB_BYTES_PER_SMALL_POINT + 64, 32)); + final BigInteger pYRe = + extractParameter(coordinates.slice(ECPAIRING_NB_BYTES_PER_SMALL_POINT + 96, 32)); + final Fq2 pX = Fq2.create(pXRe, pXIm); + final Fq2 pY = Fq2.create(pYRe, pYIm); + + if (!pX.isValid() || !pY.isValid()) { + return false; + } + + offset += ECPAIRING_NB_BYTES_PER_MILLER_LOOP; + } + + return true; + } + + private boolean callDataContainsMalformedLargePoint(MessageFrame frame, MemorySpan callData) { + long offset = callData.offset(); + long nPairsOfPoints = callData.length() / ECPAIRING_NB_BYTES_PER_MILLER_LOOP; + + for (long i = 0; i < nPairsOfPoints; i++) { + final Bytes largeCoordinates = + frame.shadowReadMemory( + offset + ECPAIRING_NB_BYTES_PER_SMALL_POINT, ECPAIRING_NB_BYTES_PER_LARGE_POINT); + + // curve membership implicitly tested + if (!isOnG2(largeCoordinates)) { + return false; + } + + offset += ECPAIRING_NB_BYTES_PER_MILLER_LOOP; + } + + return true; + } + + @Getter + private class EcpairingCounts { + int nontrivialPairs; // a pair of the form (A, B) with [A ≠ ∞] ∧ [B ≠ ∞] + int membershipTests; // a pair of the form (A, B) with [A ≡ ∞] ∧ [B ≠ ∞] + int trivialPairs; // a pair of the form (A, B) with [B ≡ ∞]; likely useless ... + + private void incrementPairingPairs() { + this.nontrivialPairs++; + } + + private void incrementMembershipTests() { + this.membershipTests++; + } + + private void incrementTrivialPairs() { + this.trivialPairs++; + } + } + + private EcpairingCounts preciseCount(MessageFrame frame, MemorySpan callData) { + EcpairingCounts counts = new EcpairingCounts(); + + long offset = callData.offset(); + long nPairsOfPoints = callData.length() / ECPAIRING_NB_BYTES_PER_MILLER_LOOP; + + for (long i = 0; i < nPairsOfPoints; i++) { + final Bytes coordinates = frame.shadowReadMemory(offset, ECPAIRING_NB_BYTES_PER_MILLER_LOOP); + + final BigInteger smallPointX = extractParameter(coordinates.slice(0, 32)); + final BigInteger smallPointY = extractParameter(coordinates.slice(32, 32)); + final AltBn128Point smallPoint = + new AltBn128Point(Fq.create(smallPointX), Fq.create(smallPointY)); + final boolean smallPointIsPointAtInfinity = smallPoint.isInfinity(); + + final BigInteger largePointXIm = + extractParameter(coordinates.slice(ECPAIRING_NB_BYTES_PER_SMALL_POINT, 32)); + final BigInteger largePointXRe = + extractParameter(coordinates.slice(ECPAIRING_NB_BYTES_PER_SMALL_POINT + 32, 32)); + final BigInteger largePointYIm = + extractParameter(coordinates.slice(ECPAIRING_NB_BYTES_PER_SMALL_POINT + 64, 32)); + final BigInteger largePointYRe = + extractParameter(coordinates.slice(ECPAIRING_NB_BYTES_PER_SMALL_POINT + 96, 32)); + final Fq2 largePointX = Fq2.create(largePointXRe, largePointXIm); + final Fq2 largePointY = Fq2.create(largePointYRe, largePointYIm); + AltBn128Fq2Point largePoint = new AltBn128Fq2Point(largePointX, largePointY); + final boolean largePointIsPointAtInfinity = largePoint.isInfinity(); + + if (largePointIsPointAtInfinity) { + counts.incrementTrivialPairs(); + } + + if (!smallPointIsPointAtInfinity && !largePointIsPointAtInfinity) { + counts.incrementPairingPairs(); + } + + if (smallPointIsPointAtInfinity && !largePointIsPointAtInfinity) { + counts.incrementMembershipTests(); + } + + offset += ECPAIRING_NB_BYTES_PER_MILLER_LOOP; + } + + return counts; + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingG2MembershipCalls.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingG2MembershipCalls.java new file mode 100644 index 0000000000..eda89c70f3 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingG2MembershipCalls.java @@ -0,0 +1,63 @@ +/* + * Copyright Consensys Software Inc. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package net.consensys.linea.zktracer.module.limits.precompiles; + +import java.nio.MappedByteBuffer; +import java.util.List; + +import lombok.RequiredArgsConstructor; +import net.consensys.linea.zktracer.ColumnHeader; +import net.consensys.linea.zktracer.module.Module; + +@RequiredArgsConstructor +public class EcPairingG2MembershipCalls implements Module { + private final EcPairingFinalExponentiations ecPairingFinalExponentiations; + + @Override + public String moduleKey() { + return "PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS"; + } + + @Override + public void enterTransaction() {} + + @Override + public void popTransaction() {} + + @Override + public int lineCount() { + long g2MembershipTests = 0; + + for (EcPairingTallier count : this.ecPairingFinalExponentiations.counts()) { + g2MembershipTests += count.numberOfG2MembershipTests(); + } + + if (g2MembershipTests > Integer.MAX_VALUE) { + throw new RuntimeException("Ludicrous amount of " + moduleKey()); + } + + return (int) g2MembershipTests; + } + + @Override + public List columnsHeaders() { + throw new UnsupportedOperationException("should never be called"); + } + + @Override + public void commit(List buffers) { + throw new UnsupportedOperationException("should never be called"); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingMillerLoop.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingMillerLoops.java similarity index 89% rename from arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingMillerLoop.java rename to arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingMillerLoops.java index a3d36ac387..15177d8b88 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingMillerLoop.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingMillerLoops.java @@ -23,8 +23,8 @@ import net.consensys.linea.zktracer.module.Module; @RequiredArgsConstructor -public final class EcPairingMillerLoop implements Module { - private final EcPairingEffectiveCall ecpairingCall; +public final class EcPairingMillerLoops implements Module { + private final EcPairingFinalExponentiations ecpairingCall; @Override public String moduleKey() { @@ -41,7 +41,7 @@ public void popTransaction() {} public int lineCount() { long r = 0; - for (EcPairingLimit count : this.ecpairingCall.counts()) { + for (EcPairingTallier count : this.ecpairingCall.counts()) { r += count.numberOfMillerLoops(); } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingLimit.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingTallier.java similarity index 84% rename from arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingLimit.java rename to arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingTallier.java index e5771c5934..75005b0d9d 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingLimit.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/limits/precompiles/EcPairingTallier.java @@ -15,4 +15,5 @@ package net.consensys.linea.zktracer.module.limits.precompiles; -record EcPairingLimit(int numberOfPrecompileCalls, long numberOfMillerLoops) {} +record EcPairingTallier( + long numberOfMillerLoops, long numberOfFinalExponentiations, long numberOfG2MembershipTests) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallFrame.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallFrame.java index 7174741781..aa8a2a3d0d 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallFrame.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/runtime/callstack/CallFrame.java @@ -61,7 +61,7 @@ public class CallFrame { /** */ @Getter private boolean underDeployment; - @Getter @Setter private TraceSection needsUnlatchingAtReEntry = null; + @Getter @Setter private TraceSection sectionToUnlatch = null; /** the ID of this {@link CallFrame} parent in the {@link CallStack}. */ @Getter private int parentFrame; diff --git a/arithmetization/src/main/resources/spillings.toml b/arithmetization/src/main/resources/spillings.toml index 58bd65a982..db471c5268 100644 --- a/arithmetization/src/main/resources/spillings.toml +++ b/arithmetization/src/main/resources/spillings.toml @@ -41,7 +41,8 @@ PRECOMPILE_RIPEMD_BLOCKS = 0 PRECOMPILE_MODEXP_EFFECTIVE_CALLS = 0 PRECOMPILE_ECADD_EFFECTIVE_CALLS = 0 PRECOMPILE_ECMUL_EFFECTIVE_CALLS = 0 -PRECOMPILE_ECPAIRING_EFFECTIVE_CALLS = 0 +PRECOMPILE_ECPAIRING_FINAL_EXPONENTIATIONS = 0 +PRECOMPILE_ECPAIRING_G2_MEMBERSHIP_CALLS = 0 PRECOMPILE_ECPAIRING_MILLER_LOOPS = 0 PRECOMPILE_BLAKE_EFFECTIVE_CALLS = 0 PRECOMPILE_BLAKE_ROUNDS = 0