Commit e2985bf7 authored by Karl Floersch's avatar Karl Floersch

integrate new tx submission lib

parent 7b43840c
/* External Imports */ /* External Imports */
import { Contract, Signer, utils, providers } from 'ethers' import {
import { TransactionReceipt } from '@ethersproject/abstract-provider' Contract,
Signer,
utils,
providers,
PopulatedTransaction,
} from 'ethers'
import {
TransactionReceipt,
TransactionResponse,
} from '@ethersproject/abstract-provider'
import { Gauge, Histogram, Counter } from 'prom-client' import { Gauge, Histogram, Counter } from 'prom-client'
import * as ynatm from '@eth-optimism/ynatm'
import { RollupInfo, sleep } from '@eth-optimism/core-utils' import { RollupInfo, sleep } from '@eth-optimism/core-utils'
import { Logger, Metrics } from '@eth-optimism/common-ts' import { Logger, Metrics } from '@eth-optimism/common-ts'
import { getContractFactory } from 'old-contracts' import { getContractFactory } from 'old-contracts'
/* Internal Imports */
import { TxSubmissionHooks } from '..'
export interface BlockRange { export interface BlockRange {
start: number start: number
end: number end: number
} }
export interface ResubmissionConfig {
resubmissionTimeout: number
minGasPriceInGwei: number
maxGasPriceInGwei: number
gasRetryIncrement: number
}
interface BatchSubmitterMetrics { interface BatchSubmitterMetrics {
batchSubmitterETHBalance: Gauge<string> batchSubmitterETHBalance: Gauge<string>
...@@ -49,10 +53,6 @@ export abstract class BatchSubmitter { ...@@ -49,10 +53,6 @@ export abstract class BatchSubmitter {
readonly finalityConfirmations: number, readonly finalityConfirmations: number,
readonly addressManagerAddress: string, readonly addressManagerAddress: string,
readonly minBalanceEther: number, readonly minBalanceEther: number,
readonly minGasPriceInGwei: number,
readonly maxGasPriceInGwei: number,
readonly gasRetryIncrement: number,
readonly gasThresholdInGwei: number,
readonly blockOffset: number, readonly blockOffset: number,
readonly logger: Logger, readonly logger: Logger,
readonly defaultMetrics: Metrics readonly defaultMetrics: Metrics
...@@ -190,69 +190,37 @@ export abstract class BatchSubmitter { ...@@ -190,69 +190,37 @@ export abstract class BatchSubmitter {
return true return true
} }
public static async getReceiptWithResubmission( protected _makeHooks(txName: string): TxSubmissionHooks {
txFunc: (gasPrice) => Promise<TransactionReceipt>, return {
resubmissionConfig: ResubmissionConfig, beforeSendTransaction: (tx: PopulatedTransaction) => {
logger: Logger this.logger.info(`Submitting ${txName} transaction`, {
): Promise<TransactionReceipt> { gasPrice: tx.gasPrice,
const { nonce: tx.nonce,
resubmissionTimeout, contractAddr: this.chainContract.address,
minGasPriceInGwei, })
maxGasPriceInGwei, },
gasRetryIncrement, onTransactionResponse: (txResponse: TransactionResponse) => {
} = resubmissionConfig this.logger.info(`Submitted ${txName} transaction`, {
txHash: txResponse.hash,
const receipt = await ynatm.send({ from: txResponse.from,
sendTransactionFunction: txFunc, })
minGasPrice: ynatm.toGwei(minGasPriceInGwei), this.logger.debug(`${txName} transaction data`, {
maxGasPrice: ynatm.toGwei(maxGasPriceInGwei), data: txResponse.data,
gasPriceScalingFunction: ynatm.LINEAR(gasRetryIncrement), })
delay: resubmissionTimeout, },
})
logger.debug('Resubmission tx receipt', { receipt })
return receipt
}
private async _getMinGasPriceInGwei(): Promise<number> {
if (this.minGasPriceInGwei !== 0) {
return this.minGasPriceInGwei
}
let minGasPriceInGwei = parseInt(
utils.formatUnits(await this.signer.getGasPrice(), 'gwei'),
10
)
if (minGasPriceInGwei > this.maxGasPriceInGwei) {
this.logger.warn(
'Minimum gas price is higher than max! Ethereum must be congested...'
)
minGasPriceInGwei = this.maxGasPriceInGwei
} }
return minGasPriceInGwei
} }
protected async _submitAndLogTx( protected async _submitAndLogTx(
txFunc: (gasPrice) => Promise<TransactionReceipt>, submitTransaction: () => Promise<TransactionReceipt>,
successMessage: string successMessage: string
): Promise<TransactionReceipt> { ): Promise<TransactionReceipt> {
this.lastBatchSubmissionTimestamp = Date.now() this.lastBatchSubmissionTimestamp = Date.now()
this.logger.debug('Waiting for receipt...') this.logger.debug('Submitting transaction & waiting for receipt...')
const resubmissionConfig: ResubmissionConfig = {
resubmissionTimeout: this.resubmissionTimeout,
minGasPriceInGwei: await this._getMinGasPriceInGwei(),
maxGasPriceInGwei: this.maxGasPriceInGwei,
gasRetryIncrement: this.gasRetryIncrement,
}
let receipt: TransactionReceipt let receipt: TransactionReceipt
try { try {
receipt = await BatchSubmitter.getReceiptWithResubmission( receipt = await submitTransaction()
txFunc,
resubmissionConfig,
this.logger
)
} catch (err) { } catch (err) {
this.metrics.failedSubmissions.inc() this.metrics.failedSubmissions.inc()
if (err.reason) { if (err.reason) {
......
...@@ -13,6 +13,7 @@ import { Logger, Metrics } from '@eth-optimism/common-ts' ...@@ -13,6 +13,7 @@ import { Logger, Metrics } from '@eth-optimism/common-ts'
/* Internal Imports */ /* Internal Imports */
import { BlockRange, BatchSubmitter } from '.' import { BlockRange, BatchSubmitter } from '.'
import { TransactionSubmitter } from '../utils'
export class StateBatchSubmitter extends BatchSubmitter { export class StateBatchSubmitter extends BatchSubmitter {
// TODO: Change this so that we calculate start = scc.totalElements() and end = ctc.totalElements()! // TODO: Change this so that we calculate start = scc.totalElements() and end = ctc.totalElements()!
...@@ -23,6 +24,7 @@ export class StateBatchSubmitter extends BatchSubmitter { ...@@ -23,6 +24,7 @@ export class StateBatchSubmitter extends BatchSubmitter {
protected syncing: boolean protected syncing: boolean
protected ctcContract: Contract protected ctcContract: Contract
private fraudSubmissionAddress: string private fraudSubmissionAddress: string
private transactionSubmitter: TransactionSubmitter
constructor( constructor(
signer: Signer, signer: Signer,
...@@ -36,10 +38,7 @@ export class StateBatchSubmitter extends BatchSubmitter { ...@@ -36,10 +38,7 @@ export class StateBatchSubmitter extends BatchSubmitter {
finalityConfirmations: number, finalityConfirmations: number,
addressManagerAddress: string, addressManagerAddress: string,
minBalanceEther: number, minBalanceEther: number,
minGasPriceInGwei: number, transactionSubmitter: TransactionSubmitter,
maxGasPriceInGwei: number,
gasRetryIncrement: number,
gasThresholdInGwei: number,
blockOffset: number, blockOffset: number,
logger: Logger, logger: Logger,
metrics: Metrics, metrics: Metrics,
...@@ -57,15 +56,12 @@ export class StateBatchSubmitter extends BatchSubmitter { ...@@ -57,15 +56,12 @@ export class StateBatchSubmitter extends BatchSubmitter {
finalityConfirmations, finalityConfirmations,
addressManagerAddress, addressManagerAddress,
minBalanceEther, minBalanceEther,
minGasPriceInGwei,
maxGasPriceInGwei,
gasRetryIncrement,
gasThresholdInGwei,
blockOffset, blockOffset,
logger, logger,
metrics metrics
) )
this.fraudSubmissionAddress = fraudSubmissionAddress this.fraudSubmissionAddress = fraudSubmissionAddress
this.transactionSubmitter = transactionSubmitter
} }
/***************************** /*****************************
...@@ -181,28 +177,16 @@ export class StateBatchSubmitter extends BatchSubmitter { ...@@ -181,28 +177,16 @@ export class StateBatchSubmitter extends BatchSubmitter {
batch, batch,
offsetStartsAtIndex offsetStartsAtIndex
) )
const contractFunction = async (gasPrice): Promise<TransactionReceipt> => { const submitTransaction = (): Promise<TransactionReceipt> => {
this.logger.info('Submitting appendStateBatch transaction', { return this.transactionSubmitter.submitTransaction(
gasPrice, tx,
contractAddr: this.chainContract.address, this._makeHooks('appendStateBatch')
})
const txResponse = await this.signer.sendTransaction({
...tx,
gasPrice,
})
this.logger.info('Submitted appendStateBatch transaction', {
txHash: txResponse.hash,
from: txResponse.from,
})
this.logger.debug('appendStateBatch transaction data', {
data: txResponse.data,
})
return this.signer.provider.waitForTransaction(
txResponse.hash,
this.numConfirmations
) )
} }
return this._submitAndLogTx(contractFunction, 'Submitted state root batch!') return this._submitAndLogTx(
submitTransaction,
'Submitted state root batch!'
)
} }
/********************* /*********************
......
...@@ -22,6 +22,7 @@ import { ...@@ -22,6 +22,7 @@ import {
} from '../transaction-chain-contract' } from '../transaction-chain-contract'
import { BlockRange, BatchSubmitter } from '.' import { BlockRange, BatchSubmitter } from '.'
import { TransactionSubmitter } from '../utils'
export interface AutoFixBatchOptions { export interface AutoFixBatchOptions {
fixDoublePlayedDeposits: boolean fixDoublePlayedDeposits: boolean
...@@ -35,6 +36,8 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -35,6 +36,8 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
protected syncing: boolean protected syncing: boolean
private disableQueueBatchAppend: boolean private disableQueueBatchAppend: boolean
private autoFixBatchOptions: AutoFixBatchOptions private autoFixBatchOptions: AutoFixBatchOptions
private transactionSubmitter: TransactionSubmitter
private gasThresholdInGwei: number
constructor( constructor(
signer: Signer, signer: Signer,
...@@ -47,10 +50,8 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -47,10 +50,8 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
resubmissionTimeout: number, resubmissionTimeout: number,
addressManagerAddress: string, addressManagerAddress: string,
minBalanceEther: number, minBalanceEther: number,
minGasPriceInGwei: number,
maxGasPriceInGwei: number,
gasRetryIncrement: number,
gasThresholdInGwei: number, gasThresholdInGwei: number,
transactionSubmitter: TransactionSubmitter,
blockOffset: number, blockOffset: number,
logger: Logger, logger: Logger,
metrics: Metrics, metrics: Metrics,
...@@ -73,16 +74,14 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -73,16 +74,14 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
0, // Supply dummy value because it is not used. 0, // Supply dummy value because it is not used.
addressManagerAddress, addressManagerAddress,
minBalanceEther, minBalanceEther,
minGasPriceInGwei,
maxGasPriceInGwei,
gasRetryIncrement,
gasThresholdInGwei,
blockOffset, blockOffset,
logger, logger,
metrics metrics
) )
this.disableQueueBatchAppend = disableQueueBatchAppend this.disableQueueBatchAppend = disableQueueBatchAppend
this.autoFixBatchOptions = autoFixBatchOptions this.autoFixBatchOptions = autoFixBatchOptions
this.gasThresholdInGwei = gasThresholdInGwei
this.transactionSubmitter = transactionSubmitter
} }
/***************************** /*****************************
...@@ -140,38 +139,7 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -140,38 +139,7 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
) )
if (!this.disableQueueBatchAppend) { if (!this.disableQueueBatchAppend) {
// Generate the transaction we will repeatedly submit return this.submitAppendQueueBatch()
const tx = await this.chainContract.populateTransaction.appendQueueBatch(
ethers.constants.MaxUint256 // Completely empty the queue by appending (up to) an enormous number of queue elements.
)
const contractFunction = async (
gasPrice
): Promise<TransactionReceipt> => {
this.logger.info('Submitting appendQueueBatch transaction', {
gasPrice,
contractAddr: this.chainContract.address,
})
const txResponse = await this.chainContract.appendQueueBatch(
ethers.constants.MaxUint256, // Completely empty the queue by appending (up to) an enormous number of queue elements.
{
gasPrice,
}
)
this.logger.info('Submitted appendQueueBatch transaction', {
txHash: txResponse.hash,
from: txResponse.from,
})
this.logger.debug('appendQueueBatch transaction data', {
data: txResponse.data,
})
return this.signer.provider.waitForTransaction(
txResponse.hash,
this.numConfirmations
)
}
// Empty the queue with a huge `appendQueueBatch(..)` call
return this._submitAndLogTx(contractFunction, 'Cleared queue!')
} }
} }
this.logger.info('Syncing mode enabled but queue is empty. Skipping...') this.logger.info('Syncing mode enabled but queue is empty. Skipping...')
...@@ -254,38 +222,43 @@ export class TransactionBatchSubmitter extends BatchSubmitter { ...@@ -254,38 +222,43 @@ export class TransactionBatchSubmitter extends BatchSubmitter {
l1tipHeight, l1tipHeight,
}) })
// Generate the transaction we will repeatedly submit return this.submitAppendSequencerBatch(batchParams)
const tx = await this.chainContract.customPopulateTransaction.appendSequencerBatch(
batchParams
)
const contractFunction = async (gasPrice): Promise<TransactionReceipt> => {
this.logger.info('Submitting appendSequencerBatch transaction', {
gasPrice,
contractAddr: this.chainContract.address,
})
const txResponse = await this.signer.sendTransaction({
...tx,
gasPrice,
})
this.logger.info('Submitted appendSequencerBatch transaction', {
txHash: txResponse.hash,
from: txResponse.from,
})
this.logger.debug('appendSequencerBatch transaction data', {
data: txResponse.data,
})
return this.signer.provider.waitForTransaction(
txResponse.hash,
this.numConfirmations
)
}
return this._submitAndLogTx(contractFunction, 'Submitted batch!')
} }
/********************* /*********************
* Private Functions * * Private Functions *
********************/ ********************/
private async submitAppendQueueBatch(): Promise<TransactionReceipt> {
const tx = await this.chainContract.populateTransaction.appendQueueBatch(
ethers.constants.MaxUint256 // Completely empty the queue by appending (up to) an enormous number of queue elements.
)
const submitTransaction = (): Promise<TransactionReceipt> => {
return this.transactionSubmitter.submitTransaction(
tx,
this._makeHooks('appendQueueBatch')
)
}
// Empty the queue with a huge `appendQueueBatch(..)` call
return this._submitAndLogTx(submitTransaction, 'Cleared queue!')
}
private async submitAppendSequencerBatch(
batchParams: AppendSequencerBatchParams
): Promise<TransactionReceipt> {
const tx =
await this.chainContract.customPopulateTransaction.appendSequencerBatch(
batchParams
)
const submitTransaction = (): Promise<TransactionReceipt> => {
return this.transactionSubmitter.submitTransaction(
tx,
this._makeHooks('appendSequencerBatch')
)
}
return this._submitAndLogTx(submitTransaction, 'Submitted batch!')
}
private async _generateSequencerBatchParams( private async _generateSequencerBatchParams(
startBlock: number, startBlock: number,
endBlock: number endBlock: number
......
...@@ -16,6 +16,11 @@ import { ...@@ -16,6 +16,11 @@ import {
STATE_BATCH_SUBMITTER_LOG_TAG, STATE_BATCH_SUBMITTER_LOG_TAG,
TX_BATCH_SUBMITTER_LOG_TAG, TX_BATCH_SUBMITTER_LOG_TAG,
} from '..' } from '..'
import {
TransactionSubmitter,
YnatmTransactionSubmitter,
ResubmissionConfig,
} from '../utils'
interface RequiredEnvVars { interface RequiredEnvVars {
// The HTTP provider URL for L1. // The HTTP provider URL for L1.
...@@ -349,6 +354,18 @@ export const run = async () => { ...@@ -349,6 +354,18 @@ export const run = async () => {
addressManagerAddress: requiredEnvVars.ADDRESS_MANAGER_ADDRESS, addressManagerAddress: requiredEnvVars.ADDRESS_MANAGER_ADDRESS,
}) })
const resubmissionConfig: ResubmissionConfig = {
resubmissionTimeout: requiredEnvVars.RESUBMISSION_TIMEOUT * 1_000,
minGasPriceInGwei: MIN_GAS_PRICE_IN_GWEI,
maxGasPriceInGwei: GAS_THRESHOLD_IN_GWEI,
gasRetryIncrement: GAS_RETRY_INCREMENT,
}
const txBatchTxSubmitter: TransactionSubmitter =
new YnatmTransactionSubmitter(
sequencerSigner,
resubmissionConfig,
requiredEnvVars.NUM_CONFIRMATIONS
)
const txBatchSubmitter = new TransactionBatchSubmitter( const txBatchSubmitter = new TransactionBatchSubmitter(
sequencerSigner, sequencerSigner,
l2Provider, l2Provider,
...@@ -360,10 +377,8 @@ export const run = async () => { ...@@ -360,10 +377,8 @@ export const run = async () => {
requiredEnvVars.RESUBMISSION_TIMEOUT * 1_000, requiredEnvVars.RESUBMISSION_TIMEOUT * 1_000,
requiredEnvVars.ADDRESS_MANAGER_ADDRESS, requiredEnvVars.ADDRESS_MANAGER_ADDRESS,
requiredEnvVars.SAFE_MINIMUM_ETHER_BALANCE, requiredEnvVars.SAFE_MINIMUM_ETHER_BALANCE,
MIN_GAS_PRICE_IN_GWEI,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI, GAS_THRESHOLD_IN_GWEI,
txBatchTxSubmitter,
BLOCK_OFFSET, BLOCK_OFFSET,
logger.child({ name: TX_BATCH_SUBMITTER_LOG_TAG }), logger.child({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
metrics, metrics,
...@@ -371,6 +386,12 @@ export const run = async () => { ...@@ -371,6 +386,12 @@ export const run = async () => {
autoFixBatchOptions autoFixBatchOptions
) )
const stateBatchTxSubmitter: TransactionSubmitter =
new YnatmTransactionSubmitter(
proposerSigner,
resubmissionConfig,
requiredEnvVars.NUM_CONFIRMATIONS
)
const stateBatchSubmitter = new StateBatchSubmitter( const stateBatchSubmitter = new StateBatchSubmitter(
proposerSigner, proposerSigner,
l2Provider, l2Provider,
...@@ -383,10 +404,7 @@ export const run = async () => { ...@@ -383,10 +404,7 @@ export const run = async () => {
requiredEnvVars.FINALITY_CONFIRMATIONS, requiredEnvVars.FINALITY_CONFIRMATIONS,
requiredEnvVars.ADDRESS_MANAGER_ADDRESS, requiredEnvVars.ADDRESS_MANAGER_ADDRESS,
requiredEnvVars.SAFE_MINIMUM_ETHER_BALANCE, requiredEnvVars.SAFE_MINIMUM_ETHER_BALANCE,
MIN_GAS_PRICE_IN_GWEI, stateBatchTxSubmitter,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI,
BLOCK_OFFSET, BLOCK_OFFSET,
logger.child({ name: STATE_BATCH_SUBMITTER_LOG_TAG }), logger.child({ name: STATE_BATCH_SUBMITTER_LOG_TAG }),
metrics, metrics,
......
/* External Imports */ /* External Imports */
import { Contract, BigNumber, ethers } from 'ethers' import { Contract, ethers } from 'ethers'
import { import {
TransactionResponse, TransactionResponse,
TransactionRequest, TransactionRequest,
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import { JsonRpcProvider } from '@ethersproject/providers'
import { keccak256 } from 'ethers/lib/utils' import { keccak256 } from 'ethers/lib/utils'
import { import {
AppendSequencerBatchParams, AppendSequencerBatchParams,
BatchContext, BatchContext,
encodeAppendSequencerBatch, encodeAppendSequencerBatch,
encodeHex,
remove0x, remove0x,
} from '@eth-optimism/core-utils' } from '@eth-optimism/core-utils'
...@@ -33,7 +31,6 @@ export class CanonicalTransactionChainContract extends Contract { ...@@ -33,7 +31,6 @@ export class CanonicalTransactionChainContract extends Contract {
from: await this.signer.getAddress(), from: await this.signer.getAddress(),
data, data,
}) })
const value = 0
return { return {
nonce, nonce,
...@@ -55,7 +52,9 @@ export class CanonicalTransactionChainContract extends Contract { ...@@ -55,7 +52,9 @@ export class CanonicalTransactionChainContract extends Contract {
* Internal Functions * * Internal Functions *
*********************/ *********************/
const APPEND_SEQUENCER_BATCH_METHOD_ID = 'appendSequencerBatch()' const APPEND_SEQUENCER_BATCH_METHOD_ID = keccak256(
Buffer.from('appendSequencerBatch()')
).slice(2, 10)
const appendSequencerBatch = async ( const appendSequencerBatch = async (
OVM_CanonicalTransactionChain: Contract, OVM_CanonicalTransactionChain: Contract,
...@@ -70,18 +69,7 @@ const appendSequencerBatch = async ( ...@@ -70,18 +69,7 @@ const appendSequencerBatch = async (
} }
const getEncodedCalldata = (batch: AppendSequencerBatchParams): string => { const getEncodedCalldata = (batch: AppendSequencerBatchParams): string => {
const methodId = keccak256( const methodId = APPEND_SEQUENCER_BATCH_METHOD_ID
Buffer.from(APPEND_SEQUENCER_BATCH_METHOD_ID)
).slice(2, 10)
const calldata = encodeAppendSequencerBatch(batch) const calldata = encodeAppendSequencerBatch(batch)
return '0x' + remove0x(methodId) + remove0x(calldata) return '0x' + remove0x(methodId) + remove0x(calldata)
} }
const encodeBatchContext = (context: BatchContext): string => {
return (
encodeHex(context.numSequencedTransactions, 6) +
encodeHex(context.numSubsequentQueueTransactions, 6) +
encodeHex(context.timestamp, 10) +
encodeHex(context.blockNumber, 10)
)
}
...@@ -5,7 +5,7 @@ import { ...@@ -5,7 +5,7 @@ import {
} from '@ethersproject/abstract-provider' } from '@ethersproject/abstract-provider'
import * as ynatm from '@eth-optimism/ynatm' import * as ynatm from '@eth-optimism/ynatm'
interface ResubmissionConfig { export interface ResubmissionConfig {
resubmissionTimeout: number resubmissionTimeout: number
minGasPriceInGwei: number minGasPriceInGwei: number
maxGasPriceInGwei: number maxGasPriceInGwei: number
......
...@@ -27,6 +27,8 @@ import { ...@@ -27,6 +27,8 @@ import {
TX_BATCH_SUBMITTER_LOG_TAG, TX_BATCH_SUBMITTER_LOG_TAG,
STATE_BATCH_SUBMITTER_LOG_TAG, STATE_BATCH_SUBMITTER_LOG_TAG,
BatchSubmitter, BatchSubmitter,
YnatmTransactionSubmitter,
ResubmissionConfig,
} from '../../src' } from '../../src'
import { import {
...@@ -200,8 +202,19 @@ describe('BatchSubmitter', () => { ...@@ -200,8 +202,19 @@ describe('BatchSubmitter', () => {
sinon.restore() sinon.restore()
}) })
const createBatchSubmitter = (timeout: number): TransactionBatchSubmitter => const createBatchSubmitter = (timeout: number): TransactionBatchSubmitter => {
new TransactionBatchSubmitter( const resubmissionConfig: ResubmissionConfig = {
resubmissionTimeout: 100000,
minGasPriceInGwei: MIN_GAS_PRICE_IN_GWEI,
maxGasPriceInGwei: GAS_THRESHOLD_IN_GWEI,
gasRetryIncrement: GAS_RETRY_INCREMENT,
}
const txBatchTxSubmitter = new YnatmTransactionSubmitter(
sequencer,
resubmissionConfig,
1
)
return new TransactionBatchSubmitter(
sequencer, sequencer,
l2Provider as any, l2Provider as any,
MIN_TX_SIZE, MIN_TX_SIZE,
...@@ -212,15 +225,14 @@ describe('BatchSubmitter', () => { ...@@ -212,15 +225,14 @@ describe('BatchSubmitter', () => {
100000, 100000,
AddressManager.address, AddressManager.address,
1, 1,
MIN_GAS_PRICE_IN_GWEI,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI, GAS_THRESHOLD_IN_GWEI,
txBatchTxSubmitter,
1, 1,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }), new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG }),
testMetrics, testMetrics,
false false
) )
}
describe('TransactionBatchSubmitter', () => { describe('TransactionBatchSubmitter', () => {
describe('submitNextBatch', () => { describe('submitNextBatch', () => {
...@@ -375,7 +387,7 @@ describe('BatchSubmitter', () => { ...@@ -375,7 +387,7 @@ describe('BatchSubmitter', () => {
.callsFake(async () => lowGasPriceWei) .callsFake(async () => lowGasPriceWei)
const receipt = await batchSubmitter.submitNextBatch() const receipt = await batchSubmitter.submitNextBatch()
expect(sequencer.getGasPrice).to.have.been.calledOnce expect(sequencer.getGasPrice).to.have.been.calledTwice
expect(receipt).to.not.be.undefined expect(receipt).to.not.be.undefined
}) })
}) })
...@@ -417,6 +429,17 @@ describe('BatchSubmitter', () => { ...@@ -417,6 +429,17 @@ describe('BatchSubmitter', () => {
// submit a batch of transactions to enable state batch submission // submit a batch of transactions to enable state batch submission
await txBatchSubmitter.submitNextBatch() await txBatchSubmitter.submitNextBatch()
const resubmissionConfig: ResubmissionConfig = {
resubmissionTimeout: 100000,
minGasPriceInGwei: MIN_GAS_PRICE_IN_GWEI,
maxGasPriceInGwei: GAS_THRESHOLD_IN_GWEI,
gasRetryIncrement: GAS_RETRY_INCREMENT,
}
const stateBatchTxSubmitter = new YnatmTransactionSubmitter(
sequencer,
resubmissionConfig,
1
)
stateBatchSubmitter = new StateBatchSubmitter( stateBatchSubmitter = new StateBatchSubmitter(
sequencer, sequencer,
l2Provider as any, l2Provider as any,
...@@ -429,10 +452,7 @@ describe('BatchSubmitter', () => { ...@@ -429,10 +452,7 @@ describe('BatchSubmitter', () => {
0, // finalityConfirmations 0, // finalityConfirmations
AddressManager.address, AddressManager.address,
1, 1,
MIN_GAS_PRICE_IN_GWEI, stateBatchTxSubmitter,
MAX_GAS_PRICE_IN_GWEI,
GAS_RETRY_INCREMENT,
GAS_THRESHOLD_IN_GWEI,
1, 1,
new Logger({ name: STATE_BATCH_SUBMITTER_LOG_TAG }), new Logger({ name: STATE_BATCH_SUBMITTER_LOG_TAG }),
testMetrics, testMetrics,
...@@ -473,54 +493,4 @@ describe('Batch Submitter with Ganache', () => { ...@@ -473,54 +493,4 @@ describe('Batch Submitter with Ganache', () => {
after(async () => { after(async () => {
await server.close() await server.close()
}) })
// Unit test for getReceiptWithResubmission function,
// tests for increasing gas price on resubmission
it('should resubmit a transaction if it is not confirmed', async () => {
const gasPrices = []
const numConfirmations = 2
const sendTxFunc = async (gasPrice) => {
// push the retried gasPrice
gasPrices.push(gasPrice)
const tx = signer.sendTransaction({
to: predeploys.OVM_SequencerEntrypoint,
value: 88,
nonce: 0,
gasPrice,
})
const response = await tx
return signer.provider.waitForTransaction(response.hash, numConfirmations)
}
const resubmissionConfig = {
numConfirmations,
resubmissionTimeout: 1_000, // retry every second
minGasPriceInGwei: 0,
maxGasPriceInGwei: 100,
gasRetryIncrement: 5,
}
BatchSubmitter.getReceiptWithResubmission(
sendTxFunc,
resubmissionConfig,
new Logger({ name: TX_BATCH_SUBMITTER_LOG_TAG })
)
// Wait 1.5s for at least 1 retry
await new Promise((r) => setTimeout(r, 1500))
// Iterate through gasPrices to ensure each entry increases from
// the last
const isIncreasing = gasPrices.reduce(
(isInc, gasPrice, i, gP) =>
(isInc && gasPrice > gP[i - 1]) || Number.NEGATIVE_INFINITY,
true
)
expect(gasPrices).to.have.lengthOf.above(1) // retried at least once
expect(isIncreasing).to.be.true
})
}) })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment