Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 401
fix: send aggregated response bump#1440
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
Merged
Uh oh!
There was an error while loading. Please reload this page.
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
3e5f215
chore: tracing to send aggregated response
MarcosNicolau c30b3f6
refactor: big int mul and logs
uri-99 f1dee66
chore: logs on RespondToTask fail
uri-99 545c003
fix: nil dereference on aggregator retries
Oppen 6198072
Merge branch 'staging' into fix/tx_overwrite
Oppen dd2e35d
Merge branch 'fix/tx_overwrite' into fix-send-aggregated-response-bump
uri-99 028466d
fix: defer recover in checkAggAndBatcherHaveEnoughBalance
uri-99 65c5ef6
chore: print err
uri-99 187f95e
chore: print err
uri-99 fe5a70d
fix: not dereference nil
uri-99 6840ae0
fix: quick fix for aggregator bump fee
MarcosNicolau 2607c64
fix: add wait for receipt timeout
MarcosNicolau fbdb7ee
feat: save sent txs and check their receipts before sending a new one
MarcosNicolau 92ff1a1
fix: re-add receipt for traces
MarcosNicolau 02e9c82
fix: merkle root logging
MarcosNicolau 56ca050
refactor: wait timeout of 500ms
MarcosNicolau e432d4e
chore: better comments on yaml
MarcosNicolau 622277f
fix: receipt check in sending agg response
MarcosNicolau 3880afb
feat: better defer recovers
uri-99 e34482f
Merge staging
MauroToscano f5d6516
Add missing whitespace to aggregator yaml
MauroToscano 9999efc
Update comment in aggregator yaml
MauroToscano b55adb7
Typo
MauroToscano 61f4a12
refactor: function docs and address reviews
MarcosNicolau c75920d
chore: docs
MarcosNicolau File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2,6 +2,7 @@ package chainio | ||
| import ( | ||
| "context" | ||
| "encoding/hex" | ||
| "fmt" | ||
| "math/big" | ||
| "time" | ||
| @@ -75,9 +76,19 @@ func NewAvsWriterFromConfig(baseConfig *config.BaseConfig, ecdsaConfig *config.E | ||
| }, nil | ||
| } | ||
| // Sends AggregatedResponse and waits for the receipt for three blocks, if not received | ||
| // it will try again bumping the last tx gas price based on `CalculateGasPriceBump` | ||
| // This process happens indefinitely until the transaction is included. | ||
| // SendAggregatedResponse continuously sends a RespondToTask transaction until it is included in the blockchain. | ||
| // This function: | ||
| // 1. Simulates the transaction to calculate the nonce and initial gas price without broadcasting it. | ||
| // 2. Repeatedly attempts to send the transaction, bumping the gas price after `timeToWaitBeforeBump` has passed. | ||
| // 3. Monitors for the receipt of previously sent transactions or checks the state to confirm if the response | ||
| // has already been processed (e.g., by another transaction). | ||
| // 4. Validates that the aggregator and batcher have sufficient balance to cover transaction costs before sending. | ||
| // | ||
| // Returns: | ||
| // - A transaction receipt if the transaction is successfully included in the blockchain. | ||
| // - If no receipt is found, but the batch state indicates the response has already been processed, it exits | ||
| // without an error (returning `nil, nil`). | ||
| // - An error if the process encounters a fatal issue (e.g., permanent failure in verifying balances or state). | ||
| func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMerkleRoot [32]byte, senderAddress [20]byte, nonSignerStakesAndSignature servicemanager.IBLSSignatureCheckerNonSignerStakesAndSignature, gasBumpPercentage uint, gasBumpIncrementalPercentage uint, timeToWaitBeforeBump time.Duration, onGasPriceBumped func(*big.Int)) (*types.Receipt, error) { | ||
| txOpts := *w.Signer.GetTxOpts() | ||
| txOpts.NoSend = true // simulate the transaction | ||
| @@ -93,39 +104,73 @@ func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMe | ||
| txOpts.NoSend = false | ||
| i := 0 | ||
| var sentTxs []*types.Transaction | ||
| batchMerkleRootHashString := hex.EncodeToString(batchMerkleRoot[:]) | ||
| respondToTaskV2Func := func() (*types.Receipt, error) { | ||
| gasPrice, err := utils.GetGasPriceRetryable(w.Client, w.ClientFallback) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| bumpedGasPrice := utils.CalculateGasPriceBumpBasedOnRetry(gasPrice, gasBumpPercentage, gasBumpIncrementalPercentage, i) | ||
| // new bumped gas price must be higher than the last one (this should hardly ever happen though) | ||
| if bumpedGasPrice.Cmp(txOpts.GasPrice) > 0 { | ||
| txOpts.GasPrice = bumpedGasPrice | ||
| previousTxGasPrice := txOpts.GasPrice | ||
| // in order to avoid replacement transaction underpriced | ||
| // the bumped gas price has to be at least 10% higher than the previous one. | ||
| minimumGasPriceBump := utils.CalculateGasPriceBumpBasedOnRetry(previousTxGasPrice, 10, 0, 0) | ||
| suggestedBumpedGasPrice := utils.CalculateGasPriceBumpBasedOnRetry( | ||
| gasPrice, | ||
| gasBumpPercentage, | ||
| gasBumpIncrementalPercentage, | ||
| i, | ||
| ) | ||
| // check the new gas price is sufficiently bumped. | ||
| // if the suggested bump does not meet the minimum threshold, use a fallback calculation to slightly increment the previous gas price. | ||
| if suggestedBumpedGasPrice.Cmp(minimumGasPriceBump) > 0 { | ||
| txOpts.GasPrice = suggestedBumpedGasPrice | ||
| } else { | ||
| // bump the last tx gas price a little by `gasBumpIncrementalPercentage` to replace it. | ||
| txOpts.GasPrice = utils.CalculateGasPriceBumpBasedOnRetry(txOpts.GasPrice, gasBumpIncrementalPercentage, 0, 0) | ||
| txOpts.GasPrice = minimumGasPriceBump | ||
| } | ||
| if i > 0 { | ||
| w.logger.Infof("Trying to get old sent transaction receipt before sending a new transaction", "merkle root", batchMerkleRootHashString) | ||
| for _, tx := range sentTxs { | ||
| receipt, _ := w.Client.TransactionReceipt(context.Background(), tx.Hash()) | ||
| if receipt == nil { | ||
| receipt, _ = w.ClientFallback.TransactionReceipt(context.Background(), tx.Hash()) | ||
| if receipt != nil { | ||
| w.checkIfAggregatorHadToPaidForBatcher(tx, batchIdentifierHash) | ||
| return receipt, nil | ||
| } | ||
| } | ||
| } | ||
| w.logger.Infof("Receipts for old transactions not found, will check if the batch state has been responded", "merkle root", batchMerkleRootHashString) | ||
| batchState, _ := w.BatchesStateRetryable(&bind.CallOpts{}, batchIdentifierHash) | ||
| if batchState.Responded { | ||
| w.logger.Infof("Batch state has been already responded", "merkle root", batchMerkleRootHashString) | ||
| return nil, nil | ||
| } | ||
| w.logger.Infof("Batch state has not been responded yet, will send a new tx", "merkle root", batchMerkleRootHashString) | ||
| onGasPriceBumped(txOpts.GasPrice) | ||
| } | ||
| // We compare both Aggregator funds and Batcher balance in Aligned against respondToTaskFeeLimit | ||
| // Both are required to have some balance, more details inside the function | ||
| err = w.checkAggAndBatcherHaveEnoughBalance(simTx, txOpts, batchIdentifierHash, senderAddress) | ||
| if err != nil { | ||
| w.logger.Errorf("Permanent error when checking aggregator and batcher balances, err %v", err, "merkle root", batchMerkleRootHashString) | ||
| return nil, retry.PermanentError{Inner: err} | ||
| } | ||
| w.logger.Infof("Sending RespondToTask transaction with a gas price of %v", txOpts.GasPrice) | ||
| w.logger.Infof("Sending RespondToTask transaction with a gas price of %v", txOpts.GasPrice, "merkle root", batchMerkleRootHashString) | ||
| realTx, err := w.RespondToTaskV2Retryable(&txOpts, batchMerkleRoot, senderAddress, nonSignerStakesAndSignature) | ||
| if err != nil { | ||
| w.logger.Errorf("Respond to task transaction err, %v", err, "merkle root", batchMerkleRootHashString) | ||
| return nil, err | ||
| } | ||
| sentTxs = append(sentTxs, realTx) | ||
| w.logger.Infof("Transaction sent, waiting for receipt", "merkle root", batchMerkleRootHashString) | ||
| receipt, err := utils.WaitForTransactionReceiptRetryable(w.Client, w.ClientFallback, realTx.Hash(), timeToWaitBeforeBump) | ||
| if receipt != nil { | ||
| w.checkIfAggregatorHadToPaidForBatcher(realTx, batchIdentifierHash) | ||
| @@ -136,14 +181,18 @@ func (w *AvsWriter) SendAggregatedResponse(batchIdentifierHash [32]byte, batchMe | ||
| // we increment the i here to add an incremental percentage to increase the odds of being included in the next blocks | ||
| i++ | ||
| w.logger.Infof("RespondToTask receipt waiting timeout has passed, will try again...") | ||
| w.logger.Infof("RespondToTask receipt waiting timeout has passed, will try again...", "merkle_root", batchMerkleRootHashString) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return nil, fmt.Errorf("transaction failed") | ||
| } | ||
| return retry.RetryWithData(respondToTaskV2Func, retry.MinDelay, retry.RetryFactor, 0, retry.MaxInterval, 0) | ||
| // This just retries the bump of a fee in case of a timeout | ||
| // The wait is done before on WaitForTransactionReceiptRetryable, and all the functions are retriable, | ||
| // so this retry doesn't need to wait more time | ||
| maxInterval := time.Millisecond * 500 | ||
MarcosNicolau marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| return retry.RetryWithData(respondToTaskV2Func, retry.MinDelay, retry.RetryFactor, 0, maxInterval, 0) | ||
| } | ||
| // Calculates the transaction cost from the receipt and compares it with the batcher respondToTaskFeeLimit | ||
| @@ -171,7 +220,9 @@ func (w *AvsWriter) checkIfAggregatorHadToPaidForBatcher(tx *types.Transaction, | ||
| func (w *AvsWriter) checkAggAndBatcherHaveEnoughBalance(tx *types.Transaction, txOpts bind.TransactOpts, batchIdentifierHash [32]byte, senderAddress [20]byte) error { | ||
| w.logger.Info("Checking if aggregator and batcher have enough balance for the transaction") | ||
| aggregatorAddress := txOpts.From | ||
| txCost := new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), txOpts.GasPrice) | ||
| txGasAsBigInt := new(big.Int).SetUint64(tx.Gas()) | ||
| txGasPrice := txOpts.GasPrice | ||
| txCost := new(big.Int).Mul(txGasAsBigInt, txGasPrice) | ||
| w.logger.Info("Transaction cost", "cost", txCost) | ||
| batchState, err := w.BatchesStateRetryable(&bind.CallOpts{}, batchIdentifierHash) | ||
| @@ -183,8 +234,8 @@ func (w *AvsWriter) checkAggAndBatcherHaveEnoughBalance(tx *types.Transaction, t | ||
| respondToTaskFeeLimit := batchState.RespondToTaskFeeLimit | ||
| w.logger.Info("Checking balance against Batch RespondToTaskFeeLimit", "RespondToTaskFeeLimit", respondToTaskFeeLimit) | ||
| // Note: we compare both Aggregator funds and Batcher balance in Aligned against respondToTaskFeeLimit | ||
| // Batcher will pay up to respondToTaskFeeLimit, for this he needs that amount of funds in Aligned | ||
| // Aggregator will pay any extra cost, for this he needs at least respondToTaskFeeLimit in his balance | ||
| // Batcher will pay up to respondToTaskFeeLimit, for this he needs that amount of funds in Aligned | ||
| // Aggregator will pay any extra cost, for this he needs at least respondToTaskFeeLimit in his balance | ||
| return w.compareBalances(respondToTaskFeeLimit, aggregatorAddress, senderAddress) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.