-
Notifications
You must be signed in to change notification settings - Fork 91
feat: integrates new lockService in sendRawTransaction #4627
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat/nonce-ordering-with-locks
Are you sure you want to change the base?
Changes from all commits
f8361fb
c28e55a
6529c42
72cae98
2e408db
f9a0c0e
8d11493
204f50b
423b92b
aa6802c
3c54ffd
e5519d0
9647830
ca4f285
f39ecd9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,7 @@ import { Precheck } from '../../../precheck'; | |
| import { ITransactionReceipt, RequestDetails, TypedEvents } from '../../../types'; | ||
| import { CacheService } from '../../cacheService/cacheService'; | ||
| import HAPIService from '../../hapiService/hapiService'; | ||
| import { ICommonService, TransactionPoolService } from '../../index'; | ||
| import { ICommonService, LockService, TransactionPoolService } from '../../index'; | ||
| import { ITransactionService } from './ITransactionService'; | ||
|
|
||
| export class TransactionService implements ITransactionService { | ||
|
|
@@ -46,6 +46,13 @@ export class TransactionService implements ITransactionService { | |
| */ | ||
| private readonly hapiService: HAPIService; | ||
|
|
||
| /** | ||
| * The lock service for managing transaction ordering. | ||
| * @private | ||
| * @readonly | ||
| */ | ||
| private readonly lockService: LockService; | ||
|
|
||
| /** | ||
| * Logger instance for logging messages. | ||
| * @private | ||
|
|
@@ -86,6 +93,7 @@ export class TransactionService implements ITransactionService { | |
| logger: Logger, | ||
| mirrorNodeClient: MirrorNodeClient, | ||
| transactionPoolService: TransactionPoolService, | ||
| lockService: LockService, | ||
| ) { | ||
| this.cacheService = cacheService; | ||
| this.chain = chain; | ||
|
|
@@ -96,6 +104,7 @@ export class TransactionService implements ITransactionService { | |
| this.mirrorNodeClient = mirrorNodeClient; | ||
| this.precheck = new Precheck(mirrorNodeClient, chain, transactionPoolService); | ||
| this.transactionPoolService = transactionPoolService; | ||
| this.lockService = lockService; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -253,36 +262,60 @@ export class TransactionService implements ITransactionService { | |
|
|
||
| const transactionBuffer = Buffer.from(this.prune0x(transaction), 'hex'); | ||
| const parsedTx = Precheck.parseRawTransaction(transaction); | ||
| const networkGasPriceInWeiBars = Utils.addPercentageBufferToGasPrice( | ||
| await this.common.getGasPriceInWeibars(requestDetails), | ||
| ); | ||
| let lockSessionKey: string | undefined; | ||
|
|
||
| // Acquire lock FIRST - before any side effects or async operations | ||
| // This ensures proper nonce ordering for transactions from the same sender | ||
| if (parsedTx.from && ConfigService.get('ENABLE_NONCE_ORDERING')) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just as we did in the transaction pool implementation, would it make sense to abstract this feature-specific flag into its own service class? In this case, |
||
| lockSessionKey = await this.lockService.acquireLock(parsedTx.from); | ||
| } | ||
|
|
||
| await this.validateRawTransaction(parsedTx, networkGasPriceInWeiBars, requestDetails); | ||
| try { | ||
| const networkGasPriceInWeiBars = Utils.addPercentageBufferToGasPrice( | ||
| await this.common.getGasPriceInWeibars(requestDetails), | ||
| ); | ||
|
|
||
| // Save the transaction to the transaction pool before submitting it to the network | ||
| await this.transactionPoolService.saveTransaction(parsedTx.from!, parsedTx); | ||
| await this.validateRawTransaction(parsedTx, networkGasPriceInWeiBars, requestDetails); | ||
|
|
||
| /** | ||
| * Note: If the USE_ASYNC_TX_PROCESSING feature flag is enabled, | ||
| * the transaction hash is calculated and returned immediately after passing all prechecks. | ||
| * All transaction processing logic is then handled asynchronously in the background. | ||
| */ | ||
| const useAsyncTxProcessing = ConfigService.get('USE_ASYNC_TX_PROCESSING'); | ||
| if (useAsyncTxProcessing) { | ||
| this.sendRawTransactionProcessor(transactionBuffer, parsedTx, networkGasPriceInWeiBars, requestDetails); | ||
| return Utils.computeTransactionHash(transactionBuffer); | ||
| } | ||
| // Save the transaction to the transaction pool before submitting it to the network | ||
| await this.transactionPoolService.saveTransaction(parsedTx.from!, parsedTx); | ||
|
|
||
| /** | ||
| * Note: If the USE_ASYNC_TX_PROCESSING feature flag is disabled, | ||
| * wait for all transaction processing logic to complete before returning the transaction hash. | ||
| */ | ||
| return await this.sendRawTransactionProcessor( | ||
| transactionBuffer, | ||
| parsedTx, | ||
| networkGasPriceInWeiBars, | ||
| requestDetails, | ||
| ); | ||
| /** | ||
| * Note: If the USE_ASYNC_TX_PROCESSING feature flag is enabled, | ||
| * the transaction hash is calculated and returned immediately after passing all prechecks. | ||
| * All transaction processing logic is then handled asynchronously in the background. | ||
| */ | ||
| const useAsyncTxProcessing = ConfigService.get('USE_ASYNC_TX_PROCESSING'); | ||
| if (useAsyncTxProcessing) { | ||
| // Fire and forget - lock will be released after consensus submission | ||
| this.sendRawTransactionProcessor( | ||
| transactionBuffer, | ||
| parsedTx, | ||
| networkGasPriceInWeiBars, | ||
| requestDetails, | ||
| lockSessionKey, | ||
| ); | ||
| return Utils.computeTransactionHash(transactionBuffer); | ||
| } | ||
|
|
||
| /** | ||
| * Note: If the USE_ASYNC_TX_PROCESSING feature flag is disabled, | ||
| * wait for all transaction processing logic to complete before returning the transaction hash. | ||
| */ | ||
| return await this.sendRawTransactionProcessor( | ||
| transactionBuffer, | ||
| parsedTx, | ||
| networkGasPriceInWeiBars, | ||
| requestDetails, | ||
| lockSessionKey, | ||
| ); | ||
| } catch (error) { | ||
| // Release lock on any error during validation or prechecks | ||
| if (lockSessionKey) { | ||
| await this.lockService.releaseLock(parsedTx.from!, lockSessionKey); | ||
| } | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -472,13 +505,15 @@ export class TransactionService implements ITransactionService { | |
| * @param {EthersTransaction} parsedTx - The parsed Ethereum transaction object. | ||
| * @param {number} networkGasPriceInWeiBars - The current network gas price in wei bars. | ||
| * @param {RequestDetails} requestDetails - Details of the request for logging and tracking purposes. | ||
| * @param {string | null} lockSessionKey - The session key for the acquired lock, null if no lock was acquired. | ||
| * @returns {Promise<string | JsonRpcError>} A promise that resolves to the transaction hash if successful, or a JsonRpcError if an error occurs. | ||
| */ | ||
| async sendRawTransactionProcessor( | ||
| transactionBuffer: Buffer, | ||
| parsedTx: EthersTransaction, | ||
| networkGasPriceInWeiBars: number, | ||
| requestDetails: RequestDetails, | ||
| lockSessionKey?: string, | ||
| ): Promise<string | JsonRpcError> { | ||
| let sendRawTransactionError: any; | ||
|
|
||
|
|
@@ -493,8 +528,12 @@ export class TransactionService implements ITransactionService { | |
| originalCallerAddress, | ||
| networkGasPriceInWeiBars, | ||
| requestDetails, | ||
| lockSessionKey, | ||
| ); | ||
|
|
||
| if (lockSessionKey) { | ||
| await this.lockService.releaseLock(originalCallerAddress.toLowerCase(), lockSessionKey); | ||
| } | ||
| // Remove the transaction from the transaction pool after submission | ||
| await this.transactionPoolService.removeTransaction(originalCallerAddress, parsedTx.serialized); | ||
|
|
||
|
|
@@ -649,6 +688,7 @@ export class TransactionService implements ITransactionService { | |
| originalCallerAddress: string, | ||
| networkGasPriceInWeiBars: number, | ||
| requestDetails: RequestDetails, | ||
| lockSessionKey?: string, | ||
| ): Promise<{ | ||
| txSubmitted: boolean; | ||
| submittedTransactionId: string; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the comment above this line is misleading.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed