mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-09 04:08:01 -05:00
feat: add support of eth_estimateGas for L1 transactions (#694)
* feat: add support of eth_estimateGas for L1 transactions * feat: revise log message for calling eth estimate gas * chore: update function signatures * fix: unit test for merge conflicts * fix: merge conflicts * feat: use web3J async helper to encapsulate useEthEstimateGas option * feat: remove calling eth_estimateGas in eth_call for aggregation tx and disable eth estimate gas on data submission * feat: seperate useEthEstimateGas option for data submission and aggregation and remove calling eth_estimateGas on eth_call for data submission * feat: add new function in LineaRollupSmartContractClient and remove unnecessary parameter * feat: correct coordinator config and update sequencer config
This commit is contained in:
@@ -152,7 +152,8 @@ max-receipt-retries=120
|
||||
blocks-to-finalization=0
|
||||
|
||||
[blob-submission]
|
||||
disabled=true
|
||||
disabled=false
|
||||
use-eth-estimate-gas=false
|
||||
db-polling-interval="PT1S"
|
||||
max-blobs-to-return=100
|
||||
proof-submission-delay="PT1S"
|
||||
@@ -165,6 +166,7 @@ priority-fee-per-gas-lower-bound=200000000 # 0.2 GWEI
|
||||
|
||||
[aggregation-finalization]
|
||||
disabled=false
|
||||
use-eth-estimate-gas=true
|
||||
db-polling-interval="PT1S"
|
||||
max-aggregations-to-finalize-per-tick=1
|
||||
proof-submission-delay="PT1S"
|
||||
|
||||
@@ -91,14 +91,16 @@ fun createLineaRollupContractClient(
|
||||
transactionManager: AsyncFriendlyTransactionManager,
|
||||
contractGasProvider: ContractGasProvider,
|
||||
web3jClient: Web3j,
|
||||
smartContractErrors: SmartContractErrors
|
||||
smartContractErrors: SmartContractErrors,
|
||||
useEthEstimateGas: Boolean
|
||||
): LineaRollupSmartContractClient {
|
||||
return Web3JLineaRollupSmartContractClient.load(
|
||||
contractAddress = l1Config.zkEvmContractAddress,
|
||||
web3j = web3jClient,
|
||||
transactionManager = transactionManager,
|
||||
contractGasProvider = contractGasProvider,
|
||||
smartContractErrors = smartContractErrors
|
||||
smartContractErrors = smartContractErrors,
|
||||
useEthEstimateGas = useEthEstimateGas
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -439,7 +439,8 @@ class L1DependentApp(
|
||||
),
|
||||
contractGasProvider = primaryOrFallbackGasProvider,
|
||||
web3jClient = l1Web3jClient,
|
||||
smartContractErrors = smartContractErrors
|
||||
smartContractErrors = smartContractErrors,
|
||||
useEthEstimateGas = configs.blobSubmission.useEthEstimateGas
|
||||
)
|
||||
}
|
||||
|
||||
@@ -526,44 +527,48 @@ class L1DependentApp(
|
||||
|
||||
private val latestBlobSubmittedBlockNumberTracker = LatestBlobSubmittedBlockNumberTracker(0UL)
|
||||
private val blobSubmissionCoordinator = run {
|
||||
metricsFacade.createGauge(
|
||||
category = LineaMetricsCategory.BLOB,
|
||||
name = "highest.submitted.on.l1",
|
||||
description = "Highest submitted blob end block number on l1",
|
||||
measurementSupplier = { latestBlobSubmittedBlockNumberTracker.get() }
|
||||
)
|
||||
if (!configs.blobSubmission.enabled) {
|
||||
DisabledLongRunningService
|
||||
} else {
|
||||
metricsFacade.createGauge(
|
||||
category = LineaMetricsCategory.BLOB,
|
||||
name = "highest.submitted.on.l1",
|
||||
description = "Highest submitted blob end block number on l1",
|
||||
measurementSupplier = { latestBlobSubmittedBlockNumberTracker.get() }
|
||||
)
|
||||
|
||||
val blobSubmissionDelayHistogram = metricsFacade.createHistogram(
|
||||
category = LineaMetricsCategory.BLOB,
|
||||
name = "submission.delay",
|
||||
description = "Delay between blob submission and end block timestamps",
|
||||
baseUnit = "seconds"
|
||||
)
|
||||
val blobSubmissionDelayHistogram = metricsFacade.createHistogram(
|
||||
category = LineaMetricsCategory.BLOB,
|
||||
name = "submission.delay",
|
||||
description = "Delay between blob submission and end block timestamps",
|
||||
baseUnit = "seconds"
|
||||
)
|
||||
|
||||
val blobSubmittedEventConsumers: Map<Consumer<BlobSubmittedEvent>, String> = mapOf(
|
||||
Consumer<BlobSubmittedEvent> { blobSubmission ->
|
||||
latestBlobSubmittedBlockNumberTracker(blobSubmission)
|
||||
} to "Submitted Blob Tracker Consumer",
|
||||
Consumer<BlobSubmittedEvent> { blobSubmission ->
|
||||
blobSubmissionDelayHistogram.record(blobSubmission.getSubmissionDelay().toDouble())
|
||||
} to "Blob Submission Delay Consumer"
|
||||
)
|
||||
val blobSubmittedEventConsumers: Map<Consumer<BlobSubmittedEvent>, String> = mapOf(
|
||||
Consumer<BlobSubmittedEvent> { blobSubmission ->
|
||||
latestBlobSubmittedBlockNumberTracker(blobSubmission)
|
||||
} to "Submitted Blob Tracker Consumer",
|
||||
Consumer<BlobSubmittedEvent> { blobSubmission ->
|
||||
blobSubmissionDelayHistogram.record(blobSubmission.getSubmissionDelay().toDouble())
|
||||
} to "Blob Submission Delay Consumer"
|
||||
)
|
||||
|
||||
BlobSubmissionCoordinator.create(
|
||||
config = BlobSubmissionCoordinator.Config(
|
||||
configs.blobSubmission.dbPollingInterval.toKotlinDuration(),
|
||||
configs.blobSubmission.proofSubmissionDelay.toKotlinDuration(),
|
||||
configs.blobSubmission.maxBlobsToSubmitPerTick.toUInt()
|
||||
),
|
||||
blobsRepository = blobsRepository,
|
||||
aggregationsRepository = aggregationsRepository,
|
||||
lineaSmartContractClient = lineaSmartContractClientForDataSubmission,
|
||||
gasPriceCapProvider = gasPriceCapProviderForDataSubmission,
|
||||
alreadySubmittedBlobsFilter = alreadySubmittedBlobsFilter,
|
||||
blobSubmittedEventDispatcher = EventDispatcher(blobSubmittedEventConsumers),
|
||||
vertx = vertx,
|
||||
clock = Clock.System
|
||||
)
|
||||
BlobSubmissionCoordinator.create(
|
||||
config = BlobSubmissionCoordinator.Config(
|
||||
configs.blobSubmission.dbPollingInterval.toKotlinDuration(),
|
||||
configs.blobSubmission.proofSubmissionDelay.toKotlinDuration(),
|
||||
configs.blobSubmission.maxBlobsToSubmitPerTick.toUInt()
|
||||
),
|
||||
blobsRepository = blobsRepository,
|
||||
aggregationsRepository = aggregationsRepository,
|
||||
lineaSmartContractClient = lineaSmartContractClientForDataSubmission,
|
||||
gasPriceCapProvider = gasPriceCapProviderForDataSubmission,
|
||||
alreadySubmittedBlobsFilter = alreadySubmittedBlobsFilter,
|
||||
blobSubmittedEventDispatcher = EventDispatcher(blobSubmittedEventConsumers),
|
||||
vertx = vertx,
|
||||
clock = Clock.System
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val proofAggregationCoordinatorService: LongRunningService = run {
|
||||
@@ -656,7 +661,8 @@ class L1DependentApp(
|
||||
transactionManager = finalizationTransactionManager,
|
||||
contractGasProvider = primaryOrFallbackGasProvider,
|
||||
web3jClient = l1Web3jClient,
|
||||
smartContractErrors = smartContractErrors
|
||||
smartContractErrors = smartContractErrors,
|
||||
useEthEstimateGas = configs.aggregationFinalization.useEthEstimateGas
|
||||
)
|
||||
|
||||
val latestFinalizationSubmittedBlockNumberTracker = LatestFinalizationSubmittedBlockNumberTracker(0UL)
|
||||
|
||||
@@ -221,6 +221,7 @@ data class BlobSubmissionConfig(
|
||||
val maxBlobsToSubmitPerTick: Int = maxBlobsToReturn,
|
||||
// defaults to 6, not supported atm, preparatory work
|
||||
val targetBlobsToSendPerTransaction: Int = 6,
|
||||
val useEthEstimateGas: Boolean = false,
|
||||
override var disabled: Boolean = false
|
||||
) : FeatureToggleable {
|
||||
init {
|
||||
@@ -236,6 +237,7 @@ data class AggregationFinalizationConfig(
|
||||
val dbPollingInterval: Duration,
|
||||
val maxAggregationsToFinalizePerTick: Int,
|
||||
val proofSubmissionDelay: Duration,
|
||||
val useEthEstimateGas: Boolean = false,
|
||||
override var disabled: Boolean = false
|
||||
) : FeatureToggleable {
|
||||
init {
|
||||
|
||||
@@ -161,13 +161,15 @@ class CoordinatorConfigTest {
|
||||
priorityFeePerGasLowerBound = 200000000UL,
|
||||
proofSubmissionDelay = Duration.parse("PT1S"),
|
||||
targetBlobsToSendPerTransaction = 6,
|
||||
disabled = true
|
||||
useEthEstimateGas = false,
|
||||
disabled = false
|
||||
)
|
||||
|
||||
private val aggregationFinalizationConfig = AggregationFinalizationConfig(
|
||||
dbPollingInterval = Duration.parse("PT1S"),
|
||||
maxAggregationsToFinalizePerTick = 1,
|
||||
proofSubmissionDelay = Duration.parse("PT1S"),
|
||||
useEthEstimateGas = true,
|
||||
disabled = false
|
||||
)
|
||||
|
||||
|
||||
@@ -133,7 +133,8 @@ max-receipt-retries=120
|
||||
blocks-to-finalization=0
|
||||
|
||||
[blob-submission]
|
||||
disabled=true
|
||||
disabled=false
|
||||
use-eth-estimate-gas=false
|
||||
db-polling-interval="PT1S"
|
||||
max-blobs-to-return=100
|
||||
proof-submission-delay="PT1S"
|
||||
@@ -146,6 +147,7 @@ priority-fee-per-gas-lower-bound=200000000 # 0.2 GWEI
|
||||
|
||||
[aggregation-finalization]
|
||||
disabled=false
|
||||
use-eth-estimate-gas=true
|
||||
db-polling-interval="PT1S"
|
||||
max-aggregations-to-finalize-per-tick=1
|
||||
proof-submission-delay="PT1S"
|
||||
|
||||
@@ -10,10 +10,13 @@ import net.consensys.linea.web3j.EIP4844GasFees
|
||||
import net.consensys.linea.web3j.EIP4844GasProvider
|
||||
import net.consensys.linea.web3j.Eip4844Transaction
|
||||
import net.consensys.linea.web3j.SmartContractErrors
|
||||
import net.consensys.linea.web3j.getRevertReason
|
||||
import net.consensys.linea.web3j.informativeEthCall
|
||||
import net.consensys.zkevm.domain.BlobRecord
|
||||
import net.consensys.zkevm.ethereum.gaspricing.GasPriceCaps
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.apache.logging.log4j.Logger
|
||||
import org.apache.tuweni.bytes.Bytes
|
||||
import org.web3j.abi.FunctionEncoder
|
||||
import org.web3j.abi.datatypes.Function
|
||||
import org.web3j.crypto.Blob
|
||||
@@ -33,8 +36,9 @@ class Web3JContractAsyncHelper(
|
||||
val contractAddress: String,
|
||||
val web3j: Web3j,
|
||||
val transactionManager: AsyncFriendlyTransactionManager,
|
||||
private val gasProvider: ContractGasProvider,
|
||||
private val smartContractErrors: SmartContractErrors
|
||||
private val contractGasProvider: ContractGasProvider,
|
||||
private val smartContractErrors: SmartContractErrors,
|
||||
private val useEthEstimateGas: Boolean
|
||||
) {
|
||||
private val log: Logger = LogManager.getLogger(this::class.java)
|
||||
|
||||
@@ -44,40 +48,127 @@ class Web3JContractAsyncHelper(
|
||||
.toSafeFuture()
|
||||
}
|
||||
|
||||
fun createEip4844Transaction(
|
||||
private fun getGasLimit(
|
||||
function: Function,
|
||||
blobs: List<Blob>,
|
||||
gasPriceCaps: GasPriceCaps? = null
|
||||
): Eip4844Transaction {
|
||||
require(blobs.size in 1..6) { "Blobs size=${blobs.size} must be between 1 and 6." }
|
||||
blobs: List<Blob>? = null,
|
||||
blobVersionedHashes: List<Bytes>? = null
|
||||
): SafeFuture<BigInteger> {
|
||||
return if (useEthEstimateGas) {
|
||||
getEthEstimatedGas(
|
||||
FunctionEncoder.encode(function),
|
||||
blobs,
|
||||
blobVersionedHashes
|
||||
).thenApply {
|
||||
it ?: contractGasProvider.getGasLimit(function.name)
|
||||
}
|
||||
} else {
|
||||
SafeFuture.completedFuture(contractGasProvider.getGasLimit(function.name))
|
||||
}
|
||||
}
|
||||
|
||||
val gasLimit = gasProvider.getGasLimit(function.name)
|
||||
val (_, maxFeePerBlobGas) = getEip4844GasFees()
|
||||
return Eip4844Transaction.createFunctionCallTransaction(
|
||||
from = transactionManager.fromAddress,
|
||||
to = contractAddress,
|
||||
data = FunctionEncoder.encode(function),
|
||||
blobs = blobs,
|
||||
maxFeePerBlobGas = gasPriceCaps?.maxFeePerBlobGasCap?.toBigInteger() ?: maxFeePerBlobGas.toBigInteger(),
|
||||
maxPriorityFeePerGas = gasPriceCaps?.maxPriorityFeePerGasCap?.toBigInteger(),
|
||||
maxFeePerGas = gasPriceCaps?.maxFeePerGasCap?.toBigInteger(),
|
||||
gasLimit = gasLimit,
|
||||
blobVersionedHashes = blobs.map { BlobUtils.kzgToVersionedHash(BlobUtils.getCommitment(it)) }
|
||||
private fun getEthEstimatedGas(
|
||||
encodedFunction: String,
|
||||
blobs: List<Blob>? = null,
|
||||
blobVersionedHashes: List<Bytes>? = null
|
||||
): SafeFuture<BigInteger?> {
|
||||
return if (blobs != null && blobVersionedHashes != null) {
|
||||
createEip4844FunctionCallTransaction(encodedFunction, blobs, blobVersionedHashes)
|
||||
} else {
|
||||
createFunctionCallTransaction(encodedFunction)
|
||||
}.run(::callEthEstimateGas)
|
||||
}
|
||||
|
||||
private fun callEthEstimateGas(tx: Transaction): SafeFuture<BigInteger?> {
|
||||
return web3j.ethEstimateGas(tx).sendAsync()
|
||||
.thenApply {
|
||||
val withBlobs = tx is Eip4844Transaction
|
||||
if (it.hasError()) {
|
||||
log.info(
|
||||
"eth_estimateGas failed for tx with blobCarrying={} error={} revertReason={}",
|
||||
withBlobs,
|
||||
it.error.message,
|
||||
getRevertReason(it.error, smartContractErrors)
|
||||
)
|
||||
null
|
||||
} else {
|
||||
log.debug(
|
||||
"eth_estimateGas for tx with blobCarrying={} estimatedGas={}",
|
||||
withBlobs,
|
||||
it.amountUsed
|
||||
)
|
||||
it.amountUsed
|
||||
}
|
||||
}
|
||||
.toSafeFuture()
|
||||
}
|
||||
|
||||
private fun createFunctionCallTransaction(
|
||||
encodedFunction: String
|
||||
): Transaction {
|
||||
return Transaction.createFunctionCallTransaction(
|
||||
transactionManager.fromAddress,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
contractAddress,
|
||||
encodedFunction
|
||||
)
|
||||
}
|
||||
|
||||
private fun createEip4844FunctionCallTransaction(
|
||||
encodedFunction: String,
|
||||
blobs: List<Blob>,
|
||||
blobVersionedHashes: List<Bytes>
|
||||
): Eip4844Transaction {
|
||||
return Eip4844Transaction.createFunctionCallTransaction(
|
||||
from = transactionManager.fromAddress,
|
||||
to = contractAddress,
|
||||
data = encodedFunction,
|
||||
blobs = blobs,
|
||||
maxFeePerBlobGas = null,
|
||||
maxPriorityFeePerGas = null,
|
||||
maxFeePerGas = null,
|
||||
gasLimit = null,
|
||||
blobVersionedHashes = blobVersionedHashes
|
||||
)
|
||||
}
|
||||
|
||||
private fun createEip4844Transaction(
|
||||
function: Function,
|
||||
blobs: List<Blob>,
|
||||
gasPriceCaps: GasPriceCaps? = null
|
||||
): SafeFuture<Eip4844Transaction> {
|
||||
require(blobs.size in 1..6) { "Blobs size=${blobs.size} must be between 1 and 6." }
|
||||
val blobVersionedHashes = blobs.map { BlobUtils.kzgToVersionedHash(BlobUtils.getCommitment(it)) }
|
||||
return getGasLimit(function, blobs, blobVersionedHashes)
|
||||
.thenApply { gasLimit ->
|
||||
val (_, maxFeePerBlobGas) = getEip4844GasFees()
|
||||
Eip4844Transaction.createFunctionCallTransaction(
|
||||
from = transactionManager.fromAddress,
|
||||
to = contractAddress,
|
||||
data = FunctionEncoder.encode(function),
|
||||
blobs = blobs,
|
||||
maxFeePerBlobGas = gasPriceCaps?.maxFeePerBlobGasCap?.toBigInteger() ?: maxFeePerBlobGas.toBigInteger(),
|
||||
maxPriorityFeePerGas = gasPriceCaps?.maxPriorityFeePerGasCap?.toBigInteger(),
|
||||
maxFeePerGas = gasPriceCaps?.maxFeePerGasCap?.toBigInteger(),
|
||||
gasLimit = gasLimit,
|
||||
blobVersionedHashes = blobVersionedHashes
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isGasProviderSupportedEIP1559(): Boolean {
|
||||
return gasProvider is ContractEIP1559GasProvider && gasProvider.isEIP1559Enabled
|
||||
return contractGasProvider is ContractEIP1559GasProvider && contractGasProvider.isEIP1559Enabled
|
||||
}
|
||||
|
||||
private fun getEip1559GasFees(
|
||||
functionName: String
|
||||
): EIP1559GasFees {
|
||||
return when (gasProvider) {
|
||||
is AtomicContractEIP1559GasProvider -> gasProvider.getEIP1559GasFees()
|
||||
return when (contractGasProvider) {
|
||||
is AtomicContractEIP1559GasProvider -> contractGasProvider.getEIP1559GasFees()
|
||||
is ContractEIP1559GasProvider -> EIP1559GasFees(
|
||||
maxPriorityFeePerGas = gasProvider.getMaxPriorityFeePerGas(functionName).toULong(),
|
||||
maxFeePerGas = gasProvider.getMaxFeePerGas(functionName).toULong()
|
||||
maxPriorityFeePerGas = contractGasProvider.getMaxPriorityFeePerGas(functionName).toULong(),
|
||||
maxFeePerGas = contractGasProvider.getMaxFeePerGas(functionName).toULong()
|
||||
)
|
||||
|
||||
else -> throw UnsupportedOperationException("GasProvider does not support EIP1559!")
|
||||
@@ -85,10 +176,10 @@ class Web3JContractAsyncHelper(
|
||||
}
|
||||
|
||||
private fun getEip4844GasFees(): EIP4844GasFees {
|
||||
if (gasProvider !is EIP4844GasProvider) {
|
||||
if (contractGasProvider !is EIP4844GasProvider) {
|
||||
throw UnsupportedOperationException("GasProvider does not support EIP4844!")
|
||||
}
|
||||
return gasProvider.getEIP4844GasFees()
|
||||
return contractGasProvider.getEIP4844GasFees()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@@ -101,10 +192,10 @@ class Web3JContractAsyncHelper(
|
||||
if (isGasProviderSupportedEIP1559()) {
|
||||
val (maxPriorityFeePerGas, maxFeePerGas) = getEip1559GasFees(function.name)
|
||||
transactionManager.sendEIP1559Transaction(
|
||||
(gasProvider as ContractEIP1559GasProvider).chainId,
|
||||
(contractGasProvider as ContractEIP1559GasProvider).chainId,
|
||||
maxPriorityFeePerGas.toBigInteger(),
|
||||
maxFeePerGas.toBigInteger(),
|
||||
gasProvider.getGasLimit(function.name),
|
||||
contractGasProvider.getGasLimit(function.name),
|
||||
contractAddress,
|
||||
encodedData,
|
||||
weiValue,
|
||||
@@ -112,8 +203,8 @@ class Web3JContractAsyncHelper(
|
||||
)
|
||||
} else {
|
||||
transactionManager.sendTransaction(
|
||||
gasProvider.getGasPrice(function.name),
|
||||
gasProvider.getGasLimit(function.name),
|
||||
contractGasProvider.getGasPrice(function.name),
|
||||
contractGasProvider.getGasLimit(function.name),
|
||||
contractAddress,
|
||||
encodedData,
|
||||
weiValue,
|
||||
@@ -124,11 +215,11 @@ class Web3JContractAsyncHelper(
|
||||
return sendRawTransactionResult
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun sendTransactionAsync(
|
||||
private fun createAndSendRawTransaction(
|
||||
function: Function,
|
||||
weiValue: BigInteger,
|
||||
gasPriceCaps: GasPriceCaps? = null
|
||||
gasPriceCaps: GasPriceCaps? = null,
|
||||
gasLimit: BigInteger
|
||||
): CompletableFuture<EthSendTransaction> {
|
||||
val transaction = if (isGasProviderSupportedEIP1559()) {
|
||||
val (maxPriorityFeePerGas, maxFeePerGas) = getEip1559GasFees(function.name)
|
||||
@@ -142,19 +233,19 @@ class Web3JContractAsyncHelper(
|
||||
)
|
||||
|
||||
transactionManager.createRawTransaction(
|
||||
chainId = (gasProvider as ContractEIP1559GasProvider).chainId,
|
||||
chainId = (contractGasProvider as ContractEIP1559GasProvider).chainId,
|
||||
maxPriorityFeePerGas = gasPriceCaps?.maxPriorityFeePerGasCap?.toBigInteger()
|
||||
?: maxPriorityFeePerGas.toBigInteger(),
|
||||
maxFeePerGas = gasPriceCaps?.maxFeePerGasCap?.toBigInteger() ?: maxFeePerGas.toBigInteger(),
|
||||
gasLimit = gasProvider.getGasLimit(function.name),
|
||||
gasLimit = gasLimit,
|
||||
to = contractAddress,
|
||||
value = weiValue,
|
||||
data = FunctionEncoder.encode(function)
|
||||
)
|
||||
} else {
|
||||
transactionManager.createRawTransaction(
|
||||
gasPrice = gasProvider.getGasPrice(function.name),
|
||||
gasLimit = gasProvider.getGasLimit(function.name),
|
||||
gasPrice = contractGasProvider.getGasPrice(function.name),
|
||||
gasLimit = gasLimit,
|
||||
to = contractAddress,
|
||||
value = weiValue,
|
||||
data = FunctionEncoder.encode(function)
|
||||
@@ -164,6 +255,33 @@ class Web3JContractAsyncHelper(
|
||||
return web3j.ethSendRawTransaction(signedMessage).sendAsync()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun sendTransactionAsync(
|
||||
function: Function,
|
||||
weiValue: BigInteger,
|
||||
gasPriceCaps: GasPriceCaps? = null
|
||||
): CompletableFuture<EthSendTransaction> {
|
||||
return getGasLimit(function)
|
||||
.thenCompose { gasLimit ->
|
||||
createAndSendRawTransaction(function, weiValue, gasPriceCaps, gasLimit)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun sendTransactionAfterEthCallAsync(
|
||||
function: Function,
|
||||
weiValue: BigInteger,
|
||||
gasPriceCaps: GasPriceCaps? = null
|
||||
): CompletableFuture<EthSendTransaction> {
|
||||
return getGasLimit(function)
|
||||
.thenCompose { gasLimit ->
|
||||
executeEthCall(function, gasLimit)
|
||||
.thenCompose {
|
||||
createAndSendRawTransaction(function, weiValue, gasPriceCaps, gasLimit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun sendBlobCarryingTransactionAndGetTxHash(
|
||||
function: Function,
|
||||
blobs: List<ByteArray>,
|
||||
@@ -185,33 +303,37 @@ class Web3JContractAsyncHelper(
|
||||
blobs: List<Blob>,
|
||||
gasPriceCaps: GasPriceCaps? = null
|
||||
): CompletableFuture<EthSendTransaction> {
|
||||
val (eip1559fees, maxFeePerBlobGas) = getEip4844GasFees()
|
||||
val eip4844GasProvider = gasProvider as EIP4844GasProvider
|
||||
val blobVersionedHashes = blobs.map { BlobUtils.kzgToVersionedHash(BlobUtils.getCommitment(it)) }
|
||||
return getGasLimit(function, blobs, blobVersionedHashes)
|
||||
.thenCompose { gasLimit ->
|
||||
val eip4844GasProvider = contractGasProvider as EIP4844GasProvider
|
||||
val (eip1559fees, maxFeePerBlobGas) = getEip4844GasFees()
|
||||
|
||||
logGasPriceCapsInfo(
|
||||
logMessagePrefix = function.name,
|
||||
maxPriorityFeePerGas = eip1559fees.maxPriorityFeePerGas,
|
||||
maxFeePerGas = eip1559fees.maxFeePerGas,
|
||||
maxFeePerBlobGas = maxFeePerBlobGas,
|
||||
dynamicMaxPriorityFeePerGas = gasPriceCaps?.maxPriorityFeePerGasCap,
|
||||
dynamicMaxFeePerGas = gasPriceCaps?.maxFeePerGasCap,
|
||||
dynamicMaxFeePerBlobGas = gasPriceCaps?.maxFeePerBlobGasCap
|
||||
)
|
||||
logGasPriceCapsInfo(
|
||||
logMessagePrefix = function.name,
|
||||
maxPriorityFeePerGas = eip1559fees.maxPriorityFeePerGas,
|
||||
maxFeePerGas = eip1559fees.maxFeePerGas,
|
||||
maxFeePerBlobGas = maxFeePerBlobGas,
|
||||
dynamicMaxPriorityFeePerGas = gasPriceCaps?.maxPriorityFeePerGasCap,
|
||||
dynamicMaxFeePerGas = gasPriceCaps?.maxFeePerGasCap,
|
||||
dynamicMaxFeePerBlobGas = gasPriceCaps?.maxFeePerBlobGasCap
|
||||
)
|
||||
|
||||
val transaction = transactionManager.createRawTransaction(
|
||||
blobs = blobs,
|
||||
chainId = eip4844GasProvider.chainId,
|
||||
maxPriorityFeePerGas = gasPriceCaps?.maxPriorityFeePerGasCap?.toBigInteger()
|
||||
?: eip1559fees.maxPriorityFeePerGas.toBigInteger(),
|
||||
maxFeePerGas = gasPriceCaps?.maxFeePerGasCap?.toBigInteger() ?: eip1559fees.maxFeePerGas.toBigInteger(),
|
||||
gasLimit = eip4844GasProvider.getGasLimit(function.name),
|
||||
to = contractAddress,
|
||||
data = FunctionEncoder.encode(function),
|
||||
value = weiValue,
|
||||
maxFeePerBlobGas = gasPriceCaps?.maxFeePerBlobGasCap?.toBigInteger() ?: maxFeePerBlobGas.toBigInteger()
|
||||
)
|
||||
val signedMessage = transactionManager.sign(transaction)
|
||||
return web3j.ethSendRawTransaction(signedMessage).sendAsync()
|
||||
val transaction = transactionManager.createRawTransaction(
|
||||
blobs = blobs,
|
||||
chainId = eip4844GasProvider.chainId,
|
||||
maxPriorityFeePerGas = gasPriceCaps?.maxPriorityFeePerGasCap?.toBigInteger()
|
||||
?: eip1559fees.maxPriorityFeePerGas.toBigInteger(),
|
||||
maxFeePerGas = gasPriceCaps?.maxFeePerGasCap?.toBigInteger() ?: eip1559fees.maxFeePerGas.toBigInteger(),
|
||||
gasLimit = gasLimit,
|
||||
to = contractAddress,
|
||||
data = FunctionEncoder.encode(function),
|
||||
value = weiValue,
|
||||
maxFeePerBlobGas = gasPriceCaps?.maxFeePerBlobGasCap?.toBigInteger() ?: maxFeePerBlobGas.toBigInteger()
|
||||
)
|
||||
val signedMessage = transactionManager.sign(transaction)
|
||||
web3j.ethSendRawTransaction(signedMessage).sendAsync()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
@@ -230,19 +352,34 @@ class Web3JContractAsyncHelper(
|
||||
): RemoteFunctionCall<TransactionReceipt> {
|
||||
return executeRemoteCallTransaction(function, BigInteger.ZERO)
|
||||
}
|
||||
fun executeEthCall(function: Function, overrideGasLimit: BigInteger? = null): SafeFuture<String?> {
|
||||
return (overrideGasLimit?.let { SafeFuture.completedFuture(overrideGasLimit) } ?: getGasLimit(function))
|
||||
.thenCompose { gasLimit ->
|
||||
Transaction.createFunctionCallTransaction(
|
||||
transactionManager.fromAddress,
|
||||
null,
|
||||
null,
|
||||
gasLimit,
|
||||
contractAddress,
|
||||
FunctionEncoder.encode(function)
|
||||
).let { tx ->
|
||||
web3j.informativeEthCall(tx, smartContractErrors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun executeEthCall(function: Function): SafeFuture<String?> {
|
||||
val gasLimit = gasProvider.getGasLimit(function.name)
|
||||
val tx = Transaction.createFunctionCallTransaction(
|
||||
transactionManager.fromAddress,
|
||||
null,
|
||||
null,
|
||||
gasLimit,
|
||||
contractAddress,
|
||||
FunctionEncoder.encode(function)
|
||||
)
|
||||
|
||||
return web3j.informativeEthCall(tx, smartContractErrors)
|
||||
fun executeBlobEthCall(
|
||||
function: Function,
|
||||
blobs: List<BlobRecord>,
|
||||
gasPriceCaps: GasPriceCaps?
|
||||
): SafeFuture<String?> {
|
||||
return createEip4844Transaction(
|
||||
function,
|
||||
blobs.map { it.blobCompressionProof!!.compressedData }.toWeb3JTxBlob(),
|
||||
gasPriceCaps
|
||||
).thenCompose { tx ->
|
||||
web3j.informativeEthCall(tx, smartContractErrors)
|
||||
}
|
||||
}
|
||||
|
||||
private fun logGasPriceCapsInfo(
|
||||
|
||||
@@ -13,23 +13,27 @@ import java.math.BigInteger
|
||||
internal class LineaRollupEnhancedWrapper(
|
||||
contractAddress: String,
|
||||
web3j: Web3j,
|
||||
asyncTransactionManager: AsyncFriendlyTransactionManager,
|
||||
transactionManager: AsyncFriendlyTransactionManager,
|
||||
contractGasProvider: ContractGasProvider,
|
||||
val helper: Web3JContractAsyncHelper
|
||||
private val web3jContractHelper: Web3JContractAsyncHelper
|
||||
) : LineaRollupV6(
|
||||
contractAddress,
|
||||
web3j,
|
||||
asyncTransactionManager,
|
||||
transactionManager,
|
||||
contractGasProvider
|
||||
) {
|
||||
@Synchronized
|
||||
|
||||
override fun executeRemoteCallTransaction(
|
||||
function: Function,
|
||||
weiValue: BigInteger
|
||||
): RemoteFunctionCall<TransactionReceipt> = helper.executeRemoteCallTransaction(function, weiValue)
|
||||
): RemoteFunctionCall<TransactionReceipt> = web3jContractHelper.executeRemoteCallTransaction(function, weiValue)
|
||||
|
||||
@Synchronized
|
||||
override fun executeRemoteCallTransaction(
|
||||
function: Function
|
||||
): RemoteFunctionCall<TransactionReceipt> = helper.executeRemoteCallTransaction(function, BigInteger.ZERO)
|
||||
): RemoteFunctionCall<TransactionReceipt> = web3jContractHelper.executeRemoteCallTransaction(
|
||||
function,
|
||||
BigInteger.ZERO
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ import linea.kotlin.toULong
|
||||
import net.consensys.linea.contract.AsyncFriendlyTransactionManager
|
||||
import net.consensys.linea.contract.Web3JContractAsyncHelper
|
||||
import net.consensys.linea.contract.throwExceptionIfJsonRpcErrorReturned
|
||||
import net.consensys.linea.contract.toWeb3JTxBlob
|
||||
import net.consensys.linea.web3j.SmartContractErrors
|
||||
import net.consensys.linea.web3j.informativeEthCall
|
||||
import net.consensys.zkevm.coordinator.clients.smartcontract.BlockAndNonce
|
||||
import net.consensys.zkevm.coordinator.clients.smartcontract.LineaRollupSmartContractClient
|
||||
import net.consensys.zkevm.domain.BlobRecord
|
||||
@@ -26,24 +24,9 @@ import java.math.BigInteger
|
||||
class Web3JLineaRollupSmartContractClient internal constructor(
|
||||
contractAddress: String,
|
||||
web3j: Web3j,
|
||||
private val asyncTransactionManager: AsyncFriendlyTransactionManager,
|
||||
contractGasProvider: ContractGasProvider,
|
||||
private val smartContractErrors: SmartContractErrors,
|
||||
private val helper: Web3JContractAsyncHelper =
|
||||
Web3JContractAsyncHelper(
|
||||
contractAddress,
|
||||
web3j,
|
||||
asyncTransactionManager,
|
||||
contractGasProvider,
|
||||
smartContractErrors
|
||||
),
|
||||
private val web3jLineaClient: LineaRollupV6 = LineaRollupEnhancedWrapper(
|
||||
contractAddress,
|
||||
web3j,
|
||||
asyncTransactionManager,
|
||||
contractGasProvider,
|
||||
helper
|
||||
),
|
||||
private val transactionManager: AsyncFriendlyTransactionManager,
|
||||
private val web3jContractHelper: Web3JContractAsyncHelper,
|
||||
private val web3jLineaClient: LineaRollupV6,
|
||||
private val log: Logger = LogManager.getLogger(Web3JLineaRollupSmartContractClient::class.java)
|
||||
) : Web3JLineaRollupSmartContractClientReadOnly(
|
||||
contractAddress = contractAddress,
|
||||
@@ -58,14 +41,30 @@ class Web3JLineaRollupSmartContractClient internal constructor(
|
||||
web3j: Web3j,
|
||||
transactionManager: AsyncFriendlyTransactionManager,
|
||||
contractGasProvider: ContractGasProvider,
|
||||
smartContractErrors: SmartContractErrors
|
||||
smartContractErrors: SmartContractErrors,
|
||||
useEthEstimateGas: Boolean = false
|
||||
): Web3JLineaRollupSmartContractClient {
|
||||
val web3JContractAsyncHelper = Web3JContractAsyncHelper(
|
||||
contractAddress = contractAddress,
|
||||
web3j = web3j,
|
||||
transactionManager = transactionManager,
|
||||
contractGasProvider = contractGasProvider,
|
||||
smartContractErrors = smartContractErrors,
|
||||
useEthEstimateGas = useEthEstimateGas
|
||||
)
|
||||
val lineaRollupEnhancedWrapper = LineaRollupEnhancedWrapper(
|
||||
contractAddress = contractAddress,
|
||||
web3j = web3j,
|
||||
transactionManager = transactionManager,
|
||||
contractGasProvider = contractGasProvider,
|
||||
web3jContractHelper = web3JContractAsyncHelper
|
||||
)
|
||||
return Web3JLineaRollupSmartContractClient(
|
||||
contractAddress,
|
||||
web3j,
|
||||
transactionManager,
|
||||
contractGasProvider,
|
||||
smartContractErrors
|
||||
contractAddress = contractAddress,
|
||||
web3j = web3j,
|
||||
transactionManager = transactionManager,
|
||||
web3jContractHelper = web3JContractAsyncHelper,
|
||||
web3jLineaClient = lineaRollupEnhancedWrapper
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,7 +73,8 @@ class Web3JLineaRollupSmartContractClient internal constructor(
|
||||
web3j: Web3j,
|
||||
credentials: Credentials,
|
||||
contractGasProvider: ContractGasProvider,
|
||||
smartContractErrors: SmartContractErrors
|
||||
smartContractErrors: SmartContractErrors,
|
||||
useEthEstimateGas: Boolean
|
||||
): Web3JLineaRollupSmartContractClient {
|
||||
return load(
|
||||
contractAddress,
|
||||
@@ -82,23 +82,24 @@ class Web3JLineaRollupSmartContractClient internal constructor(
|
||||
// chainId will default -1, which will create legacy transactions
|
||||
AsyncFriendlyTransactionManager(web3j, credentials),
|
||||
contractGasProvider,
|
||||
smartContractErrors
|
||||
smartContractErrors,
|
||||
useEthEstimateGas
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun currentNonce(): ULong {
|
||||
return asyncTransactionManager.currentNonce().toULong()
|
||||
return transactionManager.currentNonce().toULong()
|
||||
}
|
||||
|
||||
private fun resetNonce(blockNumber: BigInteger?): SafeFuture<ULong> {
|
||||
return asyncTransactionManager
|
||||
return transactionManager
|
||||
.resetNonce(blockNumber)
|
||||
.thenApply { currentNonce() }
|
||||
}
|
||||
|
||||
override fun updateNonceAndReferenceBlockToLastL1Block(): SafeFuture<BlockAndNonce> {
|
||||
return helper.getCurrentBlock()
|
||||
return web3jContractHelper.getCurrentBlock()
|
||||
.thenCompose { blockNumber ->
|
||||
web3jLineaClient.setDefaultBlockParameter(DefaultBlockParameter.valueOf(blockNumber))
|
||||
resetNonce(blockNumber)
|
||||
@@ -117,7 +118,7 @@ class Web3JLineaRollupSmartContractClient internal constructor(
|
||||
return getVersion()
|
||||
.thenCompose { version ->
|
||||
val function = buildSubmitBlobsFunction(version, blobs)
|
||||
helper.sendBlobCarryingTransactionAndGetTxHash(
|
||||
web3jContractHelper.sendBlobCarryingTransactionAndGetTxHash(
|
||||
function = function,
|
||||
blobs = blobs.map { it.blobCompressionProof!!.compressedData },
|
||||
gasPriceCaps = gasPriceCaps
|
||||
@@ -132,19 +133,13 @@ class Web3JLineaRollupSmartContractClient internal constructor(
|
||||
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)
|
||||
web3jContractHelper.executeBlobEthCall(function, blobs, gasPriceCaps)
|
||||
}
|
||||
}
|
||||
|
||||
override fun finalizeBlocks(
|
||||
aggregation: ProofToFinalize,
|
||||
aggregationLastBlob: BlobRecord,
|
||||
parentShnarf: ByteArray,
|
||||
parentL1RollingHash: ByteArray,
|
||||
parentL1RollingHashMessageNumber: Long,
|
||||
gasPriceCaps: GasPriceCaps?
|
||||
@@ -158,7 +153,7 @@ class Web3JLineaRollupSmartContractClient internal constructor(
|
||||
parentL1RollingHash,
|
||||
parentL1RollingHashMessageNumber
|
||||
)
|
||||
helper.sendTransactionAsync(function, BigInteger.ZERO, gasPriceCaps)
|
||||
web3jContractHelper.sendTransactionAsync(function, BigInteger.ZERO, gasPriceCaps)
|
||||
.thenApply { result ->
|
||||
throwExceptionIfJsonRpcErrorReturned("eth_sendRawTransaction", result)
|
||||
result.transactionHash
|
||||
@@ -169,7 +164,6 @@ class Web3JLineaRollupSmartContractClient internal constructor(
|
||||
override fun finalizeBlocksEthCall(
|
||||
aggregation: ProofToFinalize,
|
||||
aggregationLastBlob: BlobRecord,
|
||||
parentShnarf: ByteArray,
|
||||
parentL1RollingHash: ByteArray,
|
||||
parentL1RollingHashMessageNumber: Long
|
||||
): SafeFuture<String?> {
|
||||
@@ -182,7 +176,31 @@ class Web3JLineaRollupSmartContractClient internal constructor(
|
||||
parentL1RollingHash,
|
||||
parentL1RollingHashMessageNumber
|
||||
)
|
||||
helper.executeEthCall(function)
|
||||
web3jContractHelper.executeEthCall(function)
|
||||
}
|
||||
}
|
||||
|
||||
override fun finalizeBlocksAfterEthCall(
|
||||
aggregation: ProofToFinalize,
|
||||
aggregationLastBlob: BlobRecord,
|
||||
parentL1RollingHash: ByteArray,
|
||||
parentL1RollingHashMessageNumber: Long,
|
||||
gasPriceCaps: GasPriceCaps?
|
||||
): SafeFuture<String> {
|
||||
return getVersion()
|
||||
.thenCompose { version ->
|
||||
val function = buildFinalizeBlocksFunction(
|
||||
version,
|
||||
aggregation,
|
||||
aggregationLastBlob,
|
||||
parentL1RollingHash,
|
||||
parentL1RollingHashMessageNumber
|
||||
)
|
||||
web3jContractHelper.sendTransactionAfterEthCallAsync(function, BigInteger.ZERO, gasPriceCaps)
|
||||
.thenApply { result ->
|
||||
throwExceptionIfJsonRpcErrorReturned("eth_sendRawTransaction", result)
|
||||
result.transactionHash
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ interface LineaRollupSmartContractClient : LineaRollupSmartContractClientReadOnl
|
||||
fun finalizeBlocksEthCall(
|
||||
aggregation: ProofToFinalize,
|
||||
aggregationLastBlob: BlobRecord,
|
||||
parentShnarf: ByteArray,
|
||||
parentL1RollingHash: ByteArray,
|
||||
parentL1RollingHashMessageNumber: Long
|
||||
): SafeFuture<String?>
|
||||
@@ -48,7 +47,14 @@ interface LineaRollupSmartContractClient : LineaRollupSmartContractClientReadOnl
|
||||
fun finalizeBlocks(
|
||||
aggregation: ProofToFinalize,
|
||||
aggregationLastBlob: BlobRecord,
|
||||
parentShnarf: ByteArray,
|
||||
parentL1RollingHash: ByteArray,
|
||||
parentL1RollingHashMessageNumber: Long,
|
||||
gasPriceCaps: GasPriceCaps?
|
||||
): SafeFuture<String>
|
||||
|
||||
fun finalizeBlocksAfterEthCall(
|
||||
aggregation: ProofToFinalize,
|
||||
aggregationLastBlob: BlobRecord,
|
||||
parentL1RollingHash: ByteArray,
|
||||
parentL1RollingHashMessageNumber: Long,
|
||||
gasPriceCaps: GasPriceCaps?
|
||||
|
||||
@@ -8,6 +8,7 @@ import net.consensys.zkevm.domain.ProofToFinalize
|
||||
import net.consensys.zkevm.ethereum.gaspricing.GasPriceCapProvider
|
||||
import net.consensys.zkevm.ethereum.submission.logSubmissionError
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.web3j.tx.exceptions.ContractCallException
|
||||
import tech.pegasys.teku.infrastructure.async.SafeFuture
|
||||
import java.util.function.Consumer
|
||||
|
||||
@@ -42,74 +43,45 @@ class AggregationSubmitterImpl(
|
||||
parentL1RollingHash: ByteArray,
|
||||
parentL1RollingHashMessageNumber: Long
|
||||
): SafeFuture<String?> {
|
||||
log.debug(
|
||||
"eth_call submitting aggregation={}",
|
||||
aggregationProof.intervalString()
|
||||
)
|
||||
return lineaRollup.finalizeBlocksEthCall(
|
||||
aggregation = aggregationProof,
|
||||
aggregationLastBlob = aggregationEndBlob,
|
||||
parentShnarf = parentShnarf,
|
||||
parentL1RollingHash = parentL1RollingHash,
|
||||
parentL1RollingHashMessageNumber = parentL1RollingHashMessageNumber
|
||||
)
|
||||
.whenException { th -> logAggregationSubmissionError(aggregationProof.intervalString(), th, isEthCall = true) }
|
||||
.thenPeek { result ->
|
||||
log.debug("eth_call valid aggregation={} result={}", aggregationProof.intervalString(), result)
|
||||
}
|
||||
.thenApply { true }
|
||||
.exceptionally { false }
|
||||
.thenCompose { isEthCallSuccessful ->
|
||||
if (!isEthCallSuccessful) {
|
||||
SafeFuture.completedFuture(null)
|
||||
} else {
|
||||
val nonce = lineaRollup.currentNonce()
|
||||
(
|
||||
gasPriceCapProvider?.getGasPriceCaps(aggregationProof.firstBlockNumber)
|
||||
?: SafeFuture.completedFuture(null)
|
||||
).thenCompose { gasPriceCaps ->
|
||||
log.debug(
|
||||
"submitting aggregation={} nonce={} gasPriceCaps={}",
|
||||
aggregationProof.intervalString(),
|
||||
nonce,
|
||||
gasPriceCaps
|
||||
)
|
||||
lineaRollup.finalizeBlocks(
|
||||
aggregation = aggregationProof,
|
||||
aggregationLastBlob = aggregationEndBlob,
|
||||
parentShnarf = parentShnarf,
|
||||
parentL1RollingHash = parentL1RollingHash,
|
||||
parentL1RollingHashMessageNumber = parentL1RollingHashMessageNumber,
|
||||
gasPriceCaps = gasPriceCaps
|
||||
)
|
||||
.whenException { th -> logAggregationSubmissionError(aggregationProof.intervalString(), th) }
|
||||
.thenPeek { transactionHash ->
|
||||
log.info(
|
||||
"submitted aggregation={} transactionHash={} nonce={} gasPriceCaps={}",
|
||||
aggregationProof.intervalString(),
|
||||
transactionHash,
|
||||
nonce,
|
||||
gasPriceCaps
|
||||
)
|
||||
val aggregationSubmittedEvent = FinalizationSubmittedEvent(
|
||||
aggregationProof = aggregationProof,
|
||||
parentShnarf = parentShnarf,
|
||||
parentL1RollingHash = parentL1RollingHash,
|
||||
parentL1RollingHashMessageNumber = parentL1RollingHashMessageNumber,
|
||||
submissionTimestamp = clock.now(),
|
||||
transactionHash = transactionHash.toByteArray()
|
||||
)
|
||||
aggregationSubmittedEventConsumer.accept(aggregationSubmittedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
log.debug("submitting aggregation={}", aggregationProof.intervalString())
|
||||
return (
|
||||
gasPriceCapProvider?.getGasPriceCaps(aggregationProof.firstBlockNumber)
|
||||
?: SafeFuture.completedFuture(null)
|
||||
).thenCompose { gasPriceCaps ->
|
||||
val nonce = lineaRollup.currentNonce()
|
||||
lineaRollup.finalizeBlocksAfterEthCall(
|
||||
aggregation = aggregationProof,
|
||||
aggregationLastBlob = aggregationEndBlob,
|
||||
parentL1RollingHash = parentL1RollingHash,
|
||||
parentL1RollingHashMessageNumber = parentL1RollingHashMessageNumber,
|
||||
gasPriceCaps = gasPriceCaps
|
||||
).thenPeek { transactionHash ->
|
||||
log.info(
|
||||
"submitted aggregation={} transactionHash={} nonce={} gasPriceCaps={}",
|
||||
aggregationProof.intervalString(),
|
||||
transactionHash,
|
||||
nonce,
|
||||
gasPriceCaps
|
||||
)
|
||||
val aggregationSubmittedEvent = FinalizationSubmittedEvent(
|
||||
aggregationProof = aggregationProof,
|
||||
parentShnarf = parentShnarf,
|
||||
parentL1RollingHash = parentL1RollingHash,
|
||||
parentL1RollingHashMessageNumber = parentL1RollingHashMessageNumber,
|
||||
submissionTimestamp = clock.now(),
|
||||
transactionHash = transactionHash.toByteArray()
|
||||
)
|
||||
aggregationSubmittedEventConsumer.accept(aggregationSubmittedEvent)
|
||||
}
|
||||
}.whenException { th ->
|
||||
logAggregationSubmissionError(aggregationProof.intervalString(), th)
|
||||
}
|
||||
}
|
||||
|
||||
private fun logAggregationSubmissionError(
|
||||
intervalString: String,
|
||||
error: Throwable,
|
||||
isEthCall: Boolean = false
|
||||
isEthCall: Boolean = error.cause is ContractCallException
|
||||
) {
|
||||
logSubmissionError(
|
||||
log,
|
||||
|
||||
@@ -60,7 +60,7 @@ plugin-linea-tx-pool-simulation-check-api-enabled=false
|
||||
plugin-linea-tx-pool-simulation-check-p2p-enabled=false
|
||||
plugin-linea-max-block-calldata-size=109000
|
||||
plugin-linea-max-tx-calldata-size=60000
|
||||
plugin-linea-max-block-gas=50000000
|
||||
plugin-linea-max-block-gas=55000000
|
||||
plugin-linea-tx-pool-min-margin="0.8"
|
||||
plugin-linea-min-margin="1.0"
|
||||
plugin-linea-fixed-gas-cost-wei=30000000
|
||||
|
||||
@@ -130,7 +130,6 @@ fun submitBlobsAndAggregationsAndWaitExecution(
|
||||
val txHash = contractClientForAggregationSubmission.finalizeBlocks(
|
||||
aggregation = aggregation.aggregationProof!!,
|
||||
aggregationLastBlob = aggBlobs.last(),
|
||||
parentShnarf = aggBlobs.first().blobCompressionProof!!.prevShnarf,
|
||||
parentL1RollingHash = parentAgg?.aggregationProof?.l1RollingHash ?: ByteArray(32),
|
||||
parentL1RollingHashMessageNumber = parentAgg?.aggregationProof?.l1RollingHashMessageNumber ?: 0L,
|
||||
gasPriceCaps = null
|
||||
|
||||
@@ -25,7 +25,7 @@ class Eip4844Transaction(
|
||||
chainId: Long?,
|
||||
maxPriorityFeePerGas: BigInteger?,
|
||||
maxFeePerGas: BigInteger?,
|
||||
_maxFeePerBlobGas: BigInteger,
|
||||
_maxFeePerBlobGas: BigInteger?,
|
||||
@JsonProperty("blobs")
|
||||
@JsonSerialize(contentUsing = BlobSerializer::class)
|
||||
val blobs: List<Blob>,
|
||||
@@ -35,7 +35,7 @@ class Eip4844Transaction(
|
||||
val blobVersionedHashes: List<Bytes> = computeVersionedHashesFromBlobs(blobs)
|
||||
) : Transaction(from, nonce, gasPrice, gasLimit, to, value, data, chainId, maxPriorityFeePerGas, maxFeePerGas) {
|
||||
@Suppress("Unused")
|
||||
val maxFeePerBlobGas: String = Numeric.encodeQuantity(_maxFeePerBlobGas)
|
||||
val maxFeePerBlobGas: String? = _maxFeePerBlobGas?.let { Numeric.encodeQuantity(it) }
|
||||
companion object {
|
||||
fun computeVersionedHashesFromBlobs(blobs: List<Blob>): List<Bytes> {
|
||||
return blobs.map(BlobUtils::getCommitment).map(BlobUtils::kzgToVersionedHash)
|
||||
@@ -46,7 +46,7 @@ class Eip4844Transaction(
|
||||
to: String,
|
||||
data: String,
|
||||
blobs: List<Blob>,
|
||||
maxFeePerBlobGas: BigInteger,
|
||||
maxFeePerBlobGas: BigInteger? = null,
|
||||
gasLimit: BigInteger?,
|
||||
blobVersionedHashes: List<Bytes> = computeVersionedHashesFromBlobs(blobs),
|
||||
maxPriorityFeePerGas: BigInteger? = null,
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.consensys.linea.web3j
|
||||
|
||||
import org.web3j.protocol.Web3j
|
||||
import org.web3j.protocol.core.DefaultBlockParameterName
|
||||
import org.web3j.protocol.core.Response
|
||||
import org.web3j.protocol.core.methods.request.Transaction
|
||||
import org.web3j.protocol.core.methods.response.EthCall
|
||||
import org.web3j.tx.TransactionManager
|
||||
@@ -10,18 +11,25 @@ import tech.pegasys.teku.infrastructure.async.SafeFuture
|
||||
|
||||
typealias SmartContractErrors = Map<String, String>
|
||||
|
||||
private fun getErrorMessage(
|
||||
ethCall: EthCall,
|
||||
fun getRevertReason(
|
||||
error: Response.Error?,
|
||||
smartContractErrors: SmartContractErrors
|
||||
): String {
|
||||
val errorDataString = ethCall.error?.data ?: ""
|
||||
val revertReason = if (errorDataString.length > 11) {
|
||||
): String? {
|
||||
val errorDataString = error?.data ?: ""
|
||||
return if (errorDataString.length > 11) {
|
||||
// execution client can return empty data: "0x", so we need to check the length
|
||||
val revertId = errorDataString.substring(3, 11).lowercase()
|
||||
smartContractErrors[revertId]
|
||||
} else {
|
||||
"UNKNOWN"
|
||||
}
|
||||
}
|
||||
|
||||
private fun getErrorMessage(
|
||||
ethCall: EthCall,
|
||||
smartContractErrors: SmartContractErrors
|
||||
): String {
|
||||
val revertReason = getRevertReason(ethCall.error, smartContractErrors)
|
||||
|
||||
return String.format(TransactionManager.REVERT_ERR_STR, ethCall.revertReason) +
|
||||
" revertReason=$revertReason errorData=${ethCall.error?.data}"
|
||||
|
||||
Reference in New Issue
Block a user