From 24366fc9f1481cece1db4bce562a122af1411ce8 Mon Sep 17 00:00:00 2001 From: Pedro Novais <1478752+jpnovais@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:32:39 +0100 Subject: [PATCH] coordinator: support for smart contract v6 (#240) * coordinator: support for smart contract v6 --- Makefile | 15 ++ .../smart-contract-client/build.gradle | 1 + .../contract/l1/Web3JFunctionBuildersV6.kt | 153 ++++++++++++++++++ .../l1/Web3JLineaRollupSmartContractClient.kt | 139 ++++++---------- ...JLineaRollupSmartContractClientReadOnly.kt | 99 ++++++++---- .../LineaRollupSmartContractClient.kt | 11 +- .../BlobAndAggregationFinalizationIntTest.kt | 42 +++-- ...1ShnarfBasedAlreadySubmittedBlobsFilter.kt | 10 +- ...arfBasedAlreadySubmittedBlobsFilterTest.kt | 8 +- .../L1EventQuerierIntegrationTest.kt | 5 +- .../MessageServiceIntegrationTest.kt | 5 +- coordinator/ethereum/test-utils/build.gradle | 2 + .../zkevm/ethereum/ContractsManager.kt | 112 ++++++++----- .../MakefileContractDeploymentHelper.kt | 8 +- .../linea-contracts/l1-rollup/build.gradle | 6 +- 15 files changed, 424 insertions(+), 192 deletions(-) create mode 100644 coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JFunctionBuildersV6.kt diff --git a/Makefile b/Makefile index cda13531..8f72498f 100644 --- a/Makefile +++ b/Makefile @@ -80,6 +80,21 @@ deploy-linea-rollup: LINEA_ROLLUP_GENESIS_TIMESTAMP=1683325137 \ npx hardhat deploy --no-compile --network zkevm_dev --tags PlonkVerifier,LineaRollupV5 +deploy-linea-rollup-v6: + # WARNING: FOR LOCAL DEV ONLY - DO NOT REUSE THESE KEYS ELSEWHERE + cd contracts/; \ + PRIVATE_KEY=$${DEPLOYMENT_PRIVATE_KEY:-0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80} \ + BLOCKCHAIN_NODE=http:\\localhost:8445/ \ + PLONKVERIFIER_NAME=IntegrationTestTrueVerifier \ + LINEA_ROLLUP_INITIAL_STATE_ROOT_HASH=0x072ead6777750dc20232d1cee8dc9a395c2d350df4bbaa5096c6f59b214dcecd \ + LINEA_ROLLUP_INITIAL_L2_BLOCK_NUMBER=0 \ + LINEA_ROLLUP_SECURITY_COUNCIL=0x90F79bf6EB2c4f870365E785982E1f101E93b906 \ + LINEA_ROLLUP_OPERATORS=$${LINEA_ROLLUP_OPERATORS:-0x70997970C51812dc3A010C7d01b50e0d17dc79C8,0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC} \ + LINEA_ROLLUP_RATE_LIMIT_PERIOD=86400 \ + LINEA_ROLLUP_RATE_LIMIT_AMOUNT=1000000000000000000000 \ + LINEA_ROLLUP_GENESIS_TIMESTAMP=1683325137 \ + npx hardhat deploy --no-compile --network zkevm_dev --tags PlonkVerifier,LineaRollup + deploy-l2messageservice: # WARNING: FOR LOCAL DEV ONLY - DO NOT REUSE THESE KEYS ELSEWHERE cd contracts/; \ diff --git a/coordinator/clients/smart-contract-client/build.gradle b/coordinator/clients/smart-contract-client/build.gradle index ddebc978..68fd0b30 100644 --- a/coordinator/clients/smart-contract-client/build.gradle +++ b/coordinator/clients/smart-contract-client/build.gradle @@ -7,6 +7,7 @@ dependencies { implementation project(':jvm-libs:linea:web3j-extensions') api 'build.linea:l1-rollup-contract-client:0.0.1' api 'build.linea:l2-message-service-contract-client:0.0.1' + implementation(project(':jvm-libs:linea:linea-contracts:l1-rollup')) api ("org.web3j:core:${libs.versions.web3j.get()}") { exclude group: 'org.slf4j', module: 'slf4j-nop' diff --git a/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JFunctionBuildersV6.kt b/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JFunctionBuildersV6.kt new file mode 100644 index 00000000..1a66f464 --- /dev/null +++ b/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JFunctionBuildersV6.kt @@ -0,0 +1,153 @@ +package net.consensys.linea.contract.l1 + +import build.linea.contract.LineaRollupV6 +import net.consensys.toBigInteger +import net.consensys.zkevm.coordinator.clients.smartcontract.LineaContractVersion +import net.consensys.zkevm.domain.BlobRecord +import net.consensys.zkevm.domain.ProofToFinalize +import org.web3j.abi.TypeReference +import org.web3j.abi.datatypes.DynamicArray +import org.web3j.abi.datatypes.DynamicBytes +import org.web3j.abi.datatypes.Function +import org.web3j.abi.datatypes.Type +import org.web3j.abi.datatypes.generated.Bytes32 +import org.web3j.abi.datatypes.generated.Uint256 +import java.math.BigInteger +import java.util.Arrays + +internal fun buildSubmitBlobsFunction( + version: LineaContractVersion, + blobs: List +): Function { + return when (version) { + LineaContractVersion.V5 -> buildSubmitBlobsFunction(blobs) + LineaContractVersion.V6 -> buildSubmitBlobsFunctionV6(blobs) + } +} + +internal fun buildSubmitBlobsFunctionV6( + blobs: List +): Function { + val blobsSubmissionData = blobs.map { blob -> + val blobCompressionProof = blob.blobCompressionProof!! + // BlobSubmission(BigInteger dataEvaluationClaim, byte[] kzgCommitment, byte[] kzgProof, + // byte[] finalStateRootHash, byte[] snarkHash) + LineaRollupV6.BlobSubmission( + /*dataEvaluationClaim*/ BigInteger(blobCompressionProof.expectedY), + /*kzgCommitment*/ blobCompressionProof.commitment, + /*kzgProof*/ blobCompressionProof.kzgProofContract, + /*finalStateRootHash*/ blobCompressionProof.finalStateRootHash, + /*snarkHash*/ blobCompressionProof.snarkHash + ) + } + + /** + function submitBlobs( + BlobSubmission[] calldata _blobSubmissions, + bytes32 _parentShnarf, + bytes32 _finalBlobShnarf + ) + */ + return Function( + LineaRollupV6.FUNC_SUBMITBLOBS, + Arrays.asList>( + DynamicArray(LineaRollupV6.BlobSubmission::class.java, blobsSubmissionData), + Bytes32(blobs.first().blobCompressionProof!!.prevShnarf), + Bytes32(blobs.last().blobCompressionProof!!.expectedShnarf) + ), + emptyList>() + ) +} + +fun buildFinalizeBlocksFunction( + version: LineaContractVersion, + aggregationProof: ProofToFinalize, + aggregationLastBlob: BlobRecord, + parentShnarf: ByteArray, + parentL1RollingHash: ByteArray, + parentL1RollingHashMessageNumber: Long +): Function { + when (version) { + LineaContractVersion.V5 -> { + return buildFinalizeBlobsFunction( + aggregationProof, + aggregationLastBlob, + parentShnarf, + parentL1RollingHash, + parentL1RollingHashMessageNumber + ) + } + + LineaContractVersion.V6 -> { + return buildFinalizeBlockFunctionV6( + aggregationProof, + aggregationLastBlob, + parentL1RollingHash, + parentL1RollingHashMessageNumber + ) + } + } +} + +internal fun buildFinalizeBlockFunctionV6( + aggregationProof: ProofToFinalize, + aggregationLastBlob: BlobRecord, + parentL1RollingHash: ByteArray, + parentL1RollingHashMessageNumber: Long +): Function { + val aggregationEndBlobInfo = LineaRollupV6.ShnarfData( + /*parentShnarf*/ aggregationLastBlob.blobCompressionProof!!.prevShnarf, + /*snarkHash*/ aggregationLastBlob.blobCompressionProof!!.snarkHash, + /*finalStateRootHash*/ aggregationLastBlob.blobCompressionProof!!.finalStateRootHash, + /*dataEvaluationPoint*/ aggregationLastBlob.blobCompressionProof!!.expectedX, + /*dataEvaluationClaim*/ aggregationLastBlob.blobCompressionProof!!.expectedY + ) + +// FinalizationDataV3( +// byte[] parentStateRootHash, +// BigInteger endBlockNumber, +// ShnarfData shnarfData, +// BigInteger lastFinalizedTimestamp, +// BigInteger finalTimestamp, +// byte[] lastFinalizedL1RollingHash, +// byte[] l1RollingHash, +// BigInteger lastFinalizedL1RollingHashMessageNumber, +// BigInteger l1RollingHashMessageNumber, +// BigInteger l2MerkleTreesDepth, +// List l2MerkleRoots, +// byte[] l2MessagingBlocksOffsets +// ) + + val finalizationData = LineaRollupV6.FinalizationDataV3( + /*parentStateRootHash*/ aggregationProof.parentStateRootHash, + /*endBlockNumber*/ aggregationProof.endBlockNumber.toBigInteger(), + /*shnarfData*/ aggregationEndBlobInfo, + /*lastFinalizedTimestamp*/ aggregationProof.parentAggregationLastBlockTimestamp.epochSeconds.toBigInteger(), + /*finalTimestamp*/ aggregationProof.finalTimestamp.epochSeconds.toBigInteger(), + /*lastFinalizedL1RollingHash*/ parentL1RollingHash, + /*l1RollingHash*/ aggregationProof.l1RollingHash, + /*lastFinalizedL1RollingHashMessageNumber*/ parentL1RollingHashMessageNumber.toBigInteger(), + /*l1RollingHashMessageNumber*/ aggregationProof.l1RollingHashMessageNumber.toBigInteger(), + /*l2MerkleTreesDepth*/ aggregationProof.l2MerkleTreesDepth.toBigInteger(), + /*l2MerkleRoots*/ aggregationProof.l2MerkleRoots, + /*l2MessagingBlocksOffsets*/ aggregationProof.l2MessagingBlocksOffsets + ) + + /** + * function finalizeBlocks( + * bytes calldata _aggregatedProof, + * uint256 _proofType, + * FinalizationDataV3 calldata _finalizationData + * ) + */ + val function = Function( + LineaRollupV6.FUNC_FINALIZEBLOCKS, + Arrays.asList>( + DynamicBytes(aggregationProof.aggregatedProof), + Uint256(aggregationProof.aggregatedVerifierIndex.toLong()), + finalizationData + ), + emptyList>() + ) + return function +} diff --git a/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JLineaRollupSmartContractClient.kt b/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JLineaRollupSmartContractClient.kt index 51a87ab0..47d87ef3 100644 --- a/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JLineaRollupSmartContractClient.kt +++ b/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JLineaRollupSmartContractClient.kt @@ -105,53 +105,39 @@ class Web3JLineaRollupSmartContractClient internal constructor( } } - override fun submitBlobs( - blobs: List, - gasPriceCaps: GasPriceCaps? - ): SafeFuture { - return submitBlobsV5(blobs, gasPriceCaps) - } - /** * Sends EIP4844 blob carrying transaction to the smart contract. * Uses SMC `submitBlobs` function that supports multiple blobs per call. */ - private fun submitBlobsV5( + override fun submitBlobs( blobs: List, gasPriceCaps: GasPriceCaps? ): SafeFuture { - require(blobs.size in 1..6) { "Blobs size=${blobs.size} must be between 1 and 6." } - val function = buildSubmitBlobsFunction( - blobs - ) - - return helper.sendBlobCarryingTransactionAndGetTxHash( - function = function, - blobs = blobs.map { it.blobCompressionProof!!.compressedData }, - gasPriceCaps = gasPriceCaps - ) + return getVersion() + .thenCompose { version -> + val function = buildSubmitBlobsFunction(version, blobs) + helper.sendBlobCarryingTransactionAndGetTxHash( + function = function, + blobs = blobs.map { it.blobCompressionProof!!.compressedData }, + gasPriceCaps = gasPriceCaps + ) + } } override fun submitBlobsEthCall( blobs: List, gasPriceCaps: GasPriceCaps? ): SafeFuture { - return submitBlobsEthCallImpl(blobs, gasPriceCaps) - } - - private fun submitBlobsEthCallImpl( - blobs: List, - gasPriceCaps: GasPriceCaps? = null - ): SafeFuture { - val function = buildSubmitBlobsFunction(blobs) - - val transaction = helper.createEip4844Transaction( - function, - blobs.map { it.blobCompressionProof!!.compressedData }.toWeb3JTxBlob(), - gasPriceCaps - ) - - return web3j.informativeEthCall(transaction, smartContractErrors) + return getVersion() + .thenCompose { version -> + val function = buildSubmitBlobsFunction(version, blobs) + val transaction = helper.createEip4844Transaction( + function, + blobs.map { it.blobCompressionProof!!.compressedData }.toWeb3JTxBlob(), + gasPriceCaps + ) + web3j.informativeEthCall(transaction, smartContractErrors) + } } override fun finalizeBlocks( @@ -162,38 +148,22 @@ class Web3JLineaRollupSmartContractClient internal constructor( parentL1RollingHashMessageNumber: Long, gasPriceCaps: GasPriceCaps? ): SafeFuture { - return finalizeBlocksV5( - aggregation, - aggregationLastBlob, - parentShnarf, - parentL1RollingHash, - parentL1RollingHashMessageNumber, - gasPriceCaps - ) - } - - private fun finalizeBlocksV5( - aggregation: ProofToFinalize, - aggregationLastBlob: BlobRecord, - parentShnarf: ByteArray, - parentL1RollingHash: ByteArray, - parentL1RollingHashMessageNumber: Long, - gasPriceCaps: GasPriceCaps? - ): SafeFuture { - val function = buildFinalizeBlobsFunction( - aggregation, - aggregationLastBlob, - parentShnarf, - parentL1RollingHash, - parentL1RollingHashMessageNumber - ) - - return SafeFuture.of( - helper.sendTransactionAsync(function, BigInteger.ZERO, gasPriceCaps) - ).thenApply { result -> - throwExceptionIfJsonRpcErrorReturned("eth_sendRawTransaction", result) - result.transactionHash - } + return getVersion() + .thenCompose { version -> + val function = buildFinalizeBlocksFunction( + version, + aggregation, + aggregationLastBlob, + parentShnarf, + parentL1RollingHash, + parentL1RollingHashMessageNumber + ) + helper.sendTransactionAsync(function, BigInteger.ZERO, gasPriceCaps) + .thenApply { result -> + throwExceptionIfJsonRpcErrorReturned("eth_sendRawTransaction", result) + result.transactionHash + } + } } override fun finalizeBlocksEthCall( @@ -203,30 +173,17 @@ class Web3JLineaRollupSmartContractClient internal constructor( parentL1RollingHash: ByteArray, parentL1RollingHashMessageNumber: Long ): SafeFuture { - return finalizeBlocksEthCallV5( - aggregation, - aggregationLastBlob, - parentShnarf, - parentL1RollingHash, - parentL1RollingHashMessageNumber - ) - } - - private fun finalizeBlocksEthCallV5( - aggregation: ProofToFinalize, - aggregationLastBlob: BlobRecord, - parentShnarf: ByteArray, - parentL1RollingHash: ByteArray, - parentL1RollingHashMessageNumber: Long - ): SafeFuture { - val function = buildFinalizeBlobsFunction( - aggregation, - aggregationLastBlob, - parentShnarf, - parentL1RollingHash, - parentL1RollingHashMessageNumber - ) - - return helper.executeEthCall(function) + return getVersion() + .thenCompose { version -> + val function = buildFinalizeBlocksFunction( + version, + aggregation, + aggregationLastBlob, + parentShnarf, + parentL1RollingHash, + parentL1RollingHashMessageNumber + ) + helper.executeEthCall(function) + } } } diff --git a/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JLineaRollupSmartContractClientReadOnly.kt b/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JLineaRollupSmartContractClientReadOnly.kt index b13734da..e68bdc0e 100644 --- a/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JLineaRollupSmartContractClientReadOnly.kt +++ b/coordinator/clients/smart-contract-client/src/main/kotlin/net/consensys/linea/contract/l1/Web3JLineaRollupSmartContractClientReadOnly.kt @@ -1,5 +1,6 @@ package net.consensys.linea.contract.l1 +import build.linea.contract.LineaRollupV6 import net.consensys.encodeHex import net.consensys.linea.BlockParameter import net.consensys.linea.async.toSafeFuture @@ -13,6 +14,8 @@ import org.apache.logging.log4j.Logger import org.web3j.crypto.Credentials import org.web3j.protocol.Web3j import org.web3j.protocol.core.DefaultBlockParameter +import org.web3j.tx.Contract +import org.web3j.tx.exceptions.ContractCallException import org.web3j.tx.gas.StaticGasProvider import tech.pegasys.teku.infrastructure.async.SafeFuture import java.math.BigInteger @@ -34,41 +37,76 @@ open class Web3JLineaRollupSmartContractClientReadOnly( ) : LineaRollupSmartContractClientReadOnly { protected fun contractClientAtBlock(blockParameter: BlockParameter): LineaRollup { - return LineaRollup.load( - contractAddress, - web3j, - fakeCredentials, - StaticGasProvider(BigInteger.ZERO, BigInteger.ZERO) - ).apply { - this.setDefaultBlockParameter(blockParameter.toWeb3j()) - } + return contractClientAtBlock(blockParameter, LineaRollup::class.java) + } + + protected fun contractClientAtBlock(blockParameter: BlockParameter, contract: Class): T { + @Suppress("UNCHECKED_CAST") + return when { + LineaRollupV6::class.java.isAssignableFrom(contract) -> LineaRollupV6.load( + contractAddress, + web3j, + fakeCredentials, + StaticGasProvider(BigInteger.ZERO, BigInteger.ZERO) + ).apply { + this.setDefaultBlockParameter(blockParameter.toWeb3j()) + } + + LineaRollup::class.java.isAssignableFrom(contract) -> LineaRollup.load( + contractAddress, + web3j, + fakeCredentials, + StaticGasProvider(BigInteger.ZERO, BigInteger.ZERO) + ).apply { + this.setDefaultBlockParameter(blockParameter.toWeb3j()) + } + + else -> throw IllegalArgumentException("Unsupported contract type: ${contract::class.java}") + } as T } protected val smartContractVersionCache: AtomicReference = AtomicReference(fetchSmartContractVersion().get()) private fun getSmartContractVersion(): SafeFuture { - return if (smartContractVersionCache.get() == LineaContractVersion.V5) { + return if (smartContractVersionCache.get() == LineaContractVersion.V6) { // once upgraded, it's not downgraded - SafeFuture.completedFuture(LineaContractVersion.V5) + SafeFuture.completedFuture(LineaContractVersion.V6) } else { - fetchSmartContractVersion().thenPeek { contractLatestVersion -> - if (contractLatestVersion != smartContractVersionCache.get()) { - log.info( - "Smart contract upgraded: prevVersion={} upgradedVersion={}", - smartContractVersionCache.get(), - contractLatestVersion - ) + fetchSmartContractVersion() + .thenPeek { contractLatestVersion -> + if (contractLatestVersion != smartContractVersionCache.get()) { + log.info( + "Smart contract upgraded: prevVersion={} upgradedVersion={}", + smartContractVersionCache.get(), + contractLatestVersion + ) + } + smartContractVersionCache.set(contractLatestVersion) } - smartContractVersionCache.set(contractLatestVersion) - } } } private fun fetchSmartContractVersion(): SafeFuture { - // FIXME: this is a temporary solution to determine the smart contract version. - // It should rely on events - return SafeFuture.completedFuture(LineaContractVersion.V5) + return contractClientAtBlock(BlockParameter.Tag.LATEST, LineaRollupV6::class.java) + .CONTRACT_VERSION() + .sendAsync() + .toSafeFuture() + .thenApply { version -> + when { + version.startsWith("6") -> LineaContractVersion.V6 + else -> throw IllegalStateException("Unsupported contract version: $version") + } + } + .exceptionallyCompose { error -> + if (error.cause is ContractCallException) { + // means that contract does not have CONTRACT_VERSION method available yet + // so it is still V5, so defaulting to V5 + SafeFuture.completedFuture(LineaContractVersion.V5) + } else { + SafeFuture.failedFuture(error) + } + } } override fun getAddress(): String = contractAddress @@ -94,11 +132,18 @@ open class Web3JLineaRollupSmartContractClientReadOnly( return contractClientAtBlock(blockParameter).rollingHashes(messageNumber.toBigInteger()).sendAsync().toSafeFuture() } - override fun findBlobFinalBlockNumberByShnarf(blockParameter: BlockParameter, shnarf: ByteArray): SafeFuture { - return contractClientAtBlock(blockParameter) - .shnarfFinalBlockNumbers(shnarf).sendAsync() - .thenApply { if (it == BigInteger.ZERO) null else it.toULong() } - .toSafeFuture() + override fun isBlobShnarfPresent(blockParameter: BlockParameter, shnarf: ByteArray): SafeFuture { + return getVersion() + .thenCompose { version -> + if (version == LineaContractVersion.V5) { + contractClientAtBlock(blockParameter, LineaRollup::class.java).shnarfFinalBlockNumbers(shnarf) + } else { + contractClientAtBlock(blockParameter, LineaRollupV6::class.java).blobShnarfExists(shnarf) + } + .sendAsync() + .thenApply { it != BigInteger.ZERO } + .toSafeFuture() + } } override fun blockStateRootHash(blockParameter: BlockParameter, lineaL2BlockNumber: ULong): SafeFuture { diff --git a/coordinator/core/src/main/kotlin/net/consensys/zkevm/coordinator/clients/smartcontract/LineaRollupSmartContractClient.kt b/coordinator/core/src/main/kotlin/net/consensys/zkevm/coordinator/clients/smartcontract/LineaRollupSmartContractClient.kt index 63802932..a319411b 100644 --- a/coordinator/core/src/main/kotlin/net/consensys/zkevm/coordinator/clients/smartcontract/LineaRollupSmartContractClient.kt +++ b/coordinator/core/src/main/kotlin/net/consensys/zkevm/coordinator/clients/smartcontract/LineaRollupSmartContractClient.kt @@ -7,7 +7,8 @@ import net.consensys.zkevm.ethereum.gaspricing.GasPriceCaps import tech.pegasys.teku.infrastructure.async.SafeFuture enum class LineaContractVersion : Comparable { - V5 // "EIP4844 multiple blobs per tx support - version in all networks", + V5, // "EIP4844 multiple blobs per tx support - version in all networks" + V6 // more efficient data submission and new events for state recovery } interface LineaRollupSmartContractClientReadOnly : ContractVersionProvider { @@ -30,12 +31,14 @@ interface LineaRollupSmartContractClientReadOnly : ContractVersionProvider /** - * Get the final block number of a shnarf + * Checks if a blob's shnarf is already present in the smart contract + * It meant blob was sent to l1 and accepted by the smart contract. + * Note: snarf in the future may be cleanned up after finalization. */ - fun findBlobFinalBlockNumberByShnarf( + fun isBlobShnarfPresent( blockParameter: BlockParameter = BlockParameter.Tag.LATEST, shnarf: ByteArray - ): SafeFuture + ): SafeFuture /** * Gets Type 2 StateRootHash for Linea Block diff --git a/coordinator/ethereum/blob-submitter/src/integrationTest/kotlin/net/consensys/zkevm/ethereum/finalization/BlobAndAggregationFinalizationIntTest.kt b/coordinator/ethereum/blob-submitter/src/integrationTest/kotlin/net/consensys/zkevm/ethereum/finalization/BlobAndAggregationFinalizationIntTest.kt index 305e06cb..32e93d85 100644 --- a/coordinator/ethereum/blob-submitter/src/integrationTest/kotlin/net/consensys/zkevm/ethereum/finalization/BlobAndAggregationFinalizationIntTest.kt +++ b/coordinator/ethereum/blob-submitter/src/integrationTest/kotlin/net/consensys/zkevm/ethereum/finalization/BlobAndAggregationFinalizationIntTest.kt @@ -61,12 +61,12 @@ class BlobAndAggregationFinalizationIntTest : CleanDbTestSuiteParallel() { vertx: Vertx, smartContractVersion: LineaContractVersion ) { - if (smartContractVersion != LineaContractVersion.V5) { + if (listOf(LineaContractVersion.V5, LineaContractVersion.V6).contains(smartContractVersion).not()) { // V6 with prover V3 is soon comming, so we will need to update/extend this test setup - throw IllegalArgumentException("Only V5 contract version is supported") + throw IllegalArgumentException("unsupported contract version=$smartContractVersion!") } val rollupDeploymentFuture = ContractsManager.get() - .deployLineaRollup(numberOfOperators = 2, contractVersion = LineaContractVersion.V5) + .deployLineaRollup(numberOfOperators = 2, contractVersion = smartContractVersion) // load files from FS while smc deploy loadBlobsAndAggregations( blobsResponsesDir = "$testDataDir/compression/responses", @@ -90,10 +90,10 @@ class BlobAndAggregationFinalizationIntTest : CleanDbTestSuiteParallel() { ) aggregationsRepository = AggregationsRepositoryImpl(PostgresAggregationsDao(sqlClient, fakeClock)) - val lineaRollupContractForDataSubmissionV4 = rollupDeploymentResult.rollupOperatorClient + val lineaRollupContractForDataSubmissionV5 = rollupDeploymentResult.rollupOperatorClient @Suppress("DEPRECATION") - val alreadySubmittedBlobFilter = L1ShnarfBasedAlreadySubmittedBlobsFilter(lineaRollupContractForDataSubmissionV4) + val alreadySubmittedBlobFilter = L1ShnarfBasedAlreadySubmittedBlobsFilter(lineaRollupContractForDataSubmissionV5) blobSubmissionCoordinator = run { BlobSubmissionCoordinator.create( @@ -105,7 +105,7 @@ class BlobAndAggregationFinalizationIntTest : CleanDbTestSuiteParallel() { ), blobsRepository = blobsRepository, aggregationsRepository = aggregationsRepository, - lineaSmartContractClient = lineaRollupContractForDataSubmissionV4, + lineaSmartContractClient = lineaRollupContractForDataSubmissionV5, alreadySubmittedBlobsFilter = alreadySubmittedBlobFilter, gasPriceCapProvider = FakeGasPriceCapProvider(), vertx = vertx, @@ -115,9 +115,10 @@ class BlobAndAggregationFinalizationIntTest : CleanDbTestSuiteParallel() { aggregationFinalizationCoordinator = run { lineaRollupContractForAggregationSubmission = MakeFileDelegatedContractsManager - .connectToLineaRollupContractV5( + .connectToLineaRollupContract( rollupDeploymentResult.contractAddress, rollupDeploymentResult.rollupOperators[1].txManager + ) val aggregationSubmitter = AggregationSubmitterImpl( @@ -141,15 +142,6 @@ class BlobAndAggregationFinalizationIntTest : CleanDbTestSuiteParallel() { } } - @Test - @Timeout(3, timeUnit = TimeUnit.MINUTES) - fun `submission works with contract V5`( - vertx: Vertx, - testContext: VertxTestContext - ) { - testSubmission(vertx, testContext, LineaContractVersion.V5) - } - private fun testSubmission( vertx: Vertx, testContext: VertxTestContext, @@ -180,4 +172,22 @@ class BlobAndAggregationFinalizationIntTest : CleanDbTestSuiteParallel() { testContext.completeNow() }.whenException(testContext::failNow) } + + @Test + @Timeout(3, timeUnit = TimeUnit.MINUTES) + fun `submission works with contract V5`( + vertx: Vertx, + testContext: VertxTestContext + ) { + testSubmission(vertx, testContext, LineaContractVersion.V5) + } + + @Test + @Timeout(3, timeUnit = TimeUnit.MINUTES) + fun `submission works with contract V6`( + vertx: Vertx, + testContext: VertxTestContext + ) { + testSubmission(vertx, testContext, LineaContractVersion.V6) + } } diff --git a/coordinator/ethereum/blob-submitter/src/main/kotlin/net/consensys/zkevm/ethereum/submission/L1ShnarfBasedAlreadySubmittedBlobsFilter.kt b/coordinator/ethereum/blob-submitter/src/main/kotlin/net/consensys/zkevm/ethereum/submission/L1ShnarfBasedAlreadySubmittedBlobsFilter.kt index e96216d1..d0915147 100644 --- a/coordinator/ethereum/blob-submitter/src/main/kotlin/net/consensys/zkevm/ethereum/submission/L1ShnarfBasedAlreadySubmittedBlobsFilter.kt +++ b/coordinator/ethereum/blob-submitter/src/main/kotlin/net/consensys/zkevm/ethereum/submission/L1ShnarfBasedAlreadySubmittedBlobsFilter.kt @@ -20,7 +20,15 @@ class L1ShnarfBasedAlreadySubmittedBlobsFilter( blobRecords: List ): SafeFuture> { val blockByShnarfQueryFutures = blobRecords.map { blobRecord -> - lineaRollup.findBlobFinalBlockNumberByShnarf(shnarf = blobRecord.expectedShnarf) + lineaRollup + .isBlobShnarfPresent(shnarf = blobRecord.expectedShnarf) + .thenApply { isShnarfPresent -> + if (isShnarfPresent) { + blobRecord.endBlockNumber + } else { + null + } + } } return SafeFuture.collectAll(blockByShnarfQueryFutures.stream()) diff --git a/coordinator/ethereum/blob-submitter/src/test/kotlin/net/consensys/zkevm/ethereum/submission/L1ShnarfBasedAlreadySubmittedBlobsFilterTest.kt b/coordinator/ethereum/blob-submitter/src/test/kotlin/net/consensys/zkevm/ethereum/submission/L1ShnarfBasedAlreadySubmittedBlobsFilterTest.kt index 992c52b8..d4eaf1ef 100644 --- a/coordinator/ethereum/blob-submitter/src/test/kotlin/net/consensys/zkevm/ethereum/submission/L1ShnarfBasedAlreadySubmittedBlobsFilterTest.kt +++ b/coordinator/ethereum/blob-submitter/src/test/kotlin/net/consensys/zkevm/ethereum/submission/L1ShnarfBasedAlreadySubmittedBlobsFilterTest.kt @@ -26,13 +26,13 @@ class L1ShnarfBasedAlreadySubmittedBlobsFilterTest { val blobs = listOf(blob1, blob2, blob3, blob4, blob5, blob6, blob7) val l1SmcClient = mock() - whenever(l1SmcClient.findBlobFinalBlockNumberByShnarf(any(), any())) + whenever(l1SmcClient.isBlobShnarfPresent(any(), any())) .thenAnswer { invocation -> val shnarfQueried = invocation.getArgument(1) val endBlockNumber = when { - shnarfQueried.contentEquals(blob3.expectedShnarf) -> blob3.endBlockNumber - shnarfQueried.contentEquals(blob5.expectedShnarf) -> blob5.endBlockNumber - else -> null + shnarfQueried.contentEquals(blob3.expectedShnarf) -> true + shnarfQueried.contentEquals(blob5.expectedShnarf) -> true + else -> false } SafeFuture.completedFuture(endBlockNumber) } diff --git a/coordinator/ethereum/message-anchoring/src/integrationTest/kotlin/net.consensys.zkevm.ethereum.coordination.messageanchoring/L1EventQuerierIntegrationTest.kt b/coordinator/ethereum/message-anchoring/src/integrationTest/kotlin/net.consensys.zkevm.ethereum.coordination.messageanchoring/L1EventQuerierIntegrationTest.kt index 2898d4df..40dc15dc 100644 --- a/coordinator/ethereum/message-anchoring/src/integrationTest/kotlin/net.consensys.zkevm.ethereum.coordination.messageanchoring/L1EventQuerierIntegrationTest.kt +++ b/coordinator/ethereum/message-anchoring/src/integrationTest/kotlin/net.consensys.zkevm.ethereum.coordination.messageanchoring/L1EventQuerierIntegrationTest.kt @@ -8,6 +8,7 @@ import net.consensys.linea.contract.LineaRollup import net.consensys.linea.contract.LineaRollupAsyncFriendly import net.consensys.toBigInteger import net.consensys.toULong +import net.consensys.zkevm.coordinator.clients.smartcontract.LineaContractVersion import net.consensys.zkevm.ethereum.ContractsManager import net.consensys.zkevm.ethereum.Web3jClientManager import org.apache.tuweni.bytes.Bytes32 @@ -35,7 +36,9 @@ class L1EventQuerierIntegrationTest { @BeforeEach fun beforeEach() { - val deploymentResult = ContractsManager.get().deployLineaRollup().get() + val deploymentResult = ContractsManager.get() + .deployLineaRollup(contractVersion = LineaContractVersion.V5) + .get() testLineaRollupContractAddress = deploymentResult.contractAddress web3Client = Web3jClientManager.l1Client @Suppress("DEPRECATION") diff --git a/coordinator/ethereum/message-anchoring/src/integrationTest/kotlin/net.consensys.zkevm.ethereum.coordination.messageanchoring/MessageServiceIntegrationTest.kt b/coordinator/ethereum/message-anchoring/src/integrationTest/kotlin/net.consensys.zkevm.ethereum.coordination.messageanchoring/MessageServiceIntegrationTest.kt index e99a2e9a..949dcfb3 100644 --- a/coordinator/ethereum/message-anchoring/src/integrationTest/kotlin/net.consensys.zkevm.ethereum.coordination.messageanchoring/MessageServiceIntegrationTest.kt +++ b/coordinator/ethereum/message-anchoring/src/integrationTest/kotlin/net.consensys.zkevm.ethereum.coordination.messageanchoring/MessageServiceIntegrationTest.kt @@ -10,6 +10,7 @@ import net.consensys.linea.contract.LineaRollup import net.consensys.linea.contract.LineaRollupAsyncFriendly import net.consensys.toBigInteger import net.consensys.toULong +import net.consensys.zkevm.coordinator.clients.smartcontract.LineaContractVersion import net.consensys.zkevm.coordinator.clients.smartcontract.LineaRollupSmartContractClient import net.consensys.zkevm.ethereum.ContractsManager import net.consensys.zkevm.ethereum.Web3jClientManager @@ -47,7 +48,9 @@ class MessageServiceIntegrationTest { private lateinit var l2Contract: L2MessageService private fun deployContracts() { - val l1RollupDeploymentResult = ContractsManager.get().deployLineaRollup().get() + val l1RollupDeploymentResult = ContractsManager.get() + .deployLineaRollup(contractVersion = LineaContractVersion.V5) + .get() @Suppress("DEPRECATION") l1ContractLegacyClient = l1RollupDeploymentResult.rollupOperatorClientLegacy l1ContractClient = l1RollupDeploymentResult.rollupOperatorClient diff --git a/coordinator/ethereum/test-utils/build.gradle b/coordinator/ethereum/test-utils/build.gradle index 4f0b4e04..7a6848ff 100644 --- a/coordinator/ethereum/test-utils/build.gradle +++ b/coordinator/ethereum/test-utils/build.gradle @@ -14,6 +14,8 @@ dependencies { implementation("org.web3j:core:${libs.versions.web3j.get()}") { exclude group: 'org.slf4j', module: 'slf4j-nop' } + implementation "com.sksamuel.hoplite:hoplite-core:${libs.versions.hoplite.get()}" + implementation "com.sksamuel.hoplite:hoplite-toml:${libs.versions.hoplite.get()}" implementation "com.fasterxml.jackson.core:jackson-annotations:${libs.versions.jackson.get()}" implementation "com.fasterxml.jackson.core:jackson-databind:${libs.versions.jackson.get()}" implementation "com.fasterxml.jackson.module:jackson-module-kotlin:${libs.versions.jackson.get()}" diff --git a/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/ContractsManager.kt b/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/ContractsManager.kt index 171aa9ff..5f7925db 100644 --- a/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/ContractsManager.kt +++ b/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/ContractsManager.kt @@ -1,11 +1,14 @@ package net.consensys.zkevm.ethereum +import com.sksamuel.hoplite.ConfigLoaderBuilder +import com.sksamuel.hoplite.addFileSource import net.consensys.linea.contract.AsyncFriendlyTransactionManager import net.consensys.linea.contract.EIP1559GasProvider import net.consensys.linea.contract.LineaRollupAsyncFriendly import net.consensys.linea.contract.StaticGasProvider import net.consensys.linea.contract.l1.Web3JLineaRollupSmartContractClient import net.consensys.linea.contract.l2.L2MessageServiceGasLimitEstimate +import net.consensys.linea.testing.filesystem.findPathTo import net.consensys.linea.web3j.SmartContractErrors import net.consensys.zkevm.coordinator.clients.smartcontract.LineaContractVersion import net.consensys.zkevm.coordinator.clients.smartcontract.LineaRollupSmartContractClient @@ -45,17 +48,17 @@ interface ContractsManager { */ fun deployLineaRollup( numberOfOperators: Int = 1, - contractVersion: LineaContractVersion = LineaContractVersion.V5 + contractVersion: LineaContractVersion ): SafeFuture fun deployL2MessageService(): SafeFuture fun deployRollupAndL2MessageService( dataCompressionAndProofAggregationMigrationBlock: ULong = 1000UL, - numberOfOperators: Int = 1 + numberOfOperators: Int = 1, + l1ContractVersion: LineaContractVersion = LineaContractVersion.V5 ): SafeFuture - @Deprecated("Use connectToLineaRollupContractV5 instead") fun connectToLineaRollupContract( contractAddress: String, transactionManager: AsyncFriendlyTransactionManager, @@ -64,18 +67,8 @@ interface ContractsManager { maxFeePerGas = 11_000uL, maxPriorityFeePerGas = 10_000uL, gasLimit = 1_000_000uL - ) - ): LineaRollupAsyncFriendly - - fun connectToLineaRollupContractV5( - contractAddress: String, - transactionManager: AsyncFriendlyTransactionManager, - gasProvider: ContractEIP1559GasProvider = StaticGasProvider( - L1AccountManager.chainId, - maxFeePerGas = 11_000uL, - maxPriorityFeePerGas = 10_000uL, - gasLimit = 1_000_000uL - ) + ), + smartContractErrors: SmartContractErrors? = null ): LineaRollupSmartContractClient fun connectL2MessageService( @@ -94,14 +87,35 @@ interface ContractsManager { smartContractErrors: SmartContractErrors = emptyMap() ): L2MessageServiceGasLimitEstimate + @Deprecated("Use connectToLineaRollupContract instead") + fun connectToLineaRollupContractLegacy( + contractAddress: String, + transactionManager: AsyncFriendlyTransactionManager, + gasProvider: ContractEIP1559GasProvider = StaticGasProvider( + L1AccountManager.chainId, + maxFeePerGas = 11_000uL, + maxPriorityFeePerGas = 10_000uL, + gasLimit = 1_000_000uL + ) + ): LineaRollupAsyncFriendly + companion object { - // TODO: think of better get the Instance fun get(): ContractsManager = MakeFileDelegatedContractsManager } } object MakeFileDelegatedContractsManager : ContractsManager { val log = LoggerFactory.getLogger(MakeFileDelegatedContractsManager::class.java) + val lineaRollupContractErrors = findPathTo("config")!! + .resolve("common/smart-contract-errors.toml") + .let { filePath -> + data class ErrorsFile(val smartContractErrors: Map) + ConfigLoaderBuilder.default() + .addFileSource(filePath.toAbsolutePath().toString()) + .build() + .loadConfigOrThrow() + .smartContractErrors + } override fun deployLineaRollup( numberOfOperators: Int, @@ -133,12 +147,14 @@ object MakeFileDelegatedContractsManager : ContractsManager { AccountTransactionManager(it, L1AccountManager.getTransactionManager(it)) } - @Suppress("DEPRECATION") val rollupOperatorClient = connectToLineaRollupContract( deploymentResult.address, - accountsTxManagers.first().txManager + accountsTxManagers.first().txManager, + smartContractErrors = lineaRollupContractErrors ) - val rollupOperatorClientV4 = connectToLineaRollupContractV5( + + @Suppress("DEPRECATION") + val rollupOperatorClientLegacy = connectToLineaRollupContractLegacy( deploymentResult.address, accountsTxManagers.first().txManager ) @@ -147,8 +163,8 @@ object MakeFileDelegatedContractsManager : ContractsManager { contractDeploymentAccount = contractDeploymentAccount, contractDeploymentBlockNumber = deploymentResult.blockNumber.toULong(), rollupOperators = accountsTxManagers, - rollupOperatorClientLegacy = rollupOperatorClient, - rollupOperatorClient = rollupOperatorClientV4 + rollupOperatorClientLegacy = rollupOperatorClientLegacy, + rollupOperatorClient = rollupOperatorClient ) } return future @@ -174,9 +190,10 @@ object MakeFileDelegatedContractsManager : ContractsManager { override fun deployRollupAndL2MessageService( dataCompressionAndProofAggregationMigrationBlock: ULong, - numberOfOperators: Int + numberOfOperators: Int, + l1ContractVersion: LineaContractVersion ): SafeFuture { - return deployLineaRollup(numberOfOperators) + return deployLineaRollup(numberOfOperators, l1ContractVersion) .thenCombine(deployL2MessageService()) { lineaRollupDeploymentResult, l2MessageServiceDeploymentResult -> ContactsDeploymentResult( lineaRollup = lineaRollupDeploymentResult, @@ -185,32 +202,18 @@ object MakeFileDelegatedContractsManager : ContractsManager { } } - @Deprecated("Use connectToLineaRollupContractV5 instead") override fun connectToLineaRollupContract( contractAddress: String, transactionManager: AsyncFriendlyTransactionManager, - gasProvider: ContractEIP1559GasProvider - ): LineaRollupAsyncFriendly { - return LineaRollupAsyncFriendly.load( - contractAddress, - Web3jClientManager.l1Client, - transactionManager, - gasProvider, - emptyMap() - ) - } - - override fun connectToLineaRollupContractV5( - contractAddress: String, - transactionManager: AsyncFriendlyTransactionManager, - gasProvider: ContractEIP1559GasProvider + gasProvider: ContractEIP1559GasProvider, + smartContractErrors: SmartContractErrors? ): LineaRollupSmartContractClient { return Web3JLineaRollupSmartContractClient.load( contractAddress, Web3jClientManager.l1Client, transactionManager, gasProvider, - emptyMap() + smartContractErrors ?: lineaRollupContractErrors ) } @@ -229,4 +232,33 @@ object MakeFileDelegatedContractsManager : ContractsManager { smartContractErrors ) } + + @Deprecated("Use connectToLineaRollupContract instead") + override fun connectToLineaRollupContractLegacy( + contractAddress: String, + transactionManager: AsyncFriendlyTransactionManager, + gasProvider: ContractEIP1559GasProvider + ): LineaRollupAsyncFriendly { + return LineaRollupAsyncFriendly.load( + contractAddress, + Web3jClientManager.l1Client, + transactionManager, + gasProvider, + emptyMap() + ) + } +} + +fun main() { + data class SmartContractErrors(val smartContractErrors: Map) + + val lineaRollupContractErrors = findPathTo("config")!! + .resolve("common/smart-contract-errors.toml") + .let { filePath -> + ConfigLoaderBuilder.default() + .addFileSource(filePath.toAbsolutePath().toString()) + .build() + .loadConfigOrThrow() + } + println(lineaRollupContractErrors) } diff --git a/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/MakefileContractDeploymentHelper.kt b/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/MakefileContractDeploymentHelper.kt index c616b0ea..56607668 100644 --- a/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/MakefileContractDeploymentHelper.kt +++ b/coordinator/ethereum/test-utils/src/main/kotlin/net/consensys/zkevm/ethereum/MakefileContractDeploymentHelper.kt @@ -143,10 +143,10 @@ fun makeDeployLineaRollup( // "HARDHAT_DISABLE_CACHE" to "true" ) deploymentPrivateKey?.let { env["DEPLOYMENT_PRIVATE_KEY"] = it } - val command = if (contractVersion == LineaContractVersion.V5) { - "make deploy-linea-rollup" - } else { - throw IllegalArgumentException("Unsupported contract version: $contractVersion") + val command = when (contractVersion) { + LineaContractVersion.V5 -> "make deploy-linea-rollup" + LineaContractVersion.V6 -> "make deploy-linea-rollup-v6" + else -> throw IllegalArgumentException("Unsupported contract version: $contractVersion") } return deployContract( diff --git a/jvm-libs/linea/linea-contracts/l1-rollup/build.gradle b/jvm-libs/linea/linea-contracts/l1-rollup/build.gradle index 06c6a12d..f6422670 100644 --- a/jvm-libs/linea/linea-contracts/l1-rollup/build.gradle +++ b/jvm-libs/linea/linea-contracts/l1-rollup/build.gradle @@ -16,8 +16,8 @@ dependencies { web3jContractWrappers { def contractAbi = layout.buildDirectory.dir("${rootProject.projectDir}/contracts/abi").get() - .file("LineaRollupV5.0.abi").asFile.absolutePath + .file("LineaRollupV6.0.abi").asFile.absolutePath - contractsPackage = "net.consensys.linea.contract" - contracts = ["$contractAbi": "LineaRollup"] + contractsPackage = "build.linea.contract" + contracts = ["$contractAbi": "LineaRollupV6"] }