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/14] 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 722bc4e087..4e6d448f11 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/14] 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 2d5e409ba7..24ea5ea57f 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/14] 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 1c98339480..3994102608 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 a0f2622b78..011374f725 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 459201c52b..85be58c7b1 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/14] 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 0bbb00d570..78cc370743 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/14] 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 7f007d0729..ccdca49127 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 3961bbef5a..ec7843f9e1 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 80f5425f92..61d88823ce 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 8f1a3771ff..9efe79f675 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/14] 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 4e6d448f11..419be301f5 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/14] 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 0000000000..4649d5acae --- /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 e204ccc33a..306795f5ce 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 ccdca49127..171c20daa1 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 0000000000..05b1e543fe --- /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 0000000000..5742ab45ef --- /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 3cd7d7f03e..a9c72da151 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 557fe153ce..20f3797e6c 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/14] 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 348358b244..ed94e9a073 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 306795f5ce..9dc90202b2 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 171c20daa1..16015d7679 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 dd7f50f98e..4afcd450fc 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 0000000000..b5fdcf53ba --- /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 0000000000..50db86315a --- /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 b9aca5c02a..9f8f0ba3bd 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 96252c595d..09afc4f2e7 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 eeb0cd4a0c..a97b2414ad 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 488651f49a..61f1ac3c84 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 0000000000..33ea66181b --- /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 a9c72da151..23e7c66547 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 20f3797e6c..50662b6257 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 1558508134..465d3e5a3b 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 78cc370743..4a45041998 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 9845eb0023..32a7c012c5 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 415fdebfb7..65e89d74d9 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 bbbd8424ed..894b005bda 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 54df1ea4b4..1294ea600a 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 dc8447b1dd..a4ce76aee3 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/14] 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 fc7fee8626..2fe1a42c4e 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 894b005bda..d34742b6de 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 1294ea600a..4af7a79d18 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 97bac3003c..53e6b1d45b 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/14] 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 ed94e9a073..c62f1f79f7 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 b5fdcf53ba..14683bb59a 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 50db86315a..514371ebd3 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 d34742b6de..d27bca7e93 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 4af7a79d18..900bb2bc76 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 2fe1a42c4e..d15ad8e5a4 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 0000000000..dc7c736b43 --- /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 0000000000..2c4cbea303 --- /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 0000000000..78c6a7fa10 --- /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 53e6b1d45b..0000000000 --- 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 0000000000..cd09fe01c8 --- /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 0000000000..7f0e211d59 --- /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 0000000000..9ea005b212 --- /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 0000000000..11c20292b1 --- /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/14] 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 4afcd450fc..d31d093b09 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/14] 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 419be301f5..21042d1f8c 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/14] 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 011374f725..0b83fc12ac 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 0000000000..4a3a393854 --- /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 ec7843f9e1..9f456542f4 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 0000000000..a91bafefc9 --- /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/14] 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 4649d5acae..6c0e8aa158 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 9dc90202b2..1f4493a600 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 05b1e543fe..cefc9fcb1f 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 5742ab45ef..6551c9247a 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 50662b6257..23b214336e 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 4be7dac3f0..3ced8b1ee5 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'