diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b46df9e8..a3113a2c0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -58,8 +58,8 @@ jobs: id: get_version run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - - name: Build without tests - run: ./gradlew build -PreleaseNativeLibs -PreleaseVersion=${{ steps.get_version.outputs.VERSION }} -x test -x spotlessCheck + - name: Build artifacts + run: ./gradlew artifacts -PreleaseNativeLibs -PreleaseVersion=${{ steps.get_version.outputs.VERSION }} env: JAVA_OPTS: -Xmx2g -Dorg.gradle.daemon=false @@ -75,7 +75,7 @@ jobs: draft: true prerelease: false - - name: Upload Release Asset + - name: Upload Release Lib Asset id: upload-release-asset uses: actions/upload-release-asset@v1 env: @@ -85,3 +85,14 @@ jobs: asset_path: ./sequencer/build/libs/linea-sequencer-${{ steps.get_version.outputs.VERSION }}.jar asset_name: linea-sequencer-${{ steps.get_version.outputs.VERSION }}.jar asset_content_type: application/octet-stream + + - name: Upload Release Dist Asset + id: upload-release-dist-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./sequencer/build/distributions/linea-sequencer-${{ steps.get_version.outputs.VERSION }}.zip + asset_name: linea-sequencer-${{ steps.get_version.outputs.VERSION }}.zip + asset_content_type: application/octet-stream diff --git a/acceptance-tests/build.gradle b/acceptance-tests/build.gradle index e84ef399f..dc7cd1c01 100644 --- a/acceptance-tests/build.gradle +++ b/acceptance-tests/build.gradle @@ -51,11 +51,17 @@ tasks.register('acceptanceTests', Test) { } dependencies { - testImplementation project(":native:compress") + testImplementation project(':native:compress') - testImplementation project(":sequencer") + testImplementation project(':sequencer') + testImplementation "${besuArtifactGroup}:besu-datatypes" + testImplementation "${besuArtifactGroup}.internal:api" + testImplementation "${besuArtifactGroup}.internal:core" testImplementation "${besuArtifactGroup}.internal:dsl" + testImplementation "${besuArtifactGroup}.internal:eth" + testImplementation "${besuArtifactGroup}.internal:metrics-core" + testImplementation "${besuArtifactGroup}.internal:services" testImplementation 'net.consensys.linea.zktracer:arithmetization' @@ -63,3 +69,7 @@ dependencies { } test.enabled = false + +jar { + enabled = false +} 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 da41344f5..9fcf28fe3 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 @@ -71,7 +71,7 @@ public void lineaEstimateGasPriorityFeeMinGasPriceLowerBound() { final Account sender = accounts.getSecondaryBenefactor(); final CallParams callParams = - new CallParams(null, sender.getAddress(), null, "", "", "0", null, null, null); + new CallParams(null, sender.getAddress(), null, null, "", "", "0", null, null, null); final var reqLinea = new LineaEstimateGasRequest(callParams); final var respLinea = reqLinea.execute(minerNode.nodeRequests()).getResult(); diff --git a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java index cff3e6e72..7d76243db 100644 --- a/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java +++ b/acceptance-tests/src/test/java/linea/plugin/acc/test/rpc/linea/EstimateGasModuleLimitOverflowTest.java @@ -50,6 +50,7 @@ public void estimateGasFailsForExceedingModuleLineCountTest() throws Exception { new EstimateGasTest.CallParams( null, sender.getAddress(), + null, simpleStorage.getContractAddress(), null, payload.toHexString(), 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 742cb4eb5..f9aa62a2a 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 @@ -99,6 +99,7 @@ public void lineaEstimateGasMatchesEthEstimateGas() { new CallParams( null, sender.getAddress(), + null, sender.getAddress(), null, Bytes.EMPTY.toHexString(), @@ -123,6 +124,7 @@ public void passingGasPriceFieldWorks() { new CallParams( null, sender.getAddress(), + null, sender.getAddress(), null, Bytes.EMPTY.toHexString(), @@ -146,6 +148,7 @@ public void passingChainIdFieldWorks() { new CallParams( "0x539", sender.getAddress(), + null, sender.getAddress(), null, Bytes.EMPTY.toHexString(), @@ -169,6 +172,7 @@ public void passingEIP1559FieldsWorks() { new CallParams( null, sender.getAddress(), + null, sender.getAddress(), null, Bytes.EMPTY.toHexString(), @@ -192,6 +196,7 @@ public void passingChainIdAndEIP1559FieldsWorks() { new CallParams( "0x539", sender.getAddress(), + null, sender.getAddress(), null, Bytes.EMPTY.toHexString(), @@ -219,6 +224,7 @@ public void passingStateOverridesWorks() { new CallParams( "0x539", sender.getAddress(), + null, sender.getAddress(), "1", Bytes.EMPTY.toHexString(), @@ -239,6 +245,49 @@ public void passingStateOverridesWorks() { "transaction up-front cost 0x208cbab601 exceeds transaction sender account balance 0x0"); } + @Test + public void passingNonceWorks() { + + final Account sender = accounts.getSecondaryBenefactor(); + + final CallParams callParams = + new CallParams( + null, + sender.getAddress(), + "0", + sender.getAddress(), + null, + Bytes.EMPTY.toHexString(), + "0", + null, + "0x1234", + null); + + final var reqLinea = new LineaEstimateGasRequest(callParams); + final var respLinea = reqLinea.execute(minerNode.nodeRequests()); + assertThat(respLinea.hasError()).isFalse(); + assertThat(respLinea.getResult()).isNotNull(); + + // try with a future nonce + final CallParams callParamsFuture = + new CallParams( + null, + sender.getAddress(), + "10", + sender.getAddress(), + null, + Bytes.EMPTY.toHexString(), + "0", + null, + "0x1234", + null); + + final var reqLineaFuture = new LineaEstimateGasRequest(callParamsFuture); + final var respLineaFuture = reqLineaFuture.execute(minerNode.nodeRequests()); + assertThat(respLineaFuture.hasError()).isFalse(); + assertThat(respLineaFuture.getResult()).isNotNull(); + } + @Test public void lineaEstimateGasIsProfitable() { @@ -259,6 +308,7 @@ public void lineaEstimateGasIsProfitable() { new CallParams( null, sender.getAddress(), + null, sender.getAddress(), null, payload.toHexString(), @@ -320,6 +370,7 @@ public void invalidParametersLineaEstimateGasRequestReturnErrorResponse() { null, sender.getAddress(), null, + null, "", "", String.valueOf(Integer.MAX_VALUE), @@ -341,6 +392,7 @@ public void revertedTransactionReturnErrorResponse() throws Exception { new CallParams( null, sender.getAddress(), + null, simpleStorage.getContractAddress(), "", "", @@ -363,6 +415,7 @@ public void failedTransactionReturnErrorResponse() { null, sender.getAddress(), null, + null, "", Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY, "0", @@ -371,7 +424,8 @@ public void failedTransactionReturnErrorResponse() { null)); final var respLinea = reqLinea.execute(minerNode.nodeRequests()); assertThat(respLinea.getCode()).isEqualTo(-32000); - assertThat(respLinea.getMessage()).isEqualTo("Failed transaction, reason: INVALID_OPERATION"); + assertThat(respLinea.getMessage()) + .isEqualTo("Failed transaction, reason: Invalid opcode: 0xc8"); } @Test @@ -491,6 +545,7 @@ static class RawEstimateGasResponse extends org.web3j.protocol.core.Response getTestCliOptions() { @Test public void transactionOverModuleLineCountNotAccepted() throws Exception { - final SimpleStorage simpleStorage = deploySimpleStorage(); - final Web3j web3j = minerNode.nodeRequests().eth(); - final String contractAddress = simpleStorage.getContractAddress(); - final String txData = simpleStorage.add(BigInteger.valueOf(100)).encodeFunctionCall(); - // this tx will not be accepted since it goes above the line count limit - final RawTransaction txModuleLineCountTooBig = - RawTransaction.createTransaction( - CHAIN_ID, - BigInteger.valueOf(1), - GAS_LIMIT.divide(BigInteger.TEN), - contractAddress, - VALUE, - txData, - GAS_PRICE, - GAS_PRICE.multiply(BigInteger.TEN).add(BigInteger.ONE)); - final byte[] signedTxContractInteraction = - TransactionEncoder.signMessage( - txModuleLineCountTooBig, Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY)); - - final EthSendTransaction signedTxContractInteractionResp = - web3j.ethSendRawTransaction(Numeric.toHexString(signedTxContractInteraction)).send(); - - assertThat(signedTxContractInteractionResp.hasError()).isTrue(); - assertThat(signedTxContractInteractionResp.getError().getMessage()) - .isEqualTo( - "Transaction 0xe813560d9a3aedff46be12fc32706d8fe9b6565dd7e2db47457a9c416f2d45d7 line count for module ADD=2017 is above the limit 70"); + assertThatThrownBy(() -> deploySimpleStorage()) + .hasMessage( + "JsonRpcError thrown with code -32000. Message: Transaction 0x63ea5c9ec0f0fae53683c1b5f1998fe806f1ad5b655a1d03a9771f1976be45a9 line count for module ROM=2369 is above the limit 2300"); assertThat(getTxPoolContent()).isEmpty(); diff --git a/acceptance-tests/src/test/resources/log4j2.xml b/acceptance-tests/src/test/resources/log4j2.xml index e5b8c1e4b..9118547d7 100644 --- a/acceptance-tests/src/test/resources/log4j2.xml +++ b/acceptance-tests/src/test/resources/log4j2.xml @@ -1,5 +1,5 @@ - + TRACE @@ -13,4 +13,4 @@ - \ No newline at end of file + diff --git a/acceptance-tests/src/test/resources/moduleLimits_sendRawTx.toml b/acceptance-tests/src/test/resources/moduleLimits_sendRawTx.toml index bfe3d64a0..0446dcef7 100644 --- a/acceptance-tests/src/test/resources/moduleLimits_sendRawTx.toml +++ b/acceptance-tests/src/test/resources/moduleLimits_sendRawTx.toml @@ -20,14 +20,14 @@ EUC = 16384 # can probably be lower EXP = 32760 EXT = 20 GAS = 262144 -HUB = 174 +HUB = 176 MMIO = 1048576 MMU = 524288 MOD = 20 MUL = 20 MXP = 35 PHONEY_RLP = 65536 # can probably get lower -ROM = 2402 +ROM = 2300 ROM_LEX = 20 SHF = 63 TX_RLP = 131072 diff --git a/acceptance-tests/src/test/resources/txOverflowModuleLimits.toml b/acceptance-tests/src/test/resources/txOverflowModuleLimits.toml index 6d369917e..69a46f10c 100644 --- a/acceptance-tests/src/test/resources/txOverflowModuleLimits.toml +++ b/acceptance-tests/src/test/resources/txOverflowModuleLimits.toml @@ -20,7 +20,7 @@ EUC = 16384 # can probably be lower EXP = 32760 EXT = 20 GAS = 262144 -HUB = 51 +HUB = 52 MMIO = 1048576 MMU = 524288 MOD = 20 diff --git a/gradle.properties b/gradle.properties index 8ede392d9..be182dcf6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ -releaseVersion=0.8.0-rc8.4 -besuVersion=24.12-delivery41 -arithmetizationVersion=0.8.0-rc8 +releaseVersion=1.2.0-rc3.2 +besuVersion=25.1-delivery45 +arithmetizationVersion=beta-v1.2.0-rc3 besuArtifactGroup=io.consensys.linea-besu distributionIdentifier=linea-sequencer distributionBaseUrl=https://artifacts.consensys.net/public/linea-besu/raw/names/linea-besu.tar.gz/versions/ \ No newline at end of file diff --git a/gradle/build-aliases.gradle b/gradle/build-aliases.gradle index 037136473..62e93ceec 100644 --- a/gradle/build-aliases.gradle +++ b/gradle/build-aliases.gradle @@ -14,13 +14,19 @@ */ // Default tasks and build aliases -defaultTasks 'build', 'checkLicense', 'javadoc', 'jar' +defaultTasks 'build', 'checkLicense', 'javadoc', 'artifacts' -def buildAliases = ['dev': [ - 'spotlessApply', - 'build', - 'checkLicense', -]] +def buildAliases = [ + 'dev': [ + 'spotlessApply', + 'build', + 'checkLicenses' + ], + 'artifacts' : [ + 'jar', + 'distPlugin' + ] +] def expandedTaskList = [] gradle.startParameter.taskNames.each { diff --git a/gradle/common-dependencies.gradle b/gradle/common-dependencies.gradle index fcf0c25ba..40860b821 100644 --- a/gradle/common-dependencies.gradle +++ b/gradle/common-dependencies.gradle @@ -14,7 +14,7 @@ */ dependencies { - api 'org.slf4j:slf4j-api' + implementation 'org.slf4j:slf4j-api' testImplementation 'org.apache.commons:commons-lang3' testImplementation 'com.google.guava:guava' @@ -28,7 +28,6 @@ dependencies { testImplementation 'org.wiremock:wiremock' - testRuntimeOnly 'org.apache.logging.log4j:log4j-api' testRuntimeOnly 'org.apache.logging.log4j:log4j-core' testRuntimeOnly 'org.apache.logging.log4j:log4j-slf4j2-impl' diff --git a/gradle/dist.gradle b/gradle/dist.gradle index 93edbc7e5..b1d246c86 100644 --- a/gradle/dist.gradle +++ b/gradle/dist.gradle @@ -12,6 +12,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ +import de.undercouch.gradle.tasks.download.Download tasks.register('sourcesJar', Jar) { dependsOn classes @@ -25,40 +26,113 @@ tasks.register('javadocJar', Jar) { from javadoc.destinationDir } -version = project.hasProperty('releaseVersion') ? project.getProperty('releaseVersion') : 'snapshot' +def lineaBesuDistTar = new File(new File(buildDir, "downloads"), rootProject.besuFilename) +task downloadLineaBesu { + outputs.file(lineaBesuDistTar) + doLast { + try { + download.run { + src rootProject.besuUrl + dest lineaBesuDistTar + onlyIfModified true + } + } catch (Exception e) { + def localLineaBesuDir = + project.hasProperty('useLocalLineaBesuDir') + ? file("${findProperty('useLocalLineaBesuDir')}".replaceFirst('^~', System.getProperty('user.home'))) + : new File(projectDir, "../../linea-besu") + + def localLineaBesuFile = new File("${localLineaBesuDir.canonicalPath}/build/distributions/${rootProject.besuFilename}") + + logger.warn("Could not download " + rootProject.besuUrl + " trying local copy from " + localLineaBesuFile + " as fallback") + if (!file(localLineaBesuFile).exists()) { + throw new GradleException("Could not download Linea Besu distribution from: " + rootProject.besuUrl + + ", and could not find it locally at ${localLineaBesuFile} either") + } + + copy { + from localLineaBesuFile + into lineaBesuDistTar.parentFile + } + } + } +} + +task unTarLineaBesu(type: Copy) { + dependsOn downloadLineaBesu + + from tarTree(lineaBesuDistTar) + into lineaBesuDistTar.parentFile +} + +def lineaBesuLibDir = new File(lineaBesuDistTar.parentFile, rootProject.besuIdentifier + '/lib') +def lineaBesuLibs = [] + +def excludeBesuProvidedDeps = { + if(lineaBesuLibs.isEmpty()) { + // Get all the dependencies that are provided by Besu + fileTree(dir: lineaBesuLibDir, include: '*.jar').visit { + FileVisitDetails details -> + lineaBesuLibs << details.file.name + } + } + // include the dependency in the jar only if it is not already provided by Besu + !lineaBesuLibs.any { artifactName -> + if(artifactName == it.name) { + return true + } + // exclude Besu group + if(it.toString().contains(besuArtifactGroup)) { + return true + } + // try ignoring the version + def libName = it.name =~ dependencyNamePattern() + def artName = artifactName =~ dependencyNamePattern() + libName[0][1] == artName[0][1] + } +} jar { + dependsOn unTarLineaBesu archiveBaseName = distributionIdentifier + version = calculateVersion() manifest { attributes( - 'Specification-Title': 'arithmetization', - 'Specification-Version': "v${arithmetizationVersion}", - 'Implementation-Title': 'arithmetization', - 'Implementation-Version': "v${arithmetizationVersion}" + 'Specification-Title': archiveBaseName.get(), + 'Specification-Version': calculateVersion(), + 'Implementation-Title': archiveBaseName.get(), + 'Implementation-Version': calculateVersion() ) } - from { - configurations.runtimeClasspath.filter( {! (it.name =~ /log4j.*\.jar/ )} ) - .collect {it.isDirectory() ? it : zipTree(it) } - } - exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA', 'ch.qos.logback' - duplicatesStrategy(DuplicatesStrategy.EXCLUDE) + from { + configurations.runtimeClasspath.filter(excludeBesuProvidedDeps).collect { + it.isDirectory() ? it : zipTree(it) + } + } + + duplicatesStrategy('exclude') } -// Takes the version, and if -SNAPSHOT is part of it replaces SNAPSHOT -// with the git commit version. -def calculateVersion() { - String version = rootProject.version - if (version.endsWith("-SNAPSHOT")) { - version = version.replace("-SNAPSHOT", "-dev-${getCheckedOutGitCommitHash()}") - } +/** + * Create a distribution of the plugin, that only contains the plugin jar and the + * dependencies that are not provided by Besu itself, so that is can be simply + * extracted in the Besu plugins dir. + */ +tasks.register('distPlugin', Zip) { + dependsOn installDist - return version -} + archiveBaseName = distributionIdentifier -static def getCheckedOutGitCommitHash() { - def hashLength = 8 - "git rev-parse HEAD".execute().text.take(hashLength) + from("${buildDir}/libs/${distributionIdentifier}-${calculateVersion()}.jar") + from { + configurations.runtimeClasspath.filter( + excludeBesuProvidedDeps) + + } } + +static def dependencyNamePattern() { + /(.*)(\-.*?)\.jar/ +} \ No newline at end of file diff --git a/gradle/java.gradle b/gradle/java.gradle index 01c62843f..6d1a1e122 100644 --- a/gradle/java.gradle +++ b/gradle/java.gradle @@ -36,4 +36,20 @@ tasks.withType(JavaCompile) { ] options.encoding = 'UTF-8' +} + +// Takes the version, and if -SNAPSHOT is part of it replaces SNAPSHOT +// with the git commit version. +ext.calculateVersion = { -> + String version = rootProject.version + if (version.endsWith("-SNAPSHOT")) { + version = version.replace("-SNAPSHOT", "-dev-${getCheckedOutGitCommitHash()}") + } + + return version +} + +static def getCheckedOutGitCommitHash() { + def hashLength = 8 + "git rev-parse HEAD".execute().text.take(hashLength) } \ No newline at end of file diff --git a/gradle/tests.gradle b/gradle/tests.gradle index c30c8ac2e..c30ecd0fd 100644 --- a/gradle/tests.gradle +++ b/gradle/tests.gradle @@ -21,10 +21,6 @@ jacoco { toolVersion = '0.8.12' } -configurations { - testSupportImplementation.extendsFrom implementation -} - test { description = 'Runs unit tests.' diff --git a/native/compress/build.gradle b/native/compress/build.gradle index 6bb536287..55c1b6f97 100644 --- a/native/compress/build.gradle +++ b/native/compress/build.gradle @@ -23,14 +23,7 @@ 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() -} - -test { - useJUnitPlatform() -} +apply from: rootProject.file("gradle/tests.gradle") tasks.register('buildJNI', Exec) { if(project.hasProperty("releaseNativeLibs")) { @@ -52,11 +45,6 @@ compileJava{ dependencies { implementation 'io.tmio:tuweni-bytes' implementation 'net.java.dev.jna:jna' - - testImplementation 'org.assertj:assertj-core' - testImplementation 'org.junit.jupiter:junit-jupiter-api' - testImplementation 'org.junit.jupiter:junit-jupiter-engine' - testImplementation 'org.mockito:mockito-core' } @@ -92,16 +80,6 @@ processResources.dependsOn windowsLibCopy jar { archiveBaseName = 'linea-native-compress' + version = calculateVersion() 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) diff --git a/sequencer/build.gradle b/sequencer/build.gradle index b50c552a9..435f58b0e 100644 --- a/sequencer/build.gradle +++ b/sequencer/build.gradle @@ -15,11 +15,12 @@ plugins { id 'java' + id 'java-library-distribution' id 'common-plugins' + id 'de.undercouch.download' } group = 'net.consensys.linea.besu.plugin' -version = rootProject.version apply from: rootProject.file("gradle/java.gradle") apply from: rootProject.file("gradle/dependency-management.gradle") @@ -31,6 +32,7 @@ apply from: rootProject.file("gradle/lint.gradle") dependencies { // annotationProcessor generates the file META-INF/services/org.hyperledger.besu.plugin.BesuPlugin annotationProcessor 'com.google.auto.service:auto-service' + compileOnly 'com.google.auto.service:auto-service' implementation project(":native:compress") @@ -42,35 +44,21 @@ dependencies { implementation "${besuArtifactGroup}.internal:core" implementation "${besuArtifactGroup}.internal:rlp" - implementation 'com.google.auto.service:auto-service' - implementation 'com.google.code.gson:gson' - implementation 'info.picocli:picocli' - implementation 'io.tmio:tuweni-bytes' implementation 'io.tmio:tuweni-units' implementation 'io.tmio:tuweni-toml' + implementation 'info.picocli:picocli' + implementation 'net.consensys.linea.zktracer:arithmetization' implementation 'org.hibernate.validator:hibernate-validator' testImplementation "${besuArtifactGroup}.internal:besu" - - testImplementation 'org.awaitility:awaitility' } -configurations { - installedJars { - transitive = false - } -} - apply from: rootProject.file("gradle/dist.gradle") - -jar { - zip64=true -} diff --git a/sequencer/src/main/java/net/consensys/linea/rpc/methods/LineaEstimateGas.java b/sequencer/src/main/java/net/consensys/linea/rpc/methods/LineaEstimateGas.java index e7ad22565..cc5ab4d0d 100644 --- a/sequencer/src/main/java/net/consensys/linea/rpc/methods/LineaEstimateGas.java +++ b/sequencer/src/main/java/net/consensys/linea/rpc/methods/LineaEstimateGas.java @@ -44,7 +44,8 @@ import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.params.ECDomainParameters; import org.hyperledger.besu.crypto.SECPSignature; -import org.hyperledger.besu.datatypes.AccountOverrideMap; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.StateOverrideMap; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException; @@ -53,7 +54,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.evm.tracing.EstimateGasOperationTracer; -import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.RpcEndpointService; @@ -149,7 +150,7 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) { } final var callParameters = parseCallParameters(request.getParams()); - final var maybeStateOverrides = getAddressAccountOverrideMap(request.getParams()); + final var maybeStateOverrides = getStateOverrideMap(request.getParams()); final var minGasPrice = besuConfiguration.getMinGasPrice(); final var gasLimitUpperBound = calculateGasLimitUpperBound(callParameters, logId); final Wei baseFee = @@ -160,7 +161,7 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) { new PluginRpcEndpointException( RpcErrorType.INVALID_REQUEST, "Not on a baseFee market")); final var transaction = - createTransactionForSimulation(callParameters, gasLimitUpperBound, baseFee); + createTransactionForSimulation(callParameters, gasLimitUpperBound, baseFee, logId); log.atDebug() .setMessage("[{}] Parsed call parameters: {}; Transaction: {}; Gas limit upper bound {}") .addArgument(logId) @@ -194,6 +195,7 @@ public LineaEstimateGas.Response execute(final PluginRpcRequest request) { private long calculateGasLimitUpperBound( final JsonCallParameter callParameters, final long logId) { if (callParameters.getFrom() != null) { + final var sender = callParameters.getFrom(); final var maxGasPrice = calculateTxMaxGasPrice(callParameters); log.atTrace() .setMessage("[{}] Calculated max gas price {}") @@ -201,21 +203,7 @@ private long calculateGasLimitUpperBound( .addArgument(maxGasPrice) .log(); if (maxGasPrice != null) { - final var sender = callParameters.getFrom(); - final var resp = - rpcEndpointService.call( - "eth_getBalance", new Object[] {sender.toHexString(), "latest"}); - if (!resp.getType().equals(RpcResponseType.SUCCESS)) { - throw new PluginRpcEndpointException(new InternalError("Unable to query sender balance")); - } - final var balance = Wei.fromHexString((String) resp.getResult()); - log.atTrace() - .setMessage("[{}] eth_getBalance response for {} is {}, balance {}") - .addArgument(logId) - .addArgument(sender) - .addArgument(resp::getResult) - .addArgument(balance::toHumanReadableString) - .log(); + final Wei balance = getSenderBalance(sender, logId); if (balance.greaterThan(Wei.ZERO)) { final var value = callParameters.getValue(); final var balanceForGas = value == null ? balance : balance.subtract(value); @@ -242,6 +230,23 @@ private long calculateGasLimitUpperBound( return txValidatorConf.maxTxGasLimit(); } + private Wei getSenderBalance(final Address sender, final long logId) { + final var resp = + rpcEndpointService.call("eth_getBalance", new Object[] {sender.toHexString(), "latest"}); + if (!resp.getType().equals(RpcResponseType.SUCCESS)) { + throw new PluginRpcEndpointException(new InternalError("Unable to query sender balance")); + } + final Wei balance = Wei.fromHexString((String) resp.getResult()); + log.atTrace() + .setMessage("[{}] eth_getBalance response for {} is {}, balance {}") + .addArgument(logId) + .addArgument(sender) + .addArgument(resp::getResult) + .addArgument(balance::toHumanReadableString) + .log(); + return balance; + } + private Wei calculateTxMaxGasPrice(final JsonCallParameter callParameters) { return callParameters.getMaxFeePerGas().orElseGet(callParameters::getGasPrice); } @@ -268,19 +273,19 @@ private Wei getEstimatedPriorityFee( private Long estimateGasUsed( final JsonCallParameter callParameters, - final Optional maybeStateOverrides, + final Optional maybeStateOverrides, final Transaction transaction, final Wei baseFee, final long logId) { final var estimateGasTracer = new EstimateGasOperationTracer(); - final var chainHeadHeader = blockchainService.getChainHeadHeader(); - final var zkTracer = createZkTracer(chainHeadHeader, blockchainService.getChainId().get()); + final var pendingBlockHeader = transactionSimulationService.simulatePendingBlockHeader(); + final var zkTracer = createZkTracer(pendingBlockHeader, blockchainService.getChainId().get()); final TracerAggregator zkAndGasTracer = TracerAggregator.create(estimateGasTracer, zkTracer); final var maybeSimulationResults = transactionSimulationService.simulate( - transaction, maybeStateOverrides, Optional.empty(), zkAndGasTracer, false); + transaction, maybeStateOverrides, pendingBlockHeader, zkAndGasTracer, false, true); ModuleLimitsValidationResult moduleLimit = moduleLineCountValidator.validate(zkTracer.getModulesLineCount()); @@ -326,11 +331,13 @@ private Long estimateGasUsed( final var lowGasEstimation = r.result().getEstimateGasUsedByTransaction(); final var lowResult = transactionSimulationService.simulate( - createTransactionForSimulation(callParameters, lowGasEstimation, baseFee), + createTransactionForSimulation( + callParameters, lowGasEstimation, baseFee, logId), maybeStateOverrides, - Optional.empty(), + pendingBlockHeader, estimateGasTracer, - false); + false, + true); return lowResult .map( @@ -362,11 +369,13 @@ private Long estimateGasUsed( final var binarySearchResult = transactionSimulationService.simulate( - createTransactionForSimulation(callParameters, mid, baseFee), + createTransactionForSimulation( + callParameters, mid, baseFee, logId), maybeStateOverrides, - Optional.empty(), + pendingBlockHeader, estimateGasTracer, - false); + false, + true); if (binarySearchResult.isEmpty() || !binarySearchResult.get().isSuccessful()) { @@ -444,9 +453,9 @@ private void validateCallParameters(final JsonCallParameter callParameters) { } } - protected Optional getAddressAccountOverrideMap(final Object[] params) { + protected Optional getStateOverrideMap(final Object[] params) { try { - return parameterParser.optional(params, 1, AccountOverrideMap.class); + return parameterParser.optional(params, 1, StateOverrideMap.class); } catch (JsonRpcParameter.JsonRpcParameterException e) { throw new InvalidJsonRpcRequestException( "Invalid account overrides parameter (index 1)", RpcErrorType.INVALID_CALL_PARAMS, e); @@ -479,11 +488,15 @@ private long highGasEstimation( } private Transaction createTransactionForSimulation( - final JsonCallParameter callParameters, final long maxTxGasLimit, final Wei baseFee) { + final JsonCallParameter callParameters, + final long maxTxGasLimit, + final Wei baseFee, + final long logId) { final var txBuilder = Transaction.builder() .sender(callParameters.getFrom()) + .nonce(callParameters.getNonce().orElseGet(() -> getSenderNonce(callParameters, logId))) .to(callParameters.getTo()) .gasLimit(maxTxGasLimit) .payload( @@ -520,10 +533,30 @@ private Transaction createTransactionForSimulation( return txBuilder.build(); } - private ZkTracer createZkTracer(final BlockHeader chainHeadHeader, final BigInteger chainId) { + private Long getSenderNonce(final JsonCallParameter callParameters, final long logId) { + final var sender = callParameters.getFrom(); + final var resp = + rpcEndpointService.call( + "eth_getTransactionCount", new Object[] {sender.toHexString(), "latest"}); + if (!resp.getType().equals(RpcResponseType.SUCCESS)) { + throw new PluginRpcEndpointException(new InternalError("Unable to query sender nonce")); + } + final Long nonce = Long.decode((String) resp.getResult()); + log.atTrace() + .setMessage("[{}] eth_getTransactionCount response for {} is {}, nonce {}") + .addArgument(logId) + .addArgument(sender) + .addArgument(resp::getResult) + .addArgument(nonce) + .log(); + return nonce; + } + + private ZkTracer createZkTracer( + final ProcessableBlockHeader pendingBlockHeader, final BigInteger chainId) { var zkTracer = new ZkTracer(l1L2BridgeConfiguration, chainId); zkTracer.traceStartConflation(1L); - zkTracer.traceStartBlock(chainHeadHeader); + zkTracer.traceStartBlock(pendingBlockHeader, pendingBlockHeader.getCoinbase()); return zkTracer; } diff --git a/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java b/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java index 7d9638d6b..56b8761f7 100644 --- a/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java +++ b/sequencer/src/main/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidator.java @@ -32,7 +32,7 @@ import net.consensys.linea.sequencer.modulelimit.ModuleLineCountValidator; import net.consensys.linea.zktracer.ZkTracer; import org.hyperledger.besu.datatypes.Transaction; -import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; import org.hyperledger.besu.plugin.data.TransactionSimulationResult; import org.hyperledger.besu.plugin.services.BlockchainService; import org.hyperledger.besu.plugin.services.TransactionSimulationService; @@ -85,11 +85,12 @@ public Optional validateTransaction( final ModuleLineCountValidator moduleLineCountValidator = new ModuleLineCountValidator(moduleLineLimitsMap); - final var chainHeadHeader = blockchainService.getChainHeadHeader(); + final var pendingBlockHeader = transactionSimulationService.simulatePendingBlockHeader(); - final var zkTracer = createZkTracer(chainHeadHeader, blockchainService.getChainId().get()); + final var zkTracer = createZkTracer(pendingBlockHeader, blockchainService.getChainId().get()); final var maybeSimulationResults = - transactionSimulationService.simulate(transaction, Optional.empty(), zkTracer, true); + transactionSimulationService.simulate( + transaction, Optional.empty(), pendingBlockHeader, zkTracer, false, true); ModuleLimitsValidationResult moduleLimitResult = moduleLineCountValidator.validate(zkTracer.getModulesLineCount()); @@ -158,10 +159,11 @@ private void logSimulationResult( .log(); } - private ZkTracer createZkTracer(final BlockHeader chainHeadHeader, BigInteger chainId) { + private ZkTracer createZkTracer( + final ProcessableBlockHeader pendingBlockHeader, BigInteger chainId) { var zkTracer = new ZkTracer(l1L2BridgeConfiguration, chainId); zkTracer.traceStartConflation(1L); - zkTracer.traceStartBlock(chainHeadHeader); + zkTracer.traceStartBlock(pendingBlockHeader, pendingBlockHeader.getCoinbase()); return zkTracer; } diff --git a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java index 2cd6768f5..75c9518d5 100644 --- a/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java +++ b/sequencer/src/main/java/net/consensys/linea/sequencer/txselection/selectors/ProfitableTransactionSelector.java @@ -280,14 +280,13 @@ private void updateMetric( final Transaction tx, final Wei profitablePriorityFeePerGas) { + final var effectivePriorityFee = evaluationContext.getTransactionGasPrice().subtract(baseFee); + final var ratio = + effectivePriorityFee.getValue().doubleValue() + / profitablePriorityFeePerGas.getValue().doubleValue(); + maybeProfitabilityMetrics.ifPresent( histogramMetrics -> { - final var effectivePriorityFee = - evaluationContext.getTransactionGasPrice().subtract(baseFee); - final var ratio = - effectivePriorityFee.getValue().doubleValue() - / profitablePriorityFeePerGas.getValue().doubleValue(); - histogramMetrics.track(ratio, label.value()); if (ratio < lastBlockMinRatios.get(label)) { @@ -296,19 +295,20 @@ private void updateMetric( if (ratio > lastBlockMaxRatios.get(label)) { lastBlockMaxRatios.put(label, ratio); } - - log.atTrace() - .setMessage( - "POST_PROCESSING: block[{}] tx {} , baseFee {}, effectiveGasPrice {}, ratio (effectivePayingPriorityFee {} / calculatedProfitablePriorityFee {}) {}") - .addArgument(evaluationContext.getPendingBlockHeader().getNumber()) - .addArgument(tx.getHash()) - .addArgument(baseFee::toHumanReadableString) - .addArgument(evaluationContext.getTransactionGasPrice()::toHumanReadableString) - .addArgument(effectivePriorityFee::toHumanReadableString) - .addArgument(profitablePriorityFeePerGas::toHumanReadableString) - .addArgument(ratio) - .log(); }); + + log.atTrace() + .setMessage( + "{}: block[{}] tx {} , baseFee {}, effectiveGasPrice {}, ratio (effectivePayingPriorityFee {} / calculatedProfitablePriorityFee {}) {}") + .addArgument(label.name()) + .addArgument(evaluationContext.getPendingBlockHeader().getNumber()) + .addArgument(tx.getHash()) + .addArgument(baseFee::toHumanReadableString) + .addArgument(evaluationContext.getTransactionGasPrice()::toHumanReadableString) + .addArgument(effectivePriorityFee::toHumanReadableString) + .addArgument(profitablePriorityFeePerGas::toHumanReadableString) + .addArgument(ratio) + .log(); } private static void resetMinMaxRatios() { diff --git a/sequencer/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java b/sequencer/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java index d8f395978..d1af7304e 100644 --- a/sequencer/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java +++ b/sequencer/src/test/java/net/consensys/linea/sequencer/txpoolvalidation/validators/SimulationValidatorTest.java @@ -128,9 +128,10 @@ public void initialize(final WireMockRuntimeInfo wmInfo) throws MalformedURLExce .moduleLimitsFilePath(lineLimitsConfPath.toString()) .build(); lineCountLimits = new HashMap<>(ModuleLineCountValidator.createLimitModules(tracerConf)); - final var blockHeader = mock(BlockHeader.class); - when(blockHeader.getBaseFee()).thenReturn(Optional.of(BASE_FEE)); - when(blockchainService.getChainHeadHeader()).thenReturn(blockHeader); + final var pendingBlockHeader = mock(BlockHeader.class); + when(pendingBlockHeader.getBaseFee()).thenReturn(Optional.of(BASE_FEE)); + when(pendingBlockHeader.getCoinbase()).thenReturn(Address.ZERO); + when(transactionSimulationService.simulatePendingBlockHeader()).thenReturn(pendingBlockHeader); when(blockchainService.getChainId()).thenReturn(Optional.of(BigInteger.ONE)); final var rejectedTxReportingConf =