From fe1b978211256f4b3c13be7a27568ba2b04a5092 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 21 Feb 2024 21:35:10 +0100 Subject: [PATCH 01/30] Update CHANGELOG for v0.1.4-test20 (#610) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 722bc4e0..4e6d448f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.1.4-test20 +Test pre-release 20 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* Get L2L1 settings from CLI options [#591](https://github.com/Consensys/besu-sequencer-plugins/pull/591) +* feat: add a replay capture script [#600](https://github.com/Consensys/besu-sequencer-plugins/pull/600) +* move compress native into plugin repo [#604](https://github.com/Consensys/besu-sequencer-plugins/pull/604) +* Add compression [#605](https://github.com/Consensys/besu-sequencer-plugins/pull/605) +* Update for the new bad block manager [#607](https://github.com/Consensys/besu-sequencer-plugins/pull/607) + ## 0.1.4-test19 Test pre-release 19 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) * Avoid returning an estimated priority fee that is less than the min gas price [#598](https://github.com/Consensys/besu-sequencer-plugins/pull/598) From e3a966a5d14c36c85f98f001197248a10d4fc3dd Mon Sep 17 00:00:00 2001 From: delehef Date: Thu, 22 Feb 2024 10:34:19 +0100 Subject: [PATCH 02/30] fix: capture SSTORE-touched storage slots for correct gas computations (#606) --- .../consensys/linea/blockcapture/BlockCapturer.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arithmetization/src/main/java/net/consensys/linea/blockcapture/BlockCapturer.java b/arithmetization/src/main/java/net/consensys/linea/blockcapture/BlockCapturer.java index 2d5e409b..24ea5ea5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/blockcapture/BlockCapturer.java +++ b/arithmetization/src/main/java/net/consensys/linea/blockcapture/BlockCapturer.java @@ -111,6 +111,16 @@ public void tracePreExecution(MessageFrame frame) { } } + // SSTORE needs to know the previous storage value for correct gas computation + case SSTORE -> { + if (frame.stackSize() > 1) { + final Account account = frame.getWorldUpdater().get(frame.getRecipientAddress()); + final Address address = account.getAddress(); + final UInt256 key = UInt256.fromBytes(frame.getStackItem(0)); + this.reaper.touchStorage(address, key); + } + } + // These access contracts potentially existing before the conflation played out. case CALL, CALLCODE, DELEGATECALL, STATICCALL -> { if (frame.stackSize() > 1) { From 21923cc83ea6c43b1615b66a2717ce35715af602 Mon Sep 17 00:00:00 2001 From: delehef Date: Mon, 26 Feb 2024 15:42:50 +0100 Subject: [PATCH 03/30] build: make the build script portable, explicit dependency on Go & GCC, test libcompress build (#621) Signed-off-by franklin.delehelle@consensys.net --- .github/workflows/gradle.yml | 49 ++++++++++- README.md | 4 + native/build.sh | 88 +++++-------------- native/compress/build.gradle | 16 ++-- .../linea/compress/LibCompressTest.java | 1 - 5 files changed, 78 insertions(+), 80 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 1c983394..39941026 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -13,6 +13,12 @@ jobs: build: runs-on: ubuntu-latest steps: + - name: Install Go + uses: actions/setup-go@v4 + + - name: Set up GCC + uses: egor-tensin/setup-gcc@v1 + - name: Checkout repository uses: actions/checkout@v3 @@ -36,6 +42,12 @@ jobs: acceptanceTest: runs-on: ubuntu-latest steps: + - name: Install Go + uses: actions/setup-go@v4 + + - name: Set up GCC + uses: egor-tensin/setup-gcc@v1 + - name: Checkout repository uses: actions/checkout@v3 @@ -54,9 +66,39 @@ jobs: if: always() uses: actions/upload-artifact@v3 with: - name: test-report + name: acceptance-test-report path: acceptance-tests/build/reports/tests/ + libCompressTest: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v4 + + - name: Set up GCC + uses: egor-tensin/setup-gcc@v1 + + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'adopt' + + - name: Run libcompress JNI tests + run: ./gradlew :native:compress:test + env: + JAVA_OPTS: -Dorg.gradle.daemon=false + + - name: Upload test report + if: always() + uses: actions/upload-artifact@v3 + with: + name: compress-test-report + path: compress/build/reports/tests/ + tests: runs-on: ubuntu-latest steps: @@ -65,7 +107,6 @@ jobs: ssh-private-key: | ${{ secrets.CONSTRAINTS_SSH_KEY }} - - name: Checkout repository uses: actions/checkout@v3 with: @@ -103,8 +144,8 @@ jobs: - name: Upload test report uses: actions/upload-artifact@v3 with: - name: test-report - path: build/reports/tests/ + name: unit-test-report + path: arithmetization/build/reports/tests/ spotless: runs-on: ubuntu-latest diff --git a/README.md b/README.md index a0f2622b..011374f7 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,10 @@ an [existing implementation in Go](https://github.com/Consensys/zk-evm/). brew install openjdk@17 ``` +### Install the relevant CGo compiler for your platform + +### Install the Go toolchain + ### Install Rust ``` diff --git a/native/build.sh b/native/build.sh index 459201c5..85be58c7 100755 --- a/native/build.sh +++ b/native/build.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # Initialize external vars - need this to get around unbound variable errors SKIP_GRADLE="$SKIP_GRADLE" @@ -9,71 +9,25 @@ set -o nounset # Exit script if a statement returns a non-true return value. set -o errexit -# Use the error status of the first failure, rather than that of the last item in a pipeline. -set -o pipefail - -# Resolve the directory that contains this script. We have to jump through a few -# hoops for this because the usual one-liners for this don't work if the script -# is a symlink -SOURCE="${BASH_SOURCE[0]}" -while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink - DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" - SOURCE="$(readlink "$SOURCE")" - [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located -done -SCRIPTDIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" - -# Determine core count for parallel make -if [[ "$OSTYPE" == "linux-gnu" ]]; then - CORE_COUNT=$(nproc) - OSARCH=${OSTYPE%%[0-9.]*}-`arch` -fi - -if [[ "$OSTYPE" == "darwin"* ]]; then - CORE_COUNT=$(sysctl -n hw.ncpu) - if [[ "`machine`" == "arm"* ]]; then - arch_name="aarch64" - export CFLAGS="-arch arm64" - else - arch_name="x86-64" - export CFLAGS="-arch x86_64" - fi - OSARCH="darwin-$arch_name" +SCRIPTDIR=$(dirname -- "$( readlink -f -- "$0")") +OSTYPE=$(uname -o) + +# delete old build dir, if exists +rm -rf "$SCRIPTDIR/compress/build/native" || true +mkdir -p "$SCRIPTDIR/compress/build/native" + +if [ x"$OSTYPE" = x"msys" ]; then + LIBRARY_EXTENSION=dll +elif [ x"$OSTYPE" = x"GNU/Linux" ]; then + LIBRARY_EXTENSION=so +elif [ x"$OSTYPE" = x"Darwin" ]; then + LIBRARY_EXTENSION=dylib +else + echo "*** Unknown OS: $OSTYPE" + exit 1 fi -# add to path cargo -[ -f $HOME/.cargo/env ] && . $HOME/.cargo/env - -# add to path brew -[ -f $HOME/.zprofile ] && . $HOME/.zprofile - -build_compress() { - cat < Date: Tue, 27 Feb 2024 12:09:05 +0100 Subject: [PATCH 04/30] Update after the refactor of transaction selection service (#626) Signed-off-by: Fabio Di Fabio --- .../sequencer/txselection/LineaTransactionSelectorPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java index 0bbb00d5..78cc3707 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java @@ -95,7 +95,7 @@ private void createAndRegister( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, final Map limitsMap) { - transactionSelectionService.registerTransactionSelectorFactory( + transactionSelectionService.registerPluginTransactionSelectorFactory( new LineaTransactionSelectorFactory( txSelectorConfiguration, l1L2BridgeConfiguration, limitsMap)); } From 3188771c540fd6c12a5f6152beafebbbdb88f1df Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Tue, 27 Feb 2024 16:27:51 +0100 Subject: [PATCH 05/30] Use the right classloader to load the native library (#628) Signed-off-by: Fabio Di Fabio --- .../AbstractLineaSharedOptionsPlugin.java | 6 ++ native/compress/build.gradle | 85 ++++++++++--------- .../consensys/linea/compress/LibCompress.java | 78 +++++++++-------- .../linea/compress/LibCompressTest.java | 47 +++++----- 4 files changed, 113 insertions(+), 103 deletions(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java index 7f007d07..ccdca491 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -16,6 +16,7 @@ package net.consensys.linea; import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.compress.LibCompress; import net.consensys.linea.config.LineaL1L2BridgeCliOptions; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; @@ -38,6 +39,11 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { protected static LineaTransactionValidatorConfiguration transactionValidatorConfiguration; protected static LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; + static { + // force the initialization of the gnark compress native library to fail fast in case of issues + LibCompress.CompressedSize(new byte[0], 0); + } + @Override public synchronized void register(final BesuContext context) { if (!cliOptionsRegistered) { diff --git a/native/compress/build.gradle b/native/compress/build.gradle index 3961bbef..ec7843f9 100644 --- a/native/compress/build.gradle +++ b/native/compress/build.gradle @@ -14,90 +14,91 @@ */ plugins { - id 'java-library' - id 'maven-publish' - id 'com.jfrog.artifactory' version '4.20.0' + id 'java-library' + id 'common-plugins' } +apply from: rootProject.file("gradle/java.gradle") apply from: rootProject.file("gradle/dependency-management.gradle") +apply from: rootProject.file('gradle/common-dependencies.gradle') +apply from: rootProject.file("gradle/build-aliases.gradle") +apply from: rootProject.file("gradle/lint.gradle") repositories { - mavenCentral() + mavenCentral() } test { - useJUnitPlatform() + useJUnitPlatform() } tasks.register('buildJNI', Exec) { - workingDir buildscript.sourceFile.parentFile - commandLine 'sh', '-c', '../build.sh' + workingDir buildscript.sourceFile.parentFile + commandLine 'sh', '-c', '../build.sh' } compileJava{ - dependsOn buildJNI + dependsOn buildJNI } dependencies { - implementation 'net.java.dev.jna:jna' - implementation 'io.tmio:tuweni-bytes' + implementation 'net.java.dev.jna:jna' + implementation 'io.tmio:tuweni-bytes' - testImplementation 'org.junit.jupiter:junit-jupiter-api' - testImplementation 'org.junit.jupiter:junit-jupiter-engine' - testImplementation 'org.assertj:assertj-core' - testImplementation 'org.mockito:mockito-core' + testImplementation 'org.junit.jupiter:junit-jupiter-api' + testImplementation 'org.junit.jupiter:junit-jupiter-engine' + testImplementation 'org.assertj:assertj-core' + testImplementation 'org.mockito:mockito-core' } tasks.register('macArmLibCopy', Copy) { - from 'build/native/libcompress_jni.dylib' - into 'build/resources/main/darwin-aarch64' + from 'build/native/libcompress_jni.dylib' + into 'build/resources/main/darwin-aarch64' } processResources.dependsOn macArmLibCopy tasks.register('macLibCopy', Copy) { - from 'build/native/libcompress_jni.dylib' - into 'build/resources/main/darwin-x86-64' + from 'build/native/libcompress_jni.dylib' + into 'build/resources/main/darwin-x86-64' } processResources.dependsOn macLibCopy tasks.register('linuxLibCopy', Copy) { - from 'build/native/libcompress_jni.so' - into 'build/resources/main/linux-x86-64' + from 'build/native/libcompress_jni.so' + into 'build/resources/main/linux-x86-64' } processResources.dependsOn linuxLibCopy tasks.register('linuxArm64LibCopy', Copy) { - from 'build/native/libcompress_jni.so' - into 'build/resources/main/linux-aarch64' + from 'build/native/libcompress_jni.so' + into 'build/resources/main/linux-aarch64' } processResources.dependsOn linuxArm64LibCopy jar { - archiveBaseName = 'linea-native-compress' - includeEmptyDirs = false - manifest { - attributes( - 'Specification-Title': archiveBaseName, - 'Specification-Version': project.version, - 'Implementation-Title': archiveBaseName, - 'Implementation-Version': project.version, - 'Automatic-Module-Name': 'org.consensys.nativelib.compress' - ) - } + archiveBaseName = 'linea-native-compress' + includeEmptyDirs = false + manifest { + attributes( + 'Specification-Title': archiveBaseName, + 'Specification-Version': project.version, + 'Implementation-Title': archiveBaseName, + 'Implementation-Version': project.version, + 'Automatic-Module-Name': 'net.consensys.nativelib.compress' + ) + } } jar.dependsOn(buildJNI) sourceSets { - main { - resources { - srcDirs '${buildDir}/darwin-aaarch64' - srcDirs '${buildDir}/darwin-x86-64' - srcDirs '${buildDir}/linux-gnu-x86_64' - srcDirs '${buildDir}/linux-gnu-aarch64' - } + main { + resources { + srcDirs '${buildDir}/darwin-aaarch64' + srcDirs '${buildDir}/darwin-x86-64' + srcDirs '${buildDir}/linux-gnu-x86_64' + srcDirs '${buildDir}/linux-gnu-aarch64' } + } } - - diff --git a/native/compress/src/main/java/net/consensys/linea/compress/LibCompress.java b/native/compress/src/main/java/net/consensys/linea/compress/LibCompress.java index 80f5425f..61d88823 100644 --- a/native/compress/src/main/java/net/consensys/linea/compress/LibCompress.java +++ b/native/compress/src/main/java/net/consensys/linea/compress/LibCompress.java @@ -15,53 +15,57 @@ */ package net.consensys.linea.compress; -import com.sun.jna.Native; - import java.io.File; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -/** - * Java interface to compress - */ +import com.sun.jna.Native; +import lombok.extern.slf4j.Slf4j; + +/** Java interface to compress */ +@Slf4j public class LibCompress { - @SuppressWarnings("WeakerAccess") - public static final boolean ENABLED; + @SuppressWarnings("WeakerAccess") + public static final boolean ENABLED; - static { - boolean enabled; - try { - final File compressJni = Native.extractFromResourcePath("compress_jni"); - Native.register(LibCompress.class, compressJni.getAbsolutePath()); + static { + try { + final File compressJni = + Native.extractFromResourcePath("compress_jni", LibCompress.class.getClassLoader()); + Native.register(LibCompress.class, compressJni.getAbsolutePath()); - Path dictFilePath = Files.createTempFile("tempCompressor_dict", "bin"); - try (InputStream stream = LibCompress.class.getClassLoader().getResourceAsStream("compressor_dict.bin")) { - Files.copy(stream, dictFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING); - dictFilePath.toFile().deleteOnExit(); - } catch (Exception e) { - System.out.println("Problem creating temp file from compressor_dict.bin resource: " + e.getMessage()); - dictFilePath.toFile().delete(); - System.exit(-1); - } + Path dictFilePath = Files.createTempFile("tempCompressor_dict", "bin"); + try (InputStream stream = + LibCompress.class.getClassLoader().getResourceAsStream("compressor_dict.bin")) { + Files.copy(stream, dictFilePath, java.nio.file.StandardCopyOption.REPLACE_EXISTING); + dictFilePath.toFile().deleteOnExit(); + } catch (Exception e) { + log.error( + "Problem creating temp file from compressor_dict.bin resource: " + e.getMessage()); + dictFilePath.toFile().delete(); + System.exit(1); + } - final String dictPath = dictFilePath.toAbsolutePath().toString(); - if (!Init(dictPath)) { - throw new RuntimeException(Error()); - } - enabled = true; - } catch (final Throwable t) { - t.printStackTrace(); - enabled = false; - } - ENABLED = enabled; + final String dictPath = dictFilePath.toAbsolutePath().toString(); + if (!Init(dictPath)) { + throw new RuntimeException(Error()); + } + log.info( + "Loaded compress_jni native library from {} with compressor_dict {}", + compressJni, + dictPath); + } catch (final Throwable t) { + log.error("Error loading native compress_jni library", t); + System.exit(1); } + ENABLED = true; + } + + public static native boolean Init(String dictPath); - public static native boolean Init(String dictPath); - - public static native int CompressedSize( - byte[] i, int i_len); + public static native int CompressedSize(byte[] i, int i_len); - public static native String Error(); -} \ No newline at end of file + public static native String Error(); +} diff --git a/native/compress/src/test/java/net/consensys/linea/compress/LibCompressTest.java b/native/compress/src/test/java/net/consensys/linea/compress/LibCompressTest.java index 8f1a3771..9efe79f6 100644 --- a/native/compress/src/test/java/net/consensys/linea/compress/LibCompressTest.java +++ b/native/compress/src/test/java/net/consensys/linea/compress/LibCompressTest.java @@ -15,30 +15,29 @@ */ package net.consensys.linea.compress; -import org.junit.jupiter.api.Test; - import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -public class LibCompressTest { - @Test - public void testCompressZeroes() { - byte[] zeroes = new byte[128]; - int size = LibCompress.CompressedSize(zeroes, 128); - - // should not error - assertThat(size).isGreaterThan(0); - - // should have compressed into 1 backref + header, must be less than 10 - assertThat(size).isLessThan(10); - } - - @Test - public void testCompressTooLargeInput() { - byte[] zeroes = new byte[512*1024]; - int size = LibCompress.CompressedSize(zeroes, 512*1024); - - // should error --> too large payload. - assertThat(size).isLessThan(0); - } +import org.junit.jupiter.api.Test; -} \ No newline at end of file +public class LibCompressTest { + @Test + public void testCompressZeroes() { + byte[] zeroes = new byte[128]; + int size = LibCompress.CompressedSize(zeroes, 128); + + // should not error + assertThat(size).isGreaterThan(0); + + // should have compressed into 1 backref + header, must be less than 10 + assertThat(size).isLessThan(10); + } + + @Test + public void testCompressTooLargeInput() { + byte[] zeroes = new byte[512 * 1024]; + int size = LibCompress.CompressedSize(zeroes, 512 * 1024); + + // should error --> too large payload. + assertThat(size).isLessThan(0); + } +} From 2a46de10baf975e1a258f43390001efbc7b1d415 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 28 Feb 2024 02:22:27 +0100 Subject: [PATCH 06/30] Update CHANGELOG for v0.1.4-test21 (#630) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e6d448f..419be301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.1.4-test21 +Test pre-release 21 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* fix: capture SSTORE-touched storage slots for correct gas computations [#606](https://github.com/Consensys/besu-sequencer-plugins/pull/606) +* build: make the build script portable, explicit dependency on Go & GCC, test libcompress build [#621](https://github.com/Consensys/besu-sequencer-plugins/pull/621) +* Update after the refactor of transaction selection service [#626](https://github.com/Consensys/besu-sequencer-plugins/pull/626) +* Use the right classloader to load the native library [#628](https://github.com/Consensys/besu-sequencer-plugins/pull/628) + ## 0.1.4-test20 Test pre-release 20 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) * Get L2L1 settings from CLI options [#591](https://github.com/Consensys/besu-sequencer-plugins/pull/591) From c98db8521e5b21da3906c17d42f127bcadfb8631 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Thu, 29 Feb 2024 09:35:36 +0100 Subject: [PATCH 07/30] linea_estimateGas compatibility switch (#634) Signed-off-by: Fabio Di Fabio Co-authored-by: Sally MacFarlane --- .../EstimateGasCompatibilityModeTest.java | 45 ++++++++ .../acc/test/rpc/linea/EstimateGasTest.java | 100 ++++++++++++++---- .../AbstractLineaSharedOptionsPlugin.java | 9 ++ .../linea/config/LineaRpcCliOptions.java | 73 +++++++++++++ .../linea/config/LineaRpcConfiguration.java | 22 ++++ .../rpc/linea/LineaEndpointServicePlugin.java | 2 +- .../linea/rpc/linea/LineaEstimateGas.java | 49 ++++++--- 7 files changed, 259 insertions(+), 41 deletions(-) create mode 100644 acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/config/LineaRpcCliOptions.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java new file mode 100644 index 00000000..4649d5ac --- /dev/null +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java @@ -0,0 +1,45 @@ +/* + * 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 linea.plugin.acc.test.rpc.linea; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.Transaction; + +public class EstimateGasCompatibilityModeTest extends EstimateGasTest { + + @Override + public List getTestCliOptions() { + return getTestCommandLineOptionsBuilder() + .set("--plugin-linea-estimate-gas-compatibility-mode-enabled=", "true") + .build(); + } + + @Override + protected void assertIsProfitable( + final Transaction tx, + final Wei estimatedPriorityFee, + final Wei estimatedMaxGasPrice, + final long estimatedGasLimit) { + final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice(); + + // since we are in compatibility mode, we want to check that returned profitable priority fee is + // the min mineable gas price + assertThat(estimatedMaxGasPrice).isEqualTo(minGasPrice); + } +} diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java index e204ccc3..306795f5 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.math.BigInteger; +import java.nio.charset.StandardCharsets; import java.util.List; import linea.plugin.acc.test.LineaPluginTestBase; @@ -28,6 +29,7 @@ import net.consensys.linea.rpc.linea.LineaEstimateGas; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt64; +import org.bouncycastle.crypto.digests.KeccakDigest; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.tests.acceptance.dsl.account.Account; @@ -38,25 +40,28 @@ import org.web3j.protocol.core.Request; public class EstimateGasTest extends LineaPluginTestBase { - private static final int VERIFICATION_GAS_COST = 1_200_000; - private static final int VERIFICATION_CAPACITY = 90_000; - private static final int GAS_PRICE_RATIO = 15; - private static final double MIN_MARGIN = 1.0; - private static final double ESTIMATE_GAS_MIN_MARGIN = 1.0; - private static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000); - public static final int MAX_TRANSACTION_GAS_LIMIT = 30_000_000; - private LineaTransactionSelectorConfiguration txSelectorConf; + protected static final int VERIFICATION_GAS_COST = 1_200_000; + protected static final int VERIFICATION_CAPACITY = 90_000; + protected static final int GAS_PRICE_RATIO = 15; + protected static final double MIN_MARGIN = 1.0; + protected static final double ESTIMATE_GAS_MIN_MARGIN = 1.0; + protected static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000); + protected static final int MAX_TRANSACTION_GAS_LIMIT = 30_000_000; + protected LineaTransactionSelectorConfiguration txSelectorConf; @Override public List getTestCliOptions() { + return getTestCommandLineOptionsBuilder().build(); + } + + protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() { return new TestCommandLineOptionsBuilder() .set("--plugin-linea-verification-gas-cost=", String.valueOf(VERIFICATION_GAS_COST)) .set("--plugin-linea-verification-capacity=", String.valueOf(VERIFICATION_CAPACITY)) .set("--plugin-linea-gas-price-ratio=", String.valueOf(GAS_PRICE_RATIO)) .set("--plugin-linea-min-margin=", String.valueOf(MIN_MARGIN)) .set("--plugin-linea-estimate-gas-min-margin=", String.valueOf(ESTIMATE_GAS_MIN_MARGIN)) - .set("--plugin-linea-max-tx-gas-limit=", String.valueOf(MAX_TRANSACTION_GAS_LIMIT)) - .build(); + .set("--plugin-linea-max-tx-gas-limit=", String.valueOf(MAX_TRANSACTION_GAS_LIMIT)); } @BeforeEach @@ -81,7 +86,8 @@ public void lineaEstimateGasMatchesEthEstimateGas() { final Account sender = accounts.getSecondaryBenefactor(); - final CallParams callParams = new CallParams(sender.getAddress(), null); + final CallParams callParams = + new CallParams(sender.getAddress(), sender.getAddress(), null, Bytes.EMPTY.toHexString()); final var reqEth = new RawEstimateGasRequest(callParams); final var reqLinea = new LineaEstimateGasRequest(callParams); @@ -95,28 +101,58 @@ public void lineaEstimateGasIsProfitable() { final Account sender = accounts.getSecondaryBenefactor(); - final CallParams callParams = new CallParams(sender.getAddress(), null); + final KeccakDigest keccakDigest = new KeccakDigest(256); + final StringBuilder txData = new StringBuilder(); + txData.append("0x"); + for (int i = 0; i < 5; i++) { + keccakDigest.update(new byte[] {(byte) i}, 0, 1); + final byte[] out = new byte[32]; + keccakDigest.doFinal(out, 0); + txData.append(new BigInteger(out).abs()); + } + final var payload = Bytes.wrap(txData.toString().getBytes(StandardCharsets.UTF_8)); + + final CallParams callParams = + new CallParams(sender.getAddress(), sender.getAddress(), null, payload.toHexString()); final var reqLinea = new LineaEstimateGasRequest(callParams); final var respLinea = reqLinea.execute(minerNode.nodeRequests()); - final var gasLimit = UInt64.fromHexString(respLinea.gasLimit()).toLong(); + final var estimatedGasLimit = UInt64.fromHexString(respLinea.gasLimit()).toLong(); final var baseFee = Wei.fromHexString(respLinea.baseFeePerGas()); - final var priorityFee = Wei.fromHexString(respLinea.priorityFeePerGas()); - final var maxGasPrice = baseFee.add(priorityFee); + final var estimatedPriorityFee = Wei.fromHexString(respLinea.priorityFeePerGas()); + final var estimatedMaxGasPrice = baseFee.add(estimatedPriorityFee); final var tx = org.hyperledger.besu.ethereum.core.Transaction.builder() .sender(Address.fromHexString(sender.getAddress())) - .gasLimit(gasLimit) - .gasPrice(maxGasPrice) + .to(Address.fromHexString(sender.getAddress())) + .gasLimit(estimatedGasLimit) + .gasPrice(estimatedMaxGasPrice) .chainId(BigInteger.valueOf(CHAIN_ID)) .value(Wei.ZERO) - .payload(Bytes.EMPTY) + .payload(payload) .signature(LineaEstimateGas.FAKE_SIGNATURE_FOR_SIZE_CALCULATION) .build(); + assertIsProfitable(tx, estimatedPriorityFee, estimatedMaxGasPrice, estimatedGasLimit); + } + + protected void assertIsProfitable( + final org.hyperledger.besu.ethereum.core.Transaction tx, + final Wei estimatedPriorityFee, + final Wei estimatedMaxGasPrice, + final long estimatedGasLimit) { + + final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice(); + final var profitabilityCalculator = new TransactionProfitabilityCalculator(txSelectorConf); + + final var profitablePriorityFee = + profitabilityCalculator.profitablePriorityFeePerGas(tx, minGasPrice, estimatedGasLimit); + + assertThat(profitablePriorityFee.greaterThan(minGasPrice)).isTrue(); + assertThat( profitabilityCalculator.isProfitable( "Test", @@ -126,12 +162,30 @@ public void lineaEstimateGasIsProfitable() { .getMinTransactionGasPrice() .getAsBigInteger() .doubleValue(), - maxGasPrice.getAsBigInteger().doubleValue(), - gasLimit)) + estimatedMaxGasPrice.getAsBigInteger().doubleValue(), + estimatedGasLimit)) .isTrue(); } - class LineaEstimateGasRequest implements Transaction { + @Test + public void lineaEstimateGasPriorityFeeMinGasPriceLowerBound() { + + final Account sender = accounts.getSecondaryBenefactor(); + + final CallParams callParams = new CallParams(sender.getAddress(), null, "", ""); + + final var reqLinea = new LineaEstimateGasRequest(callParams); + final var respLinea = reqLinea.execute(minerNode.nodeRequests()); + + final var baseFee = Wei.fromHexString(respLinea.baseFeePerGas()); + final var estimatedPriorityFee = Wei.fromHexString(respLinea.priorityFeePerGas()); + final var estimatedMaxGasPrice = baseFee.add(estimatedPriorityFee); + final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice(); + + assertThat(estimatedMaxGasPrice).isEqualTo(minGasPrice); + } + + static class LineaEstimateGasRequest implements Transaction { private final CallParams callParams; public LineaEstimateGasRequest(final CallParams callParams) { @@ -158,7 +212,7 @@ static class LineaEstimateGasResponse extends org.web3j.protocol.core.Response { + static class RawEstimateGasRequest implements Transaction { private final CallParams callParams; public RawEstimateGasRequest(final CallParams callParams) { @@ -183,5 +237,5 @@ public String execute(final NodeRequests nodeRequests) { static class RawEstimateGasResponse extends org.web3j.protocol.core.Response {} } - record CallParams(String from, String value) {} + record CallParams(String from, String to, String value, String data) {} } diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java index ccdca491..171c20da 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -19,6 +19,8 @@ import net.consensys.linea.compress.LibCompress; import net.consensys.linea.config.LineaL1L2BridgeCliOptions; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaRpcCliOptions; +import net.consensys.linea.config.LineaRpcConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.config.LineaTransactionValidatorCliOptions; @@ -35,9 +37,11 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { private static LineaTransactionSelectorCliOptions transactionSelectorCliOptions; private static LineaTransactionValidatorCliOptions transactionValidatorCliOptions; private static LineaL1L2BridgeCliOptions l1L2BridgeCliOptions; + private static LineaRpcCliOptions rpcCliOptions; protected static LineaTransactionSelectorConfiguration transactionSelectorConfiguration; protected static LineaTransactionValidatorConfiguration transactionValidatorConfiguration; protected static LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; + protected static LineaRpcConfiguration rpcConfiguration; static { // force the initialization of the gnark compress native library to fail fast in case of issues @@ -57,10 +61,12 @@ public synchronized void register(final BesuContext context) { transactionSelectorCliOptions = LineaTransactionSelectorCliOptions.create(); transactionValidatorCliOptions = LineaTransactionValidatorCliOptions.create(); l1L2BridgeCliOptions = LineaL1L2BridgeCliOptions.create(); + rpcCliOptions = LineaRpcCliOptions.create(); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionSelectorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionValidatorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, l1L2BridgeCliOptions); + cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, rpcCliOptions); cliOptionsRegistered = true; } } @@ -71,6 +77,7 @@ public void beforeExternalServices() { transactionSelectorConfiguration = transactionSelectorCliOptions.toDomainObject(); transactionValidatorConfiguration = transactionValidatorCliOptions.toDomainObject(); l1L2BridgeConfiguration = l1L2BridgeCliOptions.toDomainObject(); + rpcConfiguration = rpcCliOptions.toDomainObject(); configured = true; } @@ -88,6 +95,8 @@ public void beforeExternalServices() { "Configured plugin {} with L1 L2 bridge configuration: {}", getName(), l1L2BridgeCliOptions); + + log.debug("Configured plugin {} with RPC configuration: {}", getName(), rpcConfiguration); } @Override diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcCliOptions.java new file mode 100644 index 00000000..05b1e543 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcCliOptions.java @@ -0,0 +1,73 @@ +/* + * 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.config; + +import com.google.common.base.MoreObjects; +import picocli.CommandLine; + +/** The Linea RPC CLI options. */ +public class LineaRpcCliOptions { + private static final String ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED = + "--plugin-linea-estimate-gas-compatibility-mode-enabled"; + + @CommandLine.Option( + names = {ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED}, + paramLabel = "", + description = + "Set to true to return the min mineable gas price, instead of the profitable price (default: ${DEFAULT-VALUE})") + private boolean estimateGasCompatibilityModeEnabled = false; + + private LineaRpcCliOptions() {} + + /** + * Create Linea RPC CLI options. + * + * @return the Linea RPC CLI options + */ + public static LineaRpcCliOptions create() { + return new LineaRpcCliOptions(); + } + + /** + * Linea RPC CLI options from config. + * + * @param config the config + * @return the Linea RPC CLI options + */ + public static LineaRpcCliOptions fromConfig(final LineaRpcConfiguration config) { + final LineaRpcCliOptions options = create(); + options.estimateGasCompatibilityModeEnabled = config.estimateGasCompatibilityModeEnabled(); + return options; + } + + /** + * To domain object Linea factory configuration. + * + * @return the Linea factory configuration + */ + public LineaRpcConfiguration toDomainObject() { + return LineaRpcConfiguration.builder() + .estimateGasCompatibilityModeEnabled(estimateGasCompatibilityModeEnabled) + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add(ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED, estimateGasCompatibilityModeEnabled) + .toString(); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java new file mode 100644 index 00000000..5742ab45 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java @@ -0,0 +1,22 @@ +/* + * 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.config; + +import lombok.Builder; + +/** The Linea RPC configuration. */ +@Builder(toBuilder = true) +public record LineaRpcConfiguration(boolean estimateGasCompatibilityModeEnabled) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java index 3cd7d7f0..a9c72da1 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java @@ -87,6 +87,6 @@ public void doRegister(final BesuContext context) { public void beforeExternalServices() { super.beforeExternalServices(); lineaEstimateGasMethod.init( - transactionValidatorConfiguration, transactionSelectorConfiguration); + rpcConfiguration, transactionValidatorConfiguration, transactionSelectorConfiguration); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java index 557fe153..20f3797e 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -23,6 +23,7 @@ import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.bl.TransactionProfitabilityCalculator; +import net.consensys.linea.config.LineaRpcConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.apache.tuweni.bytes.Bytes; @@ -65,6 +66,7 @@ public class LineaEstimateGas { private final BesuConfiguration besuConfiguration; private final TransactionSimulationService transactionSimulationService; private final BlockchainService blockchainService; + private LineaRpcConfiguration rpcConfiguration; private LineaTransactionValidatorConfiguration txValidatorConf; private LineaTransactionSelectorConfiguration txSelectorConf; private TransactionProfitabilityCalculator txProfitabilityCalculator; @@ -79,8 +81,10 @@ public LineaEstimateGas( } public void init( + LineaRpcConfiguration rpcConfiguration, final LineaTransactionValidatorConfiguration transactionValidatorConfiguration, final LineaTransactionSelectorConfiguration transactionSelectorConfiguration) { + this.rpcConfiguration = rpcConfiguration; this.txValidatorConf = transactionValidatorConfiguration; this.txSelectorConf = transactionSelectorConfiguration; this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(txSelectorConf); @@ -108,7 +112,7 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) { .log(); final var estimatedGasUsed = estimateGasUsed(callParameters, transaction, minGasPrice); - final Wei estimatedPriorityFee = + final Wei profitablePriorityFee = txProfitabilityCalculator.profitablePriorityFeePerGas( transaction, minGasPrice, estimatedGasUsed); @@ -117,28 +121,37 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) { .getNextBlockBaseFee() .orElseThrow(() -> new IllegalStateException("Not on a baseFee market")); - final Wei priorityFeeLowerBound = minGasPrice.subtract(baseFee); - final Wei boundedEstimatedPriorityFee; - if (estimatedPriorityFee.lessThan(priorityFeeLowerBound)) { - boundedEstimatedPriorityFee = priorityFeeLowerBound; - log.atDebug() - .setMessage( - "Estimated priority fee {} is lower that the lower bound {}, returning the latter") - .addArgument(estimatedPriorityFee::toHumanReadableString) - .addArgument(boundedEstimatedPriorityFee::toHumanReadableString) - .log(); - } else { - boundedEstimatedPriorityFee = estimatedPriorityFee; - } + final Wei estimatedPriorityFee = + getEstimatedPriorityFee(baseFee, profitablePriorityFee, minGasPrice); final var response = - new Response( - create(estimatedGasUsed), create(baseFee), create(boundedEstimatedPriorityFee)); + new Response(create(estimatedGasUsed), create(baseFee), create(estimatedPriorityFee)); log.debug("Response for call params {} is {}", callParameters, response); return response; } + private Wei getEstimatedPriorityFee( + final Wei baseFee, final Wei profitablePriorityFee, final Wei minGasPrice) { + final Wei priorityFeeLowerBound = minGasPrice.subtract(baseFee); + + if (rpcConfiguration.estimateGasCompatibilityModeEnabled()) { + return priorityFeeLowerBound; + } + + if (profitablePriorityFee.greaterOrEqualThan(priorityFeeLowerBound)) { + return profitablePriorityFee; + } + + log.atDebug() + .setMessage( + "Estimated priority fee {} is lower that the lower bound {}, returning the latter") + .addArgument(profitablePriorityFee::toHumanReadableString) + .addArgument(priorityFeeLowerBound::toHumanReadableString) + .log(); + return priorityFeeLowerBound; + } + private Long estimateGasUsed( final JsonCallParameter callParameters, final Transaction transaction, @@ -226,7 +239,9 @@ private Long estimateGasUsed( high = mid; log.trace( "Binary gas estimation search low={},med={},high={}, successful, call params {}", - lowGasEstimation, + low, + mid, + high, callParameters); } } From d00e6f1109137963abbbcab08e60ac8f246a4ae2 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Tue, 5 Mar 2024 10:33:13 +0100 Subject: [PATCH 08/30] Refactor profitability options and calculator + gas price adjustment option (#638) Signed-off-by: Fabio Di Fabio --- PLUGINS.md | 71 +++++-- .../acc/test/rpc/linea/EstimateGasTest.java | 22 +- .../AbstractLineaSharedOptionsPlugin.java | 12 ++ .../TransactionProfitabilityCalculator.java | 129 ++++++------ .../config/LineaProfitabilityCliOptions.java | 193 ++++++++++++++++++ .../LineaProfitabilityConfiguration.java | 30 +++ .../LineaTransactionSelectorCliOptions.java | 96 ++------- ...LineaTransactionSelectorConfiguration.java | 6 - .../LineaTransactionValidatorCliOptions.java | 2 + ...ineaTransactionValidatorConfiguration.java | 2 +- .../linea/config/converters/WeiConverter.java | 28 +++ .../rpc/linea/LineaEndpointServicePlugin.java | 2 +- .../linea/rpc/linea/LineaEstimateGas.java | 12 +- .../LineaTransactionSelectorFactory.java | 6 +- .../LineaTransactionSelectorPlugin.java | 8 +- .../selectors/LineaTransactionSelector.java | 12 +- .../ProfitableTransactionSelector.java | 39 ++-- .../LineaTransactionValidatorFactory.java | 9 +- .../LineaTransactionValidatorPlugin.java | 15 +- .../ProfitableTransactionSelectorTest.java | 20 +- 20 files changed, 483 insertions(+), 231 deletions(-) create mode 100644 arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/config/converters/WeiConverter.java diff --git a/PLUGINS.md b/PLUGINS.md index 348358b2..ed94e9a0 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -1,4 +1,35 @@ -# Linea plugins +# Linea plugins + +## Shared components +### Profitability calculator +The profitability calculator is a shared component, that is used to check if a tx is profitable. +It is applied, with different configuration to: +1. `linea_estimateGas` endpoint +2. Tx validation for the txpool +3. Tx selection during block creation + +#### CLI Options + +| Option Name | Default Value | Command Line Argument | +|--------------------------|---------------|-------------------------------------------| +| L1_VERIFICATION_GAS_COST | 1_200_000 | `--plugin-linea-verification-gas-cost` | +| L1_VERIFICATION_CAPACITY | 90_000 | `--plugin-linea-verification-capacity` | +| L1_L2_GAS_PRICE_RATIO | 15 | `--plugin-linea-gas-price-ratio` | +| L2_GAS_PRICE_ADJUSTMENT | 0 wei | `--plugin-linea-gas-price-adjustment` | +| MIN_MARGIN | 1.0 | `--plugin-linea-min-margin` | +| ESTIMATE_GAS_MIN_MARGIN | 1.0 | `--plugin-linea-estimate-gas-min-margin` | +| TX_POOL_MIN_MARGIN | 0.5 | `--plugin-linea-tx-pool-min-margin` | +| UNPROFITABLE_CACHE_SIZE | 100_000 | `--plugin-linea-unprofitable-cache-size` | +| UNPROFITABLE_RETRY_LIMIT | 10 | `--plugin-linea-unprofitable-retry-limit` | + +### L1 L2 Bridge + +#### CLI Options + +| Option Name | Default Value | Command Line Argument | +|------------------------------|---------------|---------------------------------------------| +| L1L2_BRIDGE_CONTRACT_ADDRESS | | `--plugin-linea-l1l2-bridge-contract` | +| L1L2_BRIDGE_LOG_TOPIC | | `--plugin-linea-l1l2-bridge-topic` | ## Sequencer ### Transaction Selection - LineaTransactionSelectorPlugin @@ -11,19 +42,12 @@ of a transaction. #### CLI Options -| Option Name | Default Value | Command Line Argument | -|--------------------------|---------|----------------------------------------| -| MAX_BLOCK_CALLDATA_SIZE | 70000 | `--plugin-linea-max-block-calldata-size` | -| MODULE_LIMIT_FILE_PATH | moduleLimitFile.toml | `--plugin-linea-module-limit-file-path` | -| OVER_LINE_COUNT_LIMIT_CACHE_SIZE | 10_000 | `--plugin-linea-over-line-count-limit-cache-size` | -| MAX_GAS_PER_BLOCK | 30_000_000L | `--plugin-linea-max-block-gas` | -| L1_VERIFICATION_GAS_COST | 1_200_000 | `--plugin-linea-verification-gas-cost` | -| L1_VERIFICATION_CAPACITY | 90_000 | `--plugin-linea-verification-capacity` | -| L1_L2_GAS_PRICE_RATIO | 15 | `--plugin-linea-gas-price-ratio` | -| MIN_MARGIN | 1.0 | `--plugin-linea-min-margin` | -| ADJUST_TX_SIZE | -45 | `--plugin-linea-adjust-tx-size` | -| UNPROFITABLE_CACHE_SIZE | 100_000 | `--plugin-linea-unprofitable-cache-size` | -| UNPROFITABLE_RETRY_LIMIT | 10 | `--plugin-linea-unprofitable-retry-limit` | +| Option Name | Default Value | Command Line Argument | +|----------------------------------|----------------------|---------------------------------------------------| +| MAX_BLOCK_CALLDATA_SIZE | 70000 | `--plugin-linea-max-block-calldata-size` | +| MODULE_LIMIT_FILE_PATH | moduleLimitFile.toml | `--plugin-linea-module-limit-file-path` | +| OVER_LINE_COUNT_LIMIT_CACHE_SIZE | 10_000 | `--plugin-linea-over-line-count-limit-cache-size` | +| MAX_GAS_PER_BLOCK | 30_000_000L | `--plugin-linea-max-block-gas` | ### Transaction validation - LineaTransactionValidatorPlugin @@ -35,14 +59,23 @@ that are not allowed to add transactions to the pool. #### CLI Options -| Option Name | Default Value | Command Line Argument | -| --- | --- | --- | -| DENY_LIST_PATH | lineaDenyList.txt | `--plugin-linea-deny-list-path` | -| MAX_TX_GAS_LIMIT_OPTION | 30_000_000 | `--plugin-linea-max-tx-gas-limit` | -| MAX_TX_CALLDATA_SIZE | 60_000 | `--plugin-linea-max-tx-calldata-size` | +| Option Name | Default Value | Command Line Argument | +|-------------------------|-------------------|---------------------------------------| +| DENY_LIST_PATH | lineaDenyList.txt | `--plugin-linea-deny-list-path` | +| MAX_TX_GAS_LIMIT_OPTION | 30_000_000 | `--plugin-linea-max-tx-gas-limit` | +| MAX_TX_CALLDATA_SIZE | 60_000 | `--plugin-linea-max-tx-calldata-size` | ## RPC +### Linea Estimate Gas +#### `linea_estimateGas` + +This endpoint simulates a transaction and returns the estimated gas used ( as the standard `eth_estimateGas`) plus the estimated gas price to be used when submitting the tx. + +#### Parameters + +same as `eth_estimateGas` + ### Counters - CountersEndpointServicePlugin #### `rollup_getTracesCountersByBlockNumberV0` diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java index 306795f5..9dc90202 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java @@ -24,7 +24,8 @@ import linea.plugin.acc.test.LineaPluginTestBase; import linea.plugin.acc.test.TestCommandLineOptionsBuilder; import net.consensys.linea.bl.TransactionProfitabilityCalculator; -import net.consensys.linea.config.LineaTransactionSelectorCliOptions; +import net.consensys.linea.config.LineaProfitabilityCliOptions; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.rpc.linea.LineaEstimateGas; import org.apache.tuweni.bytes.Bytes; @@ -48,6 +49,7 @@ public class EstimateGasTest extends LineaPluginTestBase { protected static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000); protected static final int MAX_TRANSACTION_GAS_LIMIT = 30_000_000; protected LineaTransactionSelectorConfiguration txSelectorConf; + protected LineaProfitabilityConfiguration profitabilityConf; @Override public List getTestCliOptions() { @@ -71,8 +73,8 @@ public void setMinGasPrice() { @BeforeEach public void createDefaultConfigurations() { - txSelectorConf = - LineaTransactionSelectorCliOptions.create().toDomainObject().toBuilder() + profitabilityConf = + LineaProfitabilityCliOptions.create().toDomainObject().toBuilder() .verificationCapacity(VERIFICATION_CAPACITY) .verificationGasCost(VERIFICATION_GAS_COST) .gasPriceRatio(GAS_PRICE_RATIO) @@ -146,10 +148,11 @@ protected void assertIsProfitable( final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice(); - final var profitabilityCalculator = new TransactionProfitabilityCalculator(txSelectorConf); + final var profitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); final var profitablePriorityFee = - profitabilityCalculator.profitablePriorityFeePerGas(tx, minGasPrice, estimatedGasLimit); + profitabilityCalculator.profitablePriorityFeePerGas( + tx, profitabilityConf.txPoolMinMargin(), minGasPrice, estimatedGasLimit); assertThat(profitablePriorityFee.greaterThan(minGasPrice)).isTrue(); @@ -157,12 +160,9 @@ protected void assertIsProfitable( profitabilityCalculator.isProfitable( "Test", tx, - minerNode - .getMiningParameters() - .getMinTransactionGasPrice() - .getAsBigInteger() - .doubleValue(), - estimatedMaxGasPrice.getAsBigInteger().doubleValue(), + profitabilityConf.txPoolMinMargin(), + minerNode.getMiningParameters().getMinTransactionGasPrice(), + estimatedMaxGasPrice, estimatedGasLimit)) .isTrue(); } diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java index 171c20da..16015d76 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -19,6 +19,8 @@ import net.consensys.linea.compress.LibCompress; import net.consensys.linea.config.LineaL1L2BridgeCliOptions; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaProfitabilityCliOptions; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcCliOptions; import net.consensys.linea.config.LineaRpcConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; @@ -38,10 +40,12 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { private static LineaTransactionValidatorCliOptions transactionValidatorCliOptions; private static LineaL1L2BridgeCliOptions l1L2BridgeCliOptions; private static LineaRpcCliOptions rpcCliOptions; + private static LineaProfitabilityCliOptions profitabilityCliOptions; protected static LineaTransactionSelectorConfiguration transactionSelectorConfiguration; protected static LineaTransactionValidatorConfiguration transactionValidatorConfiguration; protected static LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; protected static LineaRpcConfiguration rpcConfiguration; + protected static LineaProfitabilityConfiguration profitabilityConfiguration; static { // force the initialization of the gnark compress native library to fail fast in case of issues @@ -62,11 +66,13 @@ public synchronized void register(final BesuContext context) { transactionValidatorCliOptions = LineaTransactionValidatorCliOptions.create(); l1L2BridgeCliOptions = LineaL1L2BridgeCliOptions.create(); rpcCliOptions = LineaRpcCliOptions.create(); + profitabilityCliOptions = LineaProfitabilityCliOptions.create(); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionSelectorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionValidatorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, l1L2BridgeCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, rpcCliOptions); + cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, profitabilityCliOptions); cliOptionsRegistered = true; } } @@ -78,6 +84,7 @@ public void beforeExternalServices() { transactionValidatorConfiguration = transactionValidatorCliOptions.toDomainObject(); l1L2BridgeConfiguration = l1L2BridgeCliOptions.toDomainObject(); rpcConfiguration = rpcCliOptions.toDomainObject(); + profitabilityConfiguration = profitabilityCliOptions.toDomainObject(); configured = true; } @@ -97,6 +104,11 @@ public void beforeExternalServices() { l1L2BridgeCliOptions); log.debug("Configured plugin {} with RPC configuration: {}", getName(), rpcConfiguration); + + log.debug( + "Configured plugin {} with profitability calculator configuration: {}", + getName(), + profitabilityConfiguration); } @Override diff --git a/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java b/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java index dd7f50f9..4afcd450 100644 --- a/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java +++ b/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java @@ -18,7 +18,7 @@ import lombok.extern.slf4j.Slf4j; import net.consensys.linea.compress.LibCompress; -import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.datatypes.Wei; import org.slf4j.spi.LoggingEventBuilder; @@ -26,88 +26,90 @@ @Slf4j public class TransactionProfitabilityCalculator { - private final LineaTransactionSelectorConfiguration conf; + private final LineaProfitabilityConfiguration profitabilityConf; private final double preComputedValue; - public TransactionProfitabilityCalculator(final LineaTransactionSelectorConfiguration conf) { - this.conf = conf; + public TransactionProfitabilityCalculator( + final LineaProfitabilityConfiguration profitabilityConf) { + this.profitabilityConf = profitabilityConf; this.preComputedValue = - conf.estimateGasMinMargin() * conf.gasPriceRatio() * conf.verificationGasCost(); + profitabilityConf.gasPriceRatio() * profitabilityConf.verificationGasCost(); } public Wei profitablePriorityFeePerGas( - final Transaction transaction, final Wei minGasPrice, final long gas) { + final Transaction transaction, + final double minMargin, + final Wei minGasPrice, + final long gas) { final double compressedTxSize = getCompressedTxSize(transaction); final var profitAt = preComputedValue + * minMargin * compressedTxSize * minGasPrice.getAsBigInteger().doubleValue() - / (gas * conf.verificationCapacity()); + / (gas * profitabilityConf.verificationCapacity()); - final var profitAtWei = Wei.ofNumber(BigDecimal.valueOf(profitAt).toBigInteger()); + final var adjustedProfit = + Wei.ofNumber(BigDecimal.valueOf(profitAt).toBigInteger()) + .add(profitabilityConf.gasPriceAdjustment()); log.atDebug() .setMessage( "Estimated profitable priorityFeePerGas: {}; estimateGasMinMargin={}, verificationCapacity={}, " - + "verificationGasCost={}, gasPriceRatio={}, gas={}, minGasPrice={}, " + + "verificationGasCost={}, gasPriceRatio={}, gasPriceAdjustment={}, gas={}, minGasPrice={}, " + "l1GasPrice={}, txSize={}, compressedTxSize={}") - .addArgument(profitAtWei::toHumanReadableString) - .addArgument(conf.estimateGasMinMargin()) - .addArgument(conf.verificationCapacity()) - .addArgument(conf.verificationGasCost()) - .addArgument(conf.gasPriceRatio()) + .addArgument(adjustedProfit::toHumanReadableString) + .addArgument(profitabilityConf.estimateGasMinMargin()) + .addArgument(profitabilityConf.verificationCapacity()) + .addArgument(profitabilityConf.verificationGasCost()) + .addArgument(profitabilityConf.gasPriceRatio()) + .addArgument(profitabilityConf.gasPriceAdjustment()::toHumanReadableString) .addArgument(gas) .addArgument(minGasPrice::toHumanReadableString) - .addArgument(() -> minGasPrice.multiply(conf.gasPriceRatio()).toHumanReadableString()) + .addArgument( + () -> minGasPrice.multiply(profitabilityConf.gasPriceRatio()).toHumanReadableString()) .addArgument(transaction::getSize) .addArgument(compressedTxSize) .log(); - return profitAtWei; + return adjustedProfit; } public boolean isProfitable( - final String step, + final String context, final Transaction transaction, - final double minGasPrice, - final double effectiveGasPrice, + final double minMargin, + final Wei minGasPrice, + final Wei effectiveGasPrice, final long gas) { - final double revenue = effectiveGasPrice * gas; - final double l1GasPrice = minGasPrice * conf.gasPriceRatio(); - final double compressedTxSize = getCompressedTxSize(transaction); - final double verificationGasCostSlice = - (compressedTxSize / conf.verificationCapacity()) * conf.verificationGasCost(); - final double cost = l1GasPrice * verificationGasCostSlice; + final Wei profitablePriorityFee = + profitablePriorityFeePerGas(transaction, minMargin, minGasPrice, gas); - final double margin = revenue / cost; - - if (margin < conf.minMargin()) { + if (effectiveGasPrice.lessThan(profitablePriorityFee)) { log( log.atDebug(), - step, + context, transaction, - margin, + minMargin, effectiveGasPrice, + profitablePriorityFee, gas, - minGasPrice, - l1GasPrice, - compressedTxSize); + minGasPrice); return false; - } else { - log( - log.atTrace(), - step, - transaction, - margin, - effectiveGasPrice, - gas, - minGasPrice, - l1GasPrice, - compressedTxSize); - return true; } + + log( + log.atTrace(), + context, + transaction, + minMargin, + effectiveGasPrice, + profitablePriorityFee, + gas, + minGasPrice); + return true; } private double getCompressedTxSize(final Transaction transaction) { @@ -119,29 +121,30 @@ private void log( final LoggingEventBuilder leb, final String context, final Transaction transaction, - final double margin, - final double effectiveGasPrice, + final double minMargin, + final Wei effectiveGasPrice, + final Wei profitableGasPrice, final long gasUsed, - final double minGasPrice, - final double l1GasPrice, - final double compressedTxSize) { + final Wei minGasPrice) { leb.setMessage( - "Context {}. Transaction {} has a margin of {}, minMargin={}, verificationCapacity={}, " - + "verificationGasCost={}, gasPriceRatio={}, effectiveGasPrice={}, gasUsed={}, minGasPrice={}, " - + "l1GasPrice={}, txSize={}, compressedTxSize={}") + "Context {}. Transaction {} has a margin of {}, minMargin={}, effectiveGasPrice={}," + + " profitableGasPrice={}, verificationCapacity={}, verificationGasCost={}, gasPriceRatio={},, gasPriceAdjustment={}" + + " gasUsed={}, minGasPrice={}") .addArgument(context) .addArgument(transaction::getHash) - .addArgument(margin) - .addArgument(conf.minMargin()) - .addArgument(conf.verificationCapacity()) - .addArgument(conf.verificationGasCost()) - .addArgument(conf.gasPriceRatio()) - .addArgument(effectiveGasPrice) + .addArgument( + () -> + effectiveGasPrice.toBigInteger().doubleValue() + / profitableGasPrice.toBigInteger().doubleValue()) + .addArgument(minMargin) + .addArgument(effectiveGasPrice::toHumanReadableString) + .addArgument(profitableGasPrice::toHumanReadableString) + .addArgument(profitabilityConf.verificationCapacity()) + .addArgument(profitabilityConf.verificationGasCost()) + .addArgument(profitabilityConf.gasPriceRatio()) + .addArgument(profitabilityConf.gasPriceAdjustment()::toHumanReadableString) .addArgument(gasUsed) - .addArgument(minGasPrice) - .addArgument(l1GasPrice) - .addArgument(transaction::getSize) - .addArgument(compressedTxSize) + .addArgument(minGasPrice::toHumanReadableString) .log(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java new file mode 100644 index 00000000..b5fdcf53 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java @@ -0,0 +1,193 @@ +/* + * 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.config; + +import java.math.BigDecimal; + +import com.google.common.base.MoreObjects; +import jakarta.validation.constraints.Positive; +import net.consensys.linea.config.converters.WeiConverter; +import org.hyperledger.besu.datatypes.Wei; +import picocli.CommandLine; + +/** The Linea profitability calculator CLI options. */ +public class LineaProfitabilityCliOptions { + public static final String VERIFICATION_GAS_COST = "--plugin-linea-verification-gas-cost"; + public static final int DEFAULT_VERIFICATION_GAS_COST = 1_200_000; + + public static final String VERIFICATION_CAPACITY = "--plugin-linea-verification-capacity"; + public static final int DEFAULT_VERIFICATION_CAPACITY = 90_000; + + public static final String GAS_PRICE_RATIO = "--plugin-linea-gas-price-ratio"; + public static final int DEFAULT_GAS_PRICE_RATIO = 15; + + public static final String GAS_PRICE_ADJUSTMENT = "--plugin-linea-gas-price-adjustment"; + public static final Wei DEFAULT_GAS_PRICE_ADJUSTMENT = Wei.ZERO; + + public static final String MIN_MARGIN = "--plugin-linea-min-margin"; + public static final BigDecimal DEFAULT_MIN_MARGIN = BigDecimal.ONE; + + public static final String ESTIMATE_GAS_MIN_MARGIN = "--plugin-linea-estimate-gas-min-margin"; + public static final BigDecimal DEFAULT_ESTIMATE_GAS_MIN_MARGIN = BigDecimal.ONE; + + public static final String TX_POOL_MIN_MARGIN = "--plugin-linea-tx-pool-min-margin"; + public static final BigDecimal DEFAULT_TX_POOL_MIN_MARGIN = BigDecimal.valueOf(0.5); + + public static final String TX_POOL_ENABLE_CHECK_API = + "--plugin-linea-tx-pool-profitability-check-api-enabled"; + public static final boolean DEFAULT_TX_POOL_ENABLE_CHECK_API = true; + + public static final String TX_POOL_ENABLE_CHECK_P2P = + "--plugin-linea-tx-pool-profitability-check-p2p-enabled"; + public static final boolean DEFAULT_TX_POOL_ENABLE_CHECK_P2P = false; + + @Positive + @CommandLine.Option( + names = {VERIFICATION_GAS_COST}, + hidden = true, + paramLabel = "", + description = "L1 verification gas cost (default: ${DEFAULT-VALUE})") + private int verificationGasCost = DEFAULT_VERIFICATION_GAS_COST; + + @Positive + @CommandLine.Option( + names = {VERIFICATION_CAPACITY}, + hidden = true, + paramLabel = "", + description = "L1 verification capacity (default: ${DEFAULT-VALUE})") + private int verificationCapacity = DEFAULT_VERIFICATION_CAPACITY; + + @Positive + @CommandLine.Option( + names = {GAS_PRICE_RATIO}, + hidden = true, + paramLabel = "", + description = "L1/L2 gas price ratio (default: ${DEFAULT-VALUE})") + private int gasPriceRatio = DEFAULT_GAS_PRICE_RATIO; + + @CommandLine.Option( + names = {GAS_PRICE_ADJUSTMENT}, + hidden = true, + converter = WeiConverter.class, + paramLabel = "", + description = + "Amount to add to the calculated profitable gas price (default: ${DEFAULT-VALUE})") + private Wei gasPriceAdjustment = DEFAULT_GAS_PRICE_ADJUSTMENT; + + @Positive + @CommandLine.Option( + names = {MIN_MARGIN}, + hidden = true, + paramLabel = "", + description = "Minimum margin of a transaction to be selected (default: ${DEFAULT-VALUE})") + private BigDecimal minMargin = DEFAULT_MIN_MARGIN; + + @Positive + @CommandLine.Option( + names = {ESTIMATE_GAS_MIN_MARGIN}, + hidden = true, + paramLabel = "", + description = + "Recommend a specific gas price when using linea_estimateGas (default: ${DEFAULT-VALUE})") + private BigDecimal estimageGasMinMargin = DEFAULT_ESTIMATE_GAS_MIN_MARGIN; + + @Positive + @CommandLine.Option( + names = {TX_POOL_MIN_MARGIN}, + hidden = true, + paramLabel = "", + description = + "The min margin an incoming tx must have to be accepted in the txpool (default: ${DEFAULT-VALUE})") + private BigDecimal txPoolMinMargin = DEFAULT_TX_POOL_MIN_MARGIN; + + @CommandLine.Option( + names = {TX_POOL_ENABLE_CHECK_API}, + arity = "0..1", + hidden = true, + paramLabel = "", + description = + "Enable the profitability check for txs received via API? (default: ${DEFAULT-VALUE})") + private boolean txPoolCheckApiEnabled = DEFAULT_TX_POOL_ENABLE_CHECK_API; + + @CommandLine.Option( + names = {TX_POOL_ENABLE_CHECK_P2P}, + arity = "0..1", + hidden = true, + paramLabel = "", + description = + "Enable the profitability check for txs received via p2p? (default: ${DEFAULT-VALUE})") + private boolean txPoolCheckP2pEnabled = DEFAULT_TX_POOL_ENABLE_CHECK_P2P; + + private LineaProfitabilityCliOptions() {} + + /** + * Create Linea cli options. + * + * @return the Linea cli options + */ + public static LineaProfitabilityCliOptions create() { + return new LineaProfitabilityCliOptions(); + } + + /** + * Linea cli options from config. + * + * @param config the config + * @return the Linea cli options + */ + public static LineaProfitabilityCliOptions fromConfig( + final LineaProfitabilityConfiguration config) { + final LineaProfitabilityCliOptions options = create(); + options.verificationGasCost = config.verificationGasCost(); + options.verificationCapacity = config.verificationCapacity(); + options.gasPriceRatio = config.gasPriceRatio(); + options.gasPriceAdjustment = config.gasPriceAdjustment(); + options.minMargin = BigDecimal.valueOf(config.minMargin()); + options.estimageGasMinMargin = BigDecimal.valueOf(config.estimateGasMinMargin()); + options.txPoolMinMargin = BigDecimal.valueOf(config.txPoolMinMargin()); + return options; + } + + /** + * To domain object Linea factory configuration. + * + * @return the Linea factory configuration + */ + public LineaProfitabilityConfiguration toDomainObject() { + return LineaProfitabilityConfiguration.builder() + .verificationGasCost(verificationGasCost) + .verificationCapacity(verificationCapacity) + .gasPriceRatio(gasPriceRatio) + .gasPriceAdjustment(gasPriceAdjustment) + .minMargin(minMargin.doubleValue()) + .estimateGasMinMargin(estimageGasMinMargin.doubleValue()) + .txPoolMinMargin(txPoolMinMargin.doubleValue()) + .build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add(VERIFICATION_GAS_COST, verificationGasCost) + .add(VERIFICATION_CAPACITY, verificationCapacity) + .add(GAS_PRICE_RATIO, gasPriceRatio) + .add(GAS_PRICE_ADJUSTMENT, gasPriceAdjustment) + .add(MIN_MARGIN, minMargin) + .add(ESTIMATE_GAS_MIN_MARGIN, estimageGasMinMargin) + .add(TX_POOL_MIN_MARGIN, txPoolMinMargin) + .toString(); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java new file mode 100644 index 00000000..50db8631 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java @@ -0,0 +1,30 @@ +/* + * 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.config; + +import lombok.Builder; +import org.hyperledger.besu.datatypes.Wei; + +/** The Linea profitability calculator configuration. */ +@Builder(toBuilder = true) +public record LineaProfitabilityConfiguration( + int verificationGasCost, + int verificationCapacity, + int gasPriceRatio, + Wei gasPriceAdjustment, + double minMargin, + double estimateGasMinMargin, + double txPoolMinMargin) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java index b9aca5c0..9f8f0ba3 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java @@ -15,41 +15,30 @@ package net.consensys.linea.config; -import java.math.BigDecimal; - import com.google.common.base.MoreObjects; import jakarta.validation.constraints.Positive; import picocli.CommandLine; -/** The Linea CLI options. */ +/** The Linea Transaction Selector CLI options. */ public class LineaTransactionSelectorCliOptions { + public static final String MAX_BLOCK_CALLDATA_SIZE = "--plugin-linea-max-block-calldata-size"; public static final int DEFAULT_MAX_BLOCK_CALLDATA_SIZE = 70_000; - private static final String DEFAULT_MODULE_LIMIT_FILE_PATH = "moduleLimitFile.toml"; - private static final int DEFAULT_OVER_LINE_COUNT_LIMIT_CACHE_SIZE = 10_000; + + public static final String MODULE_LIMIT_FILE_PATH = "--plugin-linea-module-limit-file-path"; + public static final String DEFAULT_MODULE_LIMIT_FILE_PATH = "moduleLimitFile.toml"; + + public static final String OVER_LINE_COUNT_LIMIT_CACHE_SIZE = + "--plugin-linea-over-line-count-limit-cache-size"; + public static final int DEFAULT_OVER_LINE_COUNT_LIMIT_CACHE_SIZE = 10_000; + + public static final String MAX_GAS_PER_BLOCK = "--plugin-linea-max-block-gas"; public static final long DEFAULT_MAX_GAS_PER_BLOCK = 30_000_000L; - public static final int DEFAULT_VERIFICATION_GAS_COST = 1_200_000; - public static final int DEFAULT_VERIFICATION_CAPACITY = 90_000; - public static final int DEFAULT_GAS_PRICE_RATIO = 15; - public static final BigDecimal DEFAULT_MIN_MARGIN = BigDecimal.ONE; - public static final BigDecimal DEFAULT_ESTIMATE_GAS_MIN_MARGIN = BigDecimal.ONE; - public static final int DEFAULT_ADJUST_TX_SIZE = -45; - public static final int DEFAULT_TX_COMPRESSION_RATIO = 5; + + public static final String UNPROFITABLE_CACHE_SIZE = "--plugin-linea-unprofitable-cache-size"; public static final int DEFAULT_UNPROFITABLE_CACHE_SIZE = 100_000; + + public static final String UNPROFITABLE_RETRY_LIMIT = "--plugin-linea-unprofitable-retry-limit"; public static final int DEFAULT_UNPROFITABLE_RETRY_LIMIT = 10; - private static final String MAX_BLOCK_CALLDATA_SIZE = "--plugin-linea-max-block-calldata-size"; - private static final String MODULE_LIMIT_FILE_PATH = "--plugin-linea-module-limit-file-path"; - private static final String OVER_LINE_COUNT_LIMIT_CACHE_SIZE = - "--plugin-linea-over-line-count-limit-cache-size"; - private static final String MAX_GAS_PER_BLOCK = "--plugin-linea-max-block-gas"; - private static final String VERIFICATION_GAS_COST = "--plugin-linea-verification-gas-cost"; - private static final String VERIFICATION_CAPACITY = "--plugin-linea-verification-capacity"; - private static final String GAS_PRICE_RATIO = "--plugin-linea-gas-price-ratio"; - private static final String MIN_MARGIN = "--plugin-linea-min-margin"; - private static final String ESTIMATE_GAS_MIN_MARGIN = "--plugin-linea-estimate-gas-min-margin"; - private static final String ADJUST_TX_SIZE = "--plugin-linea-adjust-tx-size"; - private static final String TX_COMPRESSION_RATIO = "--plugin-linea-tx-compression-ratio"; - private static final String UNPROFITABLE_CACHE_SIZE = "--plugin-linea-unprofitable-cache-size"; - private static final String UNPROFITABLE_RETRY_LIMIT = "--plugin-linea-unprofitable-retry-limit"; @Positive @CommandLine.Option( @@ -84,47 +73,6 @@ public class LineaTransactionSelectorCliOptions { description = "Sets max gas per block (default: ${DEFAULT-VALUE})") private Long maxGasPerBlock = DEFAULT_MAX_GAS_PER_BLOCK; - @Positive - @CommandLine.Option( - names = {VERIFICATION_GAS_COST}, - hidden = true, - paramLabel = "", - description = "L1 verification gas cost (default: ${DEFAULT-VALUE})") - private int verificationGasCost = DEFAULT_VERIFICATION_GAS_COST; - - @Positive - @CommandLine.Option( - names = {VERIFICATION_CAPACITY}, - hidden = true, - paramLabel = "", - description = "L1 verification capacity (default: ${DEFAULT-VALUE})") - private int verificationCapacity = DEFAULT_VERIFICATION_CAPACITY; - - @Positive - @CommandLine.Option( - names = {GAS_PRICE_RATIO}, - hidden = true, - paramLabel = "", - description = "L1/L2 gas price ratio (default: ${DEFAULT-VALUE})") - private int gasPriceRatio = DEFAULT_GAS_PRICE_RATIO; - - @Positive - @CommandLine.Option( - names = {MIN_MARGIN}, - hidden = true, - paramLabel = "", - description = "Minimum margin of a transaction to be selected (default: ${DEFAULT-VALUE})") - private BigDecimal minMargin = DEFAULT_MIN_MARGIN; - - @Positive - @CommandLine.Option( - names = {ESTIMATE_GAS_MIN_MARGIN}, - hidden = true, - paramLabel = "", - description = - "Recommend a specific gas price when using linea_estimateGas (default: ${DEFAULT-VALUE})") - private BigDecimal estimageGasMinMargin = DEFAULT_ESTIMATE_GAS_MIN_MARGIN; - @Positive @CommandLine.Option( names = {UNPROFITABLE_CACHE_SIZE}, @@ -167,10 +115,6 @@ public static LineaTransactionSelectorCliOptions fromConfig( options.moduleLimitFilePath = config.moduleLimitsFilePath(); options.overLineCountLimitCacheSize = config.overLinesLimitCacheSize(); options.maxGasPerBlock = config.maxGasPerBlock(); - options.verificationGasCost = config.verificationGasCost(); - options.verificationCapacity = config.verificationCapacity(); - options.gasPriceRatio = config.gasPriceRatio(); - options.minMargin = BigDecimal.valueOf(config.minMargin()); options.unprofitableCacheSize = config.unprofitableCacheSize(); options.unprofitableRetryLimit = config.unprofitableRetryLimit(); return options; @@ -187,11 +131,6 @@ public LineaTransactionSelectorConfiguration toDomainObject() { .moduleLimitsFilePath(moduleLimitFilePath) .overLinesLimitCacheSize(overLineCountLimitCacheSize) .maxGasPerBlock(maxGasPerBlock) - .verificationGasCost(verificationGasCost) - .verificationCapacity(verificationCapacity) - .gasPriceRatio(gasPriceRatio) - .minMargin(minMargin.doubleValue()) - .estimateGasMinMargin((estimageGasMinMargin.doubleValue())) .unprofitableCacheSize(unprofitableCacheSize) .unprofitableRetryLimit(unprofitableRetryLimit) .build(); @@ -204,11 +143,6 @@ public String toString() { .add(MODULE_LIMIT_FILE_PATH, moduleLimitFilePath) .add(OVER_LINE_COUNT_LIMIT_CACHE_SIZE, overLineCountLimitCacheSize) .add(MAX_GAS_PER_BLOCK, maxGasPerBlock) - .add(VERIFICATION_GAS_COST, verificationGasCost) - .add(VERIFICATION_CAPACITY, verificationCapacity) - .add(GAS_PRICE_RATIO, gasPriceRatio) - .add(MIN_MARGIN, minMargin) - .add(ESTIMATE_GAS_MIN_MARGIN, estimageGasMinMargin) .add(UNPROFITABLE_CACHE_SIZE, unprofitableCacheSize) .add(UNPROFITABLE_RETRY_LIMIT, unprofitableRetryLimit) .toString(); diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java index 96252c59..09afc4f2 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java @@ -24,11 +24,5 @@ public record LineaTransactionSelectorConfiguration( String moduleLimitsFilePath, int overLinesLimitCacheSize, long maxGasPerBlock, - int verificationGasCost, - int verificationCapacity, - int gasPriceRatio, - double minMargin, - double estimateGasMinMargin, int unprofitableCacheSize, int unprofitableRetryLimit) {} -; diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java index eeb0cd4a..a97b2414 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java @@ -80,6 +80,7 @@ public static LineaTransactionValidatorCliOptions fromConfig( final LineaTransactionValidatorCliOptions options = create(); options.denyListPath = config.denyListPath(); options.maxTxGasLimit = config.maxTxGasLimit(); + options.maxTxCallDataSize = config.maxTxCalldataSize(); return options; } @@ -99,6 +100,7 @@ public String toString() { return MoreObjects.toStringHelper(this) .add(DENY_LIST_PATH, denyListPath) .add(MAX_TX_GAS_LIMIT_OPTION, maxTxGasLimit) + .add(MAX_TX_CALLDATA_SIZE, maxTxCallDataSize) .toString(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java index 488651f4..61f1ac3c 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java @@ -18,7 +18,7 @@ import lombok.Builder; /** - * The Linea configuration. + * The Linea transaction pool validation configuration. * * @param denyListPath the path to the file containing the addresses that are denied. * @param maxTxGasLimit the maximum gas limit allowed for transactions diff --git a/arithmetization/src/main/java/net/consensys/linea/config/converters/WeiConverter.java b/arithmetization/src/main/java/net/consensys/linea/config/converters/WeiConverter.java new file mode 100644 index 00000000..33ea6618 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/config/converters/WeiConverter.java @@ -0,0 +1,28 @@ +/* + * 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.config.converters; + +import java.math.BigInteger; + +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.Wei; +import picocli.CommandLine; + +public class WeiConverter implements CommandLine.ITypeConverter { + @Override + public Bytes convert(final String s) throws Exception { + return Wei.of(new BigInteger(s)); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java index a9c72da1..23e7c665 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java @@ -87,6 +87,6 @@ public void doRegister(final BesuContext context) { public void beforeExternalServices() { super.beforeExternalServices(); lineaEstimateGasMethod.init( - rpcConfiguration, transactionValidatorConfiguration, transactionSelectorConfiguration); + rpcConfiguration, transactionValidatorConfiguration, profitabilityConfiguration); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java index 20f3797e..50662b62 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -23,8 +23,8 @@ import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.bl.TransactionProfitabilityCalculator; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcConfiguration; -import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.apache.tuweni.bytes.Bytes; import org.bouncycastle.asn1.sec.SECNamedCurves; @@ -68,7 +68,7 @@ public class LineaEstimateGas { private final BlockchainService blockchainService; private LineaRpcConfiguration rpcConfiguration; private LineaTransactionValidatorConfiguration txValidatorConf; - private LineaTransactionSelectorConfiguration txSelectorConf; + private LineaProfitabilityConfiguration profitabilityConf; private TransactionProfitabilityCalculator txProfitabilityCalculator; public LineaEstimateGas( @@ -83,11 +83,11 @@ public LineaEstimateGas( public void init( LineaRpcConfiguration rpcConfiguration, final LineaTransactionValidatorConfiguration transactionValidatorConfiguration, - final LineaTransactionSelectorConfiguration transactionSelectorConfiguration) { + final LineaProfitabilityConfiguration profitabilityConf) { this.rpcConfiguration = rpcConfiguration; this.txValidatorConf = transactionValidatorConfiguration; - this.txSelectorConf = transactionSelectorConfiguration; - this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(txSelectorConf); + this.profitabilityConf = profitabilityConf; + this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); } public String getNamespace() { @@ -114,7 +114,7 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) { final Wei profitablePriorityFee = txProfitabilityCalculator.profitablePriorityFeePerGas( - transaction, minGasPrice, estimatedGasUsed); + transaction, profitabilityConf.estimateGasMinMargin(), minGasPrice, estimatedGasUsed); final Wei baseFee = blockchainService diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java index 15585081..465d3e5a 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java @@ -18,6 +18,7 @@ import java.util.Map; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.sequencer.txselection.selectors.LineaTransactionSelector; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; @@ -27,20 +28,23 @@ public class LineaTransactionSelectorFactory implements PluginTransactionSelectorFactory { private final LineaTransactionSelectorConfiguration txSelectorConfiguration; private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; + private final LineaProfitabilityConfiguration profitabilityConfiguration; private final Map limitsMap; public LineaTransactionSelectorFactory( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, + final LineaProfitabilityConfiguration profitabilityConfiguration, final Map limitsMap) { this.txSelectorConfiguration = txSelectorConfiguration; this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; + this.profitabilityConfiguration = profitabilityConfiguration; this.limitsMap = limitsMap; } @Override public PluginTransactionSelector create() { return new LineaTransactionSelector( - txSelectorConfiguration, l1L2BridgeConfiguration, limitsMap); + txSelectorConfiguration, l1L2BridgeConfiguration, profitabilityConfiguration, limitsMap); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java index 78cc3707..4a450419 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java @@ -27,6 +27,7 @@ import lombok.extern.slf4j.Slf4j; import net.consensys.linea.AbstractLineaRequiredPlugin; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.apache.tuweni.toml.Toml; import org.apache.tuweni.toml.TomlParseResult; @@ -80,6 +81,7 @@ public void beforeExternalServices() { transactionSelectionService, transactionSelectorConfiguration, l1L2BridgeConfiguration, + profitabilityConfiguration, limitsMap); } catch (final Exception e) { final String errorMsg = @@ -94,9 +96,13 @@ private void createAndRegister( final TransactionSelectionService transactionSelectionService, final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, + final LineaProfitabilityConfiguration profitabilityConfiguration, final Map limitsMap) { transactionSelectionService.registerPluginTransactionSelectorFactory( new LineaTransactionSelectorFactory( - txSelectorConfiguration, l1L2BridgeConfiguration, limitsMap)); + txSelectorConfiguration, + l1L2BridgeConfiguration, + profitabilityConfiguration, + limitsMap)); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java index 9845eb00..32a7c012 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.hyperledger.besu.datatypes.PendingTransaction; import org.hyperledger.besu.plugin.data.TransactionProcessingResult; @@ -37,21 +38,28 @@ public class LineaTransactionSelector implements PluginTransactionSelector { public LineaTransactionSelector( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, + final LineaProfitabilityConfiguration profitabilityConfiguration, final Map limitsMap) { this.selectors = - createTransactionSelectors(txSelectorConfiguration, l1L2BridgeConfiguration, limitsMap); + createTransactionSelectors( + txSelectorConfiguration, + l1L2BridgeConfiguration, + profitabilityConfiguration, + limitsMap); } /** * Creates a list of selectors based on Linea configuration. * * @param txSelectorConfiguration The configuration to use. + * @param profitabilityConfiguration * @param limitsMap The limits map. * @return A list of selectors. */ private List createTransactionSelectors( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, + final LineaProfitabilityConfiguration profitabilityConfiguration, final Map limitsMap) { traceLineLimitTransactionSelector = @@ -61,7 +69,7 @@ private List createTransactionSelectors( return List.of( new MaxBlockCallDataTransactionSelector(txSelectorConfiguration.maxBlockCallDataSize()), new MaxBlockGasTransactionSelector(txSelectorConfiguration.maxGasPerBlock()), - new ProfitableTransactionSelector(txSelectorConfiguration), + new ProfitableTransactionSelector(txSelectorConfiguration, profitabilityConfiguration), traceLineLimitTransactionSelector); } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java index 415fdebf..65e89d74 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java @@ -26,6 +26,7 @@ import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.bl.TransactionProfitabilityCalculator; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.apache.commons.lang3.mutable.MutableBoolean; import org.hyperledger.besu.datatypes.Hash; @@ -42,15 +43,20 @@ public class ProfitableTransactionSelector implements PluginTransactionSelector @VisibleForTesting protected static Set unprofitableCache = new LinkedHashSet<>(); @VisibleForTesting protected static Wei prevMinGasPrice = Wei.MAX_WEI; - private final LineaTransactionSelectorConfiguration conf; + private final LineaTransactionSelectorConfiguration txSelectorConf; + private final LineaProfitabilityConfiguration profitabilityConf; private final TransactionProfitabilityCalculator transactionProfitabilityCalculator; private int unprofitableRetries; private MutableBoolean minGasPriceDecreased; - public ProfitableTransactionSelector(final LineaTransactionSelectorConfiguration conf) { - this.conf = conf; - this.transactionProfitabilityCalculator = new TransactionProfitabilityCalculator(conf); + public ProfitableTransactionSelector( + final LineaTransactionSelectorConfiguration txSelectorConf, + final LineaProfitabilityConfiguration profitabilityConf) { + this.txSelectorConf = txSelectorConf; + this.profitabilityConf = profitabilityConf; + this.transactionProfitabilityCalculator = + new TransactionProfitabilityCalculator(profitabilityConf); } @Override @@ -67,16 +73,15 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( if (!evaluationContext.getPendingTransaction().hasPriority()) { final Transaction transaction = evaluationContext.getPendingTransaction().getTransaction(); - final double effectiveGasPrice = - evaluationContext.getTransactionGasPrice().getAsBigInteger().doubleValue(); final long gasLimit = transaction.getGasLimit(); // check the upfront profitability using the gas limit of the tx if (!transactionProfitabilityCalculator.isProfitable( "PreProcessing", transaction, - minGasPrice.getAsBigInteger().doubleValue(), - effectiveGasPrice, + profitabilityConf.minMargin(), + minGasPrice, + evaluationContext.getTransactionGasPrice(), gasLimit)) { return TX_UNPROFITABLE_UPFRONT; } @@ -85,18 +90,18 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( // only retry unprofitable txs if the min gas price went down if (minGasPriceDecreased.isTrue()) { - if (unprofitableRetries >= conf.unprofitableRetryLimit()) { + if (unprofitableRetries >= txSelectorConf.unprofitableRetryLimit()) { log.atTrace() .setMessage("Limit of unprofitable tx retries reached: {}/{}") .addArgument(unprofitableRetries) - .addArgument(conf.unprofitableRetryLimit()); + .addArgument(txSelectorConf.unprofitableRetryLimit()); return TX_UNPROFITABLE_RETRY_LIMIT; } log.atTrace() .setMessage("Retrying unprofitable tx. Retry: {}/{}") .addArgument(unprofitableRetries) - .addArgument(conf.unprofitableRetryLimit()); + .addArgument(txSelectorConf.unprofitableRetryLimit()); unprofitableCache.remove(transaction.getHash()); unprofitableRetries++; @@ -122,13 +127,15 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( if (!evaluationContext.getPendingTransaction().hasPriority()) { final Transaction transaction = evaluationContext.getPendingTransaction().getTransaction(); - final double minGasPrice = evaluationContext.getMinGasPrice().getAsBigInteger().doubleValue(); - final double effectiveGasPrice = - evaluationContext.getTransactionGasPrice().getAsBigInteger().doubleValue(); final long gasUsed = processingResult.getEstimateGasUsedByTransaction(); if (!transactionProfitabilityCalculator.isProfitable( - "PostProcessing", transaction, minGasPrice, effectiveGasPrice, gasUsed)) { + "PostProcessing", + transaction, + profitabilityConf.minMargin(), + evaluationContext.getMinGasPrice(), + evaluationContext.getTransactionGasPrice(), + gasUsed)) { rememberUnprofitable(transaction); return TX_UNPROFITABLE; } @@ -154,7 +161,7 @@ public void onTransactionNotSelected( } private void rememberUnprofitable(final Transaction transaction) { - while (unprofitableCache.size() >= conf.unprofitableCacheSize()) { + while (unprofitableCache.size() >= txSelectorConf.unprofitableCacheSize()) { final var it = unprofitableCache.iterator(); if (it.hasNext()) { it.next(); diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java index bbbd8424..894b005b 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java @@ -25,18 +25,17 @@ /** Represents a factory for creating transaction validators. */ public class LineaTransactionValidatorFactory implements PluginTransactionValidatorFactory { - private final LineaTransactionValidatorConfiguration transactionValidatorConfiguration; + private final LineaTransactionValidatorConfiguration txValidatorConf; private final Set
denied; public LineaTransactionValidatorFactory( - final LineaTransactionValidatorConfiguration transactionValidatorConfiguration, - final Set
denied) { - this.transactionValidatorConfiguration = transactionValidatorConfiguration; + final LineaTransactionValidatorConfiguration txValidatorConf, final Set
denied) { + this.txValidatorConf = txValidatorConf; this.denied = denied; } @Override public PluginTransactionValidator create() { - return new LineaTransactionValidator(transactionValidatorConfiguration, denied); + return new LineaTransactionValidator(txValidatorConf, denied); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java index 54df1ea4..1294ea60 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java @@ -26,7 +26,6 @@ import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.AbstractLineaRequiredPlugin; -import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; @@ -51,7 +50,6 @@ public Optional getName() { @Override public void doRegister(final BesuContext context) { - transactionValidatorService = context .getService(PluginTransactionValidatorService.class) @@ -68,17 +66,12 @@ public void beforeExternalServices() { Files.lines(Path.of(new File(transactionValidatorConfiguration.denyListPath()).toURI()))) { final Set
denied = lines.map(l -> Address.fromHexString(l.trim())).collect(Collectors.toUnmodifiableSet()); - createAndRegister(transactionValidatorService, transactionValidatorConfiguration, denied); + + transactionValidatorService.registerTransactionValidatorFactory( + new LineaTransactionValidatorFactory(transactionValidatorConfiguration, denied)); + } catch (Exception e) { throw new RuntimeException(e); } } - - private void createAndRegister( - final PluginTransactionValidatorService transactionValidationService, - final LineaTransactionValidatorConfiguration transactionValidatorConfiguration, - final Set
denied) { - transactionValidationService.registerTransactionValidatorFactory( - new LineaTransactionValidatorFactory(transactionValidatorConfiguration, denied)); - } } diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java index dc8447b1..a4ce76ae 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java @@ -23,6 +23,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import net.consensys.linea.config.LineaProfitabilityCliOptions; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.apache.tuweni.bytes.Bytes; @@ -43,15 +45,17 @@ public class ProfitableTransactionSelectorTest { private static final int VERIFICATION_CAPACITY = 90_000; private static final int GAS_PRICE_RATIO = 15; private static final double MIN_MARGIN = 1.0; - private static final int ADJUST_TX_SIZE = -45; private static final int UNPROFITABLE_CACHE_SIZE = 2; private static final int UNPROFITABLE_RETRY_LIMIT = 1; - private final LineaTransactionSelectorConfiguration conf = + private final LineaTransactionSelectorConfiguration txSelectorConf = LineaTransactionSelectorCliOptions.create().toDomainObject().toBuilder() - .gasPriceRatio(GAS_PRICE_RATIO) - .minMargin(MIN_MARGIN) .unprofitableCacheSize(UNPROFITABLE_CACHE_SIZE) .unprofitableRetryLimit(UNPROFITABLE_RETRY_LIMIT) + .build(); + private final LineaProfitabilityConfiguration profitabilityConf = + LineaProfitabilityCliOptions.create().toDomainObject().toBuilder() + .gasPriceRatio(GAS_PRICE_RATIO) + .minMargin(MIN_MARGIN) .verificationCapacity(VERIFICATION_CAPACITY) .verificationGasCost(VERIFICATION_GAS_COST) .build(); @@ -64,7 +68,7 @@ public void initialize() { } private TestableProfitableTransactionSelector newSelectorForNewBlock() { - return new TestableProfitableTransactionSelector(conf); + return new TestableProfitableTransactionSelector(txSelectorConf, profitabilityConf); } @Test @@ -447,8 +451,10 @@ private void notifySelector( private static class TestableProfitableTransactionSelector extends ProfitableTransactionSelector { - TestableProfitableTransactionSelector(final LineaTransactionSelectorConfiguration conf) { - super(conf); + TestableProfitableTransactionSelector( + final LineaTransactionSelectorConfiguration txSelectorConf, + final LineaProfitabilityConfiguration profitabilityConf) { + super(txSelectorConf, profitabilityConf); } boolean isUnprofitableTxCached(final Hash txHash) { From c611a60ec396e99cec8a4359411f74216a27e892 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 6 Mar 2024 03:13:11 +0100 Subject: [PATCH 09/30] Update code to latest plugin API (#640) Signed-off-by: Fabio Di Fabio --- .../txvalidation/LineaTransactionValidator.java | 7 ++++--- .../LineaTransactionValidatorFactory.java | 8 ++++---- .../LineaTransactionValidatorPlugin.java | 8 ++++---- .../LineaTransactionValidatorTest.java | 16 ++++++++-------- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java index fc7fee86..2fe1a42c 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java @@ -23,7 +23,7 @@ import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Transaction; -import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidator; +import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; /** * Represents an implementation of a plugin transaction validator, which validates a transaction @@ -31,7 +31,7 @@ */ @Slf4j @RequiredArgsConstructor -public class LineaTransactionValidator implements PluginTransactionValidator { +public class LineaTransactionValidator implements PluginTransactionPoolValidator { private final LineaTransactionValidatorConfiguration config; private final Set
denied; @@ -49,7 +49,8 @@ public class LineaTransactionValidator implements PluginTransactionValidator { Address.fromHexString("0x000000000000000000000000000000000000000a")); @Override - public Optional validateTransaction(final Transaction transaction) { + public Optional validateTransaction( + final Transaction transaction, final boolean isLocal, final boolean hasPriority) { Optional senderError = validateSender(transaction); if (senderError.isPresent()) return senderError; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java index 894b005b..d34742b6 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java @@ -19,11 +19,11 @@ import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidator; -import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionValidatorFactory; +import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; +import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidatorFactory; /** Represents a factory for creating transaction validators. */ -public class LineaTransactionValidatorFactory implements PluginTransactionValidatorFactory { +public class LineaTransactionValidatorFactory implements PluginTransactionPoolValidatorFactory { private final LineaTransactionValidatorConfiguration txValidatorConf; private final Set
denied; @@ -35,7 +35,7 @@ public LineaTransactionValidatorFactory( } @Override - public PluginTransactionValidator create() { + public PluginTransactionPoolValidator createTransactionValidator() { return new LineaTransactionValidator(txValidatorConf, denied); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java index 1294ea60..4af7a79d 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java @@ -29,7 +29,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; -import org.hyperledger.besu.plugin.services.PluginTransactionValidatorService; +import org.hyperledger.besu.plugin.services.TransactionPoolValidatorService; /** * This class extends the default transaction validation rules for adding transactions to the @@ -41,7 +41,7 @@ @AutoService(BesuPlugin.class) public class LineaTransactionValidatorPlugin extends AbstractLineaRequiredPlugin { public static final String NAME = "linea"; - private PluginTransactionValidatorService transactionValidatorService; + private TransactionPoolValidatorService transactionValidatorService; @Override public Optional getName() { @@ -52,7 +52,7 @@ public Optional getName() { public void doRegister(final BesuContext context) { transactionValidatorService = context - .getService(PluginTransactionValidatorService.class) + .getService(TransactionPoolValidatorService.class) .orElseThrow( () -> new RuntimeException( @@ -67,7 +67,7 @@ public void beforeExternalServices() { final Set
denied = lines.map(l -> Address.fromHexString(l.trim())).collect(Collectors.toUnmodifiableSet()); - transactionValidatorService.registerTransactionValidatorFactory( + transactionValidatorService.registerPluginTransactionValidatorFactory( new LineaTransactionValidatorFactory(transactionValidatorConfiguration, denied)); } catch (Exception e) { diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java index 97bac300..53e6b1d4 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java @@ -59,7 +59,7 @@ public void validatedIfNoneOnList() { final org.hyperledger.besu.ethereum.core.Transaction transaction = builder.sender(NOT_DENIED).to(NOT_DENIED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction), Optional.empty()); + lineaTransactionValidator.validateTransaction(transaction, false, false), Optional.empty()); } @Test @@ -69,7 +69,7 @@ public void deniedIfFromAddressIsOnList() { final org.hyperledger.besu.ethereum.core.Transaction transaction = builder.sender(DENIED).to(NOT_DENIED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction).orElseThrow(), + lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), "sender 0x0000000000000000000000000000000000001000 is blocked as appearing on the SDN or other legally prohibited list"); } @@ -80,7 +80,7 @@ public void deniedIfToAddressIsOnList() { final org.hyperledger.besu.ethereum.core.Transaction transaction = builder.sender(NOT_DENIED).to(DENIED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction).orElseThrow(), + lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), "recipient 0x0000000000000000000000000000000000001000 is blocked as appearing on the SDN or other legally prohibited list"); } @@ -91,7 +91,7 @@ public void deniedIfToAddressIsPrecompiled() { final org.hyperledger.besu.ethereum.core.Transaction transaction = builder.sender(NOT_DENIED).to(PRECOMPILED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction).orElseThrow(), + lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), "destination address is a precompile address and cannot receive transactions"); } @@ -108,7 +108,7 @@ public void validatedWithValidGasLimit() { .payload(Bytes.EMPTY) .build(); Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction), Optional.empty()); + lineaTransactionValidator.validateTransaction(transaction, false, false), Optional.empty()); } @Test @@ -124,7 +124,7 @@ public void rejectedWithMaxGasLimitPlusOne() { .payload(Bytes.EMPTY) .build(); Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction).orElseThrow(), + lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), "Gas limit of transaction is greater than the allowed max of " + MAX_TX_GAS_LIMIT); } @@ -141,7 +141,7 @@ public void validatedWithValidCalldata() { .payload(Bytes.random(MAX_TX_CALLDATA_SIZE)) .build(); Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction), Optional.empty()); + lineaTransactionValidator.validateTransaction(transaction, false, false), Optional.empty()); } @Test @@ -157,7 +157,7 @@ public void rejectedWithTooBigCalldata() { .payload(Bytes.random(MAX_TX_CALLDATA_SIZE + 1)) .build(); Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction).orElseThrow(), + lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), "Calldata of transaction is greater than the allowed max of " + MAX_TX_CALLDATA_SIZE); } } From 3985f624650e532bafc2830c43346f86c051137a Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 6 Mar 2024 11:33:05 +0100 Subject: [PATCH 10/30] Txpool profitability check (#603) Signed-off-by: Fabio Di Fabio --- PLUGINS.md | 2 + .../config/LineaProfitabilityCliOptions.java | 6 + .../LineaProfitabilityConfiguration.java | 4 +- .../LineaTransactionValidatorFactory.java | 36 ++- .../LineaTransactionValidatorPlugin.java | 27 +- .../AllowedAddressValidator.java} | 58 +--- .../validators/CalldataValidator.java | 42 +++ .../validators/GasLimitValidator.java | 42 +++ .../validators/ProfitabilityValidator.java | 84 +++++ .../LineaTransactionValidatorTest.java | 163 ---------- .../AllowedAddressValidatorTest.java | 88 ++++++ .../validators/CalldataValidatorTest.java | 64 ++++ .../validators/GasLimitValidatorTest.java | 64 ++++ .../ProfitabilityValidatorTest.java | 291 ++++++++++++++++++ 14 files changed, 757 insertions(+), 214 deletions(-) rename arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/{LineaTransactionValidator.java => validators/AllowedAddressValidator.java} (62%) create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidator.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidator.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidator.java delete mode 100644 arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidatorTest.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidatorTest.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidatorTest.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidatorTest.java diff --git a/PLUGINS.md b/PLUGINS.md index ed94e9a0..c62f1f79 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -21,6 +21,8 @@ It is applied, with different configuration to: | TX_POOL_MIN_MARGIN | 0.5 | `--plugin-linea-tx-pool-min-margin` | | UNPROFITABLE_CACHE_SIZE | 100_000 | `--plugin-linea-unprofitable-cache-size` | | UNPROFITABLE_RETRY_LIMIT | 10 | `--plugin-linea-unprofitable-retry-limit` | +| TX_POOL_ENABLE_CHECK_API | true | `--plugin-linea-tx-pool-profitability-check-api-enabled` | +| TX_POOL_ENABLE_CHECK_P2P | false | `--plugin-linea-tx-pool-profitability-check-p2p-enabled` | ### L1 L2 Bridge diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java index b5fdcf53..14683bb5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java @@ -158,6 +158,8 @@ public static LineaProfitabilityCliOptions fromConfig( options.minMargin = BigDecimal.valueOf(config.minMargin()); options.estimageGasMinMargin = BigDecimal.valueOf(config.estimateGasMinMargin()); options.txPoolMinMargin = BigDecimal.valueOf(config.txPoolMinMargin()); + options.txPoolCheckApiEnabled = config.txPoolCheckApiEnabled(); + options.txPoolCheckP2pEnabled = config.txPoolCheckP2pEnabled(); return options; } @@ -175,6 +177,8 @@ public LineaProfitabilityConfiguration toDomainObject() { .minMargin(minMargin.doubleValue()) .estimateGasMinMargin(estimageGasMinMargin.doubleValue()) .txPoolMinMargin(txPoolMinMargin.doubleValue()) + .txPoolCheckApiEnabled(txPoolCheckApiEnabled) + .txPoolCheckP2pEnabled(txPoolCheckP2pEnabled) .build(); } @@ -188,6 +192,8 @@ public String toString() { .add(MIN_MARGIN, minMargin) .add(ESTIMATE_GAS_MIN_MARGIN, estimageGasMinMargin) .add(TX_POOL_MIN_MARGIN, txPoolMinMargin) + .add(TX_POOL_ENABLE_CHECK_API, txPoolCheckApiEnabled) + .add(TX_POOL_ENABLE_CHECK_P2P, txPoolCheckP2pEnabled) .toString(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java index 50db8631..514371eb 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java @@ -27,4 +27,6 @@ public record LineaProfitabilityConfiguration( Wei gasPriceAdjustment, double minMargin, double estimateGasMinMargin, - double txPoolMinMargin) {} + double txPoolMinMargin, + boolean txPoolCheckApiEnabled, + boolean txPoolCheckP2pEnabled) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java index d34742b6..d27bca7e 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java @@ -15,27 +15,59 @@ package net.consensys.linea.sequencer.txvalidation; +import java.util.Arrays; +import java.util.Optional; import java.util.Set; +import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionValidatorConfiguration; +import net.consensys.linea.sequencer.txvalidation.validators.AllowedAddressValidator; +import net.consensys.linea.sequencer.txvalidation.validators.CalldataValidator; +import net.consensys.linea.sequencer.txvalidation.validators.GasLimitValidator; +import net.consensys.linea.sequencer.txvalidation.validators.ProfitabilityValidator; import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.plugin.services.BesuConfiguration; +import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidatorFactory; /** Represents a factory for creating transaction validators. */ public class LineaTransactionValidatorFactory implements PluginTransactionPoolValidatorFactory { + private final BesuConfiguration besuConfiguration; + private final BlockchainService blockchainService; private final LineaTransactionValidatorConfiguration txValidatorConf; + private final LineaProfitabilityConfiguration profitabilityConf; private final Set
denied; public LineaTransactionValidatorFactory( - final LineaTransactionValidatorConfiguration txValidatorConf, final Set
denied) { + final BesuConfiguration besuConfiguration, + final BlockchainService blockchainService, + final LineaTransactionValidatorConfiguration txValidatorConf, + final LineaProfitabilityConfiguration profitabilityConf, + final Set
denied) { + this.besuConfiguration = besuConfiguration; + this.blockchainService = blockchainService; this.txValidatorConf = txValidatorConf; + this.profitabilityConf = profitabilityConf; this.denied = denied; } @Override public PluginTransactionPoolValidator createTransactionValidator() { - return new LineaTransactionValidator(txValidatorConf, denied); + final var validators = + new PluginTransactionPoolValidator[] { + new AllowedAddressValidator(denied), + new GasLimitValidator(txValidatorConf), + new CalldataValidator(txValidatorConf), + new ProfitabilityValidator(besuConfiguration, blockchainService, profitabilityConf) + }; + + return (transaction, isLocal, hasPriority) -> + Arrays.stream(validators) + .map(v -> v.validateTransaction(transaction, isLocal, hasPriority)) + .filter(Optional::isPresent) + .findFirst() + .map(Optional::get); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java index 4af7a79d..900bb2bc 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java @@ -29,6 +29,8 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; +import org.hyperledger.besu.plugin.services.BesuConfiguration; +import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.TransactionPoolValidatorService; /** @@ -41,6 +43,8 @@ @AutoService(BesuPlugin.class) public class LineaTransactionValidatorPlugin extends AbstractLineaRequiredPlugin { public static final String NAME = "linea"; + private BesuConfiguration besuConfiguration; + private BlockchainService blockchainService; private TransactionPoolValidatorService transactionValidatorService; @Override @@ -50,6 +54,22 @@ public Optional getName() { @Override public void doRegister(final BesuContext context) { + besuConfiguration = + context + .getService(BesuConfiguration.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain BesuConfiguration from the BesuContext.")); + + blockchainService = + context + .getService(BlockchainService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain BlockchainService from the BesuContext.")); + transactionValidatorService = context .getService(TransactionPoolValidatorService.class) @@ -68,7 +88,12 @@ public void beforeExternalServices() { lines.map(l -> Address.fromHexString(l.trim())).collect(Collectors.toUnmodifiableSet()); transactionValidatorService.registerPluginTransactionValidatorFactory( - new LineaTransactionValidatorFactory(transactionValidatorConfiguration, denied)); + new LineaTransactionValidatorFactory( + besuConfiguration, + blockchainService, + transactionValidatorConfiguration, + profitabilityConfiguration, + denied)); } catch (Exception e) { throw new RuntimeException(e); diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidator.java similarity index 62% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java rename to arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidator.java index 2fe1a42c..d15ad8e5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidator.java @@ -12,30 +12,21 @@ * * SPDX-License-Identifier: Apache-2.0 */ - -package net.consensys.linea.sequencer.txvalidation; +package net.consensys.linea.sequencer.txvalidation.validators; import java.util.Optional; import java.util.Set; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; -/** - * Represents an implementation of a plugin transaction validator, which validates a transaction - * before it can be added to the transaction pool. - */ @Slf4j @RequiredArgsConstructor -public class LineaTransactionValidator implements PluginTransactionPoolValidator { - private final LineaTransactionValidatorConfiguration config; - private final Set
denied; - - private static final Set
precompiles = +public class AllowedAddressValidator implements PluginTransactionPoolValidator { + private static final Set
PRECOMPILES = Set.of( Address.fromHexString("0x0000000000000000000000000000000000000001"), Address.fromHexString("0x0000000000000000000000000000000000000002"), @@ -48,22 +39,16 @@ public class LineaTransactionValidator implements PluginTransactionPoolValidator Address.fromHexString("0x0000000000000000000000000000000000000009"), Address.fromHexString("0x000000000000000000000000000000000000000a")); + private final Set
denied; + @Override public Optional validateTransaction( final Transaction transaction, final boolean isLocal, final boolean hasPriority) { - Optional senderError = validateSender(transaction); - if (senderError.isPresent()) return senderError; - - Optional recipientError = validateRecipient(transaction); - if (recipientError.isPresent()) return recipientError; - - Optional gasLimitError = validateGasLimit(transaction); - if (gasLimitError.isPresent()) return gasLimitError; - - Optional calldataError = validateCalldata(transaction); - if (calldataError.isPresent()) return calldataError; - - return Optional.empty(); // returning empty indicates that the transaction is valid + final var maybeValidSender = validateSender(transaction); + if (maybeValidSender.isEmpty()) { + return validateRecipient(transaction); + } + return maybeValidSender; } private Optional validateRecipient(final Transaction transaction) { @@ -76,7 +61,7 @@ private Optional validateRecipient(final Transaction transaction) { to); log.debug(errMsg); return Optional.of(errMsg); - } else if (precompiles.contains(to)) { + } else if (PRECOMPILES.contains(to)) { final String errMsg = "destination address is a precompile address and cannot receive transactions"; log.debug(errMsg); @@ -97,25 +82,4 @@ private Optional validateSender(final Transaction transaction) { } return Optional.empty(); } - - private Optional validateGasLimit(final Transaction transaction) { - if (transaction.getGasLimit() > config.maxTxGasLimit()) { - final String errMsg = - "Gas limit of transaction is greater than the allowed max of " + config.maxTxGasLimit(); - log.debug(errMsg); - return Optional.of(errMsg); - } - return Optional.empty(); - } - - private Optional validateCalldata(final Transaction transaction) { - if (transaction.getPayload().size() > config.maxTxCalldataSize()) { - final String errMsg = - "Calldata of transaction is greater than the allowed max of " - + config.maxTxCalldataSize(); - log.debug(errMsg); - return Optional.of(errMsg); - } - return Optional.empty(); - } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidator.java new file mode 100644 index 00000000..dc7c736b --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidator.java @@ -0,0 +1,42 @@ +/* + * 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.sequencer.txvalidation.validators; + +import java.util.Optional; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTransactionValidatorConfiguration; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; + +@Slf4j +@RequiredArgsConstructor +public class CalldataValidator implements PluginTransactionPoolValidator { + final LineaTransactionValidatorConfiguration txValidatorConf; + + @Override + public Optional validateTransaction( + final Transaction transaction, final boolean isLocal, final boolean hasPriority) { + if (transaction.getPayload().size() > txValidatorConf.maxTxCalldataSize()) { + final String errMsg = + "Calldata of transaction is greater than the allowed max of " + + txValidatorConf.maxTxCalldataSize(); + log.debug(errMsg); + return Optional.of(errMsg); + } + return Optional.empty(); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidator.java new file mode 100644 index 00000000..2c4cbea3 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidator.java @@ -0,0 +1,42 @@ +/* + * 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.sequencer.txvalidation.validators; + +import java.util.Optional; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTransactionValidatorConfiguration; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; + +@Slf4j +@RequiredArgsConstructor +public class GasLimitValidator implements PluginTransactionPoolValidator { + final LineaTransactionValidatorConfiguration txValidatorConf; + + @Override + public Optional validateTransaction( + final Transaction transaction, final boolean isLocal, final boolean hasPriority) { + if (transaction.getGasLimit() > txValidatorConf.maxTxGasLimit()) { + final String errMsg = + "Gas limit of transaction is greater than the allowed max of " + + txValidatorConf.maxTxGasLimit(); + log.debug(errMsg); + return Optional.of(errMsg); + } + return Optional.empty(); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidator.java new file mode 100644 index 00000000..78c6a7fa --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidator.java @@ -0,0 +1,84 @@ +/* + * 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.sequencer.txvalidation.validators; + +import java.util.Optional; + +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.bl.TransactionProfitabilityCalculator; +import net.consensys.linea.config.LineaProfitabilityConfiguration; +import org.apache.tuweni.units.bigints.UInt256s; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.plugin.services.BesuConfiguration; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; + +@Slf4j +public class ProfitabilityValidator implements PluginTransactionPoolValidator { + final BesuConfiguration besuConfiguration; + final BlockchainService blockchainService; + final LineaProfitabilityConfiguration profitabilityConf; + final TransactionProfitabilityCalculator profitabilityCalculator; + + public ProfitabilityValidator( + final BesuConfiguration besuConfiguration, + final BlockchainService blockchainService, + final LineaProfitabilityConfiguration profitabilityConf) { + this.besuConfiguration = besuConfiguration; + this.blockchainService = blockchainService; + this.profitabilityConf = profitabilityConf; + this.profitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); + } + + @Override + public Optional validateTransaction( + final Transaction transaction, final boolean isLocal, final boolean hasPriority) { + + if (!hasPriority + && (isLocal && profitabilityConf.txPoolCheckApiEnabled() + || !isLocal && profitabilityConf.txPoolCheckP2pEnabled())) { + + return profitabilityCalculator.isProfitable( + "Txpool", + transaction, + profitabilityConf.txPoolMinMargin(), + besuConfiguration.getMinGasPrice(), + calculateUpfrontGasPrice(transaction), + transaction.getGasLimit()) + ? Optional.empty() + : Optional.of("Gas price too low"); + } + + return Optional.empty(); + } + + private Wei calculateUpfrontGasPrice(final Transaction transaction) { + final Wei baseFee = + blockchainService + .getNextBlockBaseFee() + .orElseThrow(() -> new RuntimeException("We only support a base fee market")); + + return transaction + .getMaxFeePerGas() + .map(Wei::fromQuantity) + .map( + maxFee -> + UInt256s.min( + maxFee, + baseFee.add(Wei.fromQuantity(transaction.getMaxPriorityFeePerGas().get())))) + .orElseGet(() -> Wei.fromQuantity(transaction.getGasPrice().get())); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java deleted file mode 100644 index 53e6b1d4..00000000 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorTest.java +++ /dev/null @@ -1,163 +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.sequencer.txvalidation; - -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import net.consensys.linea.config.LineaTransactionValidatorConfiguration; -import org.apache.tuweni.bytes.Bytes; -import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Wei; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -@Slf4j -@RequiredArgsConstructor -public class LineaTransactionValidatorTest { - - public static final Address DENIED = - Address.fromHexString("0x0000000000000000000000000000000000001000"); - public static final Address NOT_DENIED = - Address.fromHexString("0x0000000000000000000000000000000000001001"); - public static final Address PRECOMPILED = Address.precompiled(0xa); - public static final int MAX_TX_GAS_LIMIT = 9_000_000; - public static final int MAX_TX_CALLDATA_SIZE = 10_000; - private LineaTransactionValidator lineaTransactionValidator; - - @BeforeEach - public void initialize() { - Set
denied = new HashSet<>(); - denied.add(DENIED); - lineaTransactionValidator = - new LineaTransactionValidator( - new LineaTransactionValidatorConfiguration("", MAX_TX_GAS_LIMIT, MAX_TX_CALLDATA_SIZE), - denied); - } - - @Test - public void validatedIfNoneOnList() { - final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = - org.hyperledger.besu.ethereum.core.Transaction.builder(); - final org.hyperledger.besu.ethereum.core.Transaction transaction = - builder.sender(NOT_DENIED).to(NOT_DENIED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); - Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction, false, false), Optional.empty()); - } - - @Test - public void deniedIfFromAddressIsOnList() { - final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = - org.hyperledger.besu.ethereum.core.Transaction.builder(); - final org.hyperledger.besu.ethereum.core.Transaction transaction = - builder.sender(DENIED).to(NOT_DENIED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); - Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), - "sender 0x0000000000000000000000000000000000001000 is blocked as appearing on the SDN or other legally prohibited list"); - } - - @Test - public void deniedIfToAddressIsOnList() { - final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = - org.hyperledger.besu.ethereum.core.Transaction.builder(); - final org.hyperledger.besu.ethereum.core.Transaction transaction = - builder.sender(NOT_DENIED).to(DENIED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); - Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), - "recipient 0x0000000000000000000000000000000000001000 is blocked as appearing on the SDN or other legally prohibited list"); - } - - @Test - public void deniedIfToAddressIsPrecompiled() { - final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = - org.hyperledger.besu.ethereum.core.Transaction.builder(); - final org.hyperledger.besu.ethereum.core.Transaction transaction = - builder.sender(NOT_DENIED).to(PRECOMPILED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); - Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), - "destination address is a precompile address and cannot receive transactions"); - } - - @Test - public void validatedWithValidGasLimit() { - final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = - org.hyperledger.besu.ethereum.core.Transaction.builder(); - final org.hyperledger.besu.ethereum.core.Transaction transaction = - builder - .sender(NOT_DENIED) - .to(NOT_DENIED) - .gasLimit(MAX_TX_GAS_LIMIT) - .gasPrice(Wei.ZERO) - .payload(Bytes.EMPTY) - .build(); - Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction, false, false), Optional.empty()); - } - - @Test - public void rejectedWithMaxGasLimitPlusOne() { - final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = - org.hyperledger.besu.ethereum.core.Transaction.builder(); - final org.hyperledger.besu.ethereum.core.Transaction transaction = - builder - .sender(NOT_DENIED) - .to(NOT_DENIED) - .gasLimit(MAX_TX_GAS_LIMIT + 1) - .gasPrice(Wei.ZERO) - .payload(Bytes.EMPTY) - .build(); - Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), - "Gas limit of transaction is greater than the allowed max of " + MAX_TX_GAS_LIMIT); - } - - @Test - public void validatedWithValidCalldata() { - final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = - org.hyperledger.besu.ethereum.core.Transaction.builder(); - final org.hyperledger.besu.ethereum.core.Transaction transaction = - builder - .sender(NOT_DENIED) - .to(NOT_DENIED) - .gasLimit(MAX_TX_GAS_LIMIT) - .gasPrice(Wei.ZERO) - .payload(Bytes.random(MAX_TX_CALLDATA_SIZE)) - .build(); - Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction, false, false), Optional.empty()); - } - - @Test - public void rejectedWithTooBigCalldata() { - final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = - org.hyperledger.besu.ethereum.core.Transaction.builder(); - final org.hyperledger.besu.ethereum.core.Transaction transaction = - builder - .sender(NOT_DENIED) - .to(NOT_DENIED) - .gasLimit(MAX_TX_GAS_LIMIT) - .gasPrice(Wei.ZERO) - .payload(Bytes.random(MAX_TX_CALLDATA_SIZE + 1)) - .build(); - Assertions.assertEquals( - lineaTransactionValidator.validateTransaction(transaction, false, false).orElseThrow(), - "Calldata of transaction is greater than the allowed max of " + MAX_TX_CALLDATA_SIZE); - } -} diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidatorTest.java new file mode 100644 index 00000000..cd09fe01 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidatorTest.java @@ -0,0 +1,88 @@ +/* + * 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.sequencer.txvalidation.validators; + +import java.util.Optional; +import java.util.Set; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@Slf4j +@RequiredArgsConstructor +public class AllowedAddressValidatorTest { + public static final Address DENIED = + Address.fromHexString("0x0000000000000000000000000000000000001000"); + public static final Address NOT_DENIED = + Address.fromHexString("0x0000000000000000000000000000000000001001"); + public static final Address PRECOMPILED = Address.precompiled(0xa); + private AllowedAddressValidator allowedAddressValidator; + + @BeforeEach + public void initialize() { + Set
denied = Set.of(DENIED); + allowedAddressValidator = new AllowedAddressValidator(denied); + } + + @Test + public void validatedIfNoneOnList() { + final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = + org.hyperledger.besu.ethereum.core.Transaction.builder(); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + builder.sender(NOT_DENIED).to(NOT_DENIED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); + Assertions.assertEquals( + allowedAddressValidator.validateTransaction(transaction, false, false), Optional.empty()); + } + + @Test + public void deniedIfFromAddressIsOnList() { + final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = + org.hyperledger.besu.ethereum.core.Transaction.builder(); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + builder.sender(DENIED).to(NOT_DENIED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); + Assertions.assertEquals( + allowedAddressValidator.validateTransaction(transaction, false, false).orElseThrow(), + "sender 0x0000000000000000000000000000000000001000 is blocked as appearing on the SDN or other legally prohibited list"); + } + + @Test + public void deniedIfToAddressIsOnList() { + final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = + org.hyperledger.besu.ethereum.core.Transaction.builder(); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + builder.sender(NOT_DENIED).to(DENIED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); + Assertions.assertEquals( + allowedAddressValidator.validateTransaction(transaction, false, false).orElseThrow(), + "recipient 0x0000000000000000000000000000000000001000 is blocked as appearing on the SDN or other legally prohibited list"); + } + + @Test + public void deniedIfToAddressIsPrecompiled() { + final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = + org.hyperledger.besu.ethereum.core.Transaction.builder(); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + builder.sender(NOT_DENIED).to(PRECOMPILED).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); + Assertions.assertEquals( + allowedAddressValidator.validateTransaction(transaction, false, false).orElseThrow(), + "destination address is a precompile address and cannot receive transactions"); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidatorTest.java new file mode 100644 index 00000000..7f0e211d --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidatorTest.java @@ -0,0 +1,64 @@ +/* + * 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.sequencer.txvalidation.validators; + +import java.util.Optional; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTransactionValidatorCliOptions; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.Wei; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@Slf4j +@RequiredArgsConstructor +public class CalldataValidatorTest { + public static final int MAX_TX_CALLDATA_SIZE = 10_000; + private CalldataValidator calldataValidator; + + @BeforeEach + public void initialize() { + calldataValidator = + new CalldataValidator( + LineaTransactionValidatorCliOptions.create().toDomainObject().toBuilder() + .maxTxCalldataSize(MAX_TX_CALLDATA_SIZE) + .build()); + } + + @Test + public void validatedWithValidCalldata() { + final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = + org.hyperledger.besu.ethereum.core.Transaction.builder(); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + builder.gasPrice(Wei.ZERO).payload(Bytes.random(MAX_TX_CALLDATA_SIZE)).build(); + Assertions.assertEquals( + calldataValidator.validateTransaction(transaction, false, false), Optional.empty()); + } + + @Test + public void rejectedWithTooBigCalldata() { + final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = + org.hyperledger.besu.ethereum.core.Transaction.builder(); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + builder.gasPrice(Wei.ZERO).payload(Bytes.random(MAX_TX_CALLDATA_SIZE + 1)).build(); + Assertions.assertEquals( + calldataValidator.validateTransaction(transaction, false, false).orElseThrow(), + "Calldata of transaction is greater than the allowed max of " + MAX_TX_CALLDATA_SIZE); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidatorTest.java new file mode 100644 index 00000000..9ea005b2 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidatorTest.java @@ -0,0 +1,64 @@ +/* + * 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.sequencer.txvalidation.validators; + +import java.util.Optional; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTransactionValidatorCliOptions; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.Wei; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@Slf4j +@RequiredArgsConstructor +public class GasLimitValidatorTest { + public static final int MAX_TX_GAS_LIMIT = 9_000_000; + private GasLimitValidator gasLimitValidator; + + @BeforeEach + public void initialize() { + gasLimitValidator = + new GasLimitValidator( + LineaTransactionValidatorCliOptions.create().toDomainObject().toBuilder() + .maxTxGasLimit(MAX_TX_GAS_LIMIT) + .build()); + } + + @Test + public void validatedWithValidGasLimit() { + final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = + org.hyperledger.besu.ethereum.core.Transaction.builder(); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + builder.gasLimit(MAX_TX_GAS_LIMIT).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); + Assertions.assertEquals( + gasLimitValidator.validateTransaction(transaction, false, false), Optional.empty()); + } + + @Test + public void rejectedWithMaxGasLimitPlusOne() { + final org.hyperledger.besu.ethereum.core.Transaction.Builder builder = + org.hyperledger.besu.ethereum.core.Transaction.builder(); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + builder.gasLimit(MAX_TX_GAS_LIMIT + 1).gasPrice(Wei.ZERO).payload(Bytes.EMPTY).build(); + Assertions.assertEquals( + gasLimitValidator.validateTransaction(transaction, false, false).orElseThrow(), + "Gas limit of transaction is greater than the allowed max of " + MAX_TX_GAS_LIMIT); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidatorTest.java new file mode 100644 index 00000000..11c20292 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidatorTest.java @@ -0,0 +1,291 @@ +/* + * 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.sequencer.txvalidation.validators; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigInteger; +import java.nio.file.Path; +import java.util.Optional; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaProfitabilityCliOptions; +import org.apache.tuweni.bytes.Bytes; +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.hyperledger.besu.crypto.SECPSignature; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.plugin.data.BlockContext; +import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.services.BesuConfiguration; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@Slf4j +@RequiredArgsConstructor +public class ProfitabilityValidatorTest { + public static final Address SENDER = + Address.fromHexString("0x0000000000000000000000000000000000001000"); + public static final Address RECIPIENT = + Address.fromHexString("0x0000000000000000000000000000000000001001"); + private static Wei PROFITABLE_GAS_PRICE = Wei.of(11000000); + private static Wei UNPROFITABLE_GAS_PRICE = Wei.of(1000000); + private static final SECPSignature FAKE_SIGNATURE; + + static { + final X9ECParameters params = SECNamedCurves.getByName("secp256k1"); + final ECDomainParameters curve = + new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + FAKE_SIGNATURE = + SECPSignature.create( + new BigInteger( + "66397251408932042429874251838229702988618145381408295790259650671563847073199"), + new BigInteger( + "24729624138373455972486746091821238755870276413282629437244319694880507882088"), + (byte) 0, + curve.getN()); + } + + public static final double TX_POOL_MIN_MARGIN = 0.5; + private ProfitabilityValidator profitabilityValidatorAlways; + private ProfitabilityValidator profitabilityValidatorOnlyApi; + private ProfitabilityValidator profitabilityValidatorOnlyP2p; + private ProfitabilityValidator profitabilityValidatorNever; + + @BeforeEach + public void initialize() { + final var profitabilityConfBuilder = + LineaProfitabilityCliOptions.create().toDomainObject().toBuilder() + .txPoolMinMargin(TX_POOL_MIN_MARGIN); + + profitabilityValidatorAlways = + new ProfitabilityValidator( + new TestBesuConfiguration(), + new TestBlockchainService(), + profitabilityConfBuilder + .txPoolCheckP2pEnabled(true) + .txPoolCheckApiEnabled(true) + .build()); + + profitabilityValidatorNever = + new ProfitabilityValidator( + new TestBesuConfiguration(), + new TestBlockchainService(), + profitabilityConfBuilder + .txPoolCheckP2pEnabled(false) + .txPoolCheckApiEnabled(false) + .build()); + + profitabilityValidatorOnlyApi = + new ProfitabilityValidator( + new TestBesuConfiguration(), + new TestBlockchainService(), + profitabilityConfBuilder + .txPoolCheckP2pEnabled(false) + .txPoolCheckApiEnabled(true) + .build()); + + profitabilityValidatorOnlyP2p = + new ProfitabilityValidator( + new TestBesuConfiguration(), + new TestBlockchainService(), + profitabilityConfBuilder + .txPoolCheckP2pEnabled(true) + .txPoolCheckApiEnabled(false) + .build()); + } + + @Test + public void acceptPriorityRemoteWhenBelowMinProfitability() { + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(PROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(profitabilityValidatorAlways.validateTransaction(transaction, false, true)) + .isEmpty(); + } + + @Test + public void rejectRemoteWhenBelowMinProfitability() { + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(UNPROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(profitabilityValidatorAlways.validateTransaction(transaction, false, false)) + .isPresent() + .contains("Gas price too low"); + } + + @Test + public void acceptRemoteWhenBelowMinProfitabilityIfCheckNeverEnabled() { + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(UNPROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(profitabilityValidatorNever.validateTransaction(transaction, false, false)) + .isEmpty(); + } + + @Test + public void acceptLocalWhenBelowMinProfitabilityIfCheckNeverEnabled() { + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(UNPROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(profitabilityValidatorNever.validateTransaction(transaction, true, false)).isEmpty(); + } + + @Test + public void acceptRemoteWhenBelowMinProfitabilityIfCheckDisabledForP2p() { + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(UNPROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(profitabilityValidatorOnlyApi.validateTransaction(transaction, false, false)) + .isEmpty(); + } + + @Test + public void rejectRemoteWhenBelowMinProfitabilityIfCheckEnableForP2p() { + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(UNPROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(profitabilityValidatorOnlyP2p.validateTransaction(transaction, false, false)) + .isPresent() + .contains("Gas price too low"); + } + + @Test + public void acceptLocalWhenBelowMinProfitabilityIfCheckDisabledForApi() { + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(UNPROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(profitabilityValidatorOnlyP2p.validateTransaction(transaction, true, false)) + .isEmpty(); + } + + @Test + public void rejectLocalWhenBelowMinProfitabilityIfCheckEnableForApi() { + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(UNPROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(profitabilityValidatorOnlyApi.validateTransaction(transaction, true, false)) + .isPresent() + .contains("Gas price too low"); + } + + private static class TestBesuConfiguration implements BesuConfiguration { + @Override + public Path getStoragePath() { + throw new UnsupportedOperationException(); + } + + @Override + public Path getDataPath() { + throw new UnsupportedOperationException(); + } + + @Override + public DataStorageFormat getDatabaseFormat() { + throw new UnsupportedOperationException(); + } + + @Override + public Wei getMinGasPrice() { + return Wei.of(100_000_000); + } + } + + private static class TestBlockchainService implements BlockchainService { + + @Override + public Optional getBlockByNumber(final long l) { + throw new UnsupportedOperationException(); + } + + @Override + public Hash getChainHeadHash() { + throw new UnsupportedOperationException(); + } + + @Override + public BlockHeader getChainHeadHeader() { + throw new UnsupportedOperationException(); + } + + @Override + public Optional getNextBlockBaseFee() { + return Optional.of(Wei.of(7)); + } + } +} From 8c046a760f79d89e5a30db9136c8980667ecf7ab Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 6 Mar 2024 14:53:31 +0100 Subject: [PATCH 11/30] Fix price adjustment in profitability formula (#642) Signed-off-by: Fabio Di Fabio --- .../TransactionProfitabilityCalculator.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java b/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java index 4afcd450..d31d093b 100644 --- a/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java +++ b/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java @@ -28,12 +28,14 @@ public class TransactionProfitabilityCalculator { private final LineaProfitabilityConfiguration profitabilityConf; private final double preComputedValue; + private final double priceAdjustment; public TransactionProfitabilityCalculator( final LineaProfitabilityConfiguration profitabilityConf) { this.profitabilityConf = profitabilityConf; this.preComputedValue = profitabilityConf.gasPriceRatio() * profitabilityConf.verificationGasCost(); + this.priceAdjustment = profitabilityConf.gasPriceAdjustment().getAsBigInteger().doubleValue(); } public Wei profitablePriorityFeePerGas( @@ -44,15 +46,14 @@ public Wei profitablePriorityFeePerGas( final double compressedTxSize = getCompressedTxSize(transaction); final var profitAt = - preComputedValue - * minMargin - * compressedTxSize - * minGasPrice.getAsBigInteger().doubleValue() - / (gas * profitabilityConf.verificationCapacity()); - - final var adjustedProfit = - Wei.ofNumber(BigDecimal.valueOf(profitAt).toBigInteger()) - .add(profitabilityConf.gasPriceAdjustment()); + (preComputedValue + * compressedTxSize + * minGasPrice.getAsBigInteger().doubleValue() + / (gas * profitabilityConf.verificationCapacity()) + + priceAdjustment) + * minMargin; + + final var adjustedProfit = Wei.ofNumber(BigDecimal.valueOf(profitAt).toBigInteger()); log.atDebug() .setMessage( From c25b2c8f1a098621131bd37f2664d28679252510 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Thu, 7 Mar 2024 01:01:50 +0100 Subject: [PATCH 12/30] Update CHANGELOG for v0.1.4-test22 (#641) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 419be301..21042d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.1.4-test22 +Test pre-release 22 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* linea_estimateGas compatibility switch https://github.com/Consensys/besu-sequencer-plugins/pull/634 +* Update profitability formula with gas price adjustment option https://github.com/Consensys/besu-sequencer-plugins/pull/638 +* Update code to latest plugin API https://github.com/Consensys/besu-sequencer-plugins/pull/640 +* Txpool profitability check https://github.com/Consensys/besu-sequencer-plugins/pull/603 +* Fix price adjustment in profitability formula https://github.com/Consensys/besu-sequencer-plugins/pull/642 + ## 0.1.4-test21 Test pre-release 21 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) * fix: capture SSTORE-touched storage slots for correct gas computations [#606](https://github.com/Consensys/besu-sequencer-plugins/pull/606) From cdbe27bbe1a2141d634a61a5ed436537f40909a2 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 13 Mar 2024 11:27:31 +0100 Subject: [PATCH 13/30] Build native lib for Windows (#645) Signed-off-by: Fabio Di Fabio --- README.md | 9 +++++++-- native/Dockerfile-win-dockcross | 18 ++++++++++++++++++ native/compress/build.gradle | 15 +++++++++++++-- native/wsl.sh | 10 ++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 native/Dockerfile-win-dockcross create mode 100644 native/wsl.sh diff --git a/README.md b/README.md index 011374f7..0b83fc12 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,14 @@ an [existing implementation in Go](https://github.com/Consensys/zk-evm/). brew install openjdk@17 ``` -### Install the relevant CGo compiler for your platform +### Native Lib Prerequisites -### Install the Go toolchain +Linux/MacOs +* Install the relevant CGo compiler for your platform +* Install the Go toolchain + +Windows +* Requirement [Docker Desktop WSL 2 backend on Windows](https://docs.docker.com/desktop/wsl/) ### Install Rust diff --git a/native/Dockerfile-win-dockcross b/native/Dockerfile-win-dockcross new file mode 100644 index 00000000..4a3a3938 --- /dev/null +++ b/native/Dockerfile-win-dockcross @@ -0,0 +1,18 @@ +FROM dockcross/windows-shared-x64 + +ARG GO_VERSION=1.22.1 +ARG GO_ARCHIVE_CHECKSUM=aab8e15785c997ae20f9c88422ee35d962c4562212bb0f879d052a35c8307c7f + +ENV DEFAULT_DOCKCROSS_IMAGE native-windows-cross-compile + +RUN wget https://go.dev/dl/go$GO_VERSION.linux-amd64.tar.gz +RUN echo "$GO_ARCHIVE_CHECKSUM go$GO_VERSION.linux-amd64.tar.gz" | sha256sum --check --status +RUN tar -C /usr/local -xzf go$GO_VERSION.linux-amd64.tar.gz + +ENV PATH="$PATH:/usr/local/go/bin" + +# pre-fetch go modules +COPY compress/compress-jni/go.mod . +COPY compress/compress-jni/go.sum . +RUN go mod download +RUN rm go.mod go.sum \ No newline at end of file diff --git a/native/compress/build.gradle b/native/compress/build.gradle index ec7843f9..9f456542 100644 --- a/native/compress/build.gradle +++ b/native/compress/build.gradle @@ -33,8 +33,13 @@ test { } tasks.register('buildJNI', Exec) { - workingDir buildscript.sourceFile.parentFile - commandLine 'sh', '-c', '../build.sh' + if(org.gradle.internal.os.OperatingSystem.current().isWindows()) { + workingDir buildscript.sourceFile.parentFile.parentFile + commandLine 'wsl', './wsl.sh' + } else { + workingDir buildscript.sourceFile.parentFile + commandLine 'sh', '-c', '../build.sh' + } } compileJava{ @@ -76,6 +81,12 @@ tasks.register('linuxArm64LibCopy', Copy) { } processResources.dependsOn linuxArm64LibCopy +tasks.register('windowsLibCopy', Copy) { + from 'build/native/compress_jni.dll' + into 'build/resources/main/win32-x86-64' +} +processResources.dependsOn windowsLibCopy + jar { archiveBaseName = 'linea-native-compress' includeEmptyDirs = false diff --git a/native/wsl.sh b/native/wsl.sh new file mode 100644 index 00000000..a91bafef --- /dev/null +++ b/native/wsl.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +docker build -f Dockerfile-win-dockcross -t native-windows-cross-compile . + +docker run --rm native-windows-cross-compile > compress/build/native/native-windows-cross-compile + +compress/build/native/native-windows-cross-compile \ + bash -c "cd compress/compress-jni && + CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -buildmode=c-shared -o ../build/native/compress_jni.dll compress-jni.go" + From 07db82482a98f52ec705d0bd5c9a7116a880a5c3 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Thu, 14 Mar 2024 12:01:26 +0100 Subject: [PATCH 14/30] linea_estimateGas compatibility mode multiplier (#646) Signed-off-by: Fabio Di Fabio --- .../EstimateGasCompatibilityModeTest.java | 26 +++++++++++++++++-- .../acc/test/rpc/linea/EstimateGasTest.java | 11 +++++--- .../linea/config/LineaRpcCliOptions.java | 23 ++++++++++++++-- .../linea/config/LineaRpcConfiguration.java | 5 +++- .../linea/rpc/linea/LineaEstimateGas.java | 24 ++++++++++++----- gradle/dependency-management.gradle | 2 +- 6 files changed, 74 insertions(+), 17 deletions(-) diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java index 4649d5ac..6c0e8aa1 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java @@ -16,30 +16,52 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.List; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.Transaction; public class EstimateGasCompatibilityModeTest extends EstimateGasTest { + private static final BigDecimal PRICE_MULTIPLIER = BigDecimal.valueOf(1.2); @Override public List getTestCliOptions() { return getTestCommandLineOptionsBuilder() .set("--plugin-linea-estimate-gas-compatibility-mode-enabled=", "true") + .set( + "--plugin-linea-estimate-gas-compatibility-mode-multiplier=", + PRICE_MULTIPLIER.toPlainString()) .build(); } @Override protected void assertIsProfitable( final Transaction tx, + final Wei baseFee, final Wei estimatedPriorityFee, final Wei estimatedMaxGasPrice, final long estimatedGasLimit) { final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice(); + final var minPriorityFee = minGasPrice.subtract(baseFee); + final var compatibilityMinPriorityFee = + Wei.of( + PRICE_MULTIPLIER + .multiply(new BigDecimal(minPriorityFee.getAsBigInteger())) + .setScale(0, RoundingMode.CEILING) + .toBigInteger()); // since we are in compatibility mode, we want to check that returned profitable priority fee is - // the min mineable gas price - assertThat(estimatedMaxGasPrice).isEqualTo(minGasPrice); + // the min priority fee per gas * multiplier + base fee + final var expectedMaxGasPrice = baseFee.add(compatibilityMinPriorityFee); + assertThat(estimatedMaxGasPrice).isEqualTo(expectedMaxGasPrice); + } + + @Override + protected void assertMinGasPriceLowerBound(final Wei baseFee, final Wei estimatedMaxGasPrice) { + // since we are in compatibility mode, we want to check that returned profitable priority fee is + // the min priority fee per gas * multiplier + base fee + assertIsProfitable(null, baseFee, null, estimatedMaxGasPrice, 0); } } diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java index 9dc90202..1f4493a6 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java @@ -26,7 +26,6 @@ import net.consensys.linea.bl.TransactionProfitabilityCalculator; import net.consensys.linea.config.LineaProfitabilityCliOptions; import net.consensys.linea.config.LineaProfitabilityConfiguration; -import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.rpc.linea.LineaEstimateGas; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt64; @@ -48,7 +47,6 @@ public class EstimateGasTest extends LineaPluginTestBase { protected static final double ESTIMATE_GAS_MIN_MARGIN = 1.0; protected static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000); protected static final int MAX_TRANSACTION_GAS_LIMIT = 30_000_000; - protected LineaTransactionSelectorConfiguration txSelectorConf; protected LineaProfitabilityConfiguration profitabilityConf; @Override @@ -137,11 +135,12 @@ public void lineaEstimateGasIsProfitable() { .signature(LineaEstimateGas.FAKE_SIGNATURE_FOR_SIZE_CALCULATION) .build(); - assertIsProfitable(tx, estimatedPriorityFee, estimatedMaxGasPrice, estimatedGasLimit); + assertIsProfitable(tx, baseFee, estimatedPriorityFee, estimatedMaxGasPrice, estimatedGasLimit); } protected void assertIsProfitable( final org.hyperledger.besu.ethereum.core.Transaction tx, + final Wei baseFee, final Wei estimatedPriorityFee, final Wei estimatedMaxGasPrice, final long estimatedGasLimit) { @@ -180,8 +179,12 @@ public void lineaEstimateGasPriorityFeeMinGasPriceLowerBound() { final var baseFee = Wei.fromHexString(respLinea.baseFeePerGas()); final var estimatedPriorityFee = Wei.fromHexString(respLinea.priorityFeePerGas()); final var estimatedMaxGasPrice = baseFee.add(estimatedPriorityFee); - final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice(); + assertMinGasPriceLowerBound(baseFee, estimatedMaxGasPrice); + } + + protected void assertMinGasPriceLowerBound(final Wei baseFee, final Wei estimatedMaxGasPrice) { + final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice(); assertThat(estimatedMaxGasPrice).isEqualTo(minGasPrice); } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcCliOptions.java index 05b1e543..cefc9fcb 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcCliOptions.java @@ -15,6 +15,8 @@ package net.consensys.linea.config; +import java.math.BigDecimal; + import com.google.common.base.MoreObjects; import picocli.CommandLine; @@ -22,13 +24,27 @@ public class LineaRpcCliOptions { private static final String ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED = "--plugin-linea-estimate-gas-compatibility-mode-enabled"; + private static final boolean DEFAULT_ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED = false; + private static final String ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER = + "--plugin-linea-estimate-gas-compatibility-mode-multiplier"; + private static final BigDecimal DEFAULT_ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER = + BigDecimal.valueOf(1.2); @CommandLine.Option( names = {ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED}, paramLabel = "", description = - "Set to true to return the min mineable gas price, instead of the profitable price (default: ${DEFAULT-VALUE})") - private boolean estimateGasCompatibilityModeEnabled = false; + "Set to true to return the min mineable gas price * multiplier, instead of the profitable price (default: ${DEFAULT-VALUE})") + private boolean estimateGasCompatibilityModeEnabled = + DEFAULT_ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED; + + @CommandLine.Option( + names = {ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER}, + paramLabel = "", + description = + "Set to multiplier to apply to the min priority fee per gas when the compatibility mode is enabled (default: ${DEFAULT-VALUE})") + private BigDecimal estimateGasCompatibilityMultiplier = + DEFAULT_ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER; private LineaRpcCliOptions() {} @@ -50,6 +66,7 @@ public static LineaRpcCliOptions create() { public static LineaRpcCliOptions fromConfig(final LineaRpcConfiguration config) { final LineaRpcCliOptions options = create(); options.estimateGasCompatibilityModeEnabled = config.estimateGasCompatibilityModeEnabled(); + options.estimateGasCompatibilityMultiplier = config.estimateGasCompatibilityMultiplier(); return options; } @@ -61,6 +78,7 @@ public static LineaRpcCliOptions fromConfig(final LineaRpcConfiguration config) public LineaRpcConfiguration toDomainObject() { return LineaRpcConfiguration.builder() .estimateGasCompatibilityModeEnabled(estimateGasCompatibilityModeEnabled) + .estimateGasCompatibilityMultiplier(estimateGasCompatibilityMultiplier) .build(); } @@ -68,6 +86,7 @@ public LineaRpcConfiguration toDomainObject() { public String toString() { return MoreObjects.toStringHelper(this) .add(ESTIMATE_GAS_COMPATIBILITY_MODE_ENABLED, estimateGasCompatibilityModeEnabled) + .add(ESTIMATE_GAS_COMPATIBILITY_MODE_MULTIPLIER, estimateGasCompatibilityMultiplier) .toString(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java index 5742ab45..6551c924 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java @@ -15,8 +15,11 @@ package net.consensys.linea.config; +import java.math.BigDecimal; + import lombok.Builder; /** The Linea RPC configuration. */ @Builder(toBuilder = true) -public record LineaRpcConfiguration(boolean estimateGasCompatibilityModeEnabled) {} +public record LineaRpcConfiguration( + boolean estimateGasCompatibilityModeEnabled, BigDecimal estimateGasCompatibilityMultiplier) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java index 50662b62..23b21433 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -17,7 +17,9 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity.create; +import java.math.BigDecimal; import java.math.BigInteger; +import java.math.RoundingMode; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; @@ -112,17 +114,13 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) { .log(); final var estimatedGasUsed = estimateGasUsed(callParameters, transaction, minGasPrice); - final Wei profitablePriorityFee = - txProfitabilityCalculator.profitablePriorityFeePerGas( - transaction, profitabilityConf.estimateGasMinMargin(), minGasPrice, estimatedGasUsed); - final Wei baseFee = blockchainService .getNextBlockBaseFee() .orElseThrow(() -> new IllegalStateException("Not on a baseFee market")); final Wei estimatedPriorityFee = - getEstimatedPriorityFee(baseFee, profitablePriorityFee, minGasPrice); + getEstimatedPriorityFee(transaction, baseFee, minGasPrice, estimatedGasUsed); final var response = new Response(create(estimatedGasUsed), create(baseFee), create(estimatedPriorityFee)); @@ -132,13 +130,25 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) { } private Wei getEstimatedPriorityFee( - final Wei baseFee, final Wei profitablePriorityFee, final Wei minGasPrice) { + final Transaction transaction, + final Wei baseFee, + final Wei minGasPrice, + final long estimatedGasUsed) { final Wei priorityFeeLowerBound = minGasPrice.subtract(baseFee); if (rpcConfiguration.estimateGasCompatibilityModeEnabled()) { - return priorityFeeLowerBound; + return Wei.of( + rpcConfiguration + .estimateGasCompatibilityMultiplier() + .multiply(new BigDecimal(priorityFeeLowerBound.getAsBigInteger())) + .setScale(0, RoundingMode.CEILING) + .toBigInteger()); } + final Wei profitablePriorityFee = + txProfitabilityCalculator.profitablePriorityFeePerGas( + transaction, profitabilityConf.estimateGasMinMargin(), minGasPrice, estimatedGasUsed); + if (profitablePriorityFee.greaterOrEqualThan(priorityFeeLowerBound)) { return profitablePriorityFee; } diff --git a/gradle/dependency-management.gradle b/gradle/dependency-management.gradle index 4be7dac3..3ced8b1e 100644 --- a/gradle/dependency-management.gradle +++ b/gradle/dependency-management.gradle @@ -116,7 +116,7 @@ dependencyManagement { dependency 'com.splunk.logging:splunk-library-javalogging:1.11.5' - dependency 'io.vertx:vertx-core:4.3.8' + dependency 'io.vertx:vertx-core:4.5.4' dependency 'com.slack.api:slack-api-client:1.32.1' From 41a989efa48ee8fe251329cb905f93d55c6acb59 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Thu, 14 Mar 2024 23:56:12 +0100 Subject: [PATCH 15/30] Update CHANGELOG for v0.1.4-test23 (#647) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21042d1f..a05bb516 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.4-test23 +Test pre-release 23 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* linea_estimateGas compatibility mode multiplier https://github.com/Consensys/besu-sequencer-plugins/pull/646 + ## 0.1.4-test22 Test pre-release 22 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) * linea_estimateGas compatibility switch https://github.com/Consensys/besu-sequencer-plugins/pull/634 From 6b13c9c83a29ffeb6e6e0798b6fc4472a0ecdaa0 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Mon, 25 Mar 2024 09:57:32 +0100 Subject: [PATCH 16/30] On Windows also build Linux native lib so it can run on WSL (#651) Signed-off-by: Fabio Di Fabio --- native/Dockerfile-win-dockcross | 5 +++-- native/wsl.sh | 15 +++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/native/Dockerfile-win-dockcross b/native/Dockerfile-win-dockcross index 4a3a3938..8e511f8f 100644 --- a/native/Dockerfile-win-dockcross +++ b/native/Dockerfile-win-dockcross @@ -1,9 +1,10 @@ -FROM dockcross/windows-shared-x64 +ARG IMAGE +FROM dockcross/$IMAGE ARG GO_VERSION=1.22.1 ARG GO_ARCHIVE_CHECKSUM=aab8e15785c997ae20f9c88422ee35d962c4562212bb0f879d052a35c8307c7f -ENV DEFAULT_DOCKCROSS_IMAGE native-windows-cross-compile +ENV DEFAULT_DOCKCROSS_IMAGE native-${IMAGE}-cross-compile RUN wget https://go.dev/dl/go$GO_VERSION.linux-amd64.tar.gz RUN echo "$GO_ARCHIVE_CHECKSUM go$GO_VERSION.linux-amd64.tar.gz" | sha256sum --check --status diff --git a/native/wsl.sh b/native/wsl.sh index a91bafef..64387d15 100644 --- a/native/wsl.sh +++ b/native/wsl.sh @@ -1,10 +1,17 @@ -#!/bin/bash +#!/bin/bash -x -docker build -f Dockerfile-win-dockcross -t native-windows-cross-compile . +docker build -f Dockerfile-win-dockcross --build-arg IMAGE=windows-shared-x64 -t native-windows-shared-x64-cross-compile . +docker build -f Dockerfile-win-dockcross --build-arg IMAGE=linux-x64 -t native-linux-x64-cross-compile . -docker run --rm native-windows-cross-compile > compress/build/native/native-windows-cross-compile +mkdir -p compress/build/native/ -compress/build/native/native-windows-cross-compile \ +docker run --rm native-windows-shared-x64-cross-compile > compress/build/native/native-windows-shared-x64-cross-compile +docker run --rm native-linux-x64-cross-compile > compress/build/native/native-linux-x64-cross-compile + +compress/build/native/native-windows-shared-x64-cross-compile --image native-windows-shared-x64-cross-compile \ bash -c "cd compress/compress-jni && CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -buildmode=c-shared -o ../build/native/compress_jni.dll compress-jni.go" +compress/build/native/native-linux-x64-cross-compile --image native-linux-x64-cross-compile \ + bash -c "cd compress/compress-jni && + CGO_ENABLED=1 go build -buildmode=c-shared -o ../build/native/libcompress_jni.so compress-jni.go" From b624a4b8341595075c910fa30fa1e0dcd621ba0d Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Mon, 25 Mar 2024 10:07:54 +0100 Subject: [PATCH 17/30] Improve linea_estimateGas error response (#650) * Improve linea_estimateGas error response Signed-off-by: Fabio Di Fabio * Update arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java Co-authored-by: Sally MacFarlane --------- Signed-off-by: Fabio Di Fabio Co-authored-by: Sally MacFarlane --- .../acc/test/rpc/linea/EstimateGasTest.java | 104 +++++++++++- .../resources/txOverflowModuleLimits.toml | 2 +- .../src/test/solidity/SimpleStorage.sol | 1 + .../rpc/counters/GenerateCountersV0.java | 3 +- .../linea/rpc/linea/LineaEstimateGas.java | 152 +++++++++++++----- .../GenerateConflatedTracesV0.java | 3 +- gradle.properties | 2 +- 7 files changed, 218 insertions(+), 49 deletions(-) diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java index 1f4493a6..df80e487 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java @@ -18,11 +18,16 @@ import java.io.IOException; import java.math.BigInteger; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.List; import linea.plugin.acc.test.LineaPluginTestBase; import linea.plugin.acc.test.TestCommandLineOptionsBuilder; +import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage; import net.consensys.linea.bl.TransactionProfitabilityCalculator; import net.consensys.linea.config.LineaProfitabilityCliOptions; import net.consensys.linea.config.LineaProfitabilityConfiguration; @@ -32,12 +37,16 @@ import org.bouncycastle.crypto.digests.KeccakDigest; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.tests.acceptance.dsl.account.Account; +import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts; import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.web3j.protocol.core.Request; +import org.web3j.protocol.core.Response; +import org.web3j.protocol.http.HttpService; public class EstimateGasTest extends LineaPluginTestBase { protected static final int VERIFICATION_GAS_COST = 1_200_000; @@ -87,7 +96,8 @@ public void lineaEstimateGasMatchesEthEstimateGas() { final Account sender = accounts.getSecondaryBenefactor(); final CallParams callParams = - new CallParams(sender.getAddress(), sender.getAddress(), null, Bytes.EMPTY.toHexString()); + new CallParams( + sender.getAddress(), sender.getAddress(), null, Bytes.EMPTY.toHexString(), "0"); final var reqEth = new RawEstimateGasRequest(callParams); final var reqLinea = new LineaEstimateGasRequest(callParams); @@ -113,7 +123,7 @@ public void lineaEstimateGasIsProfitable() { final var payload = Bytes.wrap(txData.toString().getBytes(StandardCharsets.UTF_8)); final CallParams callParams = - new CallParams(sender.getAddress(), sender.getAddress(), null, payload.toHexString()); + new CallParams(sender.getAddress(), sender.getAddress(), null, payload.toHexString(), "0"); final var reqLinea = new LineaEstimateGasRequest(callParams); final var respLinea = reqLinea.execute(minerNode.nodeRequests()); @@ -168,10 +178,9 @@ protected void assertIsProfitable( @Test public void lineaEstimateGasPriorityFeeMinGasPriceLowerBound() { - final Account sender = accounts.getSecondaryBenefactor(); - final CallParams callParams = new CallParams(sender.getAddress(), null, "", ""); + final CallParams callParams = new CallParams(sender.getAddress(), null, "", "", "0"); final var reqLinea = new LineaEstimateGasRequest(callParams); final var respLinea = reqLinea.execute(minerNode.nodeRequests()); @@ -183,6 +192,63 @@ public void lineaEstimateGasPriorityFeeMinGasPriceLowerBound() { assertMinGasPriceLowerBound(baseFee, estimatedMaxGasPrice); } + @Test + public void invalidParametersLineaEstimateGasRequestReturnErrorResponse() { + final Account sender = accounts.getSecondaryBenefactor(); + final CallParams callParams = + new CallParams(sender.getAddress(), null, "", "", String.valueOf(Integer.MAX_VALUE)); + final var reqLinea = new BadLineaEstimateGasRequest(callParams); + final var respLinea = reqLinea.execute(minerNode.nodeRequests()); + assertThat(respLinea.getCode()).isEqualTo(RpcErrorType.INVALID_PARAMS.getCode()); + assertThat(respLinea.getMessage()).isEqualTo(RpcErrorType.INVALID_PARAMS.getMessage()); + } + + @Test + public void revertedTransactionReturnErrorResponse() throws Exception { + final SimpleStorage simpleStorage = deploySimpleStorage(); + final Account sender = accounts.getSecondaryBenefactor(); + final var reqLinea = + new BadLineaEstimateGasRequest( + new CallParams(sender.getAddress(), simpleStorage.getContractAddress(), "", "", "0")); + final var respLinea = reqLinea.execute(minerNode.nodeRequests()); + assertThat(respLinea.getCode()).isEqualTo(-32000); + assertThat(respLinea.getMessage()).isEqualTo("Execution reverted"); + assertThat(respLinea.getData()).isEqualTo("\"0x\""); + } + + @Test + public void failedTransactionReturnErrorResponse() { + final Account sender = accounts.getSecondaryBenefactor(); + final var reqLinea = + new BadLineaEstimateGasRequest( + new CallParams( + sender.getAddress(), null, "", Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY, "0")); + final var respLinea = reqLinea.execute(minerNode.nodeRequests()); + assertThat(respLinea.getCode()).isEqualTo(-32000); + assertThat(respLinea.getMessage()).isEqualTo("Failed transaction, reason: INVALID_OPERATION"); + } + + @Test + public void parseErrorLineaEstimateGasRequestReturnErrorResponse() + throws IOException, InterruptedException { + final var httpService = (HttpService) minerNode.nodeRequests().getWeb3jService(); + final var httpClient = HttpClient.newHttpClient(); + final var badJsonRequest = + HttpRequest.newBuilder(URI.create(httpService.getUrl())) + .headers("Content-Type", "application/json") + .POST( + HttpRequest.BodyPublishers.ofString( + """ + {"jsonrpc":"2.0","method":"linea_estimateGas","params":[malformed json],"id":53} + """)) + .build(); + final var errorResponse = httpClient.send(badJsonRequest, HttpResponse.BodyHandlers.ofString()); + assertThat(errorResponse.body()) + .isEqualTo( + """ + {"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"Parse error"}}"""); + } + protected void assertMinGasPriceLowerBound(final Wei baseFee, final Wei estimatedMaxGasPrice) { final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice(); assertThat(estimatedMaxGasPrice).isEqualTo(minGasPrice); @@ -215,6 +281,34 @@ static class LineaEstimateGasResponse extends org.web3j.protocol.core.Response { + private final CallParams badCallParams; + + public BadLineaEstimateGasRequest(final CallParams badCallParams) { + this.badCallParams = badCallParams; + } + + @Override + public org.web3j.protocol.core.Response.Error execute(final NodeRequests nodeRequests) { + try { + return new Request<>( + "linea_estimateGas", + List.of(badCallParams), + nodeRequests.getWeb3jService(), + BadLineaEstimateGasResponse.class) + .send() + .getError(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static class BadLineaEstimateGasResponse extends org.web3j.protocol.core.Response {} + + record Response(String gasLimit, String baseFeePerGas, String priorityFeePerGas) {} + } + static class RawEstimateGasRequest implements Transaction { private final CallParams callParams; @@ -240,5 +334,5 @@ public String execute(final NodeRequests nodeRequests) { static class RawEstimateGasResponse extends org.web3j.protocol.core.Response {} } - record CallParams(String from, String to, String value, String data) {} + record CallParams(String from, String to, String value, String data, String gasLimit) {} } diff --git a/acceptance-tests/src/test/resources/txOverflowModuleLimits.toml b/acceptance-tests/src/test/resources/txOverflowModuleLimits.toml index 60b0948a..3e47b417 100644 --- a/acceptance-tests/src/test/resources/txOverflowModuleLimits.toml +++ b/acceptance-tests/src/test/resources/txOverflowModuleLimits.toml @@ -30,7 +30,7 @@ PUB_HASH_INFO = 8192 PUB_LOG = 16384 PUB_LOG_INFO = 16384 RLP = 128 -ROM = 2400 +ROM = 2402 ROM_LEX = 20 SHF = 20 SHF_RT = 2305 diff --git a/acceptance-tests/src/test/solidity/SimpleStorage.sol b/acceptance-tests/src/test/solidity/SimpleStorage.sol index 27b9a99a..0f29754e 100644 --- a/acceptance-tests/src/test/solidity/SimpleStorage.sol +++ b/acceptance-tests/src/test/solidity/SimpleStorage.sol @@ -20,6 +20,7 @@ contract SimpleStorage { uint sum; function set(string memory value) public { + require(bytes(value).length != 0); data = value; } diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/counters/GenerateCountersV0.java b/arithmetization/src/main/java/net/consensys/linea/rpc/counters/GenerateCountersV0.java index 916ea1fa..98a770e4 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/counters/GenerateCountersV0.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/counters/GenerateCountersV0.java @@ -22,6 +22,7 @@ import com.google.common.cache.CacheBuilder; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.zktracer.ZkTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.services.TraceService; import org.hyperledger.besu.plugin.services.exception.PluginRpcEndpointException; @@ -97,7 +98,7 @@ public Counters execute(final PluginRpcRequest request) { log.info("counters for {} returned in {}", requestedBlockNumber, sw); return r; } catch (Exception ex) { - throw new PluginRpcEndpointException(ex.getMessage()); + throw new PluginRpcEndpointException(RpcErrorType.PLUGIN_INTERNAL_ERROR, ex.getMessage()); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java index 23b21433..bd5578ff 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -20,6 +20,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.util.concurrent.atomic.AtomicInteger; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; @@ -37,18 +38,22 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.TransactionSimulationService; +import org.hyperledger.besu.plugin.services.exception.PluginRpcEndpointException; import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; +import org.hyperledger.besu.plugin.services.rpc.RpcMethodError; @Slf4j public class LineaEstimateGas { @VisibleForTesting public static final SECPSignature FAKE_SIGNATURE_FOR_SIZE_CALCULATION; private static final double SUB_CALL_REMAINING_GAS_RATIO = 65D / 64D; + private static final AtomicInteger LOG_SEQUENCE = new AtomicInteger(); static { final X9ECParameters params = SECNamedCurves.getByName("secp256k1"); @@ -101,14 +106,21 @@ public String getName() { } public LineaEstimateGas.Response execute(final PluginRpcRequest request) { + if (log.isDebugEnabled()) { + // no matter if it overflows, since it is only used to correlate logs for this request, + // so we only print callParameters once at the beginning, and we can reference them using the + // sequence. + LOG_SEQUENCE.incrementAndGet(); + } final var callParameters = parseRequest(request.getParams()); final var minGasPrice = besuConfiguration.getMinGasPrice(); final var transaction = createTransactionForSimulation( callParameters, txValidatorConf.maxTxGasLimit(), minGasPrice); - log.atTrace() - .setMessage("Parsed call parameters: {}; Transaction: {}") + log.atDebug() + .setMessage("[{}] Parsed call parameters: {}; Transaction: {}") + .addArgument(LOG_SEQUENCE::get) .addArgument(callParameters) .addArgument(transaction::toTraceLog) .log(); @@ -117,14 +129,22 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) { final Wei baseFee = blockchainService .getNextBlockBaseFee() - .orElseThrow(() -> new IllegalStateException("Not on a baseFee market")); + .orElseThrow( + () -> + new PluginRpcEndpointException( + RpcErrorType.INVALID_REQUEST, "Not on a baseFee market")); final Wei estimatedPriorityFee = getEstimatedPriorityFee(transaction, baseFee, minGasPrice, estimatedGasUsed); final var response = new Response(create(estimatedGasUsed), create(baseFee), create(estimatedPriorityFee)); - log.debug("Response for call params {} is {}", callParameters, response); + log.atDebug() + .setMessage("[{}] Response for call params {} is {}") + .addArgument(LOG_SEQUENCE::get) + .addArgument(callParameters) + .addArgument(response) + .log(); return response; } @@ -155,7 +175,8 @@ private Wei getEstimatedPriorityFee( log.atDebug() .setMessage( - "Estimated priority fee {} is lower that the lower bound {}, returning the latter") + "[{}] Estimated priority fee {} is lower that the lower bound {}, returning the latter") + .addArgument(LOG_SEQUENCE::get) .addArgument(profitablePriorityFee::toHumanReadableString) .addArgument(priorityFeeLowerBound::toHumanReadableString) .log(); @@ -174,19 +195,35 @@ private Long estimateGasUsed( return maybeSimulationResults .map( r -> { - - // if the transaction is invalid or doesn't have enough gas with the max it never - // will! - if (r.isInvalid() || !r.isSuccessful()) { + // if the transaction is invalid or doesn't have enough gas with the max it never will + if (r.isInvalid()) { log.atDebug() - .setMessage("Invalid or unsuccessful transaction {}, reason {}") + .setMessage("[{}] Invalid transaction {}, reason {}") + .addArgument(LOG_SEQUENCE::get) .addArgument(transaction::toTraceLog) .addArgument(r.result()) .log(); + throw new PluginRpcEndpointException( + new TransactionSimulationError(r.result().getInvalidReason().orElse(""))); + } + if (!r.isSuccessful()) { + log.atDebug() + .setMessage("[{}] Failed transaction {}, reason {}") + .addArgument(LOG_SEQUENCE::get) + .addArgument(transaction::toTraceLog) + .addArgument(r.result()) + .log(); + r.getRevertReason() + .ifPresent( + rr -> { + throw new PluginRpcEndpointException( + RpcErrorType.REVERT_ERROR, rr.toHexString()); + }); final var invalidReason = r.result().getInvalidReason(); - throw new RuntimeException( - "Invalid or unsuccessful transaction" - + invalidReason.map(ir -> ", reason: " + ir).orElse("")); + throw new PluginRpcEndpointException( + new TransactionSimulationError( + "Failed transaction" + + invalidReason.map(ir -> ", reason: " + ir).orElse(""))); } final var lowGasEstimation = r.result().getEstimateGasUsedByTransaction(); @@ -200,26 +237,29 @@ private Long estimateGasUsed( return lowResult .map( lr -> { - // if with the low estimation gas is successful the return this - // estimation + // if with the low estimation gas is successful then return this estimation if (lr.isSuccessful()) { - log.trace( - "Low gas estimation {} successful, call params {}", - lowGasEstimation, - callParameters); + log.atTrace() + .setMessage("[{}] Low gas estimation {} successful") + .addArgument(LOG_SEQUENCE::get) + .addArgument(lowGasEstimation) + .log(); return lowGasEstimation; } else { - log.trace( - "Low gas estimation {} unsuccessful, result{}, call params {}", - lowGasEstimation, - lr.result(), - callParameters); + log.atTrace() + .setMessage("[{}] Low gas estimation {} unsuccessful, result{}") + .addArgument(LOG_SEQUENCE::get) + .addArgument(lowGasEstimation) + .addArgument(lr::result) + .log(); // else do a binary search to find the right estimation + int iterations = 0; var high = highGasEstimation(lr.getGasEstimate(), tracer); var mid = high; var low = lowGasEstimation; while (low + 1 < high) { + ++iterations; mid = (high + low) / 2; final var binarySearchResult = @@ -232,35 +272,53 @@ private Long estimateGasUsed( if (binarySearchResult.isEmpty() || !binarySearchResult.get().isSuccessful()) { - low = mid; log.atTrace() .setMessage( - "Binary gas estimation search low={},med={},high={}, unsuccessful result {}, call params {}") - .addArgument(lowGasEstimation) + "[{}]-[{}] Binary gas estimation search low={},mid={},high={}, unsuccessful result {}") + .addArgument(LOG_SEQUENCE::get) + .addArgument(iterations) + .addArgument(low) + .addArgument(mid) + .addArgument(high) .addArgument( () -> binarySearchResult .map(result -> result.result().toString()) .orElse("empty")) - .addArgument(callParameters) .log(); - + low = mid; } else { + log.atTrace() + .setMessage( + "[{}]-[{}} Binary gas estimation search low={},mid={},high={}, successful") + .addArgument(LOG_SEQUENCE::get) + .addArgument(iterations) + .addArgument(low) + .addArgument(mid) + .addArgument(high) + .log(); high = mid; - log.trace( - "Binary gas estimation search low={},med={},high={}, successful, call params {}", - low, - mid, - high, - callParameters); } } + log.atDebug() + .setMessage( + "[{}] Binary gas estimation search={} after {} iterations") + .addArgument(LOG_SEQUENCE::get) + .addArgument(high) + .addArgument(iterations) + .log(); return high; } }) - .orElseThrow(); + .orElseThrow( + () -> + new PluginRpcEndpointException( + RpcErrorType.PLUGIN_INTERNAL_ERROR, "Empty result from simulation")); }) - .orElseThrow(); + .orElseThrow( + () -> + new PluginRpcEndpointException( + RpcErrorType.PLUGIN_INTERNAL_ERROR, "Empty result from simulation")); } private JsonCallParameter parseRequest(final Object[] params) { @@ -272,14 +330,16 @@ private JsonCallParameter parseRequest(final Object[] params) { private void validateParameters(final JsonCallParameter callParameters) { if (callParameters.getGasPrice() != null && (callParameters.getMaxFeePerGas().isPresent() - || callParameters.getMaxPriorityFeePerGas().isPresent())) { + || callParameters.getMaxPriorityFeePerGas().isPresent() + || callParameters.getMaxFeePerBlobGas().isPresent())) { throw new InvalidJsonRpcParameters( - "gasPrice cannot be used with maxFeePerGas or maxPriorityFeePerGas"); + "gasPrice cannot be used with maxFeePerGas or maxPriorityFeePerGas or maxFeePerBlobGas"); } if (callParameters.getGasLimit() > 0 && callParameters.getGasLimit() > txValidatorConf.maxTxGasLimit()) { - throw new InvalidJsonRpcParameters("gasLimit above maximum"); + throw new InvalidJsonRpcParameters( + "gasLimit above maximum of: " + txValidatorConf.maxTxGasLimit()); } } @@ -327,4 +387,16 @@ public record Response( @JsonProperty String gasLimit, @JsonProperty String baseFeePerGas, @JsonProperty String priorityFeePerGas) {} + + private record TransactionSimulationError(String errorReason) implements RpcMethodError { + @Override + public int getCode() { + return -32000; + } + + @Override + public String getMessage() { + return errorReason; + } + } } diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/tracegeneration/GenerateConflatedTracesV0.java b/arithmetization/src/main/java/net/consensys/linea/rpc/tracegeneration/GenerateConflatedTracesV0.java index 052851ea..874bed30 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/tracegeneration/GenerateConflatedTracesV0.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/tracegeneration/GenerateConflatedTracesV0.java @@ -22,6 +22,7 @@ import com.google.common.base.Stopwatch; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.zktracer.ZkTracer; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.TraceService; @@ -85,7 +86,7 @@ public TraceFile execute(final PluginRpcRequest request) { log.info("[TRACING] trace for {}-{} serialized to {} in {}", path, toBlock, fromBlock, sw); return new TraceFile(params.runtimeVersion(), path); } catch (Exception ex) { - throw new PluginRpcEndpointException(ex.getMessage()); + throw new PluginRpcEndpointException(RpcErrorType.PLUGIN_INTERNAL_ERROR, ex.getMessage()); } } diff --git a/gradle.properties b/gradle.properties index f5cc1daf..80f6c4e3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ releaseVersion=0.1.4-SNAPSHOT -besuVersion=24.2.0-SNAPSHOT +besuVersion=24.3-develop-5a0618f151 besuArtifactGroup=io.consensys.linea-besu distributionIdentifier=besu-sequencer-plugins distributionBaseUrl=https://artifacts.consensys.net/public/linea-besu/raw/names/linea-besu.tar.gz/versions/ From cbb9d3081e56fc96896c3ee0aea77fa62400e669 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Tue, 26 Mar 2024 19:39:40 +0100 Subject: [PATCH 18/30] Update CHANGELOG for v0.1.4-test24 (#652) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a05bb516..5c040329 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.1.4-test24 +Test pre-release 24 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* Improve linea_estimateGas error response [#650](https://github.com/Consensys/besu-sequencer-plugins/pull/650) +* On Windows also build Linux native lib so it can run on WSL [#651](https://github.com/Consensys/besu-sequencer-plugins/pull/651) + ## 0.1.4-test23 Test pre-release 23 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) * linea_estimateGas compatibility mode multiplier https://github.com/Consensys/besu-sequencer-plugins/pull/646 From 27aad179693133da01099c05b142f0cda43a1073 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 27 Mar 2024 18:36:07 +0100 Subject: [PATCH 19/30] Rename to TransactionPoolValidator to make it clear it only apply to txpool (#654) Signed-off-by: Fabio Di Fabio --- .../AbstractLineaSharedOptionsPlugin.java | 18 ++++++------- ...eaTransactionPoolValidatorCliOptions.java} | 18 ++++++------- ...ransactionPoolValidatorConfiguration.java} | 2 +- .../rpc/linea/LineaEndpointServicePlugin.java | 2 +- .../linea/rpc/linea/LineaEstimateGas.java | 6 ++--- ...LineaTransactionPoolValidatorFactory.java} | 26 +++++++++---------- .../LineaTransactionPoolValidatorPlugin.java} | 19 +++++++------- .../validators/AllowedAddressValidator.java | 2 +- .../validators/CalldataValidator.java | 10 +++---- .../validators/GasLimitValidator.java | 10 +++---- .../validators/ProfitabilityValidator.java | 2 +- .../AllowedAddressValidatorTest.java | 2 +- .../validators/CalldataValidatorTest.java | 6 ++--- .../validators/GasLimitValidatorTest.java | 6 ++--- .../ProfitabilityValidatorTest.java | 2 +- 15 files changed, 66 insertions(+), 65 deletions(-) rename arithmetization/src/main/java/net/consensys/linea/config/{LineaTransactionValidatorCliOptions.java => LineaTransactionPoolValidatorCliOptions.java} (84%) rename arithmetization/src/main/java/net/consensys/linea/config/{LineaTransactionValidatorConfiguration.java => LineaTransactionPoolValidatorConfiguration.java} (94%) rename arithmetization/src/main/java/net/consensys/linea/sequencer/{txvalidation/LineaTransactionValidatorFactory.java => txpoolvalidation/LineaTransactionPoolValidatorFactory.java} (71%) rename arithmetization/src/main/java/net/consensys/linea/sequencer/{txvalidation/LineaTransactionValidatorPlugin.java => txpoolvalidation/LineaTransactionPoolValidatorPlugin.java} (82%) rename arithmetization/src/main/java/net/consensys/linea/sequencer/{txvalidation => txpoolvalidation}/validators/AllowedAddressValidator.java (98%) rename arithmetization/src/main/java/net/consensys/linea/sequencer/{txvalidation => txpoolvalidation}/validators/CalldataValidator.java (78%) rename arithmetization/src/main/java/net/consensys/linea/sequencer/{txvalidation => txpoolvalidation}/validators/GasLimitValidator.java (79%) rename arithmetization/src/main/java/net/consensys/linea/sequencer/{txvalidation => txpoolvalidation}/validators/ProfitabilityValidator.java (97%) rename arithmetization/src/test/java/net/consensys/linea/sequencer/{txvalidation => txpoolvalidation}/validators/AllowedAddressValidatorTest.java (98%) rename arithmetization/src/test/java/net/consensys/linea/sequencer/{txvalidation => txpoolvalidation}/validators/CalldataValidatorTest.java (91%) rename arithmetization/src/test/java/net/consensys/linea/sequencer/{txvalidation => txpoolvalidation}/validators/GasLimitValidatorTest.java (91%) rename arithmetization/src/test/java/net/consensys/linea/sequencer/{txvalidation => txpoolvalidation}/validators/ProfitabilityValidatorTest.java (99%) diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java index 16015d76..afdcacdc 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -23,10 +23,10 @@ import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcCliOptions; import net.consensys.linea.config.LineaRpcConfiguration; +import net.consensys.linea.config.LineaTransactionPoolValidatorCliOptions; +import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; -import net.consensys.linea.config.LineaTransactionValidatorCliOptions; -import net.consensys.linea.config.LineaTransactionValidatorConfiguration; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.services.PicoCLIOptions; @@ -37,12 +37,12 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { private static boolean cliOptionsRegistered = false; private static boolean configured = false; private static LineaTransactionSelectorCliOptions transactionSelectorCliOptions; - private static LineaTransactionValidatorCliOptions transactionValidatorCliOptions; + private static LineaTransactionPoolValidatorCliOptions transactionPoolValidatorCliOptions; private static LineaL1L2BridgeCliOptions l1L2BridgeCliOptions; private static LineaRpcCliOptions rpcCliOptions; private static LineaProfitabilityCliOptions profitabilityCliOptions; protected static LineaTransactionSelectorConfiguration transactionSelectorConfiguration; - protected static LineaTransactionValidatorConfiguration transactionValidatorConfiguration; + protected static LineaTransactionPoolValidatorConfiguration transactionPoolValidatorConfiguration; protected static LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; protected static LineaRpcConfiguration rpcConfiguration; protected static LineaProfitabilityConfiguration profitabilityConfiguration; @@ -63,13 +63,13 @@ public synchronized void register(final BesuContext context) { new IllegalStateException( "Failed to obtain PicoCLI options from the BesuContext")); transactionSelectorCliOptions = LineaTransactionSelectorCliOptions.create(); - transactionValidatorCliOptions = LineaTransactionValidatorCliOptions.create(); + transactionPoolValidatorCliOptions = LineaTransactionPoolValidatorCliOptions.create(); l1L2BridgeCliOptions = LineaL1L2BridgeCliOptions.create(); rpcCliOptions = LineaRpcCliOptions.create(); profitabilityCliOptions = LineaProfitabilityCliOptions.create(); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionSelectorCliOptions); - cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionValidatorCliOptions); + cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionPoolValidatorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, l1L2BridgeCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, rpcCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, profitabilityCliOptions); @@ -81,7 +81,7 @@ public synchronized void register(final BesuContext context) { public void beforeExternalServices() { if (!configured) { transactionSelectorConfiguration = transactionSelectorCliOptions.toDomainObject(); - transactionValidatorConfiguration = transactionValidatorCliOptions.toDomainObject(); + transactionPoolValidatorConfiguration = transactionPoolValidatorCliOptions.toDomainObject(); l1L2BridgeConfiguration = l1L2BridgeCliOptions.toDomainObject(); rpcConfiguration = rpcCliOptions.toDomainObject(); profitabilityConfiguration = profitabilityCliOptions.toDomainObject(); @@ -94,9 +94,9 @@ public void beforeExternalServices() { transactionSelectorCliOptions); log.debug( - "Configured plugin {} with transaction validator configuration: {}", + "Configured plugin {} with transaction pool validator configuration: {}", getName(), - transactionValidatorCliOptions); + transactionPoolValidatorCliOptions); log.debug( "Configured plugin {} with L1 L2 bridge configuration: {}", diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java similarity index 84% rename from arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java rename to arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java index a97b2414..4cb68f78 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java @@ -19,7 +19,7 @@ import picocli.CommandLine; /** The Linea CLI options. */ -public class LineaTransactionValidatorCliOptions { +public class LineaTransactionPoolValidatorCliOptions { public static final String DENY_LIST_PATH = "--plugin-linea-deny-list-path"; public static final String DEFAULT_DENY_LIST_PATH = "lineaDenyList.txt"; @@ -58,15 +58,15 @@ public class LineaTransactionValidatorCliOptions { + ")") private int maxTxCallDataSize = DEFAULT_MAX_TX_CALLDATA_SIZE; - private LineaTransactionValidatorCliOptions() {} + private LineaTransactionPoolValidatorCliOptions() {} /** * Create Linea cli options. * * @return the Linea cli options */ - public static LineaTransactionValidatorCliOptions create() { - return new LineaTransactionValidatorCliOptions(); + public static LineaTransactionPoolValidatorCliOptions create() { + return new LineaTransactionPoolValidatorCliOptions(); } /** @@ -75,9 +75,9 @@ public static LineaTransactionValidatorCliOptions create() { * @param config the config * @return the cli options */ - public static LineaTransactionValidatorCliOptions fromConfig( - final LineaTransactionValidatorConfiguration config) { - final LineaTransactionValidatorCliOptions options = create(); + public static LineaTransactionPoolValidatorCliOptions fromConfig( + final LineaTransactionPoolValidatorConfiguration config) { + final LineaTransactionPoolValidatorCliOptions options = create(); options.denyListPath = config.denyListPath(); options.maxTxGasLimit = config.maxTxGasLimit(); options.maxTxCallDataSize = config.maxTxCalldataSize(); @@ -90,8 +90,8 @@ public static LineaTransactionValidatorCliOptions fromConfig( * * @return the Linea factory configuration */ - public LineaTransactionValidatorConfiguration toDomainObject() { - return new LineaTransactionValidatorConfiguration( + public LineaTransactionPoolValidatorConfiguration toDomainObject() { + return new LineaTransactionPoolValidatorConfiguration( denyListPath, maxTxGasLimit, maxTxCallDataSize); } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java similarity index 94% rename from arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java rename to arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java index 61f1ac3c..90d41edf 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionValidatorConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java @@ -25,5 +25,5 @@ * @param maxTxCalldataSize the maximum size of calldata allowed for transactions */ @Builder(toBuilder = true) -public record LineaTransactionValidatorConfiguration( +public record LineaTransactionPoolValidatorConfiguration( String denyListPath, int maxTxGasLimit, int maxTxCalldataSize) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java index 23e7c665..0dd3e8d4 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java @@ -87,6 +87,6 @@ public void doRegister(final BesuContext context) { public void beforeExternalServices() { super.beforeExternalServices(); lineaEstimateGasMethod.init( - rpcConfiguration, transactionValidatorConfiguration, profitabilityConfiguration); + rpcConfiguration, transactionPoolValidatorConfiguration, profitabilityConfiguration); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java index bd5578ff..e5c91a99 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -28,7 +28,7 @@ import net.consensys.linea.bl.TransactionProfitabilityCalculator; import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcConfiguration; -import net.consensys.linea.config.LineaTransactionValidatorConfiguration; +import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import org.apache.tuweni.bytes.Bytes; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; @@ -74,7 +74,7 @@ public class LineaEstimateGas { private final TransactionSimulationService transactionSimulationService; private final BlockchainService blockchainService; private LineaRpcConfiguration rpcConfiguration; - private LineaTransactionValidatorConfiguration txValidatorConf; + private LineaTransactionPoolValidatorConfiguration txValidatorConf; private LineaProfitabilityConfiguration profitabilityConf; private TransactionProfitabilityCalculator txProfitabilityCalculator; @@ -89,7 +89,7 @@ public LineaEstimateGas( public void init( LineaRpcConfiguration rpcConfiguration, - final LineaTransactionValidatorConfiguration transactionValidatorConfiguration, + final LineaTransactionPoolValidatorConfiguration transactionValidatorConfiguration, final LineaProfitabilityConfiguration profitabilityConf) { this.rpcConfiguration = rpcConfiguration; this.txValidatorConf = transactionValidatorConfiguration; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java similarity index 71% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java rename to arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java index d27bca7e..6fabee4e 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java @@ -13,18 +13,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation; +package net.consensys.linea.sequencer.txpoolvalidation; import java.util.Arrays; import java.util.Optional; import java.util.Set; import net.consensys.linea.config.LineaProfitabilityConfiguration; -import net.consensys.linea.config.LineaTransactionValidatorConfiguration; -import net.consensys.linea.sequencer.txvalidation.validators.AllowedAddressValidator; -import net.consensys.linea.sequencer.txvalidation.validators.CalldataValidator; -import net.consensys.linea.sequencer.txvalidation.validators.GasLimitValidator; -import net.consensys.linea.sequencer.txvalidation.validators.ProfitabilityValidator; +import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; +import net.consensys.linea.sequencer.txpoolvalidation.validators.AllowedAddressValidator; +import net.consensys.linea.sequencer.txpoolvalidation.validators.CalldataValidator; +import net.consensys.linea.sequencer.txpoolvalidation.validators.GasLimitValidator; +import net.consensys.linea.sequencer.txpoolvalidation.validators.ProfitabilityValidator; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; @@ -32,23 +32,23 @@ import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidatorFactory; /** Represents a factory for creating transaction validators. */ -public class LineaTransactionValidatorFactory implements PluginTransactionPoolValidatorFactory { +public class LineaTransactionPoolValidatorFactory implements PluginTransactionPoolValidatorFactory { private final BesuConfiguration besuConfiguration; private final BlockchainService blockchainService; - private final LineaTransactionValidatorConfiguration txValidatorConf; + private final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf; private final LineaProfitabilityConfiguration profitabilityConf; private final Set
denied; - public LineaTransactionValidatorFactory( + public LineaTransactionPoolValidatorFactory( final BesuConfiguration besuConfiguration, final BlockchainService blockchainService, - final LineaTransactionValidatorConfiguration txValidatorConf, + final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf, final LineaProfitabilityConfiguration profitabilityConf, final Set
denied) { this.besuConfiguration = besuConfiguration; this.blockchainService = blockchainService; - this.txValidatorConf = txValidatorConf; + this.txPoolValidatorConf = txPoolValidatorConf; this.profitabilityConf = profitabilityConf; this.denied = denied; } @@ -58,8 +58,8 @@ public PluginTransactionPoolValidator createTransactionValidator() { final var validators = new PluginTransactionPoolValidator[] { new AllowedAddressValidator(denied), - new GasLimitValidator(txValidatorConf), - new CalldataValidator(txValidatorConf), + new GasLimitValidator(txPoolValidatorConf), + new CalldataValidator(txPoolValidatorConf), new ProfitabilityValidator(besuConfiguration, blockchainService, profitabilityConf) }; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java similarity index 82% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java rename to arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java index 900bb2bc..52eb2de9 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/LineaTransactionValidatorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java @@ -13,7 +13,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation; +package net.consensys.linea.sequencer.txpoolvalidation; import java.io.File; import java.nio.file.Files; @@ -41,11 +41,11 @@ */ @Slf4j @AutoService(BesuPlugin.class) -public class LineaTransactionValidatorPlugin extends AbstractLineaRequiredPlugin { +public class LineaTransactionPoolValidatorPlugin extends AbstractLineaRequiredPlugin { public static final String NAME = "linea"; private BesuConfiguration besuConfiguration; private BlockchainService blockchainService; - private TransactionPoolValidatorService transactionValidatorService; + private TransactionPoolValidatorService transactionPoolValidatorService; @Override public Optional getName() { @@ -70,28 +70,29 @@ public void doRegister(final BesuContext context) { new RuntimeException( "Failed to obtain BlockchainService from the BesuContext.")); - transactionValidatorService = + transactionPoolValidatorService = context .getService(TransactionPoolValidatorService.class) .orElseThrow( () -> new RuntimeException( - "Failed to obtain TransactionValidationService from the BesuContext.")); + "Failed to obtain TransactionPoolValidationService from the BesuContext.")); } @Override public void beforeExternalServices() { super.beforeExternalServices(); try (Stream lines = - Files.lines(Path.of(new File(transactionValidatorConfiguration.denyListPath()).toURI()))) { + Files.lines( + Path.of(new File(transactionPoolValidatorConfiguration.denyListPath()).toURI()))) { final Set
denied = lines.map(l -> Address.fromHexString(l.trim())).collect(Collectors.toUnmodifiableSet()); - transactionValidatorService.registerPluginTransactionValidatorFactory( - new LineaTransactionValidatorFactory( + transactionPoolValidatorService.registerPluginTransactionValidatorFactory( + new LineaTransactionPoolValidatorFactory( besuConfiguration, blockchainService, - transactionValidatorConfiguration, + transactionPoolValidatorConfiguration, profitabilityConfiguration, denied)); diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/AllowedAddressValidator.java similarity index 98% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidator.java rename to arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/AllowedAddressValidator.java index d15ad8e5..2ec608a5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/AllowedAddressValidator.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation.validators; +package net.consensys.linea.sequencer.txpoolvalidation.validators; import java.util.Optional; import java.util.Set; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/CalldataValidator.java similarity index 78% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidator.java rename to arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/CalldataValidator.java index dc7c736b..ec61c0ea 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/CalldataValidator.java @@ -12,28 +12,28 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation.validators; +package net.consensys.linea.sequencer.txpoolvalidation.validators; import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.consensys.linea.config.LineaTransactionValidatorConfiguration; +import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; @Slf4j @RequiredArgsConstructor public class CalldataValidator implements PluginTransactionPoolValidator { - final LineaTransactionValidatorConfiguration txValidatorConf; + final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf; @Override public Optional validateTransaction( final Transaction transaction, final boolean isLocal, final boolean hasPriority) { - if (transaction.getPayload().size() > txValidatorConf.maxTxCalldataSize()) { + if (transaction.getPayload().size() > txPoolValidatorConf.maxTxCalldataSize()) { final String errMsg = "Calldata of transaction is greater than the allowed max of " - + txValidatorConf.maxTxCalldataSize(); + + txPoolValidatorConf.maxTxCalldataSize(); log.debug(errMsg); return Optional.of(errMsg); } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/GasLimitValidator.java similarity index 79% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidator.java rename to arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/GasLimitValidator.java index 2c4cbea3..4ab3212d 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/GasLimitValidator.java @@ -12,28 +12,28 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation.validators; +package net.consensys.linea.sequencer.txpoolvalidation.validators; import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.consensys.linea.config.LineaTransactionValidatorConfiguration; +import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; @Slf4j @RequiredArgsConstructor public class GasLimitValidator implements PluginTransactionPoolValidator { - final LineaTransactionValidatorConfiguration txValidatorConf; + final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf; @Override public Optional validateTransaction( final Transaction transaction, final boolean isLocal, final boolean hasPriority) { - if (transaction.getGasLimit() > txValidatorConf.maxTxGasLimit()) { + if (transaction.getGasLimit() > txPoolValidatorConf.maxTxGasLimit()) { final String errMsg = "Gas limit of transaction is greater than the allowed max of " - + txValidatorConf.maxTxGasLimit(); + + txPoolValidatorConf.maxTxGasLimit(); log.debug(errMsg); return Optional.of(errMsg); } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidator.java similarity index 97% rename from arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidator.java rename to arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidator.java index 78c6a7fa..97d25fd5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidator.java @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation.validators; +package net.consensys.linea.sequencer.txpoolvalidation.validators; import java.util.Optional; diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/AllowedAddressValidatorTest.java similarity index 98% rename from arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidatorTest.java rename to arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/AllowedAddressValidatorTest.java index cd09fe01..c2f25f68 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/AllowedAddressValidatorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/AllowedAddressValidatorTest.java @@ -13,7 +13,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation.validators; +package net.consensys.linea.sequencer.txpoolvalidation.validators; import java.util.Optional; import java.util.Set; diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/CalldataValidatorTest.java similarity index 91% rename from arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidatorTest.java rename to arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/CalldataValidatorTest.java index 7f0e211d..fc775580 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/CalldataValidatorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/CalldataValidatorTest.java @@ -13,13 +13,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation.validators; +package net.consensys.linea.sequencer.txpoolvalidation.validators; import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.consensys.linea.config.LineaTransactionValidatorCliOptions; +import net.consensys.linea.config.LineaTransactionPoolValidatorCliOptions; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Wei; import org.junit.jupiter.api.Assertions; @@ -36,7 +36,7 @@ public class CalldataValidatorTest { public void initialize() { calldataValidator = new CalldataValidator( - LineaTransactionValidatorCliOptions.create().toDomainObject().toBuilder() + LineaTransactionPoolValidatorCliOptions.create().toDomainObject().toBuilder() .maxTxCalldataSize(MAX_TX_CALLDATA_SIZE) .build()); } diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/GasLimitValidatorTest.java similarity index 91% rename from arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidatorTest.java rename to arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/GasLimitValidatorTest.java index 9ea005b2..219e62f4 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/GasLimitValidatorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/GasLimitValidatorTest.java @@ -13,13 +13,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation.validators; +package net.consensys.linea.sequencer.txpoolvalidation.validators; import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.consensys.linea.config.LineaTransactionValidatorCliOptions; +import net.consensys.linea.config.LineaTransactionPoolValidatorCliOptions; import org.apache.tuweni.bytes.Bytes; import org.hyperledger.besu.datatypes.Wei; import org.junit.jupiter.api.Assertions; @@ -36,7 +36,7 @@ public class GasLimitValidatorTest { public void initialize() { gasLimitValidator = new GasLimitValidator( - LineaTransactionValidatorCliOptions.create().toDomainObject().toBuilder() + LineaTransactionPoolValidatorCliOptions.create().toDomainObject().toBuilder() .maxTxGasLimit(MAX_TX_GAS_LIMIT) .build()); } diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java similarity index 99% rename from arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidatorTest.java rename to arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java index 11c20292..34a18b6e 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txvalidation/validators/ProfitabilityValidatorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java @@ -13,7 +13,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package net.consensys.linea.sequencer.txvalidation.validators; +package net.consensys.linea.sequencer.txpoolvalidation.validators; import static org.assertj.core.api.Assertions.assertThat; From 9dbea1025eb59a9735f06ea7156791f6d8c941e2 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Wed, 10 Apr 2024 13:33:22 -0300 Subject: [PATCH 20/30] feature: disable arithmetization tests (#4) Signed-off-by: Gabriel-Trintinalia --- .github/workflows/gradle.yml | 25 ------------------------- gradle/tests.gradle | 3 ++- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 39941026..02d20dc1 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -102,15 +102,8 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: webfactory/ssh-agent@v0.7.0 - with: - ssh-private-key: | - ${{ secrets.CONSTRAINTS_SSH_KEY }} - - name: Checkout repository uses: actions/checkout@v3 - with: - submodules: recursive - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -118,24 +111,6 @@ jobs: java-version: '17' distribution: 'adopt' - # The asset URL for the latest release can be found with: - # curl -L -H "Accept: application/vnd.github+json" \ - # -H "Authorization: Bearer YOUR_GH_API_TOKEN" \ - # -H "X-GitHub-Api-Version: 2022-11-28" \ - # https://api.github.com/repos/ConsenSys/corset/releases/latest - # | jq '.assets[] | select(.name|endswith("x86_64-unknown-linux-musl.tar.gz")) | .url' - - name: Install Corset - run: | - curl -L \ - -H "Accept: application/octet-stream" \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - -o corset.tar.gz \ - https://api.github.com/repos/Consensys/corset/releases/assets/147491099 - tar xzf corset.tar.gz - mv corset $HOME - echo $HOME >> $GITHUB_PATH - - name: Run unit tests run: ./gradlew :arithmetization:test env: diff --git a/gradle/tests.gradle b/gradle/tests.gradle index 98a5a596..0e973c9c 100644 --- a/gradle/tests.gradle +++ b/gradle/tests.gradle @@ -63,7 +63,7 @@ test { useJUnitPlatform() } -tasks.test.dependsOn(buildZkevmBin) +//tasks.test.dependsOn(buildZkevmBin) tasks.register('unitTests', Test) { description = 'Runs unit tests.' @@ -79,4 +79,5 @@ tasks.register('unitTests', Test) { test { exclude('**/**ReferenceTest*') + exclude '**/net/consensys/linea/zktracer/**' } From 0be75a336cb933cb1cc5dbafa661061c7a30dfcb Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Wed, 10 Apr 2024 13:49:43 -0300 Subject: [PATCH 21/30] Extend Module Line Count Verification to `linea_estimateGas` RPC Method (#1) Signed-off-by: Gabriel-Trintinalia --- .../EstimateGasModuleLimitOverflowTest.java | 63 +++++++ .../AbstractLineaSharedOptionsPlugin.java | 9 + .../config/LineaTracerConfiguration.java | 22 +++ .../LineaTracerConfigurationCLiOptions.java | 72 ++++++++ .../LineaTransactionSelectorCliOptions.java | 15 -- ...LineaTransactionSelectorConfiguration.java | 1 - .../rpc/linea/LineaEndpointServicePlugin.java | 8 +- .../linea/rpc/linea/LineaEstimateGas.java | 74 ++++++++- .../linea/sequencer/TracerAggregator.java | 157 ++++++++++++++++++ .../ModuleLimitsValidationResult.java | 88 ++++++++++ .../modulelimit/ModuleLineCountValidator.java | 135 +++++++++++++++ .../LineaTransactionSelectorFactory.java | 11 +- .../LineaTransactionSelectorPlugin.java | 51 +----- .../selectors/LineaTransactionSelector.java | 8 +- .../TraceLineLimitTransactionSelector.java | 62 +++---- .../zktracer/module/rlp/txn/RlpTxnChunk.java | 2 +- ...TraceLineLimitTransactionSelectorTest.java | 71 +++++--- 17 files changed, 721 insertions(+), 128 deletions(-) create mode 100644 acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java new file mode 100644 index 00000000..c787e112 --- /dev/null +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.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 linea.plugin.acc.test.rpc.linea; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import linea.plugin.acc.test.LineaPluginTestBase; +import linea.plugin.acc.test.TestCommandLineOptionsBuilder; +import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage; +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.tests.acceptance.dsl.account.Account; +import org.junit.jupiter.api.Test; + +public class EstimateGasModuleLimitOverflowTest extends LineaPluginTestBase { + @Override + public List getTestCliOptions() { + return new TestCommandLineOptionsBuilder() + .set( + "--plugin-linea-module-limit-file-path=", + getResourcePath("/txOverflowModuleLimits.toml")) + .build(); + } + + @Test + public void estimateGasFailsForExceedingModuleLineCountTest() throws Exception { + + final Account sender = accounts.getSecondaryBenefactor(); + + final SimpleStorage simpleStorage = deploySimpleStorage(); + final String txData = simpleStorage.add(BigInteger.valueOf(100)).encodeFunctionCall(); + final var payload = Bytes.wrap(txData.getBytes(StandardCharsets.UTF_8)); + + final EstimateGasTest.CallParams callParams = + new EstimateGasTest.CallParams( + sender.getAddress(), + simpleStorage.getContractAddress(), + null, + payload.toHexString(), + "0"); + + final var reqLinea = new EstimateGasTest.BadLineaEstimateGasRequest(callParams); + final var respLinea = reqLinea.execute(minerNode.nodeRequests()); + assertThat(respLinea.getCode()).isEqualTo(-32000); + assertThat(respLinea.getMessage()) + .isEqualTo("Transaction line count for module HUB=66 is above the limit 30"); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java index afdcacdc..5046c8c9 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -23,6 +23,8 @@ import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcCliOptions; import net.consensys.linea.config.LineaRpcConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; +import net.consensys.linea.config.LineaTracerConfigurationCLiOptions; import net.consensys.linea.config.LineaTransactionPoolValidatorCliOptions; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; @@ -42,10 +44,12 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { private static LineaRpcCliOptions rpcCliOptions; private static LineaProfitabilityCliOptions profitabilityCliOptions; protected static LineaTransactionSelectorConfiguration transactionSelectorConfiguration; + protected static LineaTracerConfigurationCLiOptions tracerConfigurationCliOptions; protected static LineaTransactionPoolValidatorConfiguration transactionPoolValidatorConfiguration; protected static LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; protected static LineaRpcConfiguration rpcConfiguration; protected static LineaProfitabilityConfiguration profitabilityConfiguration; + protected static LineaTracerConfiguration tracerConfiguration; static { // force the initialization of the gnark compress native library to fail fast in case of issues @@ -67,12 +71,14 @@ public synchronized void register(final BesuContext context) { l1L2BridgeCliOptions = LineaL1L2BridgeCliOptions.create(); rpcCliOptions = LineaRpcCliOptions.create(); profitabilityCliOptions = LineaProfitabilityCliOptions.create(); + tracerConfigurationCliOptions = LineaTracerConfigurationCLiOptions.create(); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionSelectorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionPoolValidatorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, l1L2BridgeCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, rpcCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, profitabilityCliOptions); + cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, tracerConfigurationCliOptions); cliOptionsRegistered = true; } } @@ -85,6 +91,7 @@ public void beforeExternalServices() { l1L2BridgeConfiguration = l1L2BridgeCliOptions.toDomainObject(); rpcConfiguration = rpcCliOptions.toDomainObject(); profitabilityConfiguration = profitabilityCliOptions.toDomainObject(); + tracerConfiguration = tracerConfigurationCliOptions.toDomainObject(); configured = true; } @@ -109,6 +116,8 @@ public void beforeExternalServices() { "Configured plugin {} with profitability calculator configuration: {}", getName(), profitabilityConfiguration); + + log.debug("Configured plugin {} with tracer configuration: {}", getName(), tracerConfiguration); } @Override diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java new file mode 100644 index 00000000..a9b69033 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfiguration.java @@ -0,0 +1,22 @@ +/* + * 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.config; + +import lombok.Builder; + +/** The Linea tracer configuration. */ +@Builder(toBuilder = true) +public record LineaTracerConfiguration(String moduleLimitsFilePath) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java new file mode 100644 index 00000000..5b6f6c04 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java @@ -0,0 +1,72 @@ +/* + * 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.config; + +import com.google.common.base.MoreObjects; +import picocli.CommandLine; + +public class LineaTracerConfigurationCLiOptions { + + public static final String MODULE_LIMIT_FILE_PATH = "--plugin-linea-module-limit-file-path"; + public static final String DEFAULT_MODULE_LIMIT_FILE_PATH = "moduleLimitFile.toml"; + + @CommandLine.Option( + names = {MODULE_LIMIT_FILE_PATH}, + hidden = true, + paramLabel = "", + description = + "Path to the toml file containing the module limits (default: ${DEFAULT-VALUE})") + private String moduleLimitFilePath = DEFAULT_MODULE_LIMIT_FILE_PATH; + + private LineaTracerConfigurationCLiOptions() {} + + /** + * Create Linea cli options. + * + * @return the Linea cli options + */ + public static LineaTracerConfigurationCLiOptions create() { + return new LineaTracerConfigurationCLiOptions(); + } + + /** + * Linea cli options from config. + * + * @param config the config + * @return the Linea cli options + */ + public static LineaTracerConfigurationCLiOptions fromConfig( + final LineaTracerConfiguration config) { + final LineaTracerConfigurationCLiOptions options = create(); + options.moduleLimitFilePath = config.moduleLimitsFilePath(); + return options; + } + + /** + * To domain object Linea factory configuration. + * + * @return the Linea factory configuration + */ + public LineaTracerConfiguration toDomainObject() { + return LineaTracerConfiguration.builder().moduleLimitsFilePath(moduleLimitFilePath).build(); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add(MODULE_LIMIT_FILE_PATH, moduleLimitFilePath) + .toString(); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java index 9f8f0ba3..acfe4ad1 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorCliOptions.java @@ -23,10 +23,6 @@ public class LineaTransactionSelectorCliOptions { public static final String MAX_BLOCK_CALLDATA_SIZE = "--plugin-linea-max-block-calldata-size"; public static final int DEFAULT_MAX_BLOCK_CALLDATA_SIZE = 70_000; - - public static final String MODULE_LIMIT_FILE_PATH = "--plugin-linea-module-limit-file-path"; - public static final String DEFAULT_MODULE_LIMIT_FILE_PATH = "moduleLimitFile.toml"; - public static final String OVER_LINE_COUNT_LIMIT_CACHE_SIZE = "--plugin-linea-over-line-count-limit-cache-size"; public static final int DEFAULT_OVER_LINE_COUNT_LIMIT_CACHE_SIZE = 10_000; @@ -48,14 +44,6 @@ public class LineaTransactionSelectorCliOptions { description = "Maximum size for the calldata of a block (default: ${DEFAULT-VALUE})") private int maxBlockCallDataSize = DEFAULT_MAX_BLOCK_CALLDATA_SIZE; - @CommandLine.Option( - names = {MODULE_LIMIT_FILE_PATH}, - hidden = true, - paramLabel = "", - description = - "Path to the toml file containing the module limits (default: ${DEFAULT-VALUE})") - private String moduleLimitFilePath = DEFAULT_MODULE_LIMIT_FILE_PATH; - @Positive @CommandLine.Option( names = {OVER_LINE_COUNT_LIMIT_CACHE_SIZE}, @@ -112,7 +100,6 @@ public static LineaTransactionSelectorCliOptions fromConfig( final LineaTransactionSelectorConfiguration config) { final LineaTransactionSelectorCliOptions options = create(); options.maxBlockCallDataSize = config.maxBlockCallDataSize(); - options.moduleLimitFilePath = config.moduleLimitsFilePath(); options.overLineCountLimitCacheSize = config.overLinesLimitCacheSize(); options.maxGasPerBlock = config.maxGasPerBlock(); options.unprofitableCacheSize = config.unprofitableCacheSize(); @@ -128,7 +115,6 @@ public static LineaTransactionSelectorCliOptions fromConfig( public LineaTransactionSelectorConfiguration toDomainObject() { return LineaTransactionSelectorConfiguration.builder() .maxBlockCallDataSize(maxBlockCallDataSize) - .moduleLimitsFilePath(moduleLimitFilePath) .overLinesLimitCacheSize(overLineCountLimitCacheSize) .maxGasPerBlock(maxGasPerBlock) .unprofitableCacheSize(unprofitableCacheSize) @@ -140,7 +126,6 @@ public LineaTransactionSelectorConfiguration toDomainObject() { public String toString() { return MoreObjects.toStringHelper(this) .add(MAX_BLOCK_CALLDATA_SIZE, maxBlockCallDataSize) - .add(MODULE_LIMIT_FILE_PATH, moduleLimitFilePath) .add(OVER_LINE_COUNT_LIMIT_CACHE_SIZE, overLineCountLimitCacheSize) .add(MAX_GAS_PER_BLOCK, maxGasPerBlock) .add(UNPROFITABLE_CACHE_SIZE, unprofitableCacheSize) diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java index 09afc4f2..57b56114 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionSelectorConfiguration.java @@ -21,7 +21,6 @@ @Builder(toBuilder = true) public record LineaTransactionSelectorConfiguration( int maxBlockCallDataSize, - String moduleLimitsFilePath, int overLinesLimitCacheSize, long maxGasPerBlock, int unprofitableCacheSize, diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java index 0dd3e8d4..0aab37ab 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEndpointServicePlugin.java @@ -15,6 +15,8 @@ package net.consensys.linea.rpc.linea; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.createLimitModules; + import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.AbstractLineaRequiredPlugin; @@ -87,6 +89,10 @@ public void doRegister(final BesuContext context) { public void beforeExternalServices() { super.beforeExternalServices(); lineaEstimateGasMethod.init( - rpcConfiguration, transactionPoolValidatorConfiguration, profitabilityConfiguration); + rpcConfiguration, + transactionPoolValidatorConfiguration, + profitabilityConfiguration, + createLimitModules(tracerConfiguration), + l1L2BridgeConfiguration); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java index e5c91a99..4e9b4707 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -15,20 +15,28 @@ package net.consensys.linea.rpc.linea; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity.create; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.bl.TransactionProfitabilityCalculator; +import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcConfiguration; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; +import net.consensys.linea.sequencer.TracerAggregator; +import net.consensys.linea.sequencer.modulelimit.ModuleLimitsValidationResult; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; +import net.consensys.linea.zktracer.ZkTracer; import org.apache.tuweni.bytes.Bytes; import org.bouncycastle.asn1.sec.SECNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; @@ -77,6 +85,9 @@ public class LineaEstimateGas { private LineaTransactionPoolValidatorConfiguration txValidatorConf; private LineaProfitabilityConfiguration profitabilityConf; private TransactionProfitabilityCalculator txProfitabilityCalculator; + private LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; + + private ModuleLineCountValidator moduleLineCountValidator; public LineaEstimateGas( final BesuConfiguration besuConfiguration, @@ -90,11 +101,20 @@ public LineaEstimateGas( public void init( LineaRpcConfiguration rpcConfiguration, final LineaTransactionPoolValidatorConfiguration transactionValidatorConfiguration, - final LineaProfitabilityConfiguration profitabilityConf) { + final LineaProfitabilityConfiguration profitabilityConf, + final Map limitsMap, + final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) { this.rpcConfiguration = rpcConfiguration; this.txValidatorConf = transactionValidatorConfiguration; this.profitabilityConf = profitabilityConf; this.txProfitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); + this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; + this.moduleLineCountValidator = new ModuleLineCountValidator(limitsMap); + + if (l1L2BridgeConfiguration.isEmpty()) { + log.error("L1L2 bridge settings have not been defined."); + System.exit(1); + } } public String getNamespace() { @@ -187,10 +207,22 @@ private Long estimateGasUsed( final JsonCallParameter callParameters, final Transaction transaction, final Wei minGasPrice) { - final var tracer = new EstimateGasOperationTracer(); + + final var estimateGasOperationTracer = new EstimateGasOperationTracer(); + final var zkTracer = createZkTracer(); + TracerAggregator tracerAggregator = + TracerAggregator.create(estimateGasOperationTracer, zkTracer); + final var chainHeadHash = blockchainService.getChainHeadHash(); final var maybeSimulationResults = - transactionSimulationService.simulate(transaction, chainHeadHash, tracer, true); + transactionSimulationService.simulate(transaction, chainHeadHash, tracerAggregator, true); + + ModuleLimitsValidationResult moduleLimit = + moduleLineCountValidator.validate(zkTracer.getModulesLineCount()); + + if (moduleLimit.getResult() != ModuleLineCountValidator.ModuleLineCountResult.VALID) { + handleModuleOverLimit(moduleLimit); + } return maybeSimulationResults .map( @@ -231,7 +263,7 @@ private Long estimateGasUsed( transactionSimulationService.simulate( createTransactionForSimulation(callParameters, lowGasEstimation, minGasPrice), chainHeadHash, - tracer, + tracerAggregator, true); return lowResult @@ -255,7 +287,8 @@ private Long estimateGasUsed( // else do a binary search to find the right estimation int iterations = 0; - var high = highGasEstimation(lr.getGasEstimate(), tracer); + var high = + highGasEstimation(lr.getGasEstimate(), estimateGasOperationTracer); var mid = high; var low = lowGasEstimation; while (low + 1 < high) { @@ -267,7 +300,7 @@ private Long estimateGasUsed( createTransactionForSimulation( callParameters, mid, minGasPrice), chainHeadHash, - tracer, + tracerAggregator, true); if (binarySearchResult.isEmpty() @@ -353,6 +386,7 @@ private void validateParameters(final JsonCallParameter callParameters) { */ private long highGasEstimation( final long gasEstimation, final EstimateGasOperationTracer operationTracer) { + // no more than 63/64s of the remaining gas can be passed to the sub calls final double subCallMultiplier = Math.pow(SUB_CALL_REMAINING_GAS_RATIO, operationTracer.getMaxDepth()); @@ -383,6 +417,34 @@ private Transaction createTransactionForSimulation( return txBuilder.build(); } + private ZkTracer createZkTracer() { + var zkTracer = new ZkTracer(l1L2BridgeConfiguration); + zkTracer.traceStartConflation(1L); + zkTracer.traceStartBlock(blockchainService.getChainHeadHeader()); + return zkTracer; + } + + private void handleModuleOverLimit(ModuleLimitsValidationResult moduleLimitResult) { + // Throw specific exceptions based on the type of limit exceeded + if (moduleLimitResult.getResult() == MODULE_NOT_DEFINED) { + String moduleNotDefinedMsg = + String.format( + "Module %s does not exist in the limits file.", moduleLimitResult.getModuleName()); + log.error(moduleNotDefinedMsg); + throw new PluginRpcEndpointException(new TransactionSimulationError(moduleNotDefinedMsg)); + } + if (moduleLimitResult.getResult() == TX_MODULE_LINE_COUNT_OVERFLOW) { + String txOverflowMsg = + String.format( + "Transaction line count for module %s=%s is above the limit %s", + moduleLimitResult.getModuleName(), + moduleLimitResult.getModuleLineCount(), + moduleLimitResult.getModuleLineLimit()); + log.warn(txOverflowMsg); + throw new PluginRpcEndpointException(new TransactionSimulationError(txOverflowMsg)); + } + } + public record Response( @JsonProperty String gasLimit, @JsonProperty String baseFeePerGas, diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java new file mode 100644 index 00000000..e9733312 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/TracerAggregator.java @@ -0,0 +1,157 @@ +/* + * 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.sequencer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.log.Log; +import org.hyperledger.besu.evm.operation.Operation; +import org.hyperledger.besu.evm.tracing.OperationTracer; +import org.hyperledger.besu.evm.worldstate.WorldView; + +/** + * Aggregates multiple {@link OperationTracer} instances, allowing them to be treated as a single + * tracer. This class facilitates the registration and delegation of tracing operations to multiple + * tracers. + */ +public class TracerAggregator implements OperationTracer { + private final List tracers = new ArrayList<>(); + + /** + * Registers an {@link OperationTracer} instance with the aggregator. If a tracer of the same + * class is already registered, an {@link IllegalArgumentException} is thrown. + * + * @param tracer the tracer to register + * @throws IllegalArgumentException if a tracer of the same class is already registered + */ + public void register(OperationTracer tracer) { + // Check if a tracer of the same class is already registered + for (OperationTracer existingTracer : tracers) { + if (existingTracer.getClass().equals(tracer.getClass())) { + throw new IllegalArgumentException( + "A tracer of class " + tracer.getClass().getName() + " is already registered."); + } + } + tracers.add(tracer); + } + + /** + * Creates a {@link TracerAggregator} instance and registers the provided tracers. + * + * @param tracers the tracers to register with the aggregator + * @return a new {@link TracerAggregator} instance with the provided tracers registered + */ + public static TracerAggregator create(OperationTracer... tracers) { + TracerAggregator aggregator = new TracerAggregator(); + for (OperationTracer tracer : tracers) { + aggregator.register(tracer); + } + return aggregator; + } + + @Override + public void tracePreExecution(MessageFrame frame) { + for (OperationTracer tracer : tracers) { + tracer.tracePreExecution(frame); + } + } + + @Override + public void tracePostExecution(MessageFrame frame, Operation.OperationResult operationResult) { + for (OperationTracer tracer : tracers) { + tracer.tracePostExecution(frame, operationResult); + } + } + + @Override + public void tracePrecompileCall(MessageFrame frame, long gasRequirement, Bytes output) { + for (OperationTracer tracer : tracers) { + tracer.tracePrecompileCall(frame, gasRequirement, output); + } + } + + @Override + public void traceAccountCreationResult( + MessageFrame frame, Optional haltReason) { + for (OperationTracer tracer : tracers) { + tracer.traceAccountCreationResult(frame, haltReason); + } + } + + @Override + public void tracePrepareTransaction(WorldView worldView, Transaction transaction) { + for (OperationTracer tracer : tracers) { + tracer.tracePrepareTransaction(worldView, transaction); + } + } + + @Override + public void traceStartTransaction(WorldView worldView, Transaction transaction) { + for (OperationTracer tracer : tracers) { + tracer.traceStartTransaction(worldView, transaction); + } + } + + @Override + public void traceEndTransaction( + WorldView worldView, + Transaction tx, + boolean status, + Bytes output, + List logs, + long gasUsed, + long timeNs) { + for (OperationTracer tracer : tracers) { + tracer.traceEndTransaction(worldView, tx, status, output, logs, gasUsed, timeNs); + } + } + + @Override + public void traceContextEnter(MessageFrame frame) { + for (OperationTracer tracer : tracers) { + tracer.traceContextEnter(frame); + } + } + + @Override + public void traceContextReEnter(MessageFrame frame) { + for (OperationTracer tracer : tracers) { + tracer.traceContextReEnter(frame); + } + } + + @Override + public void traceContextExit(MessageFrame frame) { + for (OperationTracer tracer : tracers) { + tracer.traceContextExit(frame); + } + } + + @Override + public boolean isExtendedTracing() { + for (OperationTracer tracer : tracers) { + if (tracer.isExtendedTracing()) { + return true; + } + } + return false; + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java new file mode 100644 index 00000000..69728c9b --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java @@ -0,0 +1,88 @@ +/* + * 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.sequencer.modulelimit; + +import lombok.Getter; + +/** Represents the result of verifying module line counts against their limits. */ +@Getter +public class ModuleLimitsValidationResult { + private final ModuleLineCountValidator.ModuleLineCountResult result; + private final String moduleName; + private final Integer moduleLineCount; + private final Integer moduleLineLimit; + private final Integer cumulativeModuleLineCount; + private final Integer cumulativeModuleLineLimit; + + public static final ModuleLimitsValidationResult VALID = + new ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult.VALID, null, null, null, null, null); + + private ModuleLimitsValidationResult( + final ModuleLineCountValidator.ModuleLineCountResult result, + final String moduleName, + final Integer moduleLineCount, + final Integer moduleLineLimit, + final Integer cumulativeModuleLineCount, + final Integer cumulativeModuleLineLimit) { + this.result = result; + this.moduleName = moduleName; + this.moduleLineCount = moduleLineCount; + this.moduleLineLimit = moduleLineLimit; + this.cumulativeModuleLineCount = cumulativeModuleLineCount; + this.cumulativeModuleLineLimit = cumulativeModuleLineLimit; + } + + public static ModuleLimitsValidationResult moduleNotDefined(final String moduleName) { + return new ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED, + moduleName, + null, + null, + null, + null); + } + + public static ModuleLimitsValidationResult txModuleLineCountOverflow( + final String moduleName, + final Integer moduleLineCount, + final Integer moduleLineLimit, + final Integer cumulativeModuleLineCount, + final Integer cumulativeModuleLineLimit) { + return new ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW, + moduleName, + moduleLineCount, + moduleLineLimit, + cumulativeModuleLineCount, + cumulativeModuleLineLimit); + } + + public static ModuleLimitsValidationResult blockModuleLineCountFull( + final String moduleName, + final Integer moduleLineCount, + final Integer moduleLineLimit, + final Integer cumulativeModuleLineCount, + final Integer cumulativeModuleLineLimit) { + + return new ModuleLimitsValidationResult( + ModuleLineCountValidator.ModuleLineCountResult.BLOCK_MODULE_LINE_COUNT_FULL, + moduleName, + moduleLineCount, + moduleLineLimit, + cumulativeModuleLineCount, + cumulativeModuleLineLimit); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java new file mode 100644 index 00000000..5009e3ea --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLineCountValidator.java @@ -0,0 +1,135 @@ +/* + * 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.sequencer.modulelimit; + +import java.io.File; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import com.google.common.io.Resources; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaTracerConfiguration; +import org.apache.tuweni.toml.Toml; +import org.apache.tuweni.toml.TomlParseResult; +import org.apache.tuweni.toml.TomlTable; + +/** + * Accumulates and verifies line counts for modules based on provided limits. It supports verifying + * if current transactions exceed these limits and updates the accumulated counts. + */ +@Slf4j +public class ModuleLineCountValidator { + private final Map moduleLineCountLimits; + + @Getter private final Map accumulatedLineCountsPerModule = new HashMap<>(); + + /** + * Constructs a new accumulator with specified module line count limits. + * + * @param moduleLineCountLimits A map of module names to their respective line count limits. + */ + public ModuleLineCountValidator(Map moduleLineCountLimits) { + this.moduleLineCountLimits = new HashMap<>(moduleLineCountLimits); + } + + /** + * Verifies if the current accumulated line counts for modules exceed the predefined limits. + * + * @param currentAccumulatedLineCounts A map of module names to their current accumulated line + * counts. + * @return A {@link ModuleLimitsValidationResult} indicating the outcome of the verification. + */ + public ModuleLimitsValidationResult validate(Map currentAccumulatedLineCounts) { + for (Map.Entry moduleEntry : currentAccumulatedLineCounts.entrySet()) { + String moduleName = moduleEntry.getKey(); + Integer currentTotalLineCountForModule = moduleEntry.getValue(); + Integer lineCountLimitForModule = moduleLineCountLimits.get(moduleName); + + if (lineCountLimitForModule == null) { + log.error("Module '{}' is not defined in the line count limits.", moduleName); + return ModuleLimitsValidationResult.moduleNotDefined(moduleName); + } + + int previouslyAccumulatedLineCount = + accumulatedLineCountsPerModule.getOrDefault(moduleName, 0); + int lineCountAddedByCurrentTx = + currentTotalLineCountForModule - previouslyAccumulatedLineCount; + + if (lineCountAddedByCurrentTx > lineCountLimitForModule) { + return ModuleLimitsValidationResult.txModuleLineCountOverflow( + moduleName, + lineCountAddedByCurrentTx, + lineCountLimitForModule, + currentTotalLineCountForModule, + lineCountLimitForModule); + } + + if (currentTotalLineCountForModule > lineCountLimitForModule) { + return ModuleLimitsValidationResult.blockModuleLineCountFull( + moduleName, + lineCountAddedByCurrentTx, + lineCountLimitForModule, + currentTotalLineCountForModule, + lineCountLimitForModule); + } + } + return ModuleLimitsValidationResult.VALID; + } + + /** + * Updates the internal map of accumulated line counts per module. + * + * @param newAccumulatedLineCounts A map of module names to their new accumulated line counts. + */ + public void updateAccumulatedLineCounts(Map newAccumulatedLineCounts) { + accumulatedLineCountsPerModule.clear(); + accumulatedLineCountsPerModule.putAll(newAccumulatedLineCounts); + } + + /** Enumerates possible outcomes of verifying module line counts against their limits. */ + public enum ModuleLineCountResult { + VALID, + TX_MODULE_LINE_COUNT_OVERFLOW, + BLOCK_MODULE_LINE_COUNT_FULL, + MODULE_NOT_DEFINED + } + + public static Map createLimitModules( + LineaTracerConfiguration lineaTracerConfiguration) { + try { + URL url = new File(lineaTracerConfiguration.moduleLimitsFilePath()).toURI().toURL(); + final String tomlString = Resources.toString(url, StandardCharsets.UTF_8); + TomlParseResult result = Toml.parse(tomlString); + final TomlTable table = result.getTable("traces-limits"); + final Map limitsMap = + table.toMap().entrySet().stream() + .collect( + Collectors.toUnmodifiableMap( + Map.Entry::getKey, e -> Math.toIntExact((Long) e.getValue()))); + + return limitsMap; + } catch (final Exception e) { + final String errorMsg = + "Problem reading the toml file containing the limits for the modules: " + + lineaTracerConfiguration.moduleLimitsFilePath(); + log.error(errorMsg); + throw new RuntimeException(errorMsg, e); + } + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java index 465d3e5a..cab06d47 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorFactory.java @@ -19,6 +19,7 @@ import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaProfitabilityConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import net.consensys.linea.sequencer.txselection.selectors.LineaTransactionSelector; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; @@ -29,22 +30,30 @@ public class LineaTransactionSelectorFactory implements PluginTransactionSelecto private final LineaTransactionSelectorConfiguration txSelectorConfiguration; private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; private final LineaProfitabilityConfiguration profitabilityConfiguration; + private final LineaTracerConfiguration tracerConfiguration; + private final Map limitsMap; public LineaTransactionSelectorFactory( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, final LineaProfitabilityConfiguration profitabilityConfiguration, + final LineaTracerConfiguration tracerConfiguration, final Map limitsMap) { this.txSelectorConfiguration = txSelectorConfiguration; this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; this.profitabilityConfiguration = profitabilityConfiguration; + this.tracerConfiguration = tracerConfiguration; this.limitsMap = limitsMap; } @Override public PluginTransactionSelector create() { return new LineaTransactionSelector( - txSelectorConfiguration, l1L2BridgeConfiguration, profitabilityConfiguration, limitsMap); + txSelectorConfiguration, + l1L2BridgeConfiguration, + profitabilityConfiguration, + tracerConfiguration, + limitsMap); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java index 4a450419..edcfeb09 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/LineaTransactionSelectorPlugin.java @@ -15,23 +15,13 @@ package net.consensys.linea.sequencer.txselection; -import java.io.File; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Map; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.createLimitModules; + import java.util.Optional; -import java.util.stream.Collectors; import com.google.auto.service.AutoService; -import com.google.common.io.Resources; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.AbstractLineaRequiredPlugin; -import net.consensys.linea.config.LineaL1L2BridgeConfiguration; -import net.consensys.linea.config.LineaProfitabilityConfiguration; -import net.consensys.linea.config.LineaTransactionSelectorConfiguration; -import org.apache.tuweni.toml.Toml; -import org.apache.tuweni.toml.TomlParseResult; -import org.apache.tuweni.toml.TomlTable; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.services.TransactionSelectionService; @@ -66,43 +56,12 @@ public void doRegister(final BesuContext context) { @Override public void beforeExternalServices() { super.beforeExternalServices(); - try { - URL url = new File(transactionSelectorConfiguration.moduleLimitsFilePath()).toURI().toURL(); - final String tomlString = Resources.toString(url, StandardCharsets.UTF_8); - TomlParseResult result = Toml.parse(tomlString); - final TomlTable table = result.getTable("traces-limits"); - final Map limitsMap = - table.toMap().entrySet().stream() - .collect( - Collectors.toUnmodifiableMap( - Map.Entry::getKey, e -> Math.toIntExact((Long) e.getValue()))); - - createAndRegister( - transactionSelectionService, - transactionSelectorConfiguration, - l1L2BridgeConfiguration, - profitabilityConfiguration, - limitsMap); - } catch (final Exception e) { - final String errorMsg = - "Problem reading the toml file containing the limits for the modules: " - + transactionSelectorConfiguration.moduleLimitsFilePath(); - log.error(errorMsg); - throw new RuntimeException(errorMsg, e); - } - } - - private void createAndRegister( - final TransactionSelectionService transactionSelectionService, - final LineaTransactionSelectorConfiguration txSelectorConfiguration, - final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, - final LineaProfitabilityConfiguration profitabilityConfiguration, - final Map limitsMap) { transactionSelectionService.registerPluginTransactionSelectorFactory( new LineaTransactionSelectorFactory( - txSelectorConfiguration, + transactionSelectorConfiguration, l1L2BridgeConfiguration, profitabilityConfiguration, - limitsMap)); + tracerConfiguration, + createLimitModules(tracerConfiguration))); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java index 32a7c012..fadb5d78 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/LineaTransactionSelector.java @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaProfitabilityConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; import org.hyperledger.besu.datatypes.PendingTransaction; import org.hyperledger.besu.plugin.data.TransactionProcessingResult; @@ -33,18 +34,20 @@ public class LineaTransactionSelector implements PluginTransactionSelector { private TraceLineLimitTransactionSelector traceLineLimitTransactionSelector; - private List selectors; + private final List selectors; public LineaTransactionSelector( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, final LineaProfitabilityConfiguration profitabilityConfiguration, + final LineaTracerConfiguration tracerConfiguration, final Map limitsMap) { this.selectors = createTransactionSelectors( txSelectorConfiguration, l1L2BridgeConfiguration, profitabilityConfiguration, + tracerConfiguration, limitsMap); } @@ -60,11 +63,12 @@ private List createTransactionSelectors( final LineaTransactionSelectorConfiguration txSelectorConfiguration, final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, final LineaProfitabilityConfiguration profitabilityConfiguration, + final LineaTracerConfiguration tracerConfiguration, final Map limitsMap) { traceLineLimitTransactionSelector = new TraceLineLimitTransactionSelector( - limitsMap, txSelectorConfiguration, l1L2BridgeConfiguration); + limitsMap, txSelectorConfiguration, l1L2BridgeConfiguration, tracerConfiguration); return List.of( new MaxBlockCallDataTransactionSelector(txSelectorConfiguration.maxBlockCallDataSize()), diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelector.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelector.java index 5f021dd6..44c1cd74 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelector.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelector.java @@ -27,7 +27,10 @@ import com.google.common.annotations.VisibleForTesting; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import net.consensys.linea.sequencer.modulelimit.ModuleLimitsValidationResult; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; import net.consensys.linea.zktracer.ZkTracer; import net.consensys.linea.zktracer.module.Module; import org.hyperledger.besu.datatypes.Hash; @@ -56,20 +59,21 @@ public class TraceLineLimitTransactionSelector implements PluginTransactionSelec private final String limitFilePath; private final Map moduleLimits; private final int overLimitCacheSize; - private Map consolidatedCumulatedLineCount = Map.of(); + private final ModuleLineCountValidator moduleLineCountAccumulator; private Map currCumulatedLineCount; public TraceLineLimitTransactionSelector( final Map moduleLimits, final LineaTransactionSelectorConfiguration txSelectorConfiguration, - final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) { + final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration, + final LineaTracerConfiguration tracerConfiguration) { if (l1L2BridgeConfiguration.isEmpty()) { log.error("L1L2 bridge settings have not been defined."); System.exit(1); } this.moduleLimits = moduleLimits; - this.limitFilePath = txSelectorConfiguration.moduleLimitsFilePath(); + this.limitFilePath = tracerConfiguration.moduleLimitsFilePath(); this.overLimitCacheSize = txSelectorConfiguration.overLinesLimitCacheSize(); zkTracer = new ZkTracerWithLog(l1L2BridgeConfiguration); @@ -80,6 +84,7 @@ public TraceLineLimitTransactionSelector( } } zkTracer.traceStartConflation(1L); + moduleLineCountAccumulator = new ModuleLineCountValidator(moduleLimits); } /** @@ -114,7 +119,7 @@ public void onTransactionNotSelected( public void onTransactionSelected( final TransactionEvaluationContext evaluationContext, final TransactionProcessingResult processingResult) { - consolidatedCumulatedLineCount = currCumulatedLineCount; + moduleLineCountAccumulator.updateAccumulatedLineCounts(currCumulatedLineCount); } /** @@ -133,49 +138,41 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( // check that we are not exceeding line number for any module currCumulatedLineCount = zkTracer.getModulesLineCount(); - final Transaction transaction = evaluationContext.getPendingTransaction().getTransaction(); - log.atTrace() .setMessage("Tx {} line count per module: {}") .addArgument(transaction::getHash) .addArgument(this::logTxLineCount) .log(); - for (final var module : currCumulatedLineCount.keySet()) { - final Integer moduleLineCountLimit = moduleLimits.get(module); - if (moduleLineCountLimit == null) { - final String errorMsg = - "Module " + module + " does not exist in the limits file: " + limitFilePath; - log.error(errorMsg); - throw new RuntimeException(errorMsg); - } - - final int cumulatedModuleLineCount = currCumulatedLineCount.get(module); - final int txModuleLineCount = - cumulatedModuleLineCount - consolidatedCumulatedLineCount.getOrDefault(module, 0); + ModuleLimitsValidationResult result = + moduleLineCountAccumulator.validate(currCumulatedLineCount); - if (txModuleLineCount > moduleLineCountLimit) { + switch (result.getResult()) { + case MODULE_NOT_DEFINED: + log.error("Module {} does not exist in the limits file.", result.getModuleName()); + throw new RuntimeException( + "Module " + result.getModuleName() + " does not exist in the limits file."); + case TX_MODULE_LINE_COUNT_OVERFLOW: log.warn( "Tx {} line count for module {}={} is above the limit {}, removing from the txpool", transaction.getHash(), - module, - txModuleLineCount, - moduleLineCountLimit); + result.getModuleName(), + result.getModuleLineCount(), + result.getModuleLineCount()); rememberOverLineCountLimitTransaction(transaction); return TX_MODULE_LINE_COUNT_OVERFLOW; - } - - if (cumulatedModuleLineCount > moduleLineCountLimit) { + case BLOCK_MODULE_LINE_COUNT_FULL: log.atTrace() .setMessage( "Cumulated line count for module {}={} is above the limit {}, stopping selection") - .addArgument(module) - .addArgument(cumulatedModuleLineCount) - .addArgument(moduleLineCountLimit) + .addArgument(result.getModuleName()) + .addArgument(result.getCumulativeModuleLineCount()) + .addArgument(result.getCumulativeModuleLineLimit()) .log(); return BLOCK_MODULE_LINE_COUNT_FULL; - } + default: + break; } return SELECTED; } @@ -207,7 +204,10 @@ private String logTxLineCount() { // tx line count / cumulated line count / line count limit e.getKey() + "=" - + (e.getValue() - consolidatedCumulatedLineCount.getOrDefault(e.getKey(), 0)) + + (e.getValue() + - moduleLineCountAccumulator + .getAccumulatedLineCountsPerModule() + .getOrDefault(e.getKey(), 0)) + "/" + e.getValue() + "/" @@ -230,7 +230,7 @@ public void traceEndBlock(final BlockHeader blockHeader, final BlockBody blockBo .addKeyValue( "traceCounts", () -> - consolidatedCumulatedLineCount.entrySet().stream() + moduleLineCountAccumulator.getAccumulatedLineCountsPerModule().entrySet().stream() .sorted(Map.Entry.comparingByKey()) .map(e -> '"' + e.getKey() + "\":" + e.getValue()) .collect(Collectors.joining(","))) diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/rlp/txn/RlpTxnChunk.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/rlp/txn/RlpTxnChunk.java index 5c22cae4..668f99db 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/module/rlp/txn/RlpTxnChunk.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/module/rlp/txn/RlpTxnChunk.java @@ -124,7 +124,7 @@ protected int computeLineCount() { // Phase 10: AccessList if (txType == 1 || txType == 2) { - if (this.tx.getAccessList().orElseThrow().isEmpty()) { + if (this.tx.getAccessList().isEmpty() || this.tx.getAccessList().get().isEmpty()) { rowSize += 1; } else { // Rlp prefix of the AccessList list diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelectorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelectorTest.java index 2cd590da..eb9fb983 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelectorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/TraceLineLimitTransactionSelectorTest.java @@ -22,15 +22,17 @@ import static org.mockito.Mockito.when; import java.io.IOException; -import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; import java.util.Map; -import java.util.stream.Collectors; import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; import net.consensys.linea.config.LineaTransactionSelectorConfiguration; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; -import org.apache.tuweni.toml.Toml; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.PendingTransaction; @@ -39,38 +41,50 @@ import org.hyperledger.besu.plugin.data.TransactionProcessingResult; import org.hyperledger.besu.plugin.data.TransactionSelectionResult; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; public class TraceLineLimitTransactionSelectorTest { private static final int OVER_LINE_COUNT_LIMIT_CACHE_SIZE = 2; - private TestableTraceLineLimitTransactionSelector transactionSelector; + private static final String MODULE_LINE_LIMITS_RESOURCE_NAME = "/sequencer/line-limits.toml"; private Map lineCountLimits; + private LineaTracerConfiguration lineaTracerConfiguration; + + @TempDir static Path tempDir; + static Path lineLimitsConfPath; + + @BeforeAll + public static void beforeAll() throws IOException { + lineLimitsConfPath = tempDir.resolve("line-limits.toml"); + Files.copy( + TraceLineLimitTransactionSelectorTest.class.getResourceAsStream( + MODULE_LINE_LIMITS_RESOURCE_NAME), + lineLimitsConfPath); + } @BeforeEach public void initialize() { - lineCountLimits = loadLineCountLimitConf(); - transactionSelector = newSelectorForNewBlock(); - transactionSelector.reset(); + lineaTracerConfiguration = + LineaTracerConfiguration.builder() + .moduleLimitsFilePath(lineLimitsConfPath.toString()) + .build(); + lineCountLimits = + new HashMap<>(ModuleLineCountValidator.createLimitModules(lineaTracerConfiguration)); } - private TestableTraceLineLimitTransactionSelector newSelectorForNewBlock() { + private TestableTraceLineLimitTransactionSelector newSelectorForNewBlock( + final Map lineCountLimits) { return new TestableTraceLineLimitTransactionSelector( - lineCountLimits, "line-limits.toml", OVER_LINE_COUNT_LIMIT_CACHE_SIZE); - } - - private Map loadLineCountLimitConf() { - try (final InputStream is = - this.getClass().getResourceAsStream("/sequencer/line-limits.toml")) { - return Toml.parse(is).getTable("traces-limits").toMap().entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, e -> Math.toIntExact((long) e.getValue()))); - } catch (IOException e) { - throw new RuntimeException(e); - } + lineaTracerConfiguration, lineCountLimits, OVER_LINE_COUNT_LIMIT_CACHE_SIZE); } @Test public void shouldSelectWhenBelowLimits() { + final var transactionSelector = newSelectorForNewBlock(lineCountLimits); + transactionSelector.resetCache(); + final var evaluationContext = mockEvaluationContext(false, 100, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 21000); verifyTransactionSelection( @@ -88,6 +102,9 @@ public void shouldSelectWhenBelowLimits() { @Test public void shouldNotSelectWhenOverLimits() { lineCountLimits.put("ADD", 1); + final var transactionSelector = newSelectorForNewBlock(lineCountLimits); + transactionSelector.resetCache(); + final var evaluationContext = mockEvaluationContext(false, 100, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 21000); verifyTransactionSelection( @@ -105,6 +122,9 @@ public void shouldNotSelectWhenOverLimits() { @Test public void shouldNotReprocessedWhenOverLimits() { lineCountLimits.put("ADD", 1); + var transactionSelector = newSelectorForNewBlock(lineCountLimits); + transactionSelector.resetCache(); + var evaluationContext = mockEvaluationContext(false, 100, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 21000); verifyTransactionSelection( @@ -118,7 +138,7 @@ public void shouldNotReprocessedWhenOverLimits() { transactionSelector.isOverLineCountLimitTxCached( evaluationContext.getPendingTransaction().getTransaction().getHash())) .isTrue(); - transactionSelector = newSelectorForNewBlock(); + transactionSelector = newSelectorForNewBlock(lineCountLimits); assertThat( transactionSelector.isOverLineCountLimitTxCached( evaluationContext.getPendingTransaction().getTransaction().getHash())) @@ -139,6 +159,9 @@ public void shouldNotReprocessedWhenOverLimits() { @Test public void shouldEvictWhenCacheIsFull() { lineCountLimits.put("ADD", 1); + final var transactionSelector = newSelectorForNewBlock(lineCountLimits); + transactionSelector.resetCache(); + final TestTransactionEvaluationContext[] evaluationContexts = new TestTransactionEvaluationContext[OVER_LINE_COUNT_LIMIT_CACHE_SIZE + 1]; for (int i = 0; i <= OVER_LINE_COUNT_LIMIT_CACHE_SIZE; i++) { @@ -221,22 +244,22 @@ private TestTransactionEvaluationContext mockEvaluationContext( private class TestableTraceLineLimitTransactionSelector extends TraceLineLimitTransactionSelector { TestableTraceLineLimitTransactionSelector( + final LineaTracerConfiguration lineaTracerConfiguration, final Map moduleLimits, - final String limitFilePath, final int overLimitCacheSize) { super( moduleLimits, LineaTransactionSelectorConfiguration.builder() - .moduleLimitsFilePath(limitFilePath) .overLinesLimitCacheSize(overLimitCacheSize) .build(), LineaL1L2BridgeConfiguration.builder() .contract(Address.fromHexString("0xDEADBEEF")) .topic(Bytes.fromHexString("0x012345")) - .build()); + .build(), + lineaTracerConfiguration); } - void reset() { + void resetCache() { overLineCountLimitCache.clear(); } From 8e3fce7a2682709b0d0c6814f838e18fe5690eb0 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Thu, 11 Apr 2024 15:10:25 +0200 Subject: [PATCH 22/30] In the txpool, reject a tx if its simulation fails (#2) Signed-off-by: Gabriel-Trintinalia Signed-off-by: Fabio Di Fabio Co-authored-by: Gabriel-Trintinalia --- PLUGINS.md | 14 +- .../plugin/acc/test/LineaPluginTestBase.java | 12 +- .../TransactionTraceLimitOverflowTest.java | 1 + ...SendRawTransactionSimulationCheckTest.java | 103 +++++++++++ .../AbstractLineaSharedOptionsPlugin.java | 10 +- ...ptions.java => LineaTracerCliOptions.java} | 13 +- ...neaTransactionPoolValidatorCliOptions.java | 37 +++- ...TransactionPoolValidatorConfiguration.java | 6 +- .../linea/rpc/linea/LineaEstimateGas.java | 15 +- .../LineaTransactionPoolValidatorFactory.java | 25 ++- .../LineaTransactionPoolValidatorPlugin.java | 19 +- .../validators/SimulationValidator.java | 130 ++++++++++++++ .../ProfitabilityValidatorTest.java | 80 +++------ .../validators/SimulationValidatorTest.java | 166 ++++++++++++++++++ 14 files changed, 540 insertions(+), 91 deletions(-) create mode 100644 acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java rename arithmetization/src/main/java/net/consensys/linea/config/{LineaTracerConfigurationCLiOptions.java => LineaTracerCliOptions.java} (83%) create mode 100644 arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java create mode 100644 arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java diff --git a/PLUGINS.md b/PLUGINS.md index c62f1f79..645f3aa3 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -44,12 +44,14 @@ of a transaction. #### CLI Options -| Option Name | Default Value | Command Line Argument | -|----------------------------------|----------------------|---------------------------------------------------| -| MAX_BLOCK_CALLDATA_SIZE | 70000 | `--plugin-linea-max-block-calldata-size` | -| MODULE_LIMIT_FILE_PATH | moduleLimitFile.toml | `--plugin-linea-module-limit-file-path` | -| OVER_LINE_COUNT_LIMIT_CACHE_SIZE | 10_000 | `--plugin-linea-over-line-count-limit-cache-size` | -| MAX_GAS_PER_BLOCK | 30_000_000L | `--plugin-linea-max-block-gas` | +| Option Name | Default Value | Command Line Argument | +|-------------------------------------|----------------------|-------------------------------------------------------| +| MAX_BLOCK_CALLDATA_SIZE | 70000 | `--plugin-linea-max-block-calldata-size` | +| MODULE_LIMIT_FILE_PATH | moduleLimitFile.toml | `--plugin-linea-module-limit-file-path` | +| OVER_LINE_COUNT_LIMIT_CACHE_SIZE | 10_000 | `--plugin-linea-over-line-count-limit-cache-size` | +| MAX_GAS_PER_BLOCK | 30_000_000L | `--plugin-linea-max-block-gas` | +| TX_POOL_ENABLE_SIMULATION_CHECK_API | true | `--plugin-linea-tx-pool-simulation-check-api-enabled` | +| TX_POOL_ENABLE_SIMULATION_CHECK_P2P | false | `--plugin-linea-tx-pool-simulation-check-p2p-enabled` | ### Transaction validation - LineaTransactionValidatorPlugin diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java index 1c1cac21..38a898f1 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -149,7 +150,8 @@ private void assertTransactionsInCorrectBlocks(Web3j web3j, List hashes, protected SimpleStorage deploySimpleStorage() throws Exception { final Web3j web3j = minerNode.nodeRequests().eth(); final Credentials credentials = Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY); - TransactionManager txManager = new RawTransactionManager(web3j, credentials, CHAIN_ID); + TransactionManager txManager = + new RawTransactionManager(web3j, credentials, CHAIN_ID, createReceiptProcessor(web3j)); final RemoteCall deploy = SimpleStorage.deploy(web3j, txManager, new DefaultGasProvider()); @@ -198,11 +200,15 @@ protected void assertTransactionNotInThePool(String hash) { .notInTransactionPool(Hash.fromHexString(hash))); } + protected List> getTxPoolContent() { + return minerNode.execute(new TxPoolTransactions().getTxPoolContents()); + } + private TransactionReceiptProcessor createReceiptProcessor(Web3j web3j) { return new PollingTransactionReceiptProcessor( web3j, - TransactionManager.DEFAULT_POLLING_FREQUENCY, - TransactionManager.DEFAULT_POLLING_ATTEMPTS_PER_TX_HASH); + Math.max(1000, LINEA_CLIQUE_OPTIONS.blockPeriodSeconds() * 1000 / 5), + LINEA_CLIQUE_OPTIONS.blockPeriodSeconds() * 3); } protected String sendTransactionWithGivenLengthPayload( diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/TransactionTraceLimitOverflowTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/TransactionTraceLimitOverflowTest.java index 3558e4e4..21cc92fd 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/TransactionTraceLimitOverflowTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/TransactionTraceLimitOverflowTest.java @@ -43,6 +43,7 @@ public List getTestCliOptions() { .set( "--plugin-linea-module-limit-file-path=", getResourcePath("/txOverflowModuleLimits.toml")) + .set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "false") .build(); } diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java new file mode 100644 index 00000000..8534fe99 --- /dev/null +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java @@ -0,0 +1,103 @@ +/* + * 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 linea.plugin.acc.test.rpc.linea; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import linea.plugin.acc.test.LineaPluginTestBase; +import linea.plugin.acc.test.TestCommandLineOptionsBuilder; +import linea.plugin.acc.test.tests.web3j.generated.SimpleStorage; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.tests.acceptance.dsl.account.Account; +import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts; +import org.junit.jupiter.api.Test; +import org.web3j.crypto.Credentials; +import org.web3j.crypto.RawTransaction; +import org.web3j.crypto.TransactionEncoder; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.tx.gas.DefaultGasProvider; +import org.web3j.utils.Numeric; + +public class EthSendRawTransactionSimulationCheckTest extends LineaPluginTestBase { + + private static final BigInteger GAS_LIMIT = DefaultGasProvider.GAS_LIMIT; + private static final BigInteger VALUE = BigInteger.ZERO; + private static final BigInteger GAS_PRICE = BigInteger.TEN.pow(9); + + @Override + public List getTestCliOptions() { + return new TestCommandLineOptionsBuilder() + .set( + "--plugin-linea-module-limit-file-path=", + getResourcePath("/txOverflowModuleLimits.toml")) + .set("--plugin-linea-tx-pool-simulation-check-api-enabled=", "true") + .build(); + } + + @Test + public void transactionOverModuleLineCountNotAccepted() throws Exception { + final SimpleStorage simpleStorage = deploySimpleStorage(); + final Web3j web3j = minerNode.nodeRequests().eth(); + final String contractAddress = simpleStorage.getContractAddress(); + final String txData = simpleStorage.add(BigInteger.valueOf(100)).encodeFunctionCall(); + + // this tx will not be accepted since it goes above the line count limit + final RawTransaction txModuleLineCountTooBig = + RawTransaction.createTransaction( + CHAIN_ID, + BigInteger.valueOf(1), + GAS_LIMIT.divide(BigInteger.TEN), + contractAddress, + VALUE, + txData, + GAS_PRICE, + GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE)); + final byte[] signedTxContractInteraction = + TransactionEncoder.signMessage( + txModuleLineCountTooBig, Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY)); + + final EthSendTransaction signedTxContractInteractionResp = + web3j.ethSendRawTransaction(Numeric.toHexString(signedTxContractInteraction)).send(); + + assertThat(signedTxContractInteractionResp.hasError()).isTrue(); + assertThat(signedTxContractInteractionResp.getError().getMessage()) + .isEqualTo("Transaction line count for module ADD=2018 is above the limit 70"); + + assertThat(getTxPoolContent()).isEmpty(); + + // these are under the line count limit and should be accepted and selected + final Account fewLinesSender = accounts.getSecondaryBenefactor(); + final Account recipient = accounts.createAccount("recipient"); + final List expectedConfirmedTxs = new ArrayList<>(4); + + expectedConfirmedTxs.addAll( + minerNode.execute( + accountTransactions.createIncrementalTransfers(fewLinesSender, recipient, 4))); + + final var txPoolContentByHash = getTxPoolContent().stream().map(e -> e.get("hash")).toList(); + assertThat(txPoolContentByHash) + .containsExactlyInAnyOrderElementsOf( + expectedConfirmedTxs.stream().map(Hash::toHexString).toList()); + + expectedConfirmedTxs.stream() + .map(Hash::toHexString) + .forEach(hash -> minerNode.verify(eth.expectSuccessfulTransactionReceipt(hash))); + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java index 5046c8c9..93e18d6d 100644 --- a/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/AbstractLineaSharedOptionsPlugin.java @@ -23,8 +23,8 @@ import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaRpcCliOptions; import net.consensys.linea.config.LineaRpcConfiguration; +import net.consensys.linea.config.LineaTracerCliOptions; import net.consensys.linea.config.LineaTracerConfiguration; -import net.consensys.linea.config.LineaTracerConfigurationCLiOptions; import net.consensys.linea.config.LineaTransactionPoolValidatorCliOptions; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import net.consensys.linea.config.LineaTransactionSelectorCliOptions; @@ -44,7 +44,7 @@ public abstract class AbstractLineaSharedOptionsPlugin implements BesuPlugin { private static LineaRpcCliOptions rpcCliOptions; private static LineaProfitabilityCliOptions profitabilityCliOptions; protected static LineaTransactionSelectorConfiguration transactionSelectorConfiguration; - protected static LineaTracerConfigurationCLiOptions tracerConfigurationCliOptions; + protected static LineaTracerCliOptions tracerCliOptions; protected static LineaTransactionPoolValidatorConfiguration transactionPoolValidatorConfiguration; protected static LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; protected static LineaRpcConfiguration rpcConfiguration; @@ -71,14 +71,14 @@ public synchronized void register(final BesuContext context) { l1L2BridgeCliOptions = LineaL1L2BridgeCliOptions.create(); rpcCliOptions = LineaRpcCliOptions.create(); profitabilityCliOptions = LineaProfitabilityCliOptions.create(); - tracerConfigurationCliOptions = LineaTracerConfigurationCLiOptions.create(); + tracerCliOptions = LineaTracerCliOptions.create(); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionSelectorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, transactionPoolValidatorCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, l1L2BridgeCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, rpcCliOptions); cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, profitabilityCliOptions); - cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, tracerConfigurationCliOptions); + cmdlineOptions.addPicoCLIOptions(CLI_OPTIONS_PREFIX, tracerCliOptions); cliOptionsRegistered = true; } } @@ -91,7 +91,7 @@ public void beforeExternalServices() { l1L2BridgeConfiguration = l1L2BridgeCliOptions.toDomainObject(); rpcConfiguration = rpcCliOptions.toDomainObject(); profitabilityConfiguration = profitabilityCliOptions.toDomainObject(); - tracerConfiguration = tracerConfigurationCliOptions.toDomainObject(); + tracerConfiguration = tracerCliOptions.toDomainObject(); configured = true; } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerCliOptions.java similarity index 83% rename from arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java rename to arithmetization/src/main/java/net/consensys/linea/config/LineaTracerCliOptions.java index 5b6f6c04..0d75650e 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerConfigurationCLiOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTracerCliOptions.java @@ -17,7 +17,7 @@ import com.google.common.base.MoreObjects; import picocli.CommandLine; -public class LineaTracerConfigurationCLiOptions { +public class LineaTracerCliOptions { public static final String MODULE_LIMIT_FILE_PATH = "--plugin-linea-module-limit-file-path"; public static final String DEFAULT_MODULE_LIMIT_FILE_PATH = "moduleLimitFile.toml"; @@ -30,15 +30,15 @@ public class LineaTracerConfigurationCLiOptions { "Path to the toml file containing the module limits (default: ${DEFAULT-VALUE})") private String moduleLimitFilePath = DEFAULT_MODULE_LIMIT_FILE_PATH; - private LineaTracerConfigurationCLiOptions() {} + private LineaTracerCliOptions() {} /** * Create Linea cli options. * * @return the Linea cli options */ - public static LineaTracerConfigurationCLiOptions create() { - return new LineaTracerConfigurationCLiOptions(); + public static LineaTracerCliOptions create() { + return new LineaTracerCliOptions(); } /** @@ -47,9 +47,8 @@ public static LineaTracerConfigurationCLiOptions create() { * @param config the config * @return the Linea cli options */ - public static LineaTracerConfigurationCLiOptions fromConfig( - final LineaTracerConfiguration config) { - final LineaTracerConfigurationCLiOptions options = create(); + public static LineaTracerCliOptions fromConfig(final LineaTracerConfiguration config) { + final LineaTracerCliOptions options = create(); options.moduleLimitFilePath = config.moduleLimitsFilePath(); return options; } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java index 4cb68f78..9c0ef038 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorCliOptions.java @@ -30,6 +30,14 @@ public class LineaTransactionPoolValidatorCliOptions { public static final String MAX_TX_CALLDATA_SIZE = "--plugin-linea-max-tx-calldata-size"; public static final int DEFAULT_MAX_TX_CALLDATA_SIZE = 60_000; + public static final String TX_POOL_ENABLE_SIMULATION_CHECK_API = + "--plugin-linea-tx-pool-simulation-check-api-enabled"; + public static final boolean DEFAULT_TX_POOL_ENABLE_SIMULATION_CHECK_API = true; + + public static final String TX_POOL_ENABLE_SIMULATION_CHECK_P2P = + "--plugin-linea-tx-pool-simulation-check-p2p-enabled"; + public static final boolean DEFAULT_TX_POOL_ENABLE_SIMULATION_CHECK_P2P = false; + @CommandLine.Option( names = {DENY_LIST_PATH}, hidden = true, @@ -58,6 +66,24 @@ public class LineaTransactionPoolValidatorCliOptions { + ")") private int maxTxCallDataSize = DEFAULT_MAX_TX_CALLDATA_SIZE; + @CommandLine.Option( + names = {TX_POOL_ENABLE_SIMULATION_CHECK_API}, + arity = "0..1", + hidden = true, + paramLabel = "", + description = + "Enable the simulation check for txs received via API? (default: ${DEFAULT-VALUE})") + private boolean txPoolSimulationCheckApiEnabled = DEFAULT_TX_POOL_ENABLE_SIMULATION_CHECK_API; + + @CommandLine.Option( + names = {TX_POOL_ENABLE_SIMULATION_CHECK_P2P}, + arity = "0..1", + hidden = true, + paramLabel = "", + description = + "Enable the simulation check for txs received via p2p? (default: ${DEFAULT-VALUE})") + private boolean txPoolSimulationCheckP2pEnabled = DEFAULT_TX_POOL_ENABLE_SIMULATION_CHECK_P2P; + private LineaTransactionPoolValidatorCliOptions() {} /** @@ -81,7 +107,8 @@ public static LineaTransactionPoolValidatorCliOptions fromConfig( options.denyListPath = config.denyListPath(); options.maxTxGasLimit = config.maxTxGasLimit(); options.maxTxCallDataSize = config.maxTxCalldataSize(); - + options.txPoolSimulationCheckApiEnabled = config.txPoolSimulationCheckApiEnabled(); + options.txPoolSimulationCheckP2pEnabled = config.txPoolSimulationCheckP2pEnabled(); return options; } @@ -92,7 +119,11 @@ public static LineaTransactionPoolValidatorCliOptions fromConfig( */ public LineaTransactionPoolValidatorConfiguration toDomainObject() { return new LineaTransactionPoolValidatorConfiguration( - denyListPath, maxTxGasLimit, maxTxCallDataSize); + denyListPath, + maxTxGasLimit, + maxTxCallDataSize, + txPoolSimulationCheckApiEnabled, + txPoolSimulationCheckP2pEnabled); } @Override @@ -101,6 +132,8 @@ public String toString() { .add(DENY_LIST_PATH, denyListPath) .add(MAX_TX_GAS_LIMIT_OPTION, maxTxGasLimit) .add(MAX_TX_CALLDATA_SIZE, maxTxCallDataSize) + .add(TX_POOL_ENABLE_SIMULATION_CHECK_API, txPoolSimulationCheckApiEnabled) + .add(TX_POOL_ENABLE_SIMULATION_CHECK_P2P, txPoolSimulationCheckP2pEnabled) .toString(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java index 90d41edf..fdad5fa0 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaTransactionPoolValidatorConfiguration.java @@ -26,4 +26,8 @@ */ @Builder(toBuilder = true) public record LineaTransactionPoolValidatorConfiguration( - String denyListPath, int maxTxGasLimit, int maxTxCalldataSize) {} + String denyListPath, + int maxTxGasLimit, + int maxTxCalldataSize, + boolean txPoolSimulationCheckApiEnabled, + boolean txPoolSimulationCheckP2pEnabled) {} diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java index 4e9b4707..e5c38813 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -49,6 +49,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; +import org.hyperledger.besu.plugin.data.BlockHeader; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.TransactionSimulationService; @@ -209,11 +210,12 @@ private Long estimateGasUsed( final Wei minGasPrice) { final var estimateGasOperationTracer = new EstimateGasOperationTracer(); - final var zkTracer = createZkTracer(); + final var chainHeadHeader = blockchainService.getChainHeadHeader(); + final var zkTracer = createZkTracer(chainHeadHeader); TracerAggregator tracerAggregator = TracerAggregator.create(estimateGasOperationTracer, zkTracer); - final var chainHeadHash = blockchainService.getChainHeadHash(); + final var chainHeadHash = chainHeadHeader.getBlockHash(); final var maybeSimulationResults = transactionSimulationService.simulate(transaction, chainHeadHash, tracerAggregator, true); @@ -417,10 +419,10 @@ private Transaction createTransactionForSimulation( return txBuilder.build(); } - private ZkTracer createZkTracer() { + private ZkTracer createZkTracer(final BlockHeader chainHeadHeader) { var zkTracer = new ZkTracer(l1L2BridgeConfiguration); zkTracer.traceStartConflation(1L); - zkTracer.traceStartBlock(blockchainService.getChainHeadHeader()); + zkTracer.traceStartBlock(chainHeadHeader); return zkTracer; } @@ -443,6 +445,11 @@ private void handleModuleOverLimit(ModuleLimitsValidationResult moduleLimitResul log.warn(txOverflowMsg); throw new PluginRpcEndpointException(new TransactionSimulationError(txOverflowMsg)); } + + final String internalErrorMsg = + String.format("Do not know what to do with result %s", moduleLimitResult.getResult()); + log.error(internalErrorMsg); + throw new PluginRpcEndpointException(RpcErrorType.PLUGIN_INTERNAL_ERROR, internalErrorMsg); } public record Response( diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java index 6fabee4e..0873678a 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorFactory.java @@ -16,18 +16,22 @@ package net.consensys.linea.sequencer.txpoolvalidation; import java.util.Arrays; +import java.util.Map; import java.util.Optional; import java.util.Set; +import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.config.LineaProfitabilityConfiguration; import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; import net.consensys.linea.sequencer.txpoolvalidation.validators.AllowedAddressValidator; import net.consensys.linea.sequencer.txpoolvalidation.validators.CalldataValidator; import net.consensys.linea.sequencer.txpoolvalidation.validators.GasLimitValidator; import net.consensys.linea.sequencer.txpoolvalidation.validators.ProfitabilityValidator; +import net.consensys.linea.sequencer.txpoolvalidation.validators.SimulationValidator; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.TransactionSimulationService; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidatorFactory; @@ -36,21 +40,30 @@ public class LineaTransactionPoolValidatorFactory implements PluginTransactionPo private final BesuConfiguration besuConfiguration; private final BlockchainService blockchainService; + private final TransactionSimulationService transactionSimulationService; private final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf; private final LineaProfitabilityConfiguration profitabilityConf; private final Set
denied; + private final Map moduleLineLimitsMap; + private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; public LineaTransactionPoolValidatorFactory( final BesuConfiguration besuConfiguration, final BlockchainService blockchainService, + final TransactionSimulationService transactionSimulationService, final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf, final LineaProfitabilityConfiguration profitabilityConf, - final Set
denied) { + final Set
deniedAddresses, + final Map moduleLineLimitsMap, + final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) { this.besuConfiguration = besuConfiguration; this.blockchainService = blockchainService; + this.transactionSimulationService = transactionSimulationService; this.txPoolValidatorConf = txPoolValidatorConf; this.profitabilityConf = profitabilityConf; - this.denied = denied; + this.denied = deniedAddresses; + this.moduleLineLimitsMap = moduleLineLimitsMap; + this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; } @Override @@ -60,7 +73,13 @@ public PluginTransactionPoolValidator createTransactionValidator() { new AllowedAddressValidator(denied), new GasLimitValidator(txPoolValidatorConf), new CalldataValidator(txPoolValidatorConf), - new ProfitabilityValidator(besuConfiguration, blockchainService, profitabilityConf) + new ProfitabilityValidator(besuConfiguration, blockchainService, profitabilityConf), + new SimulationValidator( + blockchainService, + transactionSimulationService, + txPoolValidatorConf, + moduleLineLimitsMap, + l1L2BridgeConfiguration) }; return (transaction, isLocal, hasPriority) -> diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java index 52eb2de9..4d255cb5 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/LineaTransactionPoolValidatorPlugin.java @@ -15,6 +15,8 @@ package net.consensys.linea.sequencer.txpoolvalidation; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.createLimitModules; + import java.io.File; import java.nio.file.Files; import java.nio.file.Path; @@ -32,6 +34,7 @@ import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.TransactionPoolValidatorService; +import org.hyperledger.besu.plugin.services.TransactionSimulationService; /** * This class extends the default transaction validation rules for adding transactions to the @@ -46,6 +49,7 @@ public class LineaTransactionPoolValidatorPlugin extends AbstractLineaRequiredPl private BesuConfiguration besuConfiguration; private BlockchainService blockchainService; private TransactionPoolValidatorService transactionPoolValidatorService; + private TransactionSimulationService transactionSimulationService; @Override public Optional getName() { @@ -77,6 +81,14 @@ public void doRegister(final BesuContext context) { () -> new RuntimeException( "Failed to obtain TransactionPoolValidationService from the BesuContext.")); + + transactionSimulationService = + context + .getService(TransactionSimulationService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain TransactionSimulatorService from the BesuContext.")); } @Override @@ -85,16 +97,19 @@ public void beforeExternalServices() { try (Stream lines = Files.lines( Path.of(new File(transactionPoolValidatorConfiguration.denyListPath()).toURI()))) { - final Set
denied = + final Set
deniedAddresses = lines.map(l -> Address.fromHexString(l.trim())).collect(Collectors.toUnmodifiableSet()); transactionPoolValidatorService.registerPluginTransactionValidatorFactory( new LineaTransactionPoolValidatorFactory( besuConfiguration, blockchainService, + transactionSimulationService, transactionPoolValidatorConfiguration, profitabilityConfiguration, - denied)); + deniedAddresses, + createLimitModules(tracerConfiguration), + l1L2BridgeConfiguration)); } catch (Exception e) { throw new RuntimeException(e); diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java new file mode 100644 index 00000000..0709f4d9 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java @@ -0,0 +1,130 @@ +/* + * 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.sequencer.txpoolvalidation.validators; + +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.MODULE_NOT_DEFINED; +import static net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator.ModuleLineCountResult.TX_MODULE_LINE_COUNT_OVERFLOW; + +import java.util.Map; +import java.util.Optional; + +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; +import net.consensys.linea.sequencer.modulelimit.ModuleLimitsValidationResult; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; +import net.consensys.linea.zktracer.ZkTracer; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.TransactionSimulationService; +import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; + +@Slf4j +public class SimulationValidator implements PluginTransactionPoolValidator { + private final BlockchainService blockchainService; + private final TransactionSimulationService transactionSimulationService; + private final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf; + private final Map moduleLineLimitsMap; + private final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration; + + public SimulationValidator( + final BlockchainService blockchainService, + final TransactionSimulationService transactionSimulationService, + final LineaTransactionPoolValidatorConfiguration txPoolValidatorConf, + final Map moduleLineLimitsMap, + final LineaL1L2BridgeConfiguration l1L2BridgeConfiguration) { + this.blockchainService = blockchainService; + this.transactionSimulationService = transactionSimulationService; + this.txPoolValidatorConf = txPoolValidatorConf; + this.moduleLineLimitsMap = moduleLineLimitsMap; + this.l1L2BridgeConfiguration = l1L2BridgeConfiguration; + } + + @Override + public Optional validateTransaction( + final Transaction transaction, final boolean isLocal, final boolean hasPriority) { + + final boolean isLocalAndApiEnabled = + isLocal && txPoolValidatorConf.txPoolSimulationCheckApiEnabled(); + final boolean isRemoteAndP2pEnabled = + !isLocal && txPoolValidatorConf.txPoolSimulationCheckP2pEnabled(); + if (isRemoteAndP2pEnabled || isLocalAndApiEnabled) { + + final ModuleLineCountValidator moduleLineCountValidator = + new ModuleLineCountValidator(moduleLineLimitsMap); + final var chainHeadHeader = blockchainService.getChainHeadHeader(); + + final var zkTracer = createZkTracer(chainHeadHeader); + final var maybeSimulationResults = + transactionSimulationService.simulate( + transaction, chainHeadHeader.getBlockHash(), zkTracer, true); + + ModuleLimitsValidationResult moduleLimit = + moduleLineCountValidator.validate(zkTracer.getModulesLineCount()); + + if (moduleLimit.getResult() != ModuleLineCountValidator.ModuleLineCountResult.VALID) { + return Optional.of(handleModuleOverLimit(moduleLimit)); + } + + if (maybeSimulationResults.isPresent()) { + final var simulationResult = maybeSimulationResults.get(); + if (simulationResult.isInvalid()) { + return Optional.of( + "Invalid transaction" + + simulationResult.getInvalidReason().map(ir -> ": " + ir).orElse("")); + } + if (!simulationResult.isSuccessful()) { + return Optional.of( + "Reverted transaction" + + simulationResult + .getRevertReason() + .map(rr -> ": " + rr.toHexString()) + .orElse("")); + } + } + } + + return Optional.empty(); + } + + private ZkTracer createZkTracer(final BlockHeader chainHeadHeader) { + var zkTracer = new ZkTracer(l1L2BridgeConfiguration); + zkTracer.traceStartConflation(1L); + zkTracer.traceStartBlock(chainHeadHeader); + return zkTracer; + } + + private String handleModuleOverLimit(ModuleLimitsValidationResult moduleLimitResult) { + if (moduleLimitResult.getResult() == MODULE_NOT_DEFINED) { + String moduleNotDefinedMsg = + String.format( + "Module %s does not exist in the limits file.", moduleLimitResult.getModuleName()); + log.error(moduleNotDefinedMsg); + return moduleNotDefinedMsg; + } + if (moduleLimitResult.getResult() == TX_MODULE_LINE_COUNT_OVERFLOW) { + String txOverflowMsg = + String.format( + "Transaction line count for module %s=%s is above the limit %s", + moduleLimitResult.getModuleName(), + moduleLimitResult.getModuleLineCount(), + moduleLimitResult.getModuleLineLimit()); + log.warn(txOverflowMsg); + return txOverflowMsg; + } + return "Internal Error: do not know what to do with result: " + moduleLimitResult.getResult(); + } +} diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java index 34a18b6e..5a8a1143 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java @@ -16,9 +16,9 @@ package net.consensys.linea.sequencer.txpoolvalidation.validators; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; import java.math.BigInteger; -import java.nio.file.Path; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -30,18 +30,18 @@ import org.bouncycastle.crypto.params.ECDomainParameters; import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.plugin.data.BlockContext; -import org.hyperledger.besu.plugin.data.BlockHeader; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; -import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; @Slf4j @RequiredArgsConstructor +@ExtendWith(MockitoExtension.class) public class ProfitabilityValidatorTest { public static final Address SENDER = Address.fromHexString("0x0000000000000000000000000000000000001000"); @@ -71,6 +71,9 @@ public class ProfitabilityValidatorTest { private ProfitabilityValidator profitabilityValidatorOnlyP2p; private ProfitabilityValidator profitabilityValidatorNever; + @Mock BesuConfiguration besuConfiguration; + @Mock BlockchainService blockchainService; + @BeforeEach public void initialize() { final var profitabilityConfBuilder = @@ -79,8 +82,8 @@ public void initialize() { profitabilityValidatorAlways = new ProfitabilityValidator( - new TestBesuConfiguration(), - new TestBlockchainService(), + besuConfiguration, + blockchainService, profitabilityConfBuilder .txPoolCheckP2pEnabled(true) .txPoolCheckApiEnabled(true) @@ -88,8 +91,8 @@ public void initialize() { profitabilityValidatorNever = new ProfitabilityValidator( - new TestBesuConfiguration(), - new TestBlockchainService(), + besuConfiguration, + blockchainService, profitabilityConfBuilder .txPoolCheckP2pEnabled(false) .txPoolCheckApiEnabled(false) @@ -97,8 +100,8 @@ public void initialize() { profitabilityValidatorOnlyApi = new ProfitabilityValidator( - new TestBesuConfiguration(), - new TestBlockchainService(), + besuConfiguration, + blockchainService, profitabilityConfBuilder .txPoolCheckP2pEnabled(false) .txPoolCheckApiEnabled(true) @@ -106,8 +109,8 @@ public void initialize() { profitabilityValidatorOnlyP2p = new ProfitabilityValidator( - new TestBesuConfiguration(), - new TestBlockchainService(), + besuConfiguration, + blockchainService, profitabilityConfBuilder .txPoolCheckP2pEnabled(true) .txPoolCheckApiEnabled(false) @@ -132,6 +135,8 @@ public void acceptPriorityRemoteWhenBelowMinProfitability() { @Test public void rejectRemoteWhenBelowMinProfitability() { + when(besuConfiguration.getMinGasPrice()).thenReturn(Wei.of(100_000_000)); + when(blockchainService.getNextBlockBaseFee()).thenReturn(Optional.of(Wei.of(7))); final org.hyperledger.besu.ethereum.core.Transaction transaction = org.hyperledger.besu.ethereum.core.Transaction.builder() .sender(SENDER) @@ -196,6 +201,8 @@ public void acceptRemoteWhenBelowMinProfitabilityIfCheckDisabledForP2p() { @Test public void rejectRemoteWhenBelowMinProfitabilityIfCheckEnableForP2p() { + when(besuConfiguration.getMinGasPrice()).thenReturn(Wei.of(100_000_000)); + when(blockchainService.getNextBlockBaseFee()).thenReturn(Optional.of(Wei.of(7))); final org.hyperledger.besu.ethereum.core.Transaction transaction = org.hyperledger.besu.ethereum.core.Transaction.builder() .sender(SENDER) @@ -229,6 +236,8 @@ public void acceptLocalWhenBelowMinProfitabilityIfCheckDisabledForApi() { @Test public void rejectLocalWhenBelowMinProfitabilityIfCheckEnableForApi() { + when(besuConfiguration.getMinGasPrice()).thenReturn(Wei.of(100_000_000)); + when(blockchainService.getNextBlockBaseFee()).thenReturn(Optional.of(Wei.of(7))); final org.hyperledger.besu.ethereum.core.Transaction transaction = org.hyperledger.besu.ethereum.core.Transaction.builder() .sender(SENDER) @@ -243,49 +252,4 @@ public void rejectLocalWhenBelowMinProfitabilityIfCheckEnableForApi() { .isPresent() .contains("Gas price too low"); } - - private static class TestBesuConfiguration implements BesuConfiguration { - @Override - public Path getStoragePath() { - throw new UnsupportedOperationException(); - } - - @Override - public Path getDataPath() { - throw new UnsupportedOperationException(); - } - - @Override - public DataStorageFormat getDatabaseFormat() { - throw new UnsupportedOperationException(); - } - - @Override - public Wei getMinGasPrice() { - return Wei.of(100_000_000); - } - } - - private static class TestBlockchainService implements BlockchainService { - - @Override - public Optional getBlockByNumber(final long l) { - throw new UnsupportedOperationException(); - } - - @Override - public Hash getChainHeadHash() { - throw new UnsupportedOperationException(); - } - - @Override - public BlockHeader getChainHeadHeader() { - throw new UnsupportedOperationException(); - } - - @Override - public Optional getNextBlockBaseFee() { - return Optional.of(Wei.of(7)); - } - } } diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java new file mode 100644 index 00000000..477ca7c4 --- /dev/null +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java @@ -0,0 +1,166 @@ +/* + * 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.sequencer.txpoolvalidation.validators; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaL1L2BridgeConfiguration; +import net.consensys.linea.config.LineaTracerConfiguration; +import net.consensys.linea.config.LineaTransactionPoolValidatorConfiguration; +import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; +import net.consensys.linea.sequencer.txselection.selectors.TraceLineLimitTransactionSelectorTest; +import org.apache.tuweni.bytes.Bytes; +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.hyperledger.besu.crypto.SECPSignature; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.TransactionSimulationService; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@Slf4j +@RequiredArgsConstructor +@ExtendWith(MockitoExtension.class) +public class SimulationValidatorTest { + private static final String MODULE_LINE_LIMITS_RESOURCE_NAME = "/sequencer/line-limits.toml"; + public static final Address SENDER = + Address.fromHexString("0x0000000000000000000000000000000000001000"); + public static final Address RECIPIENT = + Address.fromHexString("0x0000000000000000000000000000000000001001"); + private static Wei BASE_FEE = Wei.of(7); + private static Wei PROFITABLE_GAS_PRICE = Wei.of(11000000); + private static final SECPSignature FAKE_SIGNATURE; + private static final Address BRIDGE_CONTRACT = + Address.fromHexString("0x508Ca82Df566dCD1B0DE8296e70a96332cD644ec"); + private static final Bytes BRIDGE_LOG_TOPIC = + Bytes.fromHexString("e856c2b8bd4eb0027ce32eeaf595c21b0b6b4644b326e5b7bd80a1cf8db72e6c"); + + static { + final X9ECParameters params = SECNamedCurves.getByName("secp256k1"); + final ECDomainParameters curve = + new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH()); + FAKE_SIGNATURE = + SECPSignature.create( + new BigInteger( + "66397251408932042429874251838229702988618145381408295790259650671563847073199"), + new BigInteger( + "24729624138373455972486746091821238755870276413282629437244319694880507882088"), + (byte) 0, + curve.getN()); + } + + private Map lineCountLimits; + + @Mock BlockchainService blockchainService; + @Mock TransactionSimulationService transactionSimulationService; + + @TempDir static Path tempDir; + static Path lineLimitsConfPath; + + @BeforeAll + public static void beforeAll() throws IOException { + lineLimitsConfPath = tempDir.resolve("line-limits.toml"); + Files.copy( + TraceLineLimitTransactionSelectorTest.class.getResourceAsStream( + MODULE_LINE_LIMITS_RESOURCE_NAME), + lineLimitsConfPath); + } + + @BeforeEach + public void initialize() { + final var tracerConf = + LineaTracerConfiguration.builder() + .moduleLimitsFilePath(lineLimitsConfPath.toString()) + .build(); + lineCountLimits = new HashMap<>(ModuleLineCountValidator.createLimitModules(tracerConf)); + final var blockHeader = mock(BlockHeader.class); + when(blockHeader.getBaseFee()).thenReturn(Optional.of(BASE_FEE)); + when(blockchainService.getChainHeadHeader()).thenReturn(blockHeader); + } + + private SimulationValidator createSimulationValidator( + final Map lineCountLimits, + final boolean enableForApi, + final boolean enableForP2p) { + return new SimulationValidator( + blockchainService, + transactionSimulationService, + LineaTransactionPoolValidatorConfiguration.builder() + .txPoolSimulationCheckApiEnabled(enableForApi) + .txPoolSimulationCheckP2pEnabled(enableForP2p) + .build(), + lineCountLimits, + LineaL1L2BridgeConfiguration.builder() + .contract(BRIDGE_CONTRACT) + .topic(BRIDGE_LOG_TOPIC) + .build()); + } + + @Test + public void successfulTransactionIsValid() { + final var simulationValidator = createSimulationValidator(lineCountLimits, true, false); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(PROFITABLE_GAS_PRICE) + .payload(Bytes.EMPTY) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(simulationValidator.validateTransaction(transaction, true, false)).isEmpty(); + } + + @Test + public void moduleLineCountOverflowTransactionIsInvalid() { + lineCountLimits.put("ADD", 1); + final var simulationValidator = createSimulationValidator(lineCountLimits, true, false); + final org.hyperledger.besu.ethereum.core.Transaction transaction = + org.hyperledger.besu.ethereum.core.Transaction.builder() + .sender(SENDER) + .to(RECIPIENT) + .gasLimit(21000) + .gasPrice(PROFITABLE_GAS_PRICE) + .payload(Bytes.repeat((byte) 1, 1000)) + .value(Wei.ONE) + .signature(FAKE_SIGNATURE) + .build(); + assertThat(simulationValidator.validateTransaction(transaction, true, false)) + .contains("Transaction line count for module ADD=2 is above the limit 1"); + } +} From 4c34399d83e8936b6bf4d3d184003167d8ec02e2 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Fri, 12 Apr 2024 03:46:25 +0200 Subject: [PATCH 23/30] Update Linea Besu (#5) * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia * feature: Add module limit verification to linea_estimateGas Signed-off-by: Gabriel-Trintinalia * feature: PR suggestion Signed-off-by: Gabriel-Trintinalia * feature: Add unit test Signed-off-by: Gabriel-Trintinalia * feature: Fix logging Signed-off-by: Gabriel-Trintinalia * Reject a tx, sent via eth_sendRawTransaction, if its simulation fails Signed-off-by: Fabio Di Fabio * remove unused method Signed-off-by: Fabio Di Fabio * Apply suggestions from code review Signed-off-by: Fabio Di Fabio * Fix test flakyness Signed-off-by: Fabio Di Fabio * Update Linea Besu version Signed-off-by: Fabio Di Fabio --------- Signed-off-by: Gabriel-Trintinalia Signed-off-by: Fabio Di Fabio Co-authored-by: Gabriel-Trintinalia --- .../org/hyperledger/besu/tests/acceptance/dsl/BlockUtils.java | 1 + gradle.properties | 2 +- .../test/java/net/consensys/linea/CorsetBlockProcessor.java | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/acceptance-tests/src/test/java/org/hyperledger/besu/tests/acceptance/dsl/BlockUtils.java b/acceptance-tests/src/test/java/org/hyperledger/besu/tests/acceptance/dsl/BlockUtils.java index ba54d9e3..9922c745 100644 --- a/acceptance-tests/src/test/java/org/hyperledger/besu/tests/acceptance/dsl/BlockUtils.java +++ b/acceptance-tests/src/test/java/org/hyperledger/besu/tests/acceptance/dsl/BlockUtils.java @@ -67,6 +67,7 @@ public static BlockHeader createBlockHeader( null, null, null, + null, blockHeaderFunctions); } } diff --git a/gradle.properties b/gradle.properties index 80f6c4e3..ae5f5b73 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ releaseVersion=0.1.4-SNAPSHOT -besuVersion=24.3-develop-5a0618f151 +besuVersion=24.4-develop-a5a3eb8 besuArtifactGroup=io.consensys.linea-besu distributionIdentifier=besu-sequencer-plugins distributionBaseUrl=https://artifacts.consensys.net/public/linea-besu/raw/names/linea-besu.tar.gz/versions/ diff --git a/reference-tests/src/test/java/net/consensys/linea/CorsetBlockProcessor.java b/reference-tests/src/test/java/net/consensys/linea/CorsetBlockProcessor.java index c2508eb6..2deddaba 100644 --- a/reference-tests/src/test/java/net/consensys/linea/CorsetBlockProcessor.java +++ b/reference-tests/src/test/java/net/consensys/linea/CorsetBlockProcessor.java @@ -46,8 +46,8 @@ import org.hyperledger.besu.ethereum.mainnet.WithdrawalsProcessor; import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; -import org.hyperledger.besu.ethereum.trie.bonsai.worldview.BonsaiWorldState; -import org.hyperledger.besu.ethereum.trie.bonsai.worldview.BonsaiWorldStateUpdateAccumulator; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; +import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldStateUpdateAccumulator; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.evm.worldstate.WorldUpdater; From 6e52d3ea213e3ec14f3ded92839ef5bd386a1afa Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Fri, 3 May 2024 16:39:48 +0200 Subject: [PATCH 24/30] Build native lib for arm64 (#8) Signed-off-by: Fabio Di Fabio --- .github/workflows/release.yml | 2 +- README.md | 4 +++ ...SendRawTransactionSimulationCheckTest.java | 2 +- ...ile-win-dockcross => Dockerfile-dockcross} | 0 native/build.sh | 10 ++++-- native/compress/build.gradle | 26 ++++++--------- native/release.sh | 32 +++++++++++++++++++ native/wsl.sh | 17 ---------- 8 files changed, 55 insertions(+), 38 deletions(-) rename native/{Dockerfile-win-dockcross => Dockerfile-dockcross} (100%) create mode 100755 native/release.sh delete mode 100644 native/wsl.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 95f007e1..53659ab7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,7 +27,7 @@ jobs: run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - name: Build without tests - run: ./gradlew build -PreleaseVersion=${{ steps.get_version.outputs.VERSION }} -x test -x spotlessCheck + run: ./gradlew build -PreleaseNativeLibs -PreleaseVersion=${{ steps.get_version.outputs.VERSION }} -x test -x spotlessCheck env: JAVA_OPTS: -Xmx2g -Dorg.gradle.daemon=false diff --git a/README.md b/README.md index 0b83fc12..6afe4a80 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,10 @@ Linux/MacOs Windows * Requirement [Docker Desktop WSL 2 backend on Windows](https://docs.docker.com/desktop/wsl/) +On release native libs are build for all the supported platforms, +if you want to test this process locally run `./gradlew -PreleaseNativeLibs jar`, +jar is generated in `arithmetization/build/libs`. + ### Install Rust ``` diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java index 8534fe99..b8450f4e 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EthSendRawTransactionSimulationCheckTest.java @@ -78,7 +78,7 @@ public void transactionOverModuleLineCountNotAccepted() throws Exception { assertThat(signedTxContractInteractionResp.hasError()).isTrue(); assertThat(signedTxContractInteractionResp.getError().getMessage()) - .isEqualTo("Transaction line count for module ADD=2018 is above the limit 70"); + .isEqualTo("Transaction line count for module ADD=402 is above the limit 70"); assertThat(getTxPoolContent()).isEmpty(); diff --git a/native/Dockerfile-win-dockcross b/native/Dockerfile-dockcross similarity index 100% rename from native/Dockerfile-win-dockcross rename to native/Dockerfile-dockcross diff --git a/native/build.sh b/native/build.sh index 85be58c7..47c3286f 100755 --- a/native/build.sh +++ b/native/build.sh @@ -14,12 +14,15 @@ OSTYPE=$(uname -o) # delete old build dir, if exists rm -rf "$SCRIPTDIR/compress/build/native" || true -mkdir -p "$SCRIPTDIR/compress/build/native" + +ARCH_DIR="" if [ x"$OSTYPE" = x"msys" ]; then LIBRARY_EXTENSION=dll elif [ x"$OSTYPE" = x"GNU/Linux" ]; then LIBRARY_EXTENSION=so + ARCHITECTURE="$(uname --machine)" + ARCH_DIR="linux-$ARCHITECTURE" elif [ x"$OSTYPE" = x"Darwin" ]; then LIBRARY_EXTENSION=dylib else @@ -27,7 +30,10 @@ else exit 1 fi +DEST_DIR="$SCRIPTDIR/compress/build/native/$ARCH_DIR" +mkdir -p "$DEST_DIR" + cd "$SCRIPTDIR/compress/compress-jni" echo "Building Go module libcompress_jni.$LIBRARY_EXTENSION for $OSTYPE" CGO_ENABLED=1 go build -buildmode=c-shared -o libcompress_jni.$LIBRARY_EXTENSION compress-jni.go -mv libcompress_jni.* "$SCRIPTDIR/compress/build/native" +mv libcompress_jni.* "$DEST_DIR" diff --git a/native/compress/build.gradle b/native/compress/build.gradle index 9f456542..ea29549c 100644 --- a/native/compress/build.gradle +++ b/native/compress/build.gradle @@ -33,12 +33,15 @@ test { } tasks.register('buildJNI', Exec) { - if(org.gradle.internal.os.OperatingSystem.current().isWindows()) { + if(project.hasProperty("releaseNativeLibs")) { workingDir buildscript.sourceFile.parentFile.parentFile - commandLine 'wsl', './wsl.sh' + commandLine 'sh', '-c', './release.sh' + } else if(org.gradle.internal.os.OperatingSystem.current().isWindows()) { + workingDir buildscript.sourceFile.parentFile.parentFile + commandLine 'wsl', './release.sh' } else { - workingDir buildscript.sourceFile.parentFile - commandLine 'sh', '-c', '../build.sh' + workingDir buildscript.sourceFile.parentFile.parentFile + commandLine 'sh', '-c', './build.sh' } } @@ -70,13 +73,13 @@ tasks.register('macLibCopy', Copy) { processResources.dependsOn macLibCopy tasks.register('linuxLibCopy', Copy) { - from 'build/native/libcompress_jni.so' + from 'build/native/linux-x86_64/libcompress_jni.so' into 'build/resources/main/linux-x86-64' } processResources.dependsOn linuxLibCopy tasks.register('linuxArm64LibCopy', Copy) { - from 'build/native/libcompress_jni.so' + from 'build/native/linux-aarch64/libcompress_jni.so' into 'build/resources/main/linux-aarch64' } processResources.dependsOn linuxArm64LibCopy @@ -102,14 +105,3 @@ jar { } jar.dependsOn(buildJNI) - -sourceSets { - main { - resources { - srcDirs '${buildDir}/darwin-aaarch64' - srcDirs '${buildDir}/darwin-x86-64' - srcDirs '${buildDir}/linux-gnu-x86_64' - srcDirs '${buildDir}/linux-gnu-aarch64' - } - } -} diff --git a/native/release.sh b/native/release.sh new file mode 100755 index 00000000..45d18f3a --- /dev/null +++ b/native/release.sh @@ -0,0 +1,32 @@ +#!/bin/bash -x + +docker build -f Dockerfile-dockcross --build-arg IMAGE=windows-shared-x64 -t native-windows-shared-x64-cross-compile . +docker build -f Dockerfile-dockcross --build-arg IMAGE=linux-x64 -t native-linux-x64-cross-compile . +docker build -f Dockerfile-dockcross --build-arg IMAGE=linux-arm64 -t native-linux-arm64-cross-compile . + +mkdir -p compress/build/native/linux-x86_64 +mkdir -p compress/build/native/linux-aarch64 + +docker run --rm native-windows-shared-x64-cross-compile > compress/build/native/native-windows-shared-x64-cross-compile +docker run --rm native-linux-x64-cross-compile > compress/build/native/native-linux-x64-cross-compile +docker run --rm native-linux-arm64-cross-compile > compress/build/native/native-linux-arm64-cross-compile + +chmod +x \ +compress/build/native/native-windows-shared-x64-cross-compile \ +compress/build/native/native-linux-x64-cross-compile \ +compress/build/native/native-linux-arm64-cross-compile + +OCI_EXE=docker \ +compress/build/native/native-windows-shared-x64-cross-compile --image native-windows-shared-x64-cross-compile \ + bash -c "cd compress/compress-jni && + CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -buildmode=c-shared -o ../build/native/compress_jni.dll compress-jni.go" + +OCI_EXE=docker \ +compress/build/native/native-linux-x64-cross-compile --image native-linux-x64-cross-compile \ + bash -c "cd compress/compress-jni && + CGO_ENABLED=1 go build -buildmode=c-shared -o ../build/native/linux-x86_64/libcompress_jni.so compress-jni.go" + +OCI_EXE=docker \ +compress/build/native/native-linux-arm64-cross-compile --image native-linux-arm64-cross-compile \ + bash -c "cd compress/compress-jni && + CGO_ENABLED=1 GOARCH=arm64 go build -buildmode=c-shared -o ../build/native/linux-aarch64/libcompress_jni.so compress-jni.go" diff --git a/native/wsl.sh b/native/wsl.sh deleted file mode 100644 index 64387d15..00000000 --- a/native/wsl.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -x - -docker build -f Dockerfile-win-dockcross --build-arg IMAGE=windows-shared-x64 -t native-windows-shared-x64-cross-compile . -docker build -f Dockerfile-win-dockcross --build-arg IMAGE=linux-x64 -t native-linux-x64-cross-compile . - -mkdir -p compress/build/native/ - -docker run --rm native-windows-shared-x64-cross-compile > compress/build/native/native-windows-shared-x64-cross-compile -docker run --rm native-linux-x64-cross-compile > compress/build/native/native-linux-x64-cross-compile - -compress/build/native/native-windows-shared-x64-cross-compile --image native-windows-shared-x64-cross-compile \ - bash -c "cd compress/compress-jni && - CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -buildmode=c-shared -o ../build/native/compress_jni.dll compress-jni.go" - -compress/build/native/native-linux-x64-cross-compile --image native-linux-x64-cross-compile \ - bash -c "cd compress/compress-jni && - CGO_ENABLED=1 go build -buildmode=c-shared -o ../build/native/libcompress_jni.so compress-jni.go" From 764914e818c463030d386238bcd7d9b5c6c1903a Mon Sep 17 00:00:00 2001 From: ahamlat Date: Thu, 16 May 2024 20:46:31 +0200 Subject: [PATCH 25/30] Improve ZkTracer initialization time (#11) Improve ZkTracer initialization time by doing only once Opcodes and spilling loading from disk resources Signed-off-by: Ameziane H --- .../ContinuousTracingPlugin.java | 3 -- .../capture/CaptureEndpointServicePlugin.java | 5 +-- .../CountersEndpointServicePlugin.java | 5 +-- .../TracesEndpointServicePlugin.java | 5 +-- .../consensys/linea/zktracer/ZkTracer.java | 43 +++++++++---------- .../linea/zktracer/opcode/OpCodes.java | 8 +++- .../consensys/linea/zktracer/types/Utils.java | 16 +++++++ .../linea/zktracer/module/EcDataTest.java | 3 -- .../linea/zktracer/module/add/addTest.java | 3 -- .../linea/zktracer/module/bin/BinTest.java | 3 -- .../module/ext/TestDuplicatedOperations.java | 3 -- .../module/hub/CallEmptyNoStopTest.java | 3 -- .../linea/zktracer/module/hub/OtherTests.java | 3 -- .../zktracer/module/hub/TestTwoPlusTwo.java | 3 -- .../linea/zktracer/module/mod/ModTest.java | 3 -- .../linea/zktracer/module/mxp/MxpTest.java | 3 -- .../module/precompiles/precTests.java | 4 -- .../module/rlpAddr/TestRlpAddress.java | 3 -- .../module/rlptxn/TestRandomTxns.java | 2 - .../zktracer/module/shf/ShfRtTracerTest.java | 3 -- .../linea/zktracer/module/stp/StpTest.java | 6 --- .../zktracer/module/trm/TrmTracerTest.java | 3 -- .../zktracer/module/wcp/wcpEdgeCaseTest.java | 3 -- .../linea/zktracer/testing/DynamicTests.java | 2 - .../linea/zktracer/testing/EvmExtension.java | 31 ------------- .../linea/zktracer/testing/ExampleTxTest.java | 2 - .../net/consensys/linea/EvmExtension.java | 31 ------------- .../BlockchainReferenceTest.java.template | 3 -- .../GeneralStateReferenceTest.java.template | 3 -- 29 files changed, 45 insertions(+), 163 deletions(-) delete mode 100644 arithmetization/src/test/java/net/consensys/linea/zktracer/testing/EvmExtension.java delete mode 100644 reference-tests/src/test/java/net/consensys/linea/EvmExtension.java diff --git a/arithmetization/src/main/java/net/consensys/linea/continoustracing/ContinuousTracingPlugin.java b/arithmetization/src/main/java/net/consensys/linea/continoustracing/ContinuousTracingPlugin.java index e0b22dd5..8a76acf8 100644 --- a/arithmetization/src/main/java/net/consensys/linea/continoustracing/ContinuousTracingPlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/continoustracing/ContinuousTracingPlugin.java @@ -19,7 +19,6 @@ import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.corset.CorsetValidator; -import net.consensys.linea.zktracer.opcode.OpCodes; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.services.BesuEvents; @@ -97,8 +96,6 @@ public void start() { System.exit(1); } - OpCodes.load(); // must be loaded explicitly - besuEvents.addBlockAddedListener( new ContinuousTracingBlockAddedListener( new ContinuousTracer(traceService, new CorsetValidator()), diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/capture/CaptureEndpointServicePlugin.java b/arithmetization/src/main/java/net/consensys/linea/rpc/capture/CaptureEndpointServicePlugin.java index b7d7bb78..3868fb07 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/capture/CaptureEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/capture/CaptureEndpointServicePlugin.java @@ -20,7 +20,6 @@ import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.AbstractLineaRequiredPlugin; -import net.consensys.linea.zktracer.opcode.OpCodes; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.services.RpcEndpointService; @@ -66,7 +65,5 @@ private void createAndRegister( /** Start the RPC service. This method loads the OpCodes. */ @Override - public void start() { - OpCodes.load(); - } + public void start() {} } diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/counters/CountersEndpointServicePlugin.java b/arithmetization/src/main/java/net/consensys/linea/rpc/counters/CountersEndpointServicePlugin.java index 5da2b58a..8c46578d 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/counters/CountersEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/counters/CountersEndpointServicePlugin.java @@ -17,7 +17,6 @@ import com.google.auto.service.AutoService; import net.consensys.linea.AbstractLineaSharedOptionsPlugin; -import net.consensys.linea.zktracer.opcode.OpCodes; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.services.RpcEndpointService; @@ -72,7 +71,5 @@ private void createAndRegister( /** Start the RPC service. This method loads the OpCodes. */ @Override - public void start() { - OpCodes.load(); - } + public void start() {} } diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/tracegeneration/TracesEndpointServicePlugin.java b/arithmetization/src/main/java/net/consensys/linea/rpc/tracegeneration/TracesEndpointServicePlugin.java index e73fb70d..b4bb0384 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/tracegeneration/TracesEndpointServicePlugin.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/tracegeneration/TracesEndpointServicePlugin.java @@ -18,7 +18,6 @@ import com.google.auto.service.AutoService; import lombok.extern.slf4j.Slf4j; import net.consensys.linea.AbstractLineaSharedOptionsPlugin; -import net.consensys.linea.zktracer.opcode.OpCodes; import org.hyperledger.besu.plugin.BesuContext; import org.hyperledger.besu.plugin.BesuPlugin; import org.hyperledger.besu.plugin.services.RpcEndpointService; @@ -74,7 +73,5 @@ private void createAndRegister( /** Start the RPC service. This method loads the OpCodes. */ @Override - public void start() { - OpCodes.load(); - } + public void start() {} } diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/ZkTracer.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/ZkTracer.java index 747a1803..1d949f8b 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/ZkTracer.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/ZkTracer.java @@ -32,10 +32,8 @@ import net.consensys.linea.config.LineaL1L2BridgeConfiguration; import net.consensys.linea.zktracer.module.Module; import net.consensys.linea.zktracer.module.hub.Hub; -import net.consensys.linea.zktracer.opcode.OpCodes; +import net.consensys.linea.zktracer.types.Utils; import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.toml.Toml; -import org.apache.tuweni.toml.TomlTable; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.PendingTransaction; import org.hyperledger.besu.datatypes.Transaction; @@ -54,8 +52,21 @@ public class ZkTracer implements ConflationAwareOperationTracer { /** The {@link GasCalculator} used in this version of the arithmetization */ public static final GasCalculator gasCalculator = new LondonGasCalculator(); + private static final Map spillings; + + static { + try { + // Load spillings configured in src/main/resources/spillings.toml. + spillings = Utils.computeSpillings(); + } catch (final Exception e) { + final String errorMsg = + "A problem happened during spillings initialization, cause " + e.getCause(); + log.error(errorMsg); + throw new RuntimeException(e); + } + } + @Getter private final Hub hub; - private final Map spillings = new HashMap<>(); private Hash hashOfLastTransactionTraced = Hash.EMPTY; public ZkTracer() { @@ -64,25 +75,11 @@ public ZkTracer() { public ZkTracer(final LineaL1L2BridgeConfiguration bridgeConfiguration) { this.hub = new Hub(bridgeConfiguration.contract(), bridgeConfiguration.topic()); - - // Load opcodes configured in src/main/resources/opcodes.yml. - OpCodes.load(); - - // Load spillings configured in src/main/resources/spillings.toml. - try { - final TomlTable table = - Toml.parse(getClass().getClassLoader().getResourceAsStream("spillings.toml")) - .getTable("spillings"); - table.toMap().keySet().forEach(k -> spillings.put(k, Math.toIntExact(table.getLong(k)))); - - for (Module m : this.hub.getModulesToCount()) { - if (!this.spillings.containsKey(m.moduleKey())) { - throw new IllegalStateException( - "Spilling for module " + m.moduleKey() + " not defined in spillings.toml"); - } + for (Module m : this.hub.getModulesToCount()) { + if (!spillings.containsKey(m.moduleKey())) { + throw new IllegalStateException( + "Spilling for module " + m.moduleKey() + " not defined in spillings.toml"); } - } catch (final Exception e) { - throw new RuntimeException(e); } } @@ -231,7 +228,7 @@ public Map getModulesLineCount() { modulesLineCount.put( m.moduleKey(), m.lineCount() - + Optional.ofNullable(this.spillings.get(m.moduleKey())) + + Optional.ofNullable(spillings.get(m.moduleKey())) .orElseThrow( () -> new IllegalStateException( diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/opcode/OpCodes.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/opcode/OpCodes.java index 2b293bfb..3ed4a836 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/opcode/OpCodes.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/opcode/OpCodes.java @@ -35,9 +35,13 @@ public class OpCodes { private static Map valueToOpCodeDataMap; private static Map opCodeToOpCodeDataMap; - /** Loads all opcode metadata from src/main/resources/opcodes.yml. */ + static { + /** Loads all opcode metadata from src/main/resources/opcodes.yml. */ + init(); + } + @SneakyThrows(IOException.class) - public static void load() { + private static void init() { JsonNode rootNode = YAML_CONVERTER .getObjectMapper() diff --git a/arithmetization/src/main/java/net/consensys/linea/zktracer/types/Utils.java b/arithmetization/src/main/java/net/consensys/linea/zktracer/types/Utils.java index 54a18464..222c91c7 100644 --- a/arithmetization/src/main/java/net/consensys/linea/zktracer/types/Utils.java +++ b/arithmetization/src/main/java/net/consensys/linea/zktracer/types/Utils.java @@ -15,10 +15,15 @@ package net.consensys.linea.zktracer.types; +import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import com.google.common.base.Preconditions; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.toml.Toml; +import org.apache.tuweni.toml.TomlTable; public class Utils { @@ -83,4 +88,15 @@ public static BitDecOutput bitDecomposition(int input, int nbStep) { } return output; } + + public static Map computeSpillings() throws IOException { + final Map spillings = new HashMap<>(); + + final TomlTable table = + Toml.parse(Utils.class.getClassLoader().getResourceAsStream("spillings.toml")) + .getTable("spillings"); + table.toMap().keySet().forEach(k -> spillings.put(k, Math.toIntExact(table.getLong(k)))); + + return spillings; + } } diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/EcDataTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/EcDataTest.java index 7be7baac..92891673 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/EcDataTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/EcDataTest.java @@ -16,12 +16,9 @@ package net.consensys.linea.zktracer.module; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class EcDataTest { @Test void testEcData() { diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/add/addTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/add/addTest.java index 77ee5a45..7aa06819 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/add/addTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/add/addTest.java @@ -18,12 +18,9 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class addTest { @Test void testSmallZeroAdd() { diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/bin/BinTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/bin/BinTest.java index ab361a71..b7bc806b 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/bin/BinTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/bin/BinTest.java @@ -18,12 +18,9 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.apache.tuweni.units.bigints.UInt256; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class BinTest { @Test public void edgeCase() { diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/ext/TestDuplicatedOperations.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/ext/TestDuplicatedOperations.java index f9a5917f..30533213 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/ext/TestDuplicatedOperations.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/ext/TestDuplicatedOperations.java @@ -20,12 +20,9 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class TestDuplicatedOperations { @Test void testDuplicate() { diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/CallEmptyNoStopTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/CallEmptyNoStopTest.java index a61f2435..4f1ea008 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/CallEmptyNoStopTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/CallEmptyNoStopTest.java @@ -21,7 +21,6 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; -import net.consensys.linea.zktracer.testing.EvmExtension; import net.consensys.linea.zktracer.testing.ToyAccount; import net.consensys.linea.zktracer.testing.ToyExecutionEnvironment; import net.consensys.linea.zktracer.testing.ToyTransaction; @@ -34,10 +33,8 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.Transaction; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; /** Ensure that calling a contract with empty code does not generate a virtual STOP trace */ -@ExtendWith(EvmExtension.class) public class CallEmptyNoStopTest { @Test void test() { diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/OtherTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/OtherTests.java index d69e4066..080270f4 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/OtherTests.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/OtherTests.java @@ -18,11 +18,8 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class OtherTests { @Test public void testMul() { diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/TestTwoPlusTwo.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/TestTwoPlusTwo.java index 70d6a4ef..8008b3f8 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/TestTwoPlusTwo.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/hub/TestTwoPlusTwo.java @@ -18,11 +18,8 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class TestTwoPlusTwo { @Test void testAdd() { diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/mod/ModTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/mod/ModTest.java index 0e10b9ac..d36a4d26 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/mod/ModTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/mod/ModTest.java @@ -18,12 +18,9 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.apache.tuweni.units.bigints.UInt256; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class ModTest { @Test void testSignedSmod() { diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/mxp/MxpTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/mxp/MxpTest.java index d71d0031..03d30ac0 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/mxp/MxpTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/mxp/MxpTest.java @@ -27,7 +27,6 @@ import net.consensys.linea.zktracer.opcode.gas.MxpType; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import net.consensys.linea.zktracer.testing.ToyAccount; import net.consensys.linea.zktracer.testing.ToyExecutionEnvironment; import net.consensys.linea.zktracer.testing.ToyTransaction; @@ -42,11 +41,9 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.Transaction; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; // https://github.com/Consensys/linea-besu-plugin/issues/197 -@ExtendWith(EvmExtension.class) public class MxpTest { private static final Random RAND = new Random(123456789123456L); public static final EWord TWO_POW_128 = EWord.of(EWord.ONE.shiftLeft(128)); diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/precompiles/precTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/precompiles/precTests.java index 5fef7916..51301e3e 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/precompiles/precTests.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/precompiles/precTests.java @@ -14,8 +14,4 @@ */ package net.consensys.linea.zktracer.module.precompiles; -import net.consensys.linea.zktracer.testing.EvmExtension; -import org.junit.jupiter.api.extension.ExtendWith; - -@ExtendWith(EvmExtension.class) public class precTests {} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/rlpAddr/TestRlpAddress.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/rlpAddr/TestRlpAddress.java index 1fd17098..dd521f2d 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/rlpAddr/TestRlpAddress.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/rlpAddr/TestRlpAddress.java @@ -26,7 +26,6 @@ import java.util.Random; import net.consensys.linea.zktracer.opcode.OpCode; -import net.consensys.linea.zktracer.opcode.OpCodes; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.ToyAccount; import net.consensys.linea.zktracer.testing.ToyExecutionEnvironment; @@ -48,8 +47,6 @@ public class TestRlpAddress { @Test void test() { - OpCodes.load(); - ToyWorld.ToyWorldBuilder world = ToyWorld.builder(); List txList = new ArrayList<>(); diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/rlptxn/TestRandomTxns.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/rlptxn/TestRandomTxns.java index 4a9c348b..3e6f5329 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/rlptxn/TestRandomTxns.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/rlptxn/TestRandomTxns.java @@ -24,7 +24,6 @@ import java.util.Random; import net.consensys.linea.zktracer.opcode.OpCode; -import net.consensys.linea.zktracer.opcode.OpCodes; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.ToyAccount; import net.consensys.linea.zktracer.testing.ToyExecutionEnvironment; @@ -48,7 +47,6 @@ class TestRandomTxns { @Test void test() { - OpCodes.load(); ToyWorld.ToyWorldBuilder world = ToyWorld.builder(); List txList = new ArrayList<>(); diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/shf/ShfRtTracerTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/shf/ShfRtTracerTest.java index 17f23ad8..6a0e7793 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/shf/ShfRtTracerTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/shf/ShfRtTracerTest.java @@ -22,18 +22,15 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @Slf4j -@ExtendWith(EvmExtension.class) class ShfRtTracerTest { private static final Random RAND = new Random(); private static final int TEST_REPETITIONS = 4; diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/stp/StpTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/stp/StpTest.java index 0f5ae87b..b03a29ba 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/stp/StpTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/stp/StpTest.java @@ -25,9 +25,7 @@ import java.util.Random; import net.consensys.linea.zktracer.opcode.OpCode; -import net.consensys.linea.zktracer.opcode.OpCodes; import net.consensys.linea.zktracer.testing.BytecodeCompiler; -import net.consensys.linea.zktracer.testing.EvmExtension; import net.consensys.linea.zktracer.testing.ToyAccount; import net.consensys.linea.zktracer.testing.ToyExecutionEnvironment; import net.consensys.linea.zktracer.testing.ToyTransaction; @@ -42,9 +40,7 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.Transaction; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class StpTest { private static final Random RAND = new Random(666L); final int NB_CALL = 200; @@ -52,7 +48,6 @@ public class StpTest { @Test void testCall() { - OpCodes.load(); ToyWorld.ToyWorldBuilder world = ToyWorld.builder(); List txList = new ArrayList<>(); @@ -78,7 +73,6 @@ void testCall() { @Test void testCreate() { - OpCodes.load(); ToyWorld.ToyWorldBuilder world = ToyWorld.builder(); List txList = new ArrayList<>(); diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/trm/TrmTracerTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/trm/TrmTracerTest.java index 44e6e8e6..0515dd86 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/trm/TrmTracerTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/trm/TrmTracerTest.java @@ -18,13 +18,10 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class TrmTracerTest { private final Bytes32 RANDOM_STRING_FROM_THE_INTERNET = Bytes32.fromHexString( diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/wcp/wcpEdgeCaseTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/wcp/wcpEdgeCaseTest.java index ac91c95a..acb8b358 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/module/wcp/wcpEdgeCaseTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/module/wcp/wcpEdgeCaseTest.java @@ -18,12 +18,9 @@ import net.consensys.linea.zktracer.opcode.OpCode; import net.consensys.linea.zktracer.testing.BytecodeCompiler; import net.consensys.linea.zktracer.testing.BytecodeRunner; -import net.consensys.linea.zktracer.testing.EvmExtension; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) public class wcpEdgeCaseTest { @Test void testZeroAndHugeArgs() { diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/DynamicTests.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/DynamicTests.java index 9a098f99..ee09c235 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/DynamicTests.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/DynamicTests.java @@ -31,7 +31,6 @@ import net.consensys.linea.zktracer.module.shf.Shf; import net.consensys.linea.zktracer.module.wcp.Wcp; import net.consensys.linea.zktracer.opcode.OpCode; -import net.consensys.linea.zktracer.opcode.OpCodes; import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.DynamicTest; @@ -48,7 +47,6 @@ public class DynamicTests { private final Module module; private DynamicTests(Module module) { - OpCodes.load(); this.module = module; this.testCaseRegistry = new LinkedList<>(); } diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/EvmExtension.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/EvmExtension.java deleted file mode 100644 index dd8f5f50..00000000 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/EvmExtension.java +++ /dev/null @@ -1,31 +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.testing; - -import net.consensys.linea.zktracer.opcode.OpCodes; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.ExtensionContext; - -/** - * A JUnit {@link org.junit.jupiter.api.extension.Extension} handling the test lifecycle for EVM - * tests. - */ -public class EvmExtension implements BeforeAllCallback { - @Override - public void beforeAll(ExtensionContext context) { - OpCodes.load(); - } -} diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ExampleTxTest.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ExampleTxTest.java index 87345394..3269537c 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ExampleTxTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ExampleTxTest.java @@ -25,9 +25,7 @@ import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.core.Transaction; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -@ExtendWith(EvmExtension.class) class ExampleTxTest { @Test diff --git a/reference-tests/src/test/java/net/consensys/linea/EvmExtension.java b/reference-tests/src/test/java/net/consensys/linea/EvmExtension.java deleted file mode 100644 index a6159f46..00000000 --- a/reference-tests/src/test/java/net/consensys/linea/EvmExtension.java +++ /dev/null @@ -1,31 +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; - -import net.consensys.linea.zktracer.opcode.OpCodes; -import org.junit.jupiter.api.extension.BeforeAllCallback; -import org.junit.jupiter.api.extension.ExtensionContext; - -/** - * A JUnit {@link org.junit.jupiter.api.extension.Extension} handling the test lifecycle for EVM - * tests. - */ -public class EvmExtension implements BeforeAllCallback { - @Override - public void beforeAll(ExtensionContext context) { - OpCodes.load(); - } -} diff --git a/reference-tests/src/test/resources/templates/BlockchainReferenceTest.java.template b/reference-tests/src/test/resources/templates/BlockchainReferenceTest.java.template index 318b5949..3bd62bc9 100644 --- a/reference-tests/src/test/resources/templates/BlockchainReferenceTest.java.template +++ b/reference-tests/src/test/resources/templates/BlockchainReferenceTest.java.template @@ -20,8 +20,6 @@ import static net.consensys.linea.BlockchainReferenceTestTools.generateTestParam import java.util.stream.Stream; -import net.consensys.linea.EvmExtension; - import org.hyperledger.besu.ethereum.referencetests.BlockchainReferenceTestCaseSpec; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.extension.ExtendWith; @@ -33,7 +31,6 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue; /** The blockchain test operation testing framework entry point. */ @Tag("BlockchainReferenceTest") -@ExtendWith(EvmExtension.class) public class %%TESTS_NAME%% { private static final String[] TEST_CONFIG_FILE_DIR_PATH = new String[] {%%TESTS_FILE%%}; diff --git a/reference-tests/src/test/resources/templates/GeneralStateReferenceTest.java.template b/reference-tests/src/test/resources/templates/GeneralStateReferenceTest.java.template index 03a58c42..b34d0f7b 100644 --- a/reference-tests/src/test/resources/templates/GeneralStateReferenceTest.java.template +++ b/reference-tests/src/test/resources/templates/GeneralStateReferenceTest.java.template @@ -19,8 +19,6 @@ import static net.consensys.linea.GeneralStateReferenceTestTools.executeTest; import static net.consensys.linea.GeneralStateReferenceTestTools.generateTestParametersForConfig; import static org.junit.jupiter.api.Assumptions.assumeTrue; -import net.consensys.linea.EvmExtension; - import java.util.stream.Stream; import org.hyperledger.besu.ethereum.referencetests.GeneralStateTestCaseEipSpec; @@ -32,7 +30,6 @@ import org.junit.jupiter.params.provider.MethodSource; /** The general state test operation testing framework entry point. */ @Tag("GeneralStateReferenceTest") -@ExtendWith(EvmExtension.class) public class %%TESTS_NAME%% { private static final String[] TEST_CONFIG_FILE_DIR_PATH = new String[] {%%TESTS_FILE%%}; From 693a8e396229eb62f1eaaf7aeb6848675a7d285b Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Tue, 21 May 2024 11:16:20 +0200 Subject: [PATCH 26/30] Add more log to txpool simulation validator (#12) Signed-off-by: Fabio Di Fabio --- .../ModuleLimitsValidationResult.java | 18 +++++++ .../validators/SimulationValidator.java | 53 ++++++++++++++++--- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java index 69728c9b..ba661cf8 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/modulelimit/ModuleLimitsValidationResult.java @@ -85,4 +85,22 @@ public static ModuleLimitsValidationResult blockModuleLineCountFull( cumulativeModuleLineCount, cumulativeModuleLineLimit); } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(result.name()); + if (moduleName != null) { + sb.append("[module=").append(moduleName); + + if (moduleLineCount != null) { + sb.append(",lineCount=").append(moduleLineCount); + sb.append(",lineLimit=").append(moduleLineLimit); + sb.append(",cumulativeLineCount=").append(cumulativeModuleLineCount); + sb.append(",cumulativeLineLimit=").append(cumulativeModuleLineLimit); + } + + sb.append(']'); + } + return sb.toString(); + } } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java index 0709f4d9..f1df6623 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java @@ -28,6 +28,7 @@ import net.consensys.linea.zktracer.ZkTracer; import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.data.TransactionSimulationResult; import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.TransactionSimulationService; import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator; @@ -62,6 +63,13 @@ public Optional validateTransaction( final boolean isRemoteAndP2pEnabled = !isLocal && txPoolValidatorConf.txPoolSimulationCheckP2pEnabled(); if (isRemoteAndP2pEnabled || isLocalAndApiEnabled) { + log.atTrace() + .setMessage( + "Starting simulation validation for tx with hash={}, isLocal={}, hasPriority={}") + .addArgument(transaction::getHash) + .addArgument(isLocal) + .addArgument(hasPriority) + .log(); final ModuleLineCountValidator moduleLineCountValidator = new ModuleLineCountValidator(moduleLineLimitsMap); @@ -72,34 +80,65 @@ public Optional validateTransaction( transactionSimulationService.simulate( transaction, chainHeadHeader.getBlockHash(), zkTracer, true); - ModuleLimitsValidationResult moduleLimit = + ModuleLimitsValidationResult moduleLimitResult = moduleLineCountValidator.validate(zkTracer.getModulesLineCount()); - if (moduleLimit.getResult() != ModuleLineCountValidator.ModuleLineCountResult.VALID) { - return Optional.of(handleModuleOverLimit(moduleLimit)); + logSimulationResult( + transaction, isLocal, hasPriority, maybeSimulationResults, moduleLimitResult); + + if (moduleLimitResult.getResult() != ModuleLineCountValidator.ModuleLineCountResult.VALID) { + return Optional.of(handleModuleOverLimit(moduleLimitResult)); } if (maybeSimulationResults.isPresent()) { final var simulationResult = maybeSimulationResults.get(); if (simulationResult.isInvalid()) { - return Optional.of( + final String errMsg = "Invalid transaction" - + simulationResult.getInvalidReason().map(ir -> ": " + ir).orElse("")); + + simulationResult.getInvalidReason().map(ir -> ": " + ir).orElse(""); + log.debug(errMsg); + return Optional.of(errMsg); } if (!simulationResult.isSuccessful()) { - return Optional.of( + final String errMsg = "Reverted transaction" + simulationResult .getRevertReason() .map(rr -> ": " + rr.toHexString()) - .orElse("")); + .orElse(""); + log.debug(errMsg); + return Optional.of(errMsg); } } } + log.atTrace() + .setMessage( + "Simulation validation not enabled for tx with hash={}, isLocal={}, hasPriority={}") + .addArgument(transaction::getHash) + .addArgument(isLocal) + .addArgument(hasPriority) + .log(); return Optional.empty(); } + private void logSimulationResult( + final Transaction transaction, + final boolean isLocal, + final boolean hasPriority, + final Optional maybeSimulationResults, + final ModuleLimitsValidationResult moduleLimitResult) { + log.atTrace() + .setMessage( + "Result of simulation validation for tx with hash={}, isLocal={}, hasPriority={}, is {}, module line counts {}") + .addArgument(transaction::getHash) + .addArgument(isLocal) + .addArgument(hasPriority) + .addArgument(maybeSimulationResults) + .addArgument(moduleLimitResult) + .log(); + } + private ZkTracer createZkTracer(final BlockHeader chainHeadHeader) { var zkTracer = new ZkTracer(l1L2BridgeConfiguration); zkTracer.traceStartConflation(1L); From eec21f8755a6c7d5067e38e21c397a5c08a8646b Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 22 May 2024 03:26:12 +0200 Subject: [PATCH 27/30] Update CHANGELOG for 0.1.4-test25 and 0.1.4-test26 (#9) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c040329..74b52ea7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.1.4-test26 +Test pre-release 26 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* Improve ZkTracer initialization time [#11](https://github.com/Consensys/linea-sequencer/pull/11) +* Add more log to txpool simulation validator [#12](https://github.com/Consensys/linea-sequencer/pull/12) + +## 0.1.4-test25 +Test pre-release 25 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* Extend Module Line Count Verification to linea_estimateGas RPC Method [#1](https://github.com/Consensys/linea-sequencer/pull/1) +* In the txpool, reject a tx if its simulation fails [#2](https://github.com/Consensys/linea-sequencer/pull/2) + ## 0.1.4-test24 Test pre-release 24 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) * Improve linea_estimateGas error response [#650](https://github.com/Consensys/besu-sequencer-plugins/pull/650) From 6249e211804cba4568f10b59428555935f3e2cf7 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Fri, 24 May 2024 14:39:37 +0200 Subject: [PATCH 28/30] Calculate line count only once in linea_estimateGas (#13) Signed-off-by: Fabio Di Fabio --- .../linea/rpc/linea/LineaEstimateGas.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java index e5c38813..d97957a7 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -209,15 +209,14 @@ private Long estimateGasUsed( final Transaction transaction, final Wei minGasPrice) { - final var estimateGasOperationTracer = new EstimateGasOperationTracer(); + final var estimateGasTracer = new EstimateGasOperationTracer(); final var chainHeadHeader = blockchainService.getChainHeadHeader(); final var zkTracer = createZkTracer(chainHeadHeader); - TracerAggregator tracerAggregator = - TracerAggregator.create(estimateGasOperationTracer, zkTracer); + final TracerAggregator zkAndGasTracer = TracerAggregator.create(estimateGasTracer, zkTracer); final var chainHeadHash = chainHeadHeader.getBlockHash(); final var maybeSimulationResults = - transactionSimulationService.simulate(transaction, chainHeadHash, tracerAggregator, true); + transactionSimulationService.simulate(transaction, chainHeadHash, zkAndGasTracer, true); ModuleLimitsValidationResult moduleLimit = moduleLineCountValidator.validate(zkTracer.getModulesLineCount()); @@ -265,7 +264,7 @@ private Long estimateGasUsed( transactionSimulationService.simulate( createTransactionForSimulation(callParameters, lowGasEstimation, minGasPrice), chainHeadHash, - tracerAggregator, + estimateGasTracer, true); return lowResult @@ -289,8 +288,7 @@ private Long estimateGasUsed( // else do a binary search to find the right estimation int iterations = 0; - var high = - highGasEstimation(lr.getGasEstimate(), estimateGasOperationTracer); + var high = highGasEstimation(lr.getGasEstimate(), estimateGasTracer); var mid = high; var low = lowGasEstimation; while (low + 1 < high) { @@ -302,7 +300,7 @@ private Long estimateGasUsed( createTransactionForSimulation( callParameters, mid, minGasPrice), chainHeadHash, - tracerAggregator, + estimateGasTracer, true); if (binarySearchResult.isEmpty() @@ -383,17 +381,17 @@ private void validateParameters(final JsonCallParameter callParameters) { * calls * * @param gasEstimation transaction gas estimation - * @param operationTracer estimate gas operation tracer + * @param estimateGasTracer estimate gas operation tracer * @return estimate gas */ private long highGasEstimation( - final long gasEstimation, final EstimateGasOperationTracer operationTracer) { + final long gasEstimation, final EstimateGasOperationTracer estimateGasTracer) { // no more than 63/64s of the remaining gas can be passed to the sub calls final double subCallMultiplier = - Math.pow(SUB_CALL_REMAINING_GAS_RATIO, operationTracer.getMaxDepth()); + Math.pow(SUB_CALL_REMAINING_GAS_RATIO, estimateGasTracer.getMaxDepth()); // and minimum gas remaining is necessary for some operation (additionalStipend) - final long gasStipend = operationTracer.getStipendNeeded(); + final long gasStipend = estimateGasTracer.getStipendNeeded(); return ((long) ((gasEstimation + gasStipend) * subCallMultiplier)); } From c216a580ef2255e722c266290cdc7304ef978e82 Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Tue, 28 May 2024 00:36:26 +0200 Subject: [PATCH 29/30] Update CHANGELOG for v0.1.4-test27 release (#14) Signed-off-by: Fabio Di Fabio --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74b52ea7..07d0e2cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.4-test27 +Test pre-release 27 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) +* Calculate line count only once in linea_estimateGas [#13](https://github.com/Consensys/linea-sequencer/pull/13) + ## 0.1.4-test26 Test pre-release 26 from [temp/issue-248/count-stack-only](https://github.com/Consensys/besu-sequencer-plugins/tree/temp/issue-248/count-stack-only) * Improve ZkTracer initialization time [#11](https://github.com/Consensys/linea-sequencer/pull/11) From 3d56714c58e177b9c766f411a32110459ed4b28f Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Thu, 30 May 2024 15:56:55 +0200 Subject: [PATCH 30/30] Extra data based pricing (#10) Signed-off-by: Fabio Di Fabio --- .../plugin/acc/test/LineaPluginTestBase.java | 2 +- .../acc/test/ProfitableTransactionTest.java | 10 +- .../test/extradata/ExtraDataPricingTest.java | 162 ++++++++++++++++++ .../EstimateGasCompatibilityModeTest.java | 3 +- .../acc/test/rpc/linea/EstimateGasTest.java | 31 ++-- .../besu/tests/acceptance/dsl/BlockUtils.java | 1 - .../TransactionProfitabilityCalculator.java | 76 ++++---- .../config/LineaProfitabilityCliOptions.java | 77 ++++----- .../LineaProfitabilityConfiguration.java | 50 ++++-- .../linea/config/LineaRpcConfiguration.java | 13 +- .../extradata/LineaExtraDataHandler.java | 145 ++++++++++++++++ .../linea/extradata/LineaExtraDataPlugin.java | 75 ++++++++ .../linea/rpc/linea/LineaEstimateGas.java | 4 +- .../validators/ProfitabilityValidator.java | 4 +- .../ProfitableTransactionSelector.java | 8 +- .../ProfitabilityValidatorTest.java | 4 +- .../ProfitableTransactionSelectorTest.java | 49 ++---- .../linea/zktracer/testing/ToyAccount.java | 5 + .../testing/ToyExecutionEnvironment.java | 2 - gradle.properties | 2 +- gradle/dependency-management.gradle | 1 + gradle/dist.gradle | 30 +++- .../consensys/linea/CorsetBlockProcessor.java | 8 +- .../linea/GeneralStateReferenceTestTools.java | 1 - 24 files changed, 575 insertions(+), 188 deletions(-) create mode 100644 acceptance-tests/src/test/java/linea/plugin/acc/test/extradata/ExtraDataPricingTest.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/extradata/LineaExtraDataHandler.java create mode 100644 arithmetization/src/main/java/net/consensys/linea/extradata/LineaExtraDataPlugin.java diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java index 38a898f1..625fb32a 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/LineaPluginTestBase.java @@ -67,7 +67,7 @@ public class LineaPluginTestBase extends AcceptanceTestBase { public void setup() throws Exception { minerNode = besu.createCliqueNodeWithExtraCliOptionsAndRpcApis( - "miner1", LINEA_CLIQUE_OPTIONS, getTestCliOptions(), Set.of("LINEA")); + "miner1", LINEA_CLIQUE_OPTIONS, getTestCliOptions(), Set.of("LINEA", "MINER")); minerNode.setTransactionPoolConfiguration( ImmutableTransactionPoolConfiguration.builder() .from(TransactionPoolConfiguration.DEFAULT) diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/ProfitableTransactionTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/ProfitableTransactionTest.java index 5ac2a6cc..ca2cf265 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/ProfitableTransactionTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/ProfitableTransactionTest.java @@ -30,18 +30,12 @@ import org.web3j.tx.TransactionManager; public class ProfitableTransactionTest extends LineaPluginTestBase { - private static final int VERIFICATION_GAS_COST = 1_200_000; - private static final int VERIFICATION_CAPACITY = 90_000; - private static final int GAS_PRICE_RATIO = 15; - private static final double MIN_MARGIN = 1.0; + private static final double MIN_MARGIN = 1.5; private static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000); @Override public List getTestCliOptions() { return new TestCommandLineOptionsBuilder() - .set("--plugin-linea-verification-gas-cost=", String.valueOf(VERIFICATION_GAS_COST)) - .set("--plugin-linea-verification-capacity=", String.valueOf(VERIFICATION_CAPACITY)) - .set("--plugin-linea-gas-price-ratio=", String.valueOf(GAS_PRICE_RATIO)) .set("--plugin-linea-min-margin=", String.valueOf(MIN_MARGIN)) .build(); } @@ -70,7 +64,7 @@ public void transactionIsNotMinedWhenUnprofitable() throws Exception { final var txUnprofitable = txManager.sendTransaction( - MIN_GAS_PRICE.getAsBigInteger(), + MIN_GAS_PRICE.getAsBigInteger().divide(BigInteger.valueOf(100)), BigInteger.valueOf(MAX_TX_GAS_LIMIT / 2), credentials.getAddress(), txData.toString(), diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/extradata/ExtraDataPricingTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/extradata/ExtraDataPricingTest.java new file mode 100644 index 00000000..d489b865 --- /dev/null +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/extradata/ExtraDataPricingTest.java @@ -0,0 +1,162 @@ +/* + * 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 linea.plugin.acc.test.extradata; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +import linea.plugin.acc.test.LineaPluginTestBase; +import linea.plugin.acc.test.TestCommandLineOptionsBuilder; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.units.bigints.UInt32; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.tests.acceptance.dsl.account.Account; +import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction; +import org.junit.jupiter.api.Test; +import org.web3j.crypto.Credentials; +import org.web3j.crypto.RawTransaction; +import org.web3j.crypto.TransactionEncoder; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.Request; +import org.web3j.protocol.core.methods.response.EthSendTransaction; +import org.web3j.utils.Numeric; + +public class ExtraDataPricingTest extends LineaPluginTestBase { + private static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000); + private static final int WEI_IN_KWEI = 1000; + + @Override + public List getTestCliOptions() { + return getTestCommandLineOptionsBuilder().build(); + } + + protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() { + return new TestCommandLineOptionsBuilder() + .set("--plugin-linea-extra-data-pricing-enabled=", Boolean.TRUE.toString()); + } + + @Test + public void updateMinGasPriceViaExtraData() { + minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE); + final var doubleMinGasPrice = MIN_GAS_PRICE.multiply(2); + + final var extraData = + createExtraDataPricingField( + 0, MIN_GAS_PRICE.toLong() / WEI_IN_KWEI, doubleMinGasPrice.toLong() / WEI_IN_KWEI); + final var reqSetExtraData = new ExtraDataPricingTest.MinerSetExtraDataRequest(extraData); + final var respSetExtraData = reqSetExtraData.execute(minerNode.nodeRequests()); + + assertThat(respSetExtraData).isTrue(); + + final Account sender = accounts.getSecondaryBenefactor(); + final Account recipient = accounts.createAccount("recipient"); + + final TransferTransaction transferTx = accountTransactions.createTransfer(sender, recipient, 1); + final var txHash = minerNode.execute(transferTx); + + minerNode.verify(eth.expectSuccessfulTransactionReceipt(txHash.toHexString())); + + assertThat(minerNode.getMiningParameters().getMinTransactionGasPrice()) + .isEqualTo(doubleMinGasPrice); + } + + @Test + public void updateProfitabilityParamsViaExtraData() throws IOException { + final Web3j web3j = minerNode.nodeRequests().eth(); + final Account sender = accounts.getSecondaryBenefactor(); + final Account recipient = accounts.createAccount("recipient"); + minerNode.getMiningParameters().setMinTransactionGasPrice(MIN_GAS_PRICE); + + final var extraData = + createExtraDataPricingField( + MIN_GAS_PRICE.multiply(2).toLong() / WEI_IN_KWEI, + MIN_GAS_PRICE.toLong() / WEI_IN_KWEI, + MIN_GAS_PRICE.toLong() / WEI_IN_KWEI); + final var reqSetExtraData = new ExtraDataPricingTest.MinerSetExtraDataRequest(extraData); + final var respSetExtraData = reqSetExtraData.execute(minerNode.nodeRequests()); + + assertThat(respSetExtraData).isTrue(); + + // when this first tx is mined the above extra data pricing will have effect on following txs + final TransferTransaction profitableTx = + accountTransactions.createTransfer(sender, recipient, 1); + final var protitableTx = minerNode.execute(profitableTx); + + minerNode.verify(eth.expectSuccessfulTransactionReceipt(protitableTx.toHexString())); + + // this tx will be evaluated with the previously set extra data pricing to be unprofitable + final RawTransaction unprofitableTx = + RawTransaction.createTransaction( + BigInteger.ZERO, + MIN_GAS_PRICE.getAsBigInteger(), + BigInteger.valueOf(21000), + recipient.getAddress(), + ""); + + final byte[] signedUnprofitableTx = + TransactionEncoder.signMessage( + unprofitableTx, Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY)); + + final EthSendTransaction signedUnprofitableTxResp = + web3j.ethSendRawTransaction(Numeric.toHexString(signedUnprofitableTx)).send(); + + assertThat(signedUnprofitableTxResp.hasError()).isTrue(); + assertThat(signedUnprofitableTxResp.getError().getMessage()).isEqualTo("Gas price too low"); + + assertThat(getTxPoolContent()).isEmpty(); + } + + static class MinerSetExtraDataRequest implements Transaction { + private final Bytes32 extraData; + + public MinerSetExtraDataRequest(final Bytes32 extraData) { + this.extraData = extraData; + } + + @Override + public Boolean execute(final NodeRequests nodeRequests) { + try { + return new Request<>( + "miner_setExtraData", + List.of(extraData.toHexString()), + nodeRequests.getWeb3jService(), + MinerSetExtraDataResponse.class) + .send() + .getResult(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + static class MinerSetExtraDataResponse extends org.web3j.protocol.core.Response {} + } + + private Bytes32 createExtraDataPricingField( + final long fixedCostKWei, final long variableCostKWei, final long minGasPriceKWei) { + final UInt32 fixed = UInt32.valueOf(BigInteger.valueOf(fixedCostKWei)); + final UInt32 variable = UInt32.valueOf(BigInteger.valueOf(variableCostKWei)); + final UInt32 min = UInt32.valueOf(BigInteger.valueOf(minGasPriceKWei)); + + return Bytes32.rightPad( + Bytes.concatenate(Bytes.of((byte) 1), fixed.toBytes(), variable.toBytes(), min.toBytes())); + } +} diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java index 6c0e8aa1..623c2f1f 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasCompatibilityModeTest.java @@ -40,7 +40,6 @@ public List getTestCliOptions() { protected void assertIsProfitable( final Transaction tx, final Wei baseFee, - final Wei estimatedPriorityFee, final Wei estimatedMaxGasPrice, final long estimatedGasLimit) { final var minGasPrice = minerNode.getMiningParameters().getMinTransactionGasPrice(); @@ -62,6 +61,6 @@ protected void assertIsProfitable( protected void assertMinGasPriceLowerBound(final Wei baseFee, final Wei estimatedMaxGasPrice) { // since we are in compatibility mode, we want to check that returned profitable priority fee is // the min priority fee per gas * multiplier + base fee - assertIsProfitable(null, baseFee, null, estimatedMaxGasPrice, 0); + assertIsProfitable(null, baseFee, estimatedMaxGasPrice, 0); } } diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java index df80e487..a06891f9 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasTest.java @@ -45,13 +45,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.web3j.protocol.core.Request; -import org.web3j.protocol.core.Response; import org.web3j.protocol.http.HttpService; public class EstimateGasTest extends LineaPluginTestBase { - protected static final int VERIFICATION_GAS_COST = 1_200_000; - protected static final int VERIFICATION_CAPACITY = 90_000; - protected static final int GAS_PRICE_RATIO = 15; + protected static final int FIXED_GAS_COST_WEI = 0; + protected static final int VARIABLE_GAS_COST_WEI = 1_000_000_000; protected static final double MIN_MARGIN = 1.0; protected static final double ESTIMATE_GAS_MIN_MARGIN = 1.0; protected static final Wei MIN_GAS_PRICE = Wei.of(1_000_000_000); @@ -65,9 +63,8 @@ public List getTestCliOptions() { protected TestCommandLineOptionsBuilder getTestCommandLineOptionsBuilder() { return new TestCommandLineOptionsBuilder() - .set("--plugin-linea-verification-gas-cost=", String.valueOf(VERIFICATION_GAS_COST)) - .set("--plugin-linea-verification-capacity=", String.valueOf(VERIFICATION_CAPACITY)) - .set("--plugin-linea-gas-price-ratio=", String.valueOf(GAS_PRICE_RATIO)) + .set("--plugin-linea-fixed-gas-cost-wei=", String.valueOf(FIXED_GAS_COST_WEI)) + .set("--plugin-linea-variable-gas-cost-wei=", String.valueOf(VARIABLE_GAS_COST_WEI)) .set("--plugin-linea-min-margin=", String.valueOf(MIN_MARGIN)) .set("--plugin-linea-estimate-gas-min-margin=", String.valueOf(ESTIMATE_GAS_MIN_MARGIN)) .set("--plugin-linea-max-tx-gas-limit=", String.valueOf(MAX_TRANSACTION_GAS_LIMIT)); @@ -82,9 +79,8 @@ public void setMinGasPrice() { public void createDefaultConfigurations() { profitabilityConf = LineaProfitabilityCliOptions.create().toDomainObject().toBuilder() - .verificationCapacity(VERIFICATION_CAPACITY) - .verificationGasCost(VERIFICATION_GAS_COST) - .gasPriceRatio(GAS_PRICE_RATIO) + .fixedCostWei(FIXED_GAS_COST_WEI) + .variableCostWei(VARIABLE_GAS_COST_WEI) .minMargin(MIN_MARGIN) .estimateGasMinMargin(ESTIMATE_GAS_MIN_MARGIN) .build(); @@ -145,13 +141,12 @@ public void lineaEstimateGasIsProfitable() { .signature(LineaEstimateGas.FAKE_SIGNATURE_FOR_SIZE_CALCULATION) .build(); - assertIsProfitable(tx, baseFee, estimatedPriorityFee, estimatedMaxGasPrice, estimatedGasLimit); + assertIsProfitable(tx, baseFee, estimatedMaxGasPrice, estimatedGasLimit); } protected void assertIsProfitable( final org.hyperledger.besu.ethereum.core.Transaction tx, final Wei baseFee, - final Wei estimatedPriorityFee, final Wei estimatedMaxGasPrice, final long estimatedGasLimit) { @@ -159,20 +154,16 @@ protected void assertIsProfitable( final var profitabilityCalculator = new TransactionProfitabilityCalculator(profitabilityConf); - final var profitablePriorityFee = - profitabilityCalculator.profitablePriorityFeePerGas( - tx, profitabilityConf.txPoolMinMargin(), minGasPrice, estimatedGasLimit); - - assertThat(profitablePriorityFee.greaterThan(minGasPrice)).isTrue(); + assertThat(estimatedMaxGasPrice.greaterOrEqualThan(minGasPrice)).isTrue(); assertThat( profitabilityCalculator.isProfitable( "Test", tx, - profitabilityConf.txPoolMinMargin(), - minerNode.getMiningParameters().getMinTransactionGasPrice(), + profitabilityConf.estimateGasMinMargin(), estimatedMaxGasPrice, - estimatedGasLimit)) + estimatedGasLimit, + minGasPrice)) .isTrue(); } diff --git a/acceptance-tests/src/test/java/org/hyperledger/besu/tests/acceptance/dsl/BlockUtils.java b/acceptance-tests/src/test/java/org/hyperledger/besu/tests/acceptance/dsl/BlockUtils.java index 9922c745..ba54d9e3 100644 --- a/acceptance-tests/src/test/java/org/hyperledger/besu/tests/acceptance/dsl/BlockUtils.java +++ b/acceptance-tests/src/test/java/org/hyperledger/besu/tests/acceptance/dsl/BlockUtils.java @@ -67,7 +67,6 @@ public static BlockHeader createBlockHeader( null, null, null, - null, blockHeaderFunctions); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java b/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java index d31d093b..fa01afff 100644 --- a/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java +++ b/arithmetization/src/main/java/net/consensys/linea/bl/TransactionProfitabilityCalculator.java @@ -25,68 +25,56 @@ @Slf4j public class TransactionProfitabilityCalculator { - private final LineaProfitabilityConfiguration profitabilityConf; - private final double preComputedValue; - private final double priceAdjustment; public TransactionProfitabilityCalculator( final LineaProfitabilityConfiguration profitabilityConf) { this.profitabilityConf = profitabilityConf; - this.preComputedValue = - profitabilityConf.gasPriceRatio() * profitabilityConf.verificationGasCost(); - this.priceAdjustment = profitabilityConf.gasPriceAdjustment().getAsBigInteger().doubleValue(); } public Wei profitablePriorityFeePerGas( final Transaction transaction, final double minMargin, - final Wei minGasPrice, - final long gas) { - final double compressedTxSize = getCompressedTxSize(transaction); + final long gas, + final Wei minGasPriceWei) { + final int compressedTxSize = getCompressedTxSize(transaction); + + final long variableCostWei = + profitabilityConf.extraDataPricingEnabled() + ? profitabilityConf.variableCostWei() + : minGasPriceWei.toLong(); final var profitAt = - (preComputedValue - * compressedTxSize - * minGasPrice.getAsBigInteger().doubleValue() - / (gas * profitabilityConf.verificationCapacity()) - + priceAdjustment) - * minMargin; + minMargin * (variableCostWei * compressedTxSize / gas + profitabilityConf.fixedCostWei()); - final var adjustedProfit = Wei.ofNumber(BigDecimal.valueOf(profitAt).toBigInteger()); + final var profitAtWei = Wei.ofNumber(BigDecimal.valueOf(profitAt).toBigInteger()); log.atDebug() .setMessage( - "Estimated profitable priorityFeePerGas: {}; estimateGasMinMargin={}, verificationCapacity={}, " - + "verificationGasCost={}, gasPriceRatio={}, gasPriceAdjustment={}, gas={}, minGasPrice={}, " - + "l1GasPrice={}, txSize={}, compressedTxSize={}") - .addArgument(adjustedProfit::toHumanReadableString) - .addArgument(profitabilityConf.estimateGasMinMargin()) - .addArgument(profitabilityConf.verificationCapacity()) - .addArgument(profitabilityConf.verificationGasCost()) - .addArgument(profitabilityConf.gasPriceRatio()) - .addArgument(profitabilityConf.gasPriceAdjustment()::toHumanReadableString) + "Estimated profitable priorityFeePerGas: {}; minMargin={}, fixedCostWei={}, " + + "variableCostWei={}, gas={}, txSize={}, compressedTxSize={}") + .addArgument(profitAtWei::toHumanReadableString) + .addArgument(minMargin) + .addArgument(profitabilityConf.fixedCostWei()) + .addArgument(variableCostWei) .addArgument(gas) - .addArgument(minGasPrice::toHumanReadableString) - .addArgument( - () -> minGasPrice.multiply(profitabilityConf.gasPriceRatio()).toHumanReadableString()) .addArgument(transaction::getSize) .addArgument(compressedTxSize) .log(); - return adjustedProfit; + return profitAtWei; } public boolean isProfitable( final String context, final Transaction transaction, final double minMargin, - final Wei minGasPrice, final Wei effectiveGasPrice, - final long gas) { + final long gas, + final Wei minGasPriceWei) { final Wei profitablePriorityFee = - profitablePriorityFeePerGas(transaction, minMargin, minGasPrice, gas); + profitablePriorityFeePerGas(transaction, minMargin, gas, minGasPriceWei); if (effectiveGasPrice.lessThan(profitablePriorityFee)) { log( @@ -97,7 +85,7 @@ public boolean isProfitable( effectiveGasPrice, profitablePriorityFee, gas, - minGasPrice); + minGasPriceWei); return false; } @@ -109,11 +97,11 @@ public boolean isProfitable( effectiveGasPrice, profitablePriorityFee, gas, - minGasPrice); + minGasPriceWei); return true; } - private double getCompressedTxSize(final Transaction transaction) { + private int getCompressedTxSize(final Transaction transaction) { final byte[] bytes = transaction.encoded().toArrayUnsafe(); return LibCompress.CompressedSize(bytes, bytes.length); } @@ -126,11 +114,12 @@ private void log( final Wei effectiveGasPrice, final Wei profitableGasPrice, final long gasUsed, - final Wei minGasPrice) { + final Wei minGasPriceWei) { + leb.setMessage( "Context {}. Transaction {} has a margin of {}, minMargin={}, effectiveGasPrice={}," - + " profitableGasPrice={}, verificationCapacity={}, verificationGasCost={}, gasPriceRatio={},, gasPriceAdjustment={}" - + " gasUsed={}, minGasPrice={}") + + " profitableGasPrice={}, fixedCostWei={}, variableCostWei={}, " + + " gasUsed={}") .addArgument(context) .addArgument(transaction::getHash) .addArgument( @@ -140,12 +129,13 @@ private void log( .addArgument(minMargin) .addArgument(effectiveGasPrice::toHumanReadableString) .addArgument(profitableGasPrice::toHumanReadableString) - .addArgument(profitabilityConf.verificationCapacity()) - .addArgument(profitabilityConf.verificationGasCost()) - .addArgument(profitabilityConf.gasPriceRatio()) - .addArgument(profitabilityConf.gasPriceAdjustment()::toHumanReadableString) + .addArgument(profitabilityConf.fixedCostWei()) + .addArgument( + () -> + profitabilityConf.extraDataPricingEnabled() + ? profitabilityConf.variableCostWei() + : minGasPriceWei.toLong()) .addArgument(gasUsed) - .addArgument(minGasPrice::toHumanReadableString) .log(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java index 14683bb5..ea48ae7d 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityCliOptions.java @@ -19,23 +19,15 @@ import com.google.common.base.MoreObjects; import jakarta.validation.constraints.Positive; -import net.consensys.linea.config.converters.WeiConverter; -import org.hyperledger.besu.datatypes.Wei; import picocli.CommandLine; /** The Linea profitability calculator CLI options. */ public class LineaProfitabilityCliOptions { - public static final String VERIFICATION_GAS_COST = "--plugin-linea-verification-gas-cost"; - public static final int DEFAULT_VERIFICATION_GAS_COST = 1_200_000; + public static final String FIXED_GAS_COST_WEI = "--plugin-linea-fixed-gas-cost-wei"; + public static final long DEFAULT_FIXED_GAS_COST_WEI = 0; - public static final String VERIFICATION_CAPACITY = "--plugin-linea-verification-capacity"; - public static final int DEFAULT_VERIFICATION_CAPACITY = 90_000; - - public static final String GAS_PRICE_RATIO = "--plugin-linea-gas-price-ratio"; - public static final int DEFAULT_GAS_PRICE_RATIO = 15; - - public static final String GAS_PRICE_ADJUSTMENT = "--plugin-linea-gas-price-adjustment"; - public static final Wei DEFAULT_GAS_PRICE_ADJUSTMENT = Wei.ZERO; + public static final String VARIABLE_GAS_COST_WEI = "--plugin-linea-variable-gas-cost-wei"; + public static final long DEFAULT_VARIABLE_GAS_COST_WEI = 1_000_000_000; public static final String MIN_MARGIN = "--plugin-linea-min-margin"; public static final BigDecimal DEFAULT_MIN_MARGIN = BigDecimal.ONE; @@ -54,38 +46,25 @@ public class LineaProfitabilityCliOptions { "--plugin-linea-tx-pool-profitability-check-p2p-enabled"; public static final boolean DEFAULT_TX_POOL_ENABLE_CHECK_P2P = false; - @Positive - @CommandLine.Option( - names = {VERIFICATION_GAS_COST}, - hidden = true, - paramLabel = "", - description = "L1 verification gas cost (default: ${DEFAULT-VALUE})") - private int verificationGasCost = DEFAULT_VERIFICATION_GAS_COST; + public static final String EXTRA_DATA_PRICING_ENABLED = + "--plugin-linea-extra-data-pricing-enabled"; + public static final boolean DEFAULT_EXTRA_DATA_PRICING_ENABLED = false; @Positive @CommandLine.Option( - names = {VERIFICATION_CAPACITY}, + names = {FIXED_GAS_COST_WEI}, hidden = true, paramLabel = "", - description = "L1 verification capacity (default: ${DEFAULT-VALUE})") - private int verificationCapacity = DEFAULT_VERIFICATION_CAPACITY; + description = "Fixed gas cost in Wei (default: ${DEFAULT-VALUE})") + private long fixedGasCostWei = DEFAULT_FIXED_GAS_COST_WEI; @Positive @CommandLine.Option( - names = {GAS_PRICE_RATIO}, + names = {VARIABLE_GAS_COST_WEI}, hidden = true, paramLabel = "", - description = "L1/L2 gas price ratio (default: ${DEFAULT-VALUE})") - private int gasPriceRatio = DEFAULT_GAS_PRICE_RATIO; - - @CommandLine.Option( - names = {GAS_PRICE_ADJUSTMENT}, - hidden = true, - converter = WeiConverter.class, - paramLabel = "", - description = - "Amount to add to the calculated profitable gas price (default: ${DEFAULT-VALUE})") - private Wei gasPriceAdjustment = DEFAULT_GAS_PRICE_ADJUSTMENT; + description = "Variable gas cost in Wei (default: ${DEFAULT-VALUE})") + private long variableGasCostWei = DEFAULT_VARIABLE_GAS_COST_WEI; @Positive @CommandLine.Option( @@ -131,6 +110,15 @@ public class LineaProfitabilityCliOptions { "Enable the profitability check for txs received via p2p? (default: ${DEFAULT-VALUE})") private boolean txPoolCheckP2pEnabled = DEFAULT_TX_POOL_ENABLE_CHECK_P2P; + @CommandLine.Option( + names = {EXTRA_DATA_PRICING_ENABLED}, + arity = "0..1", + hidden = true, + paramLabel = "", + description = + "Enable setting pricing parameters via extra data field (default: ${DEFAULT-VALUE})") + private boolean extraDataPricingEnabled = DEFAULT_EXTRA_DATA_PRICING_ENABLED; + private LineaProfitabilityCliOptions() {} /** @@ -151,15 +139,14 @@ public static LineaProfitabilityCliOptions create() { public static LineaProfitabilityCliOptions fromConfig( final LineaProfitabilityConfiguration config) { final LineaProfitabilityCliOptions options = create(); - options.verificationGasCost = config.verificationGasCost(); - options.verificationCapacity = config.verificationCapacity(); - options.gasPriceRatio = config.gasPriceRatio(); - options.gasPriceAdjustment = config.gasPriceAdjustment(); + options.fixedGasCostWei = config.fixedCostWei(); + options.variableGasCostWei = config.variableCostWei(); options.minMargin = BigDecimal.valueOf(config.minMargin()); options.estimageGasMinMargin = BigDecimal.valueOf(config.estimateGasMinMargin()); options.txPoolMinMargin = BigDecimal.valueOf(config.txPoolMinMargin()); options.txPoolCheckApiEnabled = config.txPoolCheckApiEnabled(); options.txPoolCheckP2pEnabled = config.txPoolCheckP2pEnabled(); + options.extraDataPricingEnabled = config.extraDataPricingEnabled(); return options; } @@ -170,30 +157,28 @@ public static LineaProfitabilityCliOptions fromConfig( */ public LineaProfitabilityConfiguration toDomainObject() { return LineaProfitabilityConfiguration.builder() - .verificationGasCost(verificationGasCost) - .verificationCapacity(verificationCapacity) - .gasPriceRatio(gasPriceRatio) - .gasPriceAdjustment(gasPriceAdjustment) + .fixedCostWei(fixedGasCostWei) + .variableCostWei(variableGasCostWei) .minMargin(minMargin.doubleValue()) .estimateGasMinMargin(estimageGasMinMargin.doubleValue()) .txPoolMinMargin(txPoolMinMargin.doubleValue()) .txPoolCheckApiEnabled(txPoolCheckApiEnabled) .txPoolCheckP2pEnabled(txPoolCheckP2pEnabled) + .extraDataPricingEnabled(extraDataPricingEnabled) .build(); } @Override public String toString() { return MoreObjects.toStringHelper(this) - .add(VERIFICATION_GAS_COST, verificationGasCost) - .add(VERIFICATION_CAPACITY, verificationCapacity) - .add(GAS_PRICE_RATIO, gasPriceRatio) - .add(GAS_PRICE_ADJUSTMENT, gasPriceAdjustment) + .add(FIXED_GAS_COST_WEI, fixedGasCostWei) + .add(VARIABLE_GAS_COST_WEI, variableGasCostWei) .add(MIN_MARGIN, minMargin) .add(ESTIMATE_GAS_MIN_MARGIN, estimageGasMinMargin) .add(TX_POOL_MIN_MARGIN, txPoolMinMargin) .add(TX_POOL_ENABLE_CHECK_API, txPoolCheckApiEnabled) .add(TX_POOL_ENABLE_CHECK_P2P, txPoolCheckP2pEnabled) + .add(EXTRA_DATA_PRICING_ENABLED, extraDataPricingEnabled) .toString(); } } diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java index 514371eb..dfc4d517 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaProfitabilityConfiguration.java @@ -16,17 +16,45 @@ package net.consensys.linea.config; import lombok.Builder; -import org.hyperledger.besu.datatypes.Wei; +import lombok.Getter; +import lombok.ToString; +import lombok.experimental.Accessors; /** The Linea profitability calculator configuration. */ @Builder(toBuilder = true) -public record LineaProfitabilityConfiguration( - int verificationGasCost, - int verificationCapacity, - int gasPriceRatio, - Wei gasPriceAdjustment, - double minMargin, - double estimateGasMinMargin, - double txPoolMinMargin, - boolean txPoolCheckApiEnabled, - boolean txPoolCheckP2pEnabled) {} +@Accessors(fluent = true) +@Getter +@ToString +public class LineaProfitabilityConfiguration { + /** It is safe to keep this as long, since it will store value <= max_int * 1000 */ + private long fixedCostWei; + /** It is safe to keep this as long, since it will store value <= max_int * 1000 */ + private long variableCostWei; + + private double minMargin; + private double estimateGasMinMargin; + private double txPoolMinMargin; + private boolean txPoolCheckApiEnabled; + private boolean txPoolCheckP2pEnabled; + private boolean extraDataPricingEnabled; + + /** + * These 2 parameters must be atomically updated + * + * @param fixedCostWei fixed cost in Wei + * @param variableCostWei variable cost in Wei + */ + public synchronized void updateFixedAndVariableCost( + final long fixedCostWei, final long variableCostWei) { + this.fixedCostWei = fixedCostWei; + this.variableCostWei = variableCostWei; + } + + public synchronized long fixedCostWei() { + return fixedCostWei; + } + + public synchronized long variableCostWei() { + return variableCostWei; + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java index 6551c924..4945013b 100644 --- a/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java +++ b/arithmetization/src/main/java/net/consensys/linea/config/LineaRpcConfiguration.java @@ -18,8 +18,17 @@ import java.math.BigDecimal; import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; /** The Linea RPC configuration. */ @Builder(toBuilder = true) -public record LineaRpcConfiguration( - boolean estimateGasCompatibilityModeEnabled, BigDecimal estimateGasCompatibilityMultiplier) {} +@Accessors(fluent = true) +@Getter +@ToString +public class LineaRpcConfiguration { + @Setter private volatile boolean estimateGasCompatibilityModeEnabled; + private BigDecimal estimateGasCompatibilityMultiplier; +} diff --git a/arithmetization/src/main/java/net/consensys/linea/extradata/LineaExtraDataHandler.java b/arithmetization/src/main/java/net/consensys/linea/extradata/LineaExtraDataHandler.java new file mode 100644 index 00000000..2aeed3c6 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/extradata/LineaExtraDataHandler.java @@ -0,0 +1,145 @@ +/* + * 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.extradata; + +import java.util.function.Consumer; +import java.util.function.Function; + +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.config.LineaProfitabilityConfiguration; +import org.apache.commons.lang3.mutable.MutableLong; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.units.bigints.UInt32; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.datatypes.rpc.JsonRpcResponseType; +import org.hyperledger.besu.plugin.data.AddedBlockContext; +import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.services.BesuEvents; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.RpcEndpointService; + +@Slf4j +public class LineaExtraDataHandler implements BesuEvents.BlockAddedListener { + private final RpcEndpointService rpcEndpointService; + private final ExtraDataParser[] extraDataParsers; + + public LineaExtraDataHandler( + final RpcEndpointService rpcEndpointService, + final BlockchainService blockchainService, + final LineaProfitabilityConfiguration profitabilityConf) { + this.rpcEndpointService = rpcEndpointService; + extraDataParsers = new ExtraDataParser[] {new Version1Parser(profitabilityConf)}; + onStartup(blockchainService); + } + + private void onStartup(final BlockchainService blockchainService) { + consumeExtraData(blockchainService.getChainHeadHeader()); + } + + @Override + public void onBlockAdded(final AddedBlockContext addedBlockContext) { + consumeExtraData(addedBlockContext.getBlockHeader()); + } + + private void consumeExtraData(final BlockHeader blockHeader) { + final var rawExtraData = blockHeader.getExtraData(); + + if (!Bytes.EMPTY.equals(rawExtraData)) { + for (final ExtraDataParser extraDataParser : extraDataParsers) { + if (extraDataParser.canParse(rawExtraData)) { + final var extraData = rawExtraData.slice(1); + extraDataParser.parse(extraData); + return; + } + } + log.warn("unsupported extra data field {}", rawExtraData.toHexString()); + } + } + + private interface ExtraDataParser { + boolean canParse(Bytes extraData); + + void parse(Bytes extraData); + + static Long toLong(final Bytes fieldBytes) { + return UInt32.fromBytes(fieldBytes).toLong(); + } + } + + @SuppressWarnings("rawtypes") + private class Version1Parser implements ExtraDataParser { + private static final int WEI_IN_KWEI = 1_000; + private final LineaProfitabilityConfiguration profitabilityConf; + private final FieldConsumer[] fieldsSequence; + private final MutableLong currFixedCostKWei = new MutableLong(); + private final MutableLong currVariableCostKWei = new MutableLong(); + + public Version1Parser(final LineaProfitabilityConfiguration profitabilityConf) { + this.profitabilityConf = profitabilityConf; + + final FieldConsumer fixedGasCostField = + new FieldConsumer<>( + "fixedGasCost", 4, ExtraDataParser::toLong, currFixedCostKWei::setValue); + final FieldConsumer variableGasCostField = + new FieldConsumer<>( + "variableGasCost", 4, ExtraDataParser::toLong, currVariableCostKWei::setValue); + final FieldConsumer minGasPriceField = + new FieldConsumer<>("minGasPrice", 4, ExtraDataParser::toLong, this::updateMinGasPrice); + + this.fieldsSequence = + new FieldConsumer[] {fixedGasCostField, variableGasCostField, minGasPriceField}; + } + + public boolean canParse(final Bytes rawExtraData) { + return rawExtraData.get(0) == (byte) 1; + } + + public synchronized void parse(final Bytes extraData) { + log.info("Parsing extra data version 1: {}", extraData.toHexString()); + int startIndex = 0; + for (final FieldConsumer fieldConsumer : fieldsSequence) { + fieldConsumer.accept(extraData.slice(startIndex, fieldConsumer.length)); + startIndex += fieldConsumer.length; + } + + profitabilityConf.updateFixedAndVariableCost( + currFixedCostKWei.longValue() * WEI_IN_KWEI, + currVariableCostKWei.longValue() * WEI_IN_KWEI); + } + + void updateMinGasPrice(final Long minGasPriceKWei) { + final var minGasPriceWei = Wei.of(minGasPriceKWei).multiply(WEI_IN_KWEI); + final var resp = + rpcEndpointService.call( + "miner_setMinGasPrice", new Object[] {minGasPriceWei.toShortHexString()}); + if (!resp.getType().equals(JsonRpcResponseType.SUCCESS)) { + log.error("setMinGasPrice failed: {}", resp); + } + } + } + + private record FieldConsumer( + String name, int length, Function converter, Consumer consumer) + implements Consumer { + + @Override + public void accept(final Bytes fieldBytes) { + final var converted = converter.apply(fieldBytes); + log.debug("Field {}={} (raw bytes: {})", name, converted, fieldBytes.toHexString()); + consumer.accept(converted); + } + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/extradata/LineaExtraDataPlugin.java b/arithmetization/src/main/java/net/consensys/linea/extradata/LineaExtraDataPlugin.java new file mode 100644 index 00000000..d3243f55 --- /dev/null +++ b/arithmetization/src/main/java/net/consensys/linea/extradata/LineaExtraDataPlugin.java @@ -0,0 +1,75 @@ +/* + * 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.extradata; + +import java.util.Optional; + +import com.google.auto.service.AutoService; +import lombok.extern.slf4j.Slf4j; +import net.consensys.linea.AbstractLineaRequiredPlugin; +import org.hyperledger.besu.plugin.BesuContext; +import org.hyperledger.besu.plugin.BesuPlugin; +import org.hyperledger.besu.plugin.services.BesuEvents; +import org.hyperledger.besu.plugin.services.BlockchainService; +import org.hyperledger.besu.plugin.services.RpcEndpointService; + +/** This plugin registers handlers that are activated when new blocks are imported */ +@Slf4j +@AutoService(BesuPlugin.class) +public class LineaExtraDataPlugin extends AbstractLineaRequiredPlugin { + public static final String NAME = "linea"; + private BesuEvents besuEventsService; + private RpcEndpointService rpcEndpointService; + private BlockchainService blockchainService; + + @Override + public Optional getName() { + return Optional.of(NAME); + } + + @Override + public void doRegister(final BesuContext context) { + besuEventsService = + context + .getService(BesuEvents.class) + .orElseThrow( + () -> new RuntimeException("Failed to obtain BesuEvents from the BesuContext.")); + rpcEndpointService = + context + .getService(RpcEndpointService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain RpcEndpointService from the BesuContext.")); + blockchainService = + context + .getService(BlockchainService.class) + .orElseThrow( + () -> + new RuntimeException( + "Failed to obtain BlockchainService from the BesuContext.")); + } + + @Override + public void start() { + super.start(); + if (profitabilityConfiguration.extraDataPricingEnabled()) { + besuEventsService.addBlockAddedListener( + new LineaExtraDataHandler( + rpcEndpointService, blockchainService, profitabilityConfiguration)); + } + } +} diff --git a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java index d97957a7..a390ff7c 100644 --- a/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java +++ b/arithmetization/src/main/java/net/consensys/linea/rpc/linea/LineaEstimateGas.java @@ -43,6 +43,7 @@ import org.bouncycastle.crypto.params.ECDomainParameters; import org.hyperledger.besu.crypto.SECPSignature; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.datatypes.rpc.RpcMethodError; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; @@ -55,7 +56,6 @@ import org.hyperledger.besu.plugin.services.TransactionSimulationService; import org.hyperledger.besu.plugin.services.exception.PluginRpcEndpointException; import org.hyperledger.besu.plugin.services.rpc.PluginRpcRequest; -import org.hyperledger.besu.plugin.services.rpc.RpcMethodError; @Slf4j public class LineaEstimateGas { @@ -188,7 +188,7 @@ private Wei getEstimatedPriorityFee( final Wei profitablePriorityFee = txProfitabilityCalculator.profitablePriorityFeePerGas( - transaction, profitabilityConf.estimateGasMinMargin(), minGasPrice, estimatedGasUsed); + transaction, profitabilityConf.estimateGasMinMargin(), estimatedGasUsed, minGasPrice); if (profitablePriorityFee.greaterOrEqualThan(priorityFeeLowerBound)) { return profitablePriorityFee; diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidator.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidator.java index 97d25fd5..12be4142 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidator.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidator.java @@ -55,9 +55,9 @@ public Optional validateTransaction( "Txpool", transaction, profitabilityConf.txPoolMinMargin(), - besuConfiguration.getMinGasPrice(), calculateUpfrontGasPrice(transaction), - transaction.getGasLimit()) + transaction.getGasLimit(), + besuConfiguration.getMinGasPrice()) ? Optional.empty() : Optional.of("Gas price too low"); } diff --git a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java index 65e89d74..5c5c53bd 100644 --- a/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java +++ b/arithmetization/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java @@ -80,9 +80,9 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( "PreProcessing", transaction, profitabilityConf.minMargin(), - minGasPrice, evaluationContext.getTransactionGasPrice(), - gasLimit)) { + gasLimit, + minGasPrice)) { return TX_UNPROFITABLE_UPFRONT; } @@ -133,9 +133,9 @@ public TransactionSelectionResult evaluateTransactionPostProcessing( "PostProcessing", transaction, profitabilityConf.minMargin(), - evaluationContext.getMinGasPrice(), evaluationContext.getTransactionGasPrice(), - gasUsed)) { + gasUsed, + evaluationContext.getMinGasPrice())) { rememberUnprofitable(transaction); return TX_UNPROFITABLE; } diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java index 5a8a1143..3178a5ec 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/ProfitabilityValidatorTest.java @@ -47,8 +47,8 @@ public class ProfitabilityValidatorTest { Address.fromHexString("0x0000000000000000000000000000000000001000"); public static final Address RECIPIENT = Address.fromHexString("0x0000000000000000000000000000000000001001"); - private static Wei PROFITABLE_GAS_PRICE = Wei.of(11000000); - private static Wei UNPROFITABLE_GAS_PRICE = Wei.of(1000000); + private static Wei PROFITABLE_GAS_PRICE = Wei.of(11_000_000); + private static Wei UNPROFITABLE_GAS_PRICE = Wei.of(200_000); private static final SECPSignature FAKE_SIGNATURE; static { diff --git a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java index a4ce76ae..a4635a65 100644 --- a/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java +++ b/arithmetization/src/test/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelectorTest.java @@ -41,10 +41,9 @@ import org.junit.jupiter.api.Test; public class ProfitableTransactionSelectorTest { - private static final int VERIFICATION_GAS_COST = 1_200_000; - private static final int VERIFICATION_CAPACITY = 90_000; - private static final int GAS_PRICE_RATIO = 15; - private static final double MIN_MARGIN = 1.0; + private static final int FIXED_GAS_COST_WEI = 600_000; + private static final int VARIABLE_GAS_COST_WEI = 1_000_000; + private static final double MIN_MARGIN = 1.5; private static final int UNPROFITABLE_CACHE_SIZE = 2; private static final int UNPROFITABLE_RETRY_LIMIT = 1; private final LineaTransactionSelectorConfiguration txSelectorConf = @@ -54,10 +53,9 @@ public class ProfitableTransactionSelectorTest { .build(); private final LineaProfitabilityConfiguration profitabilityConf = LineaProfitabilityCliOptions.create().toDomainObject().toBuilder() - .gasPriceRatio(GAS_PRICE_RATIO) .minMargin(MIN_MARGIN) - .verificationCapacity(VERIFICATION_CAPACITY) - .verificationGasCost(VERIFICATION_GAS_COST) + .fixedCostWei(FIXED_GAS_COST_WEI) + .variableCostWei(VARIABLE_GAS_COST_WEI) .build(); private TestableProfitableTransactionSelector transactionSelector; @@ -82,23 +80,12 @@ public void shouldSelectWhenProfitable() { SELECTED); } - @Test - public void shouldSelectWhenProfitableWithAdjustedSize() { - var mockTransactionProcessingResult = mockTransactionProcessingResult(21000); - verifyTransactionSelection( - transactionSelector, - mockEvaluationContext(false, 150, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 21000), - mockTransactionProcessingResult, - SELECTED, - SELECTED); - } - @Test public void shouldNotSelectWhenUnprofitableUpfront() { var mockTransactionProcessingResult = mockTransactionProcessingResult(21000); verifyTransactionSelection( transactionSelector, - mockEvaluationContext(false, 1000, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 21000), + mockEvaluationContext(false, 10000, Wei.of(1_000_100), Wei.of(1_000_000), 21000), mockTransactionProcessingResult, TX_UNPROFITABLE_UPFRONT, null); @@ -109,7 +96,7 @@ public void shouldNotSelectWhenUnprofitable() { var mockTransactionProcessingResult = mockTransactionProcessingResult(21000); verifyTransactionSelection( transactionSelector, - mockEvaluationContext(false, 1000, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 210000), + mockEvaluationContext(false, 10000, Wei.of(1_000_100), Wei.of(1_000_000), 210000), mockTransactionProcessingResult, SELECTED, TX_UNPROFITABLE); @@ -142,7 +129,7 @@ public void shouldSelectPriorityTxEvenWhenUnprofitable() { public void shouldRetryUnprofitableTxWhenBelowLimit() { var mockTransactionProcessingResult = mockTransactionProcessingResult(21000); var mockEvaluationContext = - mockEvaluationContext(false, 1000, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 210000); + mockEvaluationContext(false, 10000, Wei.of(1_000_010), Wei.of(1_000_000), 210000); // first try verifyTransactionSelection( transactionSelector, @@ -177,7 +164,7 @@ public void shouldEvictWhenUnprofitableCacheIsFull() { for (int i = 0; i <= UNPROFITABLE_CACHE_SIZE; i++) { var mockTransactionProcessingResult = mockTransactionProcessingResult(21000); var mockEvaluationContext = - mockEvaluationContext(false, 1000, Wei.of(1_100_000_000), Wei.of(1_000_000_000), 210000); + mockEvaluationContext(false, 10000, Wei.of(1_000_010), Wei.of(1_000_000), 210000); evaluationContexts[i] = mockEvaluationContext; verifyTransactionSelection( transactionSelector, @@ -207,10 +194,10 @@ public void shouldEvictWhenUnprofitableCacheIsFull() { @Test public void shouldNotRetryUnprofitableTxWhenRetryLimitReached() { - var minGasPriceBlock1 = Wei.of(1_000_000_000); + var minGasPriceBlock1 = Wei.of(1_000_000); var mockTransactionProcessingResult1 = mockTransactionProcessingResult(21000); var mockEvaluationContext1 = - mockEvaluationContext(false, 1000, Wei.of(1_100_000_000), minGasPriceBlock1, 210000); + mockEvaluationContext(false, 10000, Wei.of(1_000_010), minGasPriceBlock1, 210000); // first try of first tx verifyTransactionSelection( transactionSelector, @@ -221,7 +208,7 @@ public void shouldNotRetryUnprofitableTxWhenRetryLimitReached() { var mockTransactionProcessingResult2 = mockTransactionProcessingResult(21000); var mockEvaluationContext2 = - mockEvaluationContext(false, 1000, Wei.of(1_100_000_000), minGasPriceBlock1, 210000); + mockEvaluationContext(false, 10000, Wei.of(1_000_010), minGasPriceBlock1, 210000); // first try of second tx verifyTransactionSelection( transactionSelector, @@ -242,7 +229,7 @@ public void shouldNotRetryUnprofitableTxWhenRetryLimitReached() { // simulate another block transactionSelector = newSelectorForNewBlock(); // we need to decrease the min gas price in order to allow a retry - var minGasPriceBlock2 = Wei.of(1_000_000_000).subtract(1); + var minGasPriceBlock2 = minGasPriceBlock1.subtract(1); // we should remember of the unprofitable txs for the new block assertThat( @@ -273,10 +260,10 @@ public void shouldNotRetryUnprofitableTxWhenRetryLimitReached() { @Test public void shouldNotRetryUnprofitableTxWhenMinGasPriceNotDecreased() { - var minGasPriceBlock1 = Wei.of(1_000_000_000); + var minGasPriceBlock1 = Wei.of(1_000_000); var mockTransactionProcessingResult1 = mockTransactionProcessingResult(21000); var mockEvaluationContext1 = - mockEvaluationContext(false, 1000, Wei.of(1_100_000_000), minGasPriceBlock1, 210000); + mockEvaluationContext(false, 10000, Wei.of(1_000_010), minGasPriceBlock1, 210000); // first try of first tx verifyTransactionSelection( transactionSelector, @@ -312,10 +299,10 @@ public void shouldNotRetryUnprofitableTxWhenMinGasPriceNotDecreased() { @Test public void profitableAndUnprofitableTxsMix() { - var minGasPriceBlock1 = Wei.of(1_000_000_000); + var minGasPriceBlock1 = Wei.of(1_000_000); var mockTransactionProcessingResult1 = mockTransactionProcessingResult(21000); var mockEvaluationContext1 = - mockEvaluationContext(false, 1000, Wei.of(1_100_000_000), minGasPriceBlock1, 210000); + mockEvaluationContext(false, 10000, Wei.of(1_000_010), minGasPriceBlock1, 210000); // first try of first tx verifyTransactionSelection( transactionSelector, @@ -326,7 +313,7 @@ public void profitableAndUnprofitableTxsMix() { var mockTransactionProcessingResult2 = mockTransactionProcessingResult(21000); var mockEvaluationContext2 = - mockEvaluationContext(false, 100, Wei.of(1_100_000_000), minGasPriceBlock1, 210000); + mockEvaluationContext(false, 1000, Wei.of(1_000_010), minGasPriceBlock1, 210000); // first try of second tx verifyTransactionSelection( transactionSelector, diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ToyAccount.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ToyAccount.java index c426c6c9..4dcf2480 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ToyAccount.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ToyAccount.java @@ -68,6 +68,11 @@ public Address getAddress() { return address; } + @Override + public boolean isStorageEmpty() { + return false; + } + @Override public Hash getAddressHash() { return addressHash.get(); diff --git a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ToyExecutionEnvironment.java b/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ToyExecutionEnvironment.java index 20b688b1..1bec795e 100644 --- a/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ToyExecutionEnvironment.java +++ b/arithmetization/src/test/java/net/consensys/linea/zktracer/testing/ToyExecutionEnvironment.java @@ -165,7 +165,6 @@ private void executeFrom(final ConflationSnapshot conflation) { for (Transaction tx : body.getTransactions()) { final TransactionProcessingResult result = transactionProcessor.processTransaction( - null, overridenToyWorld.updater(), (ProcessableBlockHeader) header, tx, @@ -198,7 +197,6 @@ private void execute() { for (Transaction tx : mockBlockBody.getTransactions()) { final TransactionProcessingResult result = transactionProcessor.processTransaction( - null, toyWorld.updater(), (ProcessableBlockHeader) header, tx, diff --git a/gradle.properties b/gradle.properties index ae5f5b73..48878dea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ releaseVersion=0.1.4-SNAPSHOT -besuVersion=24.4-develop-a5a3eb8 +besuVersion=24.5-develop-0cfa46b besuArtifactGroup=io.consensys.linea-besu distributionIdentifier=besu-sequencer-plugins distributionBaseUrl=https://artifacts.consensys.net/public/linea-besu/raw/names/linea-besu.tar.gz/versions/ diff --git a/gradle/dependency-management.gradle b/gradle/dependency-management.gradle index 3ced8b1e..2efaf7f3 100644 --- a/gradle/dependency-management.gradle +++ b/gradle/dependency-management.gradle @@ -31,6 +31,7 @@ repositories { content { includeGroupByRegex('com\\.splunk\\..*') } } mavenCentral() + mavenLocal() } configurations.all { diff --git a/gradle/dist.gradle b/gradle/dist.gradle index 28631a9c..72d6913b 100644 --- a/gradle/dist.gradle +++ b/gradle/dist.gradle @@ -27,16 +27,37 @@ tasks.register('javadocJar', Jar) { from javadoc.destinationDir } +def lineaBesuDistTar = new File(buildDir, rootProject.besuFilename) + +tasks.register('copyLocalLineaBesu', Copy) { + onlyIf { + project.hasProperty('useLocalLineaBesuDir') + } + def localLineaBesuDir = "${findProperty('useLocalLineaBesuDir')}".replaceFirst('^~', System.getProperty('user.home')) + doFirst { + if (!file(localLineaBesuDir).exists()) { + throw new GradleException("${localLineaBesuDir} not found") + } + } + + from new File("${localLineaBesuDir}/build/distributions/${rootProject.besuFilename}") + into lineaBesuDistTar.parentFile +} + tasks.register('downloadLatestLineaBesu', Download) { - src rootProject.besuUrl - dest new File(buildDir, rootProject.besuFilename) - onlyIfModified true + onlyIf { + !project.hasProperty('useLocalLineaBesuDir') + } + src rootProject.besuUrl + dest lineaBesuDistTar + onlyIfModified true } version = project.hasProperty('releaseVersion') ? project.getProperty('releaseVersion') : 'snapshot' jar { dependsOn downloadLatestLineaBesu + dependsOn copyLocalLineaBesu archiveBaseName = distributionIdentifier @@ -76,8 +97,9 @@ static def getCheckedOutGitCommitHash() { tasks.register('distTar', Tar) { dependsOn jar dependsOn downloadLatestLineaBesu + dependsOn copyLocalLineaBesu - from(tarTree(downloadLatestLineaBesu.dest), { + from(tarTree(lineaBesuDistTar), { eachFile { path = path.replaceFirst(rootProject.besuIdentifier, '') } includeEmptyDirs = false exclude "**/LICENSE" diff --git a/reference-tests/src/test/java/net/consensys/linea/CorsetBlockProcessor.java b/reference-tests/src/test/java/net/consensys/linea/CorsetBlockProcessor.java index 2deddaba..fff9d818 100644 --- a/reference-tests/src/test/java/net/consensys/linea/CorsetBlockProcessor.java +++ b/reference-tests/src/test/java/net/consensys/linea/CorsetBlockProcessor.java @@ -31,7 +31,6 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.Deposit; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.TransactionReceipt; @@ -48,8 +47,8 @@ import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldStateUpdateAccumulator; -import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; +import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.worldstate.WorldUpdater; @Slf4j @@ -84,7 +83,6 @@ public BlockProcessingResult processBlock( final List transactions, final List ommers, final Optional> maybeWithdrawals, - final Optional> maybeDeposits, final PrivateMetadataUpdater privateMetadataUpdater) { final List receipts = new ArrayList<>(); long currentGasUsed = 0; @@ -105,7 +103,8 @@ public BlockProcessingResult processBlock( final WorldUpdater worldStateUpdater = worldState.updater(); - final BlockHashLookup blockHashLookup = new CachingBlockHashLookup(blockHeader, blockchain); + final BlockHashOperation.BlockHashLookup blockHashLookup = + new CachingBlockHashLookup(blockHeader, blockchain); final Address miningBeneficiary = miningBeneficiaryCalculator.calculateBeneficiary(blockHeader); @@ -126,7 +125,6 @@ public BlockProcessingResult processBlock( zkTracer.traceStartBlock(blockHeader, blockBody); final TransactionProcessingResult result = transactionProcessor.processTransaction( - blockchain, worldStateUpdater, blockHeader, transaction, diff --git a/reference-tests/src/test/java/net/consensys/linea/GeneralStateReferenceTestTools.java b/reference-tests/src/test/java/net/consensys/linea/GeneralStateReferenceTestTools.java index 0aebb4a3..a3c3f6ef 100644 --- a/reference-tests/src/test/java/net/consensys/linea/GeneralStateReferenceTestTools.java +++ b/reference-tests/src/test/java/net/consensys/linea/GeneralStateReferenceTestTools.java @@ -175,7 +175,6 @@ public static void executeTest(final GeneralStateTestCaseEipSpec spec) { final TransactionProcessingResult result = processor.processTransaction( - blockchain, worldStateUpdater, blockHeader, transaction,