Skip to content

Commit

Permalink
feat(modexp-data): implement MODEXP_DATA module (#547)
Browse files Browse the repository at this point in the history
Resolves: #288 
Signed-off-by: Tsvetan Dimitrov <tsvetan.dimitrov@consensys.net>
  • Loading branch information
powerslider authored Jan 23, 2024
1 parent 7b71c68 commit 0e44a9f
Show file tree
Hide file tree
Showing 8 changed files with 629 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ public void addTraceSection(TraceSection section) {
private final List<Module> modules;
/* Those modules are not traced, we just compute the number of calls to those precompile to meet the prover limits */
private final List<Module> precompileLimitModules;
private final List<Module> refTableModules;

public Hub() {
this.pch = new PlatformController(this);
Expand Down Expand Up @@ -221,6 +222,8 @@ public Hub() {
new Keccak(this, ecRec, this.l2Block),
new L2L1Logs(this.l2Block));

this.refTableModules = List.of(new BinRt(), new InstructionDecoder(), new ShfRt());

this.modules =
Stream.concat(
Stream.of(
Expand All @@ -240,9 +243,9 @@ public Hub() {
this.rlpTxrcpt,
this.rom,
this.shf,
this.stp,
this.trm,
this.txnData,
this.stp,
this.wcp),
this.precompileLimitModules.stream())
.toList();
Expand All @@ -252,32 +255,32 @@ public Hub() {
* @return a list of all modules for which to generate traces
*/
public List<Module> getModulesToTrace() {
return List.of(
// Reference tables
new BinRt(),
new InstructionDecoder(),
new ShfRt(),
// Modules
this,
this.add,
this.bin,
this.ext,
// this.ecData, // TODO: not yet
this.euc,
this.logData,
this.logInfo,
this.mod,
this.mul,
this.mxp,
this.rlpAddr,
this.rlpTxn,
this.rlpTxrcpt,
this.rom,
this.romLex,
this.shf,
this.stp,
this.txnData,
this.wcp);
return Stream.concat(
this.refTableModules.stream(),
// Modules
Stream.of(
this,
this.romLex,
this.add,
this.bin,
this.ext,
// this.ecData, // TODO: not yet
this.euc,
this.logData,
this.logInfo,
this.mod,
this.modexp.data(),
this.mul,
this.mxp,
this.rlpAddr,
this.rlpTxn,
this.rlpTxrcpt,
this.rom,
this.shf,
this.stp,
this.txnData,
this.wcp))
.toList();
}

public List<Module> getModulesToCount() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@
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.module.modexpdata.ModexpData;
import net.consensys.linea.zktracer.module.modexpdata.ModexpDataOperation;
import net.consensys.linea.zktracer.opcode.OpCode;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.datatypes.Address;
Expand All @@ -35,12 +39,17 @@

@Slf4j
@RequiredArgsConstructor
@Accessors(fluent = true)
public class ModexpEffectiveCall implements Module {
private final Hub hub;

@Getter private final ModexpData data = new ModexpData();
private final Stack<Integer> counts = new Stack<>();
private static final BigInteger PROVER_MAX_INPUT_BIT_SIZE = BigInteger.valueOf(4096);
private static final int EVM_WORD_SIZE = 32;

private int lastModexpDataCallHubStamp = 0;

@Override
public String moduleKey() {
return "PRECOMPILE_MODEXP_EFFECTIVE_CALL";
Expand All @@ -60,105 +69,124 @@ public void popTransaction() {
public void tracePreOpcode(MessageFrame frame) {
final OpCode opCode = hub.opCode();

switch (opCode) {
case CALL, STATICCALL, DELEGATECALL, CALLCODE -> {
final Address target = Words.toAddress(frame.getStackItem(1));
if (target.equals(Address.MODEXP)) {
long length = 0;
long offset = 0;
switch (opCode) {
case CALL, CALLCODE -> {
length = Words.clampedToLong(frame.getStackItem(4));
offset = Words.clampedToLong(frame.getStackItem(3));
}
case DELEGATECALL, STATICCALL -> {
length = Words.clampedToLong(frame.getStackItem(3));
offset = Words.clampedToLong(frame.getStackItem(2));
}
if (opCode.isAnyOf(OpCode.CALL, OpCode.STATICCALL, OpCode.DELEGATECALL, OpCode.CALLCODE)) {
final Address target = Words.toAddress(frame.getStackItem(1));
if (target.equals(Address.MODEXP)) {
long length = 0;
long offset = 0;
switch (opCode) {
case CALL, CALLCODE -> {
length = Words.clampedToLong(frame.getStackItem(4));
offset = Words.clampedToLong(frame.getStackItem(3));
}
final Bytes inputData = frame.shadowReadMemory(offset, length);

// Get the Base length
final BigInteger baseLength = slice(inputData, 0, EVM_WORD_SIZE).toUnsignedBigInteger();
if (baseLength.multiply(BigInteger.valueOf(8)).compareTo(PROVER_MAX_INPUT_BIT_SIZE) > 0) {
log.info(
"Too big argument, base bit length = {} > {}",
baseLength,
PROVER_MAX_INPUT_BIT_SIZE);
this.counts.pop();
this.counts.push(Integer.MAX_VALUE);
return;
case DELEGATECALL, STATICCALL -> {
length = Words.clampedToLong(frame.getStackItem(3));
offset = Words.clampedToLong(frame.getStackItem(2));
}
}
final Bytes inputData = frame.shadowReadMemory(offset, length);

// Get the Base length
final BigInteger baseLength = slice(inputData, 0, EVM_WORD_SIZE).toUnsignedBigInteger();
if (isInProverInputBounds(baseLength)) {
log.info(
"Too big argument, base bit length = {} > {}", baseLength, PROVER_MAX_INPUT_BIT_SIZE);
this.counts.pop();
this.counts.push(Integer.MAX_VALUE);
return;
}

// Get the Exponent length
final BigInteger expLength =
slice(inputData, EVM_WORD_SIZE, EVM_WORD_SIZE).toUnsignedBigInteger();
if (expLength.multiply(BigInteger.valueOf(8)).compareTo(PROVER_MAX_INPUT_BIT_SIZE) > 0) {
log.info(
"Too big argument, exp bit length = {} > {}", expLength, PROVER_MAX_INPUT_BIT_SIZE);
this.counts.pop();
this.counts.push(Integer.MAX_VALUE);
return;
}
// Get the Exponent length
final BigInteger expLength =
slice(inputData, EVM_WORD_SIZE, EVM_WORD_SIZE).toUnsignedBigInteger();
if (isInProverInputBounds(expLength)) {
log.info(
"Too big argument, expComponent bit length = {} > {}",
expLength,
PROVER_MAX_INPUT_BIT_SIZE);
this.counts.pop();
this.counts.push(Integer.MAX_VALUE);
return;
}

// Get the Modulo length
final BigInteger moduloLength =
slice(inputData, 2 * EVM_WORD_SIZE, EVM_WORD_SIZE).toUnsignedBigInteger();
if (moduloLength.multiply(BigInteger.valueOf(8)).compareTo(PROVER_MAX_INPUT_BIT_SIZE)
> 0) {
log.info(
"Too big argument, modulo bit length = {} > {}",
moduloLength,
PROVER_MAX_INPUT_BIT_SIZE);
this.counts.pop();
this.counts.push(Integer.MAX_VALUE);
return;
}
// Get the Modulo length
final BigInteger modLength =
slice(inputData, 2 * EVM_WORD_SIZE, EVM_WORD_SIZE).toUnsignedBigInteger();
if (isInProverInputBounds(modLength)) {
log.info(
"Too big argument, modulo bit length = {} > {}",
modLength,
PROVER_MAX_INPUT_BIT_SIZE);
this.counts.pop();
this.counts.push(Integer.MAX_VALUE);
return;
}

// Get the Exponent
final Bytes exp =
slice(
inputData,
3 * EVM_WORD_SIZE + baseLength.intValueExact(),
expLength.intValueExact());

final long gasPaid = Words.clampedToLong(frame.getStackItem(0));

// If enough gas, add 1 to the call of the precompile
if (gasPaid
>= gasPrice(
baseLength.intValueExact(),
expLength.intValueExact(),
moduloLength.intValueExact(),
exp)) {
this.counts.push(this.counts.pop() + 1);
}
final int baseLengthInt = baseLength.intValueExact();
final int expLengthInt = expLength.intValueExact();
final int modLengthInt = modLength.intValueExact();

// Get the Base.
final Bytes baseComponent = slice(inputData, 3 * EVM_WORD_SIZE, baseLengthInt);

// Get the Exponent.
final Bytes expComponent =
slice(inputData, 3 * EVM_WORD_SIZE + baseLengthInt, expLength.intValueExact());

// Get the Modulus.
final Bytes modComponent =
slice(
inputData,
3 * EVM_WORD_SIZE + baseLengthInt + expLengthInt,
modLength.intValueExact());

final long gasPaid = Words.clampedToLong(frame.getStackItem(0));
final long gasPrice = gasPrice(baseLengthInt, expLengthInt, modLengthInt, expComponent);

// If enough gas, add 1 to the call of the precompile.
if (gasPaid >= gasPrice) {
this.lastModexpDataCallHubStamp =
this.data.call(
new ModexpDataOperation(
hub.stamp(),
lastModexpDataCallHubStamp,
baseComponent,
expComponent,
modComponent));
this.counts.push(this.counts.pop() + 1);
}
}
default -> {}
}
}

private long gasPrice(int lB, int lE, int lM, Bytes e) {
final long maxLbLmSquared = (long) Math.sqrt((double) (Math.max(lB, lM) + 7) / 8);
final long secondArg = (maxLbLmSquared * expLengthPrime(lE, e)) / 3;
private long gasPrice(int baseLength, int expLength, int moduloLength, Bytes e) {
final long maxLbLmSquared =
(long) Math.sqrt((double) (Math.max(baseLength, moduloLength) + 7) / 8);
final long secondArg = (maxLbLmSquared * expLengthPrime(expLength, e)) / 3;

return Math.max(200, secondArg);
}

private int expLengthPrime(int lE, Bytes e) {
if (lE <= 32) {
private int expLengthPrime(int expLength, Bytes e) {
if (expLength <= 32) {
if (e.toUnsignedBigInteger().equals(BigInteger.ZERO)) {
return 0;
} else {
return (e.toUnsignedBigInteger().bitLength() - 1);
}
} else {
if (e.slice(0, EVM_WORD_SIZE).toUnsignedBigInteger().compareTo(BigInteger.ZERO) != 0) {
return 8 * (lE - 32) + e.slice(0, EVM_WORD_SIZE).toUnsignedBigInteger().bitLength() - 1;
} else {
return 8 * (lE - 32);
}
return e.toUnsignedBigInteger().bitLength() - 1;
} else if (e.slice(0, EVM_WORD_SIZE).toUnsignedBigInteger().compareTo(BigInteger.ZERO) != 0) {
return 8 * (expLength - 32)
+ e.slice(0, EVM_WORD_SIZE).toUnsignedBigInteger().bitLength()
- 1;
}

return 8 * (expLength - 32);
}

private boolean isInProverInputBounds(BigInteger modexpComponentLength) {
return modexpComponentLength
.multiply(BigInteger.valueOf(8))
.compareTo(PROVER_MAX_INPUT_BIT_SIZE)
> 0;
}

@Override
Expand All @@ -168,11 +196,11 @@ public int lineCount() {

@Override
public List<ColumnHeader> columnsHeaders() {
throw new IllegalStateException("should never be called");
throw new UnsupportedOperationException("should never be called");
}

@Override
public void commit(List<MappedByteBuffer> buffers) {
throw new IllegalStateException("should never be called");
throw new UnsupportedOperationException("should never be called");
}
}
Loading

0 comments on commit 0e44a9f

Please sign in to comment.