-
Notifications
You must be signed in to change notification settings - Fork 111
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
feat: support for Bitcoin RBF (Replace-By-Fee) and CPFP (Child-Pays-for-Parent) #3306
base: develop
Are you sure you want to change the base?
Conversation
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the 📝 WalkthroughWalkthroughThis pull request introduces comprehensive support for Bitcoin Replace-By-Fee (RBF) functionality across the ZetaChain node implementation. The changes span multiple packages and modules, focusing on enhancing Bitcoin transaction handling, fee management, and end-to-end testing capabilities. The implementation includes new methods for fee bumping, transaction signing, mempool monitoring, and robust error handling for RBF scenarios. Changes
Sequence DiagramsequenceDiagram
participant Observer
participant Signer
participant RPCClient
participant Mempool
participant ZetaCore
Observer->>Mempool: Monitor Stuck Transaction
Mempool-->>Observer: Identify Stuck Transaction
Observer->>Signer: Request Fee Bump
Signer->>RPCClient: Fetch Current Fee Rates
RPCClient-->>Signer: Return Fee Rates
Signer->>Mempool: Broadcast Replacement Transaction
Mempool-->>ZetaCore: Update Transaction Status
Possibly Related PRs
Suggested Labels
Suggested Reviewers
The implementation demonstrates a robust approach to implementing Replace-By-Fee functionality, with comprehensive test coverage and careful consideration of various edge cases in Bitcoin transaction management. Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
|
GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
---|---|---|---|---|---|
14567965 | Triggered | Generic Password | 7cd37e9 | cmd/zetaclientd/start.go | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
!!!WARNING!!! Be very careful about using Only suppress a single rule (or a specific set of rules) within a section of code, while continuing to scan for other problems. To do this, you can list the rule(s) to be suppressed within the #nosec annotation, e.g: /* #nosec G401 */ or //#nosec G201 G202 G203 Pay extra attention to the way |
Context #3279 (comment) |
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.
Actionable comments posted: 29
🧹 Nitpick comments (56)
zetaclient/chains/bitcoin/signer/sign_test.go (3)
24-46
: Consider extracting setup into helper functions.The test setup could be more maintainable by extracting the TSS and receiver address setup into helper functions.
+func setupTestSigner(t *testing.T) *signer.Signer { + return signer.NewSigner( + chains.BitcoinMainnet, + mocks.NewBTCRPCClient(t), + mocks.NewTSS(t).FakePubKey(testutils.TSSPubKeyMainnet), + base.DefaultLogger(), + ) +} + +func setupTestAddresses(t *testing.T, s *signer.Signer) (btcutil.Address, []byte, btcutil.Address, []byte) { + tssAddr, err := s.TSS().PubKey().AddressBTC(chains.BitcoinMainnet.ChainId) + require.NoError(t, err) + tssScript, err := txscript.PayToAddrScript(tssAddr) + require.NoError(t, err) + + receiver := "bc1qaxf82vyzy8y80v000e7t64gpten7gawewzu42y" + to, err := chains.DecodeBtcAddress(receiver, chains.BitcoinMainnet.ChainId) + require.NoError(t, err) + toScript, err := txscript.PayToAddrScript(to) + require.NoError(t, err) + + return tssAddr, tssScript, to, toScript +}
47-171
: Enhance test table structure for better maintainability.Consider these improvements to the test table:
- Use constants for repeated values.
- Group related test cases using subtests.
- Add test case documentation.
+const ( + defaultAmount = 0.2 + defaultNonceMark = 10000 + defaultFees = 2000 +) + +// testCase represents a test case for AddWithdrawTxOutputs +type testCase struct { + name string + tx *wire.MsgTx + to btcutil.Address + total float64 + amount float64 + nonceMark int64 + fees int64 + cancelTx bool + fail bool + message string + txout []*wire.TxOut +} + +// successCases returns test cases for successful scenarios +func successCases(to btcutil.Address, tssScript, toScript []byte) []testCase { + return []testCase{ + { + name: "should add outputs successfully", + // ... existing test case + }, + // ... other success cases + } +}
173-188
: Enable parallel test execution for better performance.Consider running the test cases in parallel since they are independent.
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() err := signer.AddWithdrawTxOutputs(tt.tx, tt.to, tt.total, tt.amount, tt.nonceMark, tt.fees, tt.cancelTx)
pkg/math/integer.go (2)
5-7
: Enhance function documentation for better clarity.While the examples are helpful, the documentation could be more comprehensive. Consider adding:
- Parameter descriptions including valid ranges
- Return value description
- Edge case behavior documentation
- Named variables in examples for better readability
-// IncreaseIntByPercent is a function that increases integer by a percentage. -// Example1: IncreaseIntByPercent(10, 15, true) = 10 * 1.15 = 12 -// Example2: IncreaseIntByPercent(10, 15, false) = 10 + 10 * 0.15 = 11 +// IncreaseIntByPercent increases an integer value by a specified percentage. +// Parameters: +// - value: The base integer value to increase +// - percent: The percentage to increase by (0-MaxUint32) +// - round: If true, rounds the increase to nearest integer; if false, truncates +// Returns: +// The increased value after applying the percentage +// Examples: +// baseValue := int64(10) +// withRounding := IncreaseIntByPercent(baseValue, 15, true) // Returns 12 (10 * 1.15 rounded) +// withoutRounding := IncreaseIntByPercent(baseValue, 15, false) // Returns 11 (10 + 10 * 0.15 truncated)
12-15
: Well-implemented optimization for multiples of 100.Good use of integer arithmetic for better performance when handling multiples of 100. Consider enhancing the comment to explain the performance benefit.
- // optimization: a simple multiplication + // Optimization: Using integer multiplication for multiples of 100 + // avoids floating-point operations for better performancezetaclient/chains/bitcoin/signer/signer.go (8)
158-158
: Correct the error message for clarityThe error message lacks the word "to," making it grammatically incorrect. Updating it enhances readability and professionalism.
Apply this diff to correct the error message:
-logger.Error().Err(err).Msgf("failed get bitcoin network info") +logger.Error().Err(err).Msgf("failed to get bitcoin network info")
181-181
: Use past tense in success log messagesThe log message should use "succeeded" instead of "succeed" to accurately reflect the completion of the action.
Apply this diff to correct the log message:
-logger.Info().Str(logs.FieldTx, signedTx.TxID()).Msg("SignRBFTx succeed") +logger.Info().Str(logs.FieldTx, signedTx.TxID()).Msg("SignRBFTx succeeded")
196-196
: Use past tense in success log messagesSimilarly, update the log message to use "succeeded" for grammatical correctness.
Apply this diff to correct the log message:
-logger.Info().Str(logs.FieldTx, signedTx.TxID()).Msg("SignWithdrawTx succeed") +logger.Info().Str(logs.FieldTx, signedTx.TxID()).Msg("SignWithdrawTx succeeded")
223-223
: Fix typo in documentation commentThere is a typo in the comment. Correcting it improves code documentation quality.
Apply this diff to fix the typo:
-// try broacasting tx with increasing backoff (1s, 2s, 4s, 8s, 16s) in case of RPC error +// try broadcasting tx with increasing backoff (1s, 2s, 4s, 8s, 16s) in case of RPC error
143-146
: Handle potential errors when retrieving the signer addressCurrently, if an error occurs while getting the signer address, it's silently ignored. To prevent unforeseen issues, handle the error explicitly.
Apply this diff to handle the error:
signerAddress, err := zetacoreClient.GetKeys().GetAddress() -if err == nil { +if err != nil { + logger.Error().Err(err).Msg("Failed to get signer address") +} else { lf["signer"] = signerAddress.String() }
225-235
: Attempt initial broadcast without delayTo reduce latency, consider attempting to broadcast the transaction immediately before entering the retry loop with backoff.
Apply this diff to modify the broadcasting logic:
backOff := broadcastBackoff +// Attempt initial broadcast without delay +err := signer.Broadcast(tx) +if err == nil { + logger.Info().Fields(lf).Msgf("broadcasted Bitcoin outbound successfully") + // Proceed with successful flow + // Save transaction, add tracker, etc. + // ... + return +} +logger.Warn().Err(err).Fields(lf).Msg("broadcasting Bitcoin outbound, retrying with backoff") // Retry broadcasting with backoff for i := 0; i < broadcastRetries; i++ { time.Sleep(backOff)
225-235
: Adjust retry counter for accurate loggingThe retry counter
i
starts at zero, which may be misleading in logs. Start counting retries from one for better clarity.Apply this diff to adjust the retry counter:
for i := 0; i < broadcastRetries; i++ { time.Sleep(backOff) // broadcast tx err := signer.Broadcast(tx) if err != nil { - logger.Warn().Err(err).Fields(lf).Msgf("broadcasting Bitcoin outbound, retry %d", i) + logger.Warn().Err(err).Fields(lf).Msgf("broadcasting Bitcoin outbound, retry %d", i+1) backOff *= 2 continue }
250-253
: Log when outbound inclusion failsCurrently, if the outbound transaction isn't included, there's no log indicating the failure. Adding a log enhances observability.
Apply this diff to log the failure:
_, included := ob.TryIncludeOutbound(ctx, cctx, txHash) if included { logger.Info().Fields(lf).Msgf("included newly broadcasted Bitcoin outbound") +} else { + logger.Warn().Fields(lf).Msgf("failed to include newly broadcasted Bitcoin outbound") }e2e/e2etests/test_bitcoin_withdraw_rbf.go (1)
20-21
: Correct the typo and clarify the comment for better understanding.The comment contains a typo and is unclear. "Chainging" should be "Changing", and the sentence structure needs adjustment for clarity.
Please apply the following change to improve the comment:
-// Chainging the 'minTxConfirmations' to 1 to not include Bitcoin a pending tx. +// Changing the 'minTxConfirmations' to 1 to include pending Bitcoin transactions.zetaclient/chains/bitcoin/signer/sign_rbf.go (1)
58-58
: Ensure consistent error handling by usingerrors.Wrapf
.For consistency and better error stack traces, consider using
errors.Wrapf
instead offmt.Errorf
, as other parts of the code utilize theerrors
package for wrapping errors.Update the error handling as follows:
-return nil, fmt.Errorf("invalid fee rate %s", params.GasPriorityFee) +return nil, errors.Wrapf(err, "invalid fee rate %s", params.GasPriorityFee)zetaclient/chains/bitcoin/observer/gas_price.go (3)
16-47
: Add unit tests for theWatchGasPrice
function to improve reliability.The
WatchGasPrice
function introduces critical functionality that is currently not covered by tests. Adding unit tests will ensure its correctness and prevent future regressions.🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 16-21: zetaclient/chains/bitcoin/observer/gas_price.go#L16-L21
Added lines #L16 - L21 were not covered by tests
[warning] 24-36: zetaclient/chains/bitcoin/observer/gas_price.go#L24-L36
Added lines #L24 - L36 were not covered by tests
[warning] 38-45: zetaclient/chains/bitcoin/observer/gas_price.go#L38-L45
Added lines #L38 - L45 were not covered by tests
51-89
: Increase test coverage for thePostGasPrice
function.The
PostGasPrice
function is essential for accurate gas price reporting but lacks test coverage. Implementing tests will enhance confidence in its operation and handle edge cases effectively.🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 51-70: zetaclient/chains/bitcoin/observer/gas_price.go#L51-L70
Added lines #L51 - L70 were not covered by tests
[warning] 74-77: zetaclient/chains/bitcoin/observer/gas_price.go#L74-L77
Added lines #L74 - L77 were not covered by tests
[warning] 80-87: zetaclient/chains/bitcoin/observer/gas_price.go#L80-L87
Added lines #L80 - L87 were not covered by tests
[warning] 89-89: zetaclient/chains/bitcoin/observer/gas_price.go#L89
Added line #L89 was not covered by tests
93-106
: Handle unsupported network types more gracefully.In the
specialHandleFeeRate
function, consider handling additional network types or providing a clearer error message without the leading space. This will improve the function's robustness and clarity.Apply this diff to adjust the error message:
return 0, fmt.Errorf(" unsupported bitcoin network type %d", ob.Chain().NetworkType) +return 0, fmt.Errorf("unsupported bitcoin network type %d", ob.Chain().NetworkType)
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 93-104: zetaclient/chains/bitcoin/observer/gas_price.go#L93-L104
Added lines #L93 - L104 were not covered by testsx/crosschain/keeper/abci.go (1)
63-64
: Improve test coverage for new code pathsThe newly added lines at 63-64, where
updateFunc
is assigned when found, are not covered by unit tests. Enhancing test coverage for this conditional logic is important to ensure that the correct gas price updater functions are selected and executed for different chain types. This will increase code reliability and help prevent future regressions.🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 63-64: x/crosschain/keeper/abci.go#L63-L64
Added lines #L63 - L64 were not covered by testszetaclient/chains/bitcoin/observer/mempool.go (2)
55-70
: Add unit tests forWatchMempoolTxs
functionalityThe
WatchMempoolTxs
method introduces critical functionality for monitoring pending outbound transactions in the Bitcoin mempool. It is essential to have unit tests covering this method to ensure its reliability and correctness.Consider adding unit tests that simulate various scenarios, such as transactions becoming stuck, to verify that the method behaves as expected.
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 55-61: zetaclient/chains/bitcoin/observer/mempool.go#L55-L61
Added lines #L55 - L61 were not covered by tests
[warning] 64-70: zetaclient/chains/bitcoin/observer/mempool.go#L64-L70
Added lines #L64 - L70 were not covered by tests
207-221
: Consolidate configuration forPendingTxFeeBumpWaitBlocks
The functions
GetStuckTxChecker
andGetFeeBumpWaitBlocks
handle chain-specific logic based on thechainID
. Consider refactoring these configurations into a centralized location or configuration file for easier maintenance and scalability.zetaclient/chains/bitcoin/observer/observer.go (2)
172-174
: Add tests for theWatchMempoolTxs
background workerThe addition of the
WatchMempoolTxs
background worker is crucial for monitoring mempool transactions. It is important to include tests that validate its integration and interaction with other components.Implement unit tests or integration tests to ensure the new worker operates correctly under various conditions.
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 172-174: zetaclient/chains/bitcoin/observer/observer.go#L172-L174
Added lines #L172 - L174 were not covered by tests
254-273
: Ensure thread-safe updates inSetLastStuckOutbound
In
SetLastStuckOutbound
, the method updateslastStuckTx
while holding a lock. However, the logging statements inside the lock could potentially introduce latency.Minimize the locked section to only the assignment, and perform logging outside the lock to improve concurrency.
- ob.Mu().Lock() - defer ob.Mu().Unlock() - // logging inside lock - ob.lastStuckTx = stuckTx + ob.Mu().Lock() + ob.lastStuckTx = stuckTx + ob.Mu().Unlock() + // logging outside lockzetaclient/chains/bitcoin/rpc/rpc.go (1)
175-177
: Refine the upper bound check for fee rate validationThe current check compares
*feeResult.FeeRate
tomaxBTCSupply
, which represents the total supply of Bitcoin (21000000.0
). Fee rates are typically much smaller values and unlikely to approach this limit. UsingmaxBTCSupply
as an upper bound may not be appropriate and could mask unrealistic high fee rates. Consider defining a more realistic upper limit based on expected maximum fee rates to ensure accurate validation without false negatives.zetaclient/chains/bitcoin/rpc/rpc_rbf_live_test.go (1)
99-102
: Remove commented-out code to improve code clarityThe code between lines 99 and 102 is commented out, which may lead to confusion and reduce code readability. Remove the unused code to maintain a clean codebase.
Apply this diff to remove the commented code:
// STEP 2 - // build and send tx2 (child of tx1) - // nonceMark += 1 - // txHash2 := buildAndSendRBFTx(t, client, privKey, txHash1, sender, to, amount, nonceMark, feeRate, bumpFeeReserved) - // fmt.Printf("sent tx2: %s\n", txHash2)x/observer/types/crosschain_flags.go (1)
8-10
: Consider documenting the rationale for the 10-minute interval.While the
RetryInterval
of 10 minutes seems reasonable, it would be beneficial to document why this specific duration was chosen, especially in the context of Bitcoin transaction fee dynamics.e2e/utils/bitcoin.go (2)
13-32
: Enhance error messages for better debugging.Consider adding more descriptive error messages to the assertions to help identify which specific condition failed:
- require.Negative(t, txResult.Confirmations) + require.Negative(t, txResult.Confirmations, "transaction should have negative confirmations") - require.Error(t, err) - require.Nil(t, entry) + require.Error(t, err, "transaction should not be in mempool") + require.Nil(t, entry, "mempool entry should be nil") - require.Error(t, err) - require.Nil(t, rawTx) + require.Error(t, err, "transaction should not exist in blockchain") + require.Nil(t, rawTx, "raw transaction should be nil")
34-48
: Add return value documentation and enhance error messages.The function would benefit from:
- Documentation of the return value
- More descriptive error messages in assertions
-// MustHaveMinedTx ensures the given tx has been mined +// MustHaveMinedTx ensures the given tx has been mined and returns the detailed transaction information. +// It panics if the transaction is not mined or cannot be retrieved. func MustHaveMinedTx(ctx context.Context, client *rpcclient.Client, txHash *chainhash.Hash) *btcjson.TxRawResult { t := TestingFromContext(ctx) // positive confirmations txResult, err := client.GetTransaction(txHash) - require.NoError(t, err) - require.Positive(t, txResult.Confirmations) + require.NoError(t, err, "failed to get transaction") + require.Positive(t, txResult.Confirmations, "transaction should have positive confirmations") // tx exists in blockchain rawResult, err := client.GetRawTransactionVerbose(txHash) - require.NoError(t, err) + require.NoError(t, err, "failed to get raw transaction")zetaclient/chains/bitcoin/rpc/rpc_test.go (2)
14-59
: Extract magic numbers as named constants.Consider defining constants for commonly used values to improve maintainability:
+const ( + normalFeeRate = 0.0001 + negativeFeeRate = -0.0001 + maxSupplyFeeRate = 21000000 + expectedNormalRate = 10 + expectedRegnetRate = 1 +) func Test_GetEstimatedFeeRate(t *testing.T) { tests := []struct {
61-91
: Simplify switch statement using if-else.The switch statement with a single condition could be simplified:
- switch { - case tt.rpcError: + if tt.rpcError { client.On("EstimateSmartFee", mock.Anything, mock.Anything).Return(nil, errors.New("error")) - case tt.resultError: + } else if tt.resultError { client.On("EstimateSmartFee", mock.Anything, mock.Anything).Return(&btcjson.EstimateSmartFeeResult{ Errors: []string{"error"}, }, nil) - default: + } else { client.On("EstimateSmartFee", mock.Anything, mock.Anything). Maybe(). Return(&btcjson.EstimateSmartFeeResult{ Errors: nil, FeeRate: &tt.rate, }, nil) - } + }e2e/e2etests/test_bitcoin_deposit_and_withdraw_with_dust.go (1)
16-22
: Improve documentation for the WaitGroup usage.The documentation should explain why synchronization is needed:
-// wgDeposit is a wait group for deposit runner to finish +// wgDepositRunner coordinates the completion of Bitcoin deposit runners across E2E tests. +// It ensures that all deposit-related operations are completed before the test suite ends, +// preventing potential race conditions or resource leaks. var wgDepositRunner sync.WaitGroup func init() { - // there is one single deposit runner for Bitcoin E2E tests + // Initialize with count 1 as there is exactly one deposit runner for Bitcoin E2E tests wgDepositRunner.Add(1) }zetaclient/chains/bitcoin/observer/db.go (1)
42-43
: Improve security comment documentation.The "#nosec G115" comment should include a brief explanation of why this case is safe to ignore.
- // #nosec G115 always positive + // #nosec G115 blockNumber from Bitcoin RPC is guaranteed to be positivezetaclient/chains/bitcoin/observer/db_test.go (2)
15-35
: Add edge cases to SaveBroadcastedTx tests.Consider adding test cases for:
- Empty transaction hash
- Zero nonce
- Database connection failures
90-116
: Add negative test case for LoadBroadcastedTxMap.Consider adding a test case for database read failures to ensure proper error handling.
zetaclient/chains/bitcoin/signer/sign_rbf_test.go (2)
21-28
: Add documentation for the test transaction reference.Consider adding a comment explaining why this specific transaction was chosen as a test case and what aspects it validates.
173-228
: Consider extracting test setup logic.The test setup logic, particularly the mock configuration, could be moved to a separate helper function to improve readability.
+func setupTestMocks(s *testSuite, tt *testCase) { + // mock cctx rate + tt.cctx.GetCurrentOutboundParam().GasPriorityFee = tt.cctxRate + + // mock RPC live fee rate setup + setupFeeMocks(s, tt) + + // mock RPC transactions setup + setupTransactionMocks(s, tt) +} + func Test_SignRBFTx(t *testing.T) { // ... existing setup code ... for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := newTestSuite(t, tt.chain) - // mock setup code + setupTestMocks(s, &tt)zetaclient/chains/bitcoin/signer/outbound_data_test.go (2)
95-141
: Consider adding more specific error messages for better debugging.While the error cases are well-covered, the error messages could be more specific to help with debugging.
Apply this diff to enhance error messages:
- errMsg: "cctx is nil", + errMsg: "invalid input: cctx is nil", - errMsg: "can only send gas token to a Bitcoin network", + errMsg: "invalid coin type: can only send gas token to a Bitcoin network", - errMsg: "cannot convert gas price", + errMsg: "invalid gas price format: cannot convert to numeric value", - errMsg: "cannot decode receiver address", + errMsg: "invalid receiver address format: cannot decode Bitcoin address", - errMsg: "unsupported receiver address", + errMsg: "unsupported receiver address type: expected P2PKH or P2WPKH",
142-194
: Consider adding test cases for boundary conditions.The test cases for restricted CCTX and dust amount are good, but consider adding tests for boundary conditions.
Add test cases for:
- Amount exactly equal to dust threshold
- Maximum allowed amount
- Edge cases for gas prices (0, maximum allowed)
zetaclient/chains/interfaces/interfaces.go (1)
167-167
: Consider adding documentation for the new method.The new
GetMempoolEntry
method is well-placed, but would benefit from documentation explaining its purpose in the context of RBF functionality.Add documentation above the method:
+ // GetMempoolEntry retrieves detailed information about a transaction in the mempool. + // This is used for monitoring transaction status and implementing RBF (Replace-By-Fee) functionality. GetMempoolEntry(txHash string) (*btcjson.GetMempoolEntryResult, error)zetaclient/chains/bitcoin/fee.go (2)
22-32
: Consider using iota for sequential constants.The transaction size-related constants could benefit from using
iota
for better maintainability and to prevent manual value assignments.const ( - bytesPerInput = 41 // each input is 41 bytes - bytesPerOutputP2TR = 43 // each P2TR output is 43 bytes - bytesPerOutputP2WSH = 43 // each P2WSH output is 43 bytes - bytesPerOutputP2WPKH = 31 // each P2WPKH output is 31 bytes - bytesPerOutputP2SH = 32 // each P2SH output is 32 bytes - bytesPerOutputP2PKH = 34 // each P2PKH output is 34 bytes + bytesPerInput = 41 + bytesPerOutputP2TR = iota + 43 // 43 bytes + bytesPerOutputP2WSH // 43 bytes + bytesPerOutputP2WPKH = iota + 31 // 31 bytes + bytesPerOutputP2SH // 32 bytes + bytesPerOutputP2PKH // 34 bytes
49-49
: Improve comment clarity.The comment could be more descriptive about the purpose of this variable in the context of gas limits.
- // This will be the suggested gas limit used for zetacore + // Suggested gas limit (177vB) used by zetacore for withdrawer transactionse2e/utils/zetacore.go (1)
32-48
: Add retry mechanism for resilience.The function makes a single attempt to query the CCTX data. Consider adding a retry mechanism with backoff for better reliability in e2e tests.
+func withRetry(ctx context.Context, maxAttempts int, fn func() error) error { + var err error + for i := 0; i < maxAttempts; i++ { + if err = fn(); err == nil { + return nil + } + time.Sleep(time.Second * time.Duration(i+1)) + } + return err +} func GetCctxByInboundHash( ctx context.Context, inboundHash string, client crosschaintypes.QueryClient, ) *crosschaintypes.CrossChainTx { t := TestingFromContext(ctx) - // query cctx by inbound hash - in := &crosschaintypes.QueryInboundHashToCctxDataRequest{InboundHash: inboundHash} - res, err := client.InTxHashToCctxData(ctx, in) + var res *crosschaintypes.QueryInboundHashToCctxDataResponse + err := withRetry(ctx, 3, func() error { + var queryErr error + in := &crosschaintypes.QueryInboundHashToCctxDataRequest{InboundHash: inboundHash} + res, queryErr = client.InTxHashToCctxData(ctx, in) + return queryErr + })zetaclient/chains/bitcoin/signer/fee_bumper_test.go (2)
21-105
: Add test cases for edge cases.The test function could benefit from additional test cases covering edge cases such as zero values and maximum values for fees and rates.
+ { + chain: chains.BitcoinMainnet, + name: "should handle maximum values", + client: mocks.NewBTCRPCClient(t), + tx: btcutil.NewTx(wire.NewMsgTx(wire.TxVersion)), + cctxRate: math.MaxInt64, + liveRate: float64(math.MaxInt64) / btcutil.SatoshiPerBitcoin, + errMsg: "rate exceeds maximum allowed value", + },
249-326
: Consider adding parallel test execution.The test cases are independent and could benefit from parallel execution using
t.Parallel()
.func Test_FetchFeeBumpInfo(t *testing.T) { + t.Parallel() liveRate := 0.00012 mockClient := mocks.NewBTCRPCClient(t)
zetaclient/chains/bitcoin/observer/mempool_test.go (3)
423-443
: Add detailed documentation for helper functions.The helper functions would benefit from comprehensive documentation explaining:
- The purpose and usage of each parameter
- The expected behavior of the returned functions
- Example usage in test cases
Add documentation blocks like this:
// makePendingTxFinder creates a mock implementation of PendingTxFinder for testing. // Parameters: // - tx: The transaction to return, or nil to simulate no pending transaction // - nonce: The nonce to return with the transaction // - errMsg: If non-empty, the function will return an error with this message // Returns a function that implements the PendingTxFinder interface. func makePendingTxFinder(tx *btcutil.Tx, nonce uint64, errMsg string) observer.PendingTxFinder {
34-34
: Fix typo in function name.The function name contains a typo:
Test_FefreshLastStuckOutbound
should beTest_RefreshLastStuckOutbound
.-func Test_FefreshLastStuckOutbound(t *testing.T) { +func Test_RefreshLastStuckOutbound(t *testing.T) {
108-349
: Enhance test coverage with additional assertions.The
Test_GetLastPendingOutbound
function could benefit from additional assertions to validate:
- The transaction weight and size
- The correctness of input and output scripts
- Error message content for failure cases
Add assertions like this to the test cases:
// For successful cases require.NotZero(t, lastTx.MsgTx().SerializeSize()) require.Greater(t, len(lastTx.MsgTx().TxIn), 0) require.Greater(t, len(lastTx.MsgTx().TxOut), 0) // For error cases require.ErrorContains(t, err, "expected error message")zetaclient/chains/bitcoin/rpc/rpc_live_test.go (1)
57-65
: Well-structured testnet4 configuration.The addition of testnet4 support is well-implemented with clear configuration parameters. Consider adding a comment explaining why testnet4 uses testnet3 network name.
Add a comment explaining the network name:
// testnet4 uses testnet3 network name because it shares the same network parameters // and address format with testnet3 Params: "testnet3",cmd/zetae2e/local/local.go (1)
320-320
: Document reason for commented test.The RBF test case is commented out without explanation. Add a comment explaining why this test is disabled and any conditions for re-enabling it.
// TODO(RBF-123): Temporarily disabled due to <reason> // Re-enable when <condition> is met //e2etests.TestBitcoinWithdrawRBFName,changelog.md (2)
9-13
: Consider removing empty sections.The sections "Tests", "Refactor", and "Fixes" are currently empty. Consider removing them until there are actual entries to add, as empty sections don't provide value and can make the changelog harder to scan.
-### Tests - -## Refactor - -### Fixes
7-7
: Enhance PR reference formatting.Consider adding a brief description of the RBF feature for better context. The current format only mentions the PR number.
-* [3306](https://github.com/zeta-chain/node/pull/3306) - add support for Bitcoin RBF (Replace-By-Fee) +* [3306](https://github.com/zeta-chain/node/pull/3306) - Add support for Bitcoin RBF (Replace-By-Fee) to enable stuck transaction replacement using fee bumpingx/crosschain/keeper/abci_test.go (3)
5-5
: Consider removing duplicate chain imports.The file imports both
chains
andzetachains
from the same package. Consider using a single import to maintain clarity.-import ( "github.com/zeta-chain/node/pkg/chains" zetachains "github.com/zeta-chain/node/pkg/chains" +import ( "github.com/zeta-chain/node/pkg/chains"Also applies to: 14-14
461-622
: Consider adding edge cases to Bitcoin gas rate tests.While the test coverage is good, consider adding these edge cases:
- Maximum gas rate boundary conditions
- Zero gas rate handling
- Negative gas rate scenarios
- Race conditions in gas rate updates
624-676
: Consider documenting chain support status.The test clearly defines which chains are supported for gas price updates. Consider adding comments to explain why certain chains are not supported and any future plans for support.
zetaclient/chains/bitcoin/observer/inbound_test.go (1)
170-170
: Consider adding address validation checks.While converting the address to string is correct, consider adding validation to ensure the address format is valid before conversion.
-FromAddress: sample.BtcAddressP2WPKH(t, &chaincfg.MainNetParams).String(), +addr := sample.BtcAddressP2WPKH(t, &chaincfg.MainNetParams) +require.NotNil(t, addr) +FromAddress: addr.String(),e2e/e2etests/e2etests.go (1)
721-729
: Consider enhancing test documentation.While the test registration is correct, consider adding more detailed documentation about:
- Expected RBF behavior
- Fee calculation strategy
- Success/failure conditions
- Required network conditions
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (58)
changelog.md
(1 hunks)cmd/zetae2e/local/local.go
(1 hunks)contrib/localnet/docker-compose.yml
(1 hunks)e2e/e2etests/e2etests.go
(2 hunks)e2e/e2etests/helpers.go
(3 hunks)e2e/e2etests/test_bitcoin_deposit_and_withdraw_with_dust.go
(2 hunks)e2e/e2etests/test_bitcoin_withdraw_rbf.go
(1 hunks)e2e/utils/bitcoin.go
(1 hunks)e2e/utils/zetacore.go
(3 hunks)go.mod
(1 hunks)pkg/math/integer.go
(1 hunks)pkg/math/integer_test.go
(1 hunks)testutil/sample/crypto.go
(2 hunks)x/crosschain/keeper/abci.go
(3 hunks)x/crosschain/keeper/abci_test.go
(5 hunks)x/crosschain/module.go
(1 hunks)x/crosschain/types/cctx_test.go
(1 hunks)x/crosschain/types/revert_options_test.go
(1 hunks)x/observer/types/crosschain_flags.go
(1 hunks)zetaclient/chains/bitcoin/fee.go
(7 hunks)zetaclient/chains/bitcoin/fee_test.go
(11 hunks)zetaclient/chains/bitcoin/observer/db.go
(1 hunks)zetaclient/chains/bitcoin/observer/db_test.go
(1 hunks)zetaclient/chains/bitcoin/observer/event_test.go
(5 hunks)zetaclient/chains/bitcoin/observer/gas_price.go
(1 hunks)zetaclient/chains/bitcoin/observer/inbound_test.go
(2 hunks)zetaclient/chains/bitcoin/observer/mempool.go
(1 hunks)zetaclient/chains/bitcoin/observer/mempool_test.go
(1 hunks)zetaclient/chains/bitcoin/observer/observer.go
(6 hunks)zetaclient/chains/bitcoin/observer/observer_test.go
(7 hunks)zetaclient/chains/bitcoin/observer/outbound.go
(7 hunks)zetaclient/chains/bitcoin/observer/utxos.go
(1 hunks)zetaclient/chains/bitcoin/rpc/rpc.go
(4 hunks)zetaclient/chains/bitcoin/rpc/rpc_live_test.go
(6 hunks)zetaclient/chains/bitcoin/rpc/rpc_rbf_live_test.go
(1 hunks)zetaclient/chains/bitcoin/rpc/rpc_test.go
(1 hunks)zetaclient/chains/bitcoin/signer/fee_bumper.go
(1 hunks)zetaclient/chains/bitcoin/signer/fee_bumper_test.go
(1 hunks)zetaclient/chains/bitcoin/signer/outbound_data.go
(1 hunks)zetaclient/chains/bitcoin/signer/outbound_data_test.go
(1 hunks)zetaclient/chains/bitcoin/signer/sign.go
(1 hunks)zetaclient/chains/bitcoin/signer/sign_rbf.go
(1 hunks)zetaclient/chains/bitcoin/signer/sign_rbf_test.go
(1 hunks)zetaclient/chains/bitcoin/signer/sign_test.go
(1 hunks)zetaclient/chains/bitcoin/signer/signer.go
(5 hunks)zetaclient/chains/bitcoin/signer/signer_test.go
(10 hunks)zetaclient/chains/interfaces/interfaces.go
(1 hunks)zetaclient/common/constant.go
(1 hunks)zetaclient/common/env.go
(1 hunks)zetaclient/logs/fields.go
(1 hunks)zetaclient/orchestrator/bootstrap.go
(1 hunks)zetaclient/orchestrator/orchestrator.go
(14 hunks)zetaclient/testdata/btc/chain_8332_msgtx_030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0.json
(1 hunks)zetaclient/testdata/btc/chain_8332_tx_030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0.json
(1 hunks)zetaclient/testutils/mocks/btc_rpc.go
(1 hunks)zetaclient/testutils/mocks/zetacore_client_opts.go
(1 hunks)zetaclient/testutils/testdata.go
(1 hunks)zetaclient/testutils/testdata_naming.go
(1 hunks)
✅ Files skipped from review due to trivial changes (4)
- zetaclient/testdata/btc/chain_8332_tx_030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0.json
- zetaclient/orchestrator/orchestrator.go
- go.mod
- contrib/localnet/docker-compose.yml
👮 Files not reviewed due to content moderation or server errors (4)
- zetaclient/chains/bitcoin/signer/signer_test.go
- zetaclient/chains/bitcoin/observer/event_test.go
- zetaclient/orchestrator/bootstrap.go
- zetaclient/testutils/mocks/btc_rpc.go
🧰 Additional context used
📓 Path-based instructions (52)
zetaclient/logs/fields.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/common/constant.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
x/crosschain/module.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
pkg/math/integer_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/testutils/testdata.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/interfaces/interfaces.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
pkg/math/integer.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
e2e/e2etests/test_bitcoin_deposit_and_withdraw_with_dust.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
x/crosschain/types/revert_options_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
x/observer/types/crosschain_flags.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/db_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
testutil/sample/crypto.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
e2e/utils/bitcoin.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/event_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/common/env.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
e2e/e2etests/test_bitcoin_withdraw_rbf.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
e2e/e2etests/e2etests.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/gas_price.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/testutils/mocks/btc_rpc.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/fee_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
e2e/e2etests/helpers.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/outbound_data_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
e2e/utils/zetacore.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/inbound_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
x/crosschain/types/cctx_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
cmd/zetae2e/local/local.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/sign_rbf.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/outbound_data.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/utxos.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/testutils/testdata_naming.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/sign_rbf_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/sign.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/db.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
x/crosschain/keeper/abci.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/mempool.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/signer.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/rpc/rpc_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/orchestrator/bootstrap.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/fee_bumper.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/testutils/mocks/zetacore_client_opts.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/rpc/rpc_live_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/rpc/rpc_rbf_live_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/fee_bumper_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/mempool_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
x/crosschain/keeper/abci_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/fee.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/signer_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/observer.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/signer/sign_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/observer_test.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/observer/outbound.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
zetaclient/chains/bitcoin/rpc/rpc.go (1)
Pattern **/*.go
: Review the Go code, point out issues relative to principles of clean code, expressiveness, and performance.
📓 Learnings (1)
zetaclient/chains/bitcoin/observer/event_test.go (1)
Learnt from: ws4charlie
PR: zeta-chain/node#2987
File: pkg/memo/fields_v0_test.go:270-320
Timestamp: 2024-11-12T13:20:12.658Z
Learning: In the `FieldsV0` struct of the `memo` package, `RevertAddress` may be left empty when `CallOnRevert` is set to true.
🪛 GitHub Check: codecov/patch
zetaclient/chains/bitcoin/observer/gas_price.go
[warning] 16-21: zetaclient/chains/bitcoin/observer/gas_price.go#L16-L21
Added lines #L16 - L21 were not covered by tests
[warning] 24-36: zetaclient/chains/bitcoin/observer/gas_price.go#L24-L36
Added lines #L24 - L36 were not covered by tests
[warning] 38-45: zetaclient/chains/bitcoin/observer/gas_price.go#L38-L45
Added lines #L38 - L45 were not covered by tests
[warning] 51-70: zetaclient/chains/bitcoin/observer/gas_price.go#L51-L70
Added lines #L51 - L70 were not covered by tests
[warning] 74-77: zetaclient/chains/bitcoin/observer/gas_price.go#L74-L77
Added lines #L74 - L77 were not covered by tests
[warning] 80-87: zetaclient/chains/bitcoin/observer/gas_price.go#L80-L87
Added lines #L80 - L87 were not covered by tests
[warning] 89-89: zetaclient/chains/bitcoin/observer/gas_price.go#L89
Added line #L89 was not covered by tests
[warning] 93-104: zetaclient/chains/bitcoin/observer/gas_price.go#L93-L104
Added lines #L93 - L104 were not covered by tests
zetaclient/chains/bitcoin/observer/db.go
[warning] 20-23: zetaclient/chains/bitcoin/observer/db.go#L20-L23
Added lines #L20 - L23 were not covered by tests
[warning] 59-61: zetaclient/chains/bitcoin/observer/db.go#L59-L61
Added lines #L59 - L61 were not covered by tests
x/crosschain/keeper/abci.go
[warning] 63-64: x/crosschain/keeper/abci.go#L63-L64
Added lines #L63 - L64 were not covered by tests
zetaclient/chains/bitcoin/observer/mempool.go
[warning] 55-61: zetaclient/chains/bitcoin/observer/mempool.go#L55-L61
Added lines #L55 - L61 were not covered by tests
[warning] 64-70: zetaclient/chains/bitcoin/observer/mempool.go#L64-L70
Added lines #L64 - L70 were not covered by tests
zetaclient/chains/bitcoin/fee.go
[warning] 241-241: zetaclient/chains/bitcoin/fee.go#L241
Added line #L241 was not covered by tests
[warning] 281-281: zetaclient/chains/bitcoin/fee.go#L281
Added line #L281 was not covered by tests
zetaclient/chains/bitcoin/observer/observer.go
[warning] 172-174: zetaclient/chains/bitcoin/observer/observer.go#L172-L174
Added lines #L172 - L174 were not covered by tests
zetaclient/chains/bitcoin/observer/outbound.go
[warning] 38-38: zetaclient/chains/bitcoin/observer/outbound.go#L38
Added line #L38 was not covered by tests
[warning] 51-51: zetaclient/chains/bitcoin/observer/outbound.go#L51
Added line #L51 was not covered by tests
[warning] 58-58: zetaclient/chains/bitcoin/observer/outbound.go#L58
Added line #L58 was not covered by tests
[warning] 63-63: zetaclient/chains/bitcoin/observer/outbound.go#L63
Added line #L63 was not covered by tests
[warning] 65-65: zetaclient/chains/bitcoin/observer/outbound.go#L65
Added line #L65 was not covered by tests
[warning] 70-70: zetaclient/chains/bitcoin/observer/outbound.go#L70
Added line #L70 was not covered by tests
[warning] 77-82: zetaclient/chains/bitcoin/observer/outbound.go#L77-L82
Added lines #L77 - L82 were not covered by tests
[warning] 85-98: zetaclient/chains/bitcoin/observer/outbound.go#L85-L98
Added lines #L85 - L98 were not covered by tests
[warning] 100-102: zetaclient/chains/bitcoin/observer/outbound.go#L100-L102
Added lines #L100 - L102 were not covered by tests
[warning] 111-114: zetaclient/chains/bitcoin/observer/outbound.go#L111-L114
Added lines #L111 - L114 were not covered by tests
[warning] 119-119: zetaclient/chains/bitcoin/observer/outbound.go#L119
Added line #L119 was not covered by tests
[warning] 136-143: zetaclient/chains/bitcoin/observer/outbound.go#L136-L143
Added lines #L136 - L143 were not covered by tests
[warning] 145-145: zetaclient/chains/bitcoin/observer/outbound.go#L145
Added line #L145 was not covered by tests
[warning] 149-149: zetaclient/chains/bitcoin/observer/outbound.go#L149
Added line #L149 was not covered by tests
🔇 Additional comments (38)
zetaclient/chains/bitcoin/signer/sign_test.go (1)
1-22
: LGTM! Well-organized imports.The imports are logically grouped and all are being used in the test file.
zetaclient/chains/bitcoin/signer/outbound_data.go (3)
91-91
: Verify units when adding minimum relay fee to fee rateEnsure that the units of
satPerByte
andfeeRate
are consistent before addition. Both should be insatoshis/vByte
to maintain correctness in fee calculations.Please confirm that
rpc.FeeRateToSatPerByte(minRelayFee)
returns a value insatoshis/vByte
and that adding it tofeeRate
is appropriate.
93-98
: Ensure compliance checks are enforced downstreamWhile the compliance check flags
restrictedCCTX
, it's essential to verify that subsequent processing respects this flag. Ensure that restricted transactions are not inadvertently processed further.Review the transaction pipeline to confirm that transactions with
cancelTx
set totrue
are appropriately handled and excluded from processing.
28-28
:⚠️ Potential issueUse integer type for monetary amount to prevent precision errors
Using
float64
for theamount
can lead to precision and rounding errors in financial calculations, which is unacceptable in transaction processing. It is advisable to represent monetary amounts in the smallest currency unit—in this case, satoshis—using an integer type likeuint64
.Apply the following change to the
OutboundData
struct:- amount float64 + amount uint64 // amount in satoshis⛔ Skipped due to learnings
Learnt from: ws4charlie PR: zeta-chain/node#2899 File: zetaclient/chains/bitcoin/observer/inbound.go:37-38 Timestamp: 2024-11-12T13:20:12.658Z Learning: In `BTCInboundEvent`, it's acceptable to use `float64` for monetary values (`Value` and `DepositorFee`) because precision is ensured through conversion to integer when building the vote message.
zetaclient/chains/bitcoin/signer/signer.go (1)
238-238
: Verify the success of saving broadcasted transactionsEnsure that
SaveBroadcastedTx
operates as expected and consider handling any potential errors, even if the current implementation does not return an error.Please confirm whether
SaveBroadcastedTx
can fail or return errors. If it can, handle errors appropriately:err := ob.SaveBroadcastedTx(txHash, nonce) -if err != nil { - logger.Error().Err(err).Fields(lf).Msg("Failed to save broadcasted transaction") -}zetaclient/chains/bitcoin/signer/fee_bumper.go (2)
93-165
: Well-structured fee bumping logic inBumpTxFee
methodThe
BumpTxFee
method is well-designed and follows best practices for implementing Child Pays for Parent (CPFP) fee bumping in Bitcoin transactions. The method carefully calculates the necessary additional fees, adheres to Bitcoin protocol requirements, and includes appropriate safeguards to prevent excessive fees and ensure transaction replacement effectiveness.
170-170
:⚠️ Potential issuePossible typo in function name
IsBitcoinRegnet
At line 170, the function
IsBitcoinRegnet
is used to determine if the chain ID corresponds to the Bitcoin regression test network. The standard term for Bitcoin's regression test network is "regtest". Please verify if the function should beIsBitcoinRegtest
instead ofIsBitcoinRegnet
to adhere to standard Bitcoin nomenclature and avoid potential confusion.zetaclient/chains/bitcoin/observer/utxos.go (1)
107-202
: Efficient UTXO selection inSelectUTXOs
methodThe
SelectUTXOs
method effectively selects UTXOs to cover the transaction amount while considering consolidation of smaller UTXOs. The use of deterministic sorting and careful handling of nonce-mark UTXOs enhances the reliability of UTXO management. The logic appears sound, and the method balances optimal UTXO selection with performance considerations.zetaclient/chains/bitcoin/signer/sign.go (2)
83-96
: Verify the transaction size limits and fee calculationsThe conditional checks and adjustments for
txSize
between lines 83-96 ensure that the transaction size is within acceptable bounds. However, ensure that the variablestxData.txSize
andtxSize
are consistently and correctly used. Additionally, confirm that the fee calculation aligns with the latest fee structures and network policies.
247-251
: Ensure valid construction of ECDSA signaturesIn lines 247-251, the code reconstructs the signature using
btcec.ModNScalar
. It is important to validate thatR
andS
are correctly derived from the signature bytes to prevent invalid signatures.Consider adding checks to ensure
R
andS
are valid scalars within the acceptable range.zetaclient/chains/bitcoin/observer/mempool.go (1)
80-128
: Robust error handling inRefreshLastStuckOutbound
The
RefreshLastStuckOutbound
function contains complex logic for updating the state of stuck transactions. Ensure that all possible error paths are adequately handled and that the observer's state remains consistent in case of failures.Review the error handling mechanisms to confirm that they prevent partial updates or inconsistent states.
zetaclient/chains/bitcoin/observer/outbound.go (2)
77-120
: Add unit tests for new outbound processing functionsThe
ProcessOutboundTrackers
andTryIncludeOutbound
methods are key additions that handle outbound transaction processing. Currently, there is no test coverage for these methods, which may lead to unanticipated bugs or regressions. Develop comprehensive unit tests covering various scenarios, including normal operation, error conditions, and edge cases, to ensure these methods function correctly and reliably.🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 77-82: zetaclient/chains/bitcoin/observer/outbound.go#L77-L82
Added lines #L77 - L82 were not covered by tests
[warning] 85-98: zetaclient/chains/bitcoin/observer/outbound.go#L85-L98
Added lines #L85 - L98 were not covered by tests
[warning] 100-102: zetaclient/chains/bitcoin/observer/outbound.go#L100-L102
Added lines #L100 - L102 were not covered by tests
[warning] 111-114: zetaclient/chains/bitcoin/observer/outbound.go#L111-L114
Added lines #L111 - L114 were not covered by tests
[warning] 119-119: zetaclient/chains/bitcoin/observer/outbound.go#L119
Added line #L119 was not covered by tests
285-292
: Confirm regnet transaction stuck detection logicIn
IsTxStuckInMempoolRegnet
, the conditionif pendingTime > pendingTimeAllowed && memplEntry.Height == lastBlock
relies on the assumption that block mining is frozen in Regnet for end-to-end testing. Verify that this assumption holds in your testing environment. If block mining resumes or the environment changes, the condition may fail to accurately detect stuck transactions. Adjust the logic to align with the actual behavior of Regnet to maintain accurate detection.zetaclient/logs/fields.go (1)
12-12
: LGTM! Well-structured constant addition.The new
FieldOutboundID
constant follows the established naming patterns and integrates well with the existing logging infrastructure.zetaclient/common/constant.go (1)
17-19
: LGTM! Well-documented constant addition.The
MempoolStuckTxCheckInterval
constant is appropriately placed and documented, with a reasonable 30-second interval for mempool monitoring.zetaclient/common/env.go (1)
17-19
: LGTM! Consistent environment variable addition.The
EnvBtcRPCTestnet4
constant maintains consistency with existing BTC RPC network environment variables in both naming and documentation.x/observer/types/crosschain_flags.go (1)
8-8
: Verify removal of EpochLength field.The removal of the
EpochLength
field represents a significant change from block-based to time-based intervals. Please ensure all dependent code has been updated accordingly.✅ Verification successful
Transition from block-based to time-based intervals verified successfully.
The removal of
EpochLength
is part of a well-implemented architectural change from block-based to time-based intervals. The codebase has been properly updated to useRetryInterval
with appropriate time-based checks in the gas price update mechanism.🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for any remaining references to EpochLength rg -l "EpochLength" --type goLength of output: 489
Script:
#!/bin/bash # Show actual usage of EpochLength with context, excluding test files rg "EpochLength" --type go -g '!*_test.go' -B 2 -A 2 # Also check for any struct definitions or interface requirements ast-grep --pattern 'type $_ struct { $$$ EpochLength $_ $$$ }'Length of output: 6993
Script:
#!/bin/bash # Check for recent changes in gas price increase related files rg "RetryInterval" --type go -B 2 -A 2 # Look for any block height based checks that might need updating ast-grep --pattern 'if $_.BlockHeight() $$ { $$$ }'Length of output: 10800
zetaclient/testutils/mocks/zetacore_client_opts.go (1)
37-46
: LGTM! Well-structured mock setup method.The implementation follows established patterns in the codebase and properly handles both success and error cases while maintaining the chainable interface.
e2e/e2etests/test_bitcoin_deposit_and_withdraw_with_dust.go (1)
29-33
: LGTM! Proper cleanup with deferred WaitGroup completion.The deferred cleanup ensures proper synchronization while maintaining the existing stop functionality.
x/crosschain/types/revert_options_test.go (1)
52-52
: LGTM! Test update maintains consistency.The change correctly handles the Bitcoin address string conversion.
zetaclient/testutils/testdata_naming.go (1)
67-70
: LGTM! Consistent with existing naming patterns.The function follows the established naming convention and maintains consistency with other file naming functions in the module.
testutil/sample/crypto.go (2)
69-78
: Improved type safety with stronger return type.The change from returning a string to returning
*btcutil.AddressWitnessPubKeyHash
provides better type safety and direct access to address-specific methods.
80-85
: LGTM! Well-structured script generation function.The function properly generates P2WPKH scripts with appropriate error handling.
x/crosschain/module.go (1)
175-175
: Verify the impact of removing gas price update callback.The change from using
keeper.CheckAndUpdateCctxGasPrice
tonil
might affect how gas prices are updated. Please ensure this change aligns with the new RBF implementation.✅ Verification successful
Passing nil callback is correct and safe.
The change aligns with the architecture where each chain type (EVM, Bitcoin) has its specific gas price update mechanism, and nil is explicitly handled for ZetaChain itself.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Search for other uses of CheckAndUpdateCctxGasPrice to ensure they're properly handled rg -A 5 "CheckAndUpdateCctxGasPrice" # Look for any new gas price update mechanisms ast-grep --pattern 'func $_(ctx sdk.Context, chains []$_, $_) { $$$ }'Length of output: 4409
zetaclient/chains/bitcoin/signer/sign_rbf_test.go (1)
82-94
: LGTM! Well-structured test cases.The test cases cover essential scenarios including:
- Successful RBF transaction signing
- Missing fee rate handling
- Fee bumper creation errors
- High fee rate scenarios
zetaclient/chains/bitcoin/signer/outbound_data_test.go (2)
17-66
: LGTM! Well-structured test case with comprehensive assertions.The test case effectively validates the successful creation of outbound data with proper fee calculations and parameter validations.
67-94
: LGTM! Good test coverage for gas rate updates.The test case properly verifies that the current gas rate takes precedence over the old rate, which is crucial for the RBF functionality.
x/crosschain/types/cctx_test.go (1)
143-143
: LGTM! Consistent string representation of Bitcoin addresses.Using the
String()
method ensures consistent string representation of Bitcoin addresses across the codebase.zetaclient/chains/bitcoin/observer/observer_test.go (3)
243-274
: LGTM! Well-structured test with clear steps.The test effectively validates the lifecycle of a stuck outbound transaction with clear steps:
- Initial state verification
- Setting stuck outbound
- Updating stuck outbound
- Clearing stuck outbound
312-317
: LGTM! Chain-specific TSS signer setup.The conditional setup of TSS signer based on chain type improves test accuracy.
319-326
: LGTM! Flexible database setup.The database setup now supports both in-memory and file-based databases, improving test flexibility.
zetaclient/chains/bitcoin/fee.go (1)
61-61
:⚠️ Potential issueValidate input parameters.
The function accepts unsigned integers but returns a signed integer. Consider adding validation for large input values to prevent potential overflow.
func WiredTxSize(numInputs uint64, numOutputs uint64) int64 { + // Validate input parameters to prevent overflow + if numInputs > math.MaxInt64 || numOutputs > math.MaxInt64 { + return -1 + } // Version 4 bytes + LockTime 4 bytes + Serialized varint size for the // number of transaction inputs and outputs.Also applies to: 65-65
zetaclient/chains/bitcoin/signer/fee_bumper_test.go (1)
107-247
: LGTM! Comprehensive test coverage.The test cases cover various scenarios including successful fee bumping, reserved fee handling, and error conditions.
zetaclient/chains/bitcoin/fee_test.go (1)
187-191
: Good addition of edge case test.The new test case for handling no UTXOs is a valuable addition that ensures the system behaves correctly when there are no unspent transaction outputs.
zetaclient/chains/bitcoin/rpc/rpc_live_test.go (1)
115-139
: Good test organization.The separation of tests into logical groups (
Test_BitcoinLive
andTest_BitcoinFeeLive
) improves maintainability and clarity.zetaclient/testdata/btc/chain_8332_msgtx_030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0.json (1)
1-95
: Transaction structure correctly supports RBF.The transaction is well-formed with:
- Proper witness data for each input
- Maximum sequence numbers (4294967295) enabling RBF
- Correctly structured outputs with values and scripts
x/crosschain/keeper/abci_test.go (1)
92-106
: LGTM: Test coverage expanded for Bitcoin chain.The test coverage has been appropriately expanded to include Bitcoin chain test cases, with proper assertions for chain-specific indices.
e2e/e2etests/e2etests.go (1)
93-93
: LGTM: Test constant follows naming convention.The new test constant follows the established naming pattern for Bitcoin-related tests.
pkg/math/integer.go
Outdated
default: | ||
increase := float64(value) * float64(percent) / 100 | ||
if round { | ||
return value + int64(math.Round(increase)) | ||
} | ||
return value + int64(increase) | ||
} |
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.
🛠️ Refactor suggestion
Add input validation and overflow protection.
The implementation correctly handles the percentage calculation, but consider adding:
- Input validation for negative values
- Overflow protection for large numbers
func IncreaseIntByPercent(value int64, percent uint32, round bool) int64 {
+ // Handle negative values
+ if value < 0 {
+ return -IncreaseIntByPercent(-value, percent, round)
+ }
+
+ // Protect against overflow
+ if value > math.MaxInt64/2 {
+ // Handle large numbers carefully
+ ratio := 1.0 + float64(percent)/100.0
+ if ratio > float64(math.MaxInt64)/float64(value) {
+ return math.MaxInt64
+ }
+ }
+
switch {
Committable suggestion skipped: line range outside the PR's diff.
pkg/math/integer_test.go
Outdated
func Test_IncreaseIntByPercent(t *testing.T) { | ||
for i, tt := range []struct { | ||
value int64 | ||
percent uint32 | ||
round bool | ||
expected int64 | ||
}{ | ||
{value: 10, percent: 0, round: false, expected: 10}, | ||
{value: 10, percent: 15, round: false, expected: 11}, | ||
{value: 10, percent: 15, round: true, expected: 12}, | ||
{value: 10, percent: 14, round: false, expected: 11}, | ||
{value: 10, percent: 14, round: true, expected: 11}, | ||
{value: 10, percent: 200, round: false, expected: 30}, | ||
{value: 10, percent: 200, round: true, expected: 30}, | ||
} { | ||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { | ||
result := IncreaseIntByPercent(tt.value, tt.percent, tt.round) | ||
assert.Equal(t, tt.expected, result) | ||
}) | ||
} |
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.
🛠️ Refactor suggestion
Enhance test coverage with additional edge cases.
While the current test cases cover basic functionality, consider adding:
- Edge cases for negative values
- Boundary tests for large numbers near int64 limits
- Maximum uint32 percentage tests
- More descriptive test names instead of numeric indices
func Test_IncreaseIntByPercent(t *testing.T) {
for i, tt := range []struct {
value int64
percent uint32
round bool
expected int64
+ name string
}{
- {value: 10, percent: 0, round: false, expected: 10},
+ {
+ name: "zero_percent_no_change",
+ value: 10, percent: 0, round: false, expected: 10,
+ },
+ // ... existing cases with descriptive names ...
+ {
+ name: "negative_value",
+ value: -10, percent: 15, round: true, expected: -12,
+ },
+ {
+ name: "large_value_no_overflow",
+ value: math.MaxInt64/2, percent: 50, round: false,
+ expected: math.MaxInt64/2 + math.MaxInt64/4,
+ },
+ {
+ name: "max_percentage",
+ value: 10, percent: math.MaxUint32, round: true,
+ expected: math.MaxInt64, // or appropriate expected value
+ },
} {
- t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+ t.Run(tt.name, func(t *testing.T) {
Committable suggestion skipped: line range outside the PR's diff.
amount: amount, | ||
feeRate: feeRate, | ||
// #nosec G115 always in range | ||
txSize: int64(params.CallOptions.GasLimit), |
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.
Validate GasLimit
before type conversion to prevent overflow
Casting params.CallOptions.GasLimit
from uint64
to int64
without validation can cause overflow errors if the value exceeds the maximum value of int64
. Ensure that GasLimit
is within the acceptable range before performing the conversion.
Include a validation check:
if params.CallOptions.GasLimit > math.MaxInt64 {
return nil, fmt.Errorf("GasLimit %d exceeds int64 maximum value", params.CallOptions.GasLimit)
}
txSize := int64(params.CallOptions.GasLimit)
Remember to import the math
package:
import (
"math"
)
if err != nil || feeRate < 0 { | ||
return nil, fmt.Errorf("cannot convert gas price %s", params.GasPrice) | ||
} |
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.
Handle zero or negative fee rates appropriately
A feeRate
of zero or negative value is invalid for transaction processing and should be explicitly checked and rejected to prevent transaction failures or unexpected behaviors.
Update the condition to include zero:
- if err != nil || feeRate < 0 {
+ if err != nil || feeRate <= 0 {
Adjust the error message for clarity:
- return nil, fmt.Errorf("cannot convert gas price %s", params.GasPrice)
+ return nil, fmt.Errorf("invalid gas price %s", params.GasPrice)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if err != nil || feeRate < 0 { | |
return nil, fmt.Errorf("cannot convert gas price %s", params.GasPrice) | |
} | |
if err != nil || feeRate <= 0 { | |
return nil, fmt.Errorf("invalid gas price %s", params.GasPrice) | |
} |
func EstimateOutboundSize(numInputs int64, payees []btcutil.Address) (int64, error) { | ||
if numInputs <= 0 { | ||
return 0, nil | ||
} | ||
// #nosec G115 always positive | ||
numOutputs := 2 + uint64(len(payees)) | ||
bytesWiredTx := WiredTxSize(numInputs, numOutputs) | ||
// #nosec G115 checked positive | ||
bytesWiredTx := WiredTxSize(uint64(numInputs), numOutputs) |
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.
🛠️ Refactor suggestion
Consider error handling for negative inputs.
While the function now accepts negative inputs and returns early, it might be better to return an error to provide more context about the invalid input.
-func EstimateOutboundSize(numInputs int64, payees []btcutil.Address) (int64, error) {
- if numInputs <= 0 {
- return 0, nil
- }
+func EstimateOutboundSize(numInputs int64, payees []btcutil.Address) (int64, error) {
+ if numInputs <= 0 {
+ return 0, fmt.Errorf("invalid number of inputs: %d", numInputs)
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
func EstimateOutboundSize(numInputs int64, payees []btcutil.Address) (int64, error) { | |
if numInputs <= 0 { | |
return 0, nil | |
} | |
// #nosec G115 always positive | |
numOutputs := 2 + uint64(len(payees)) | |
bytesWiredTx := WiredTxSize(numInputs, numOutputs) | |
// #nosec G115 checked positive | |
bytesWiredTx := WiredTxSize(uint64(numInputs), numOutputs) | |
func EstimateOutboundSize(numInputs int64, payees []btcutil.Address) (int64, error) { | |
if numInputs <= 0 { | |
return 0, fmt.Errorf("invalid number of inputs: %d", numInputs) | |
} | |
// #nosec G115 always positive | |
numOutputs := 2 + uint64(len(payees)) | |
// #nosec G115 checked positive | |
bytesWiredTx := WiredTxSize(uint64(numInputs), numOutputs) |
@@ -246,7 +238,7 @@ | |||
|
|||
// GetRecentFeeRate gets the highest fee rate from recent blocks | |||
// Note: this method should be used for testnet ONLY | |||
func GetRecentFeeRate(rpcClient interfaces.BTCRPCClient, netParams *chaincfg.Params) (uint64, error) { | |||
func GetRecentFeeRate(rpcClient interfaces.BTCRPCClient, netParams *chaincfg.Params) (int64, error) { |
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.
💡 Codebase verification
Add comprehensive unit test coverage for GetRecentFeeRate
The function requires unit tests covering:
- Mainnet rejection validation
- RPC error scenarios (GetBlockCount, GetBlockHash, GetBlockVerboseTx)
- Fee rate calculation logic
- Edge cases with zero or low fee transactions
While a live test exists in the RPC package, it only covers the happy path and requires a testnet connection. Consider using a mock BTCRPCClient interface to create proper unit tests that can run in CI/CD pipelines.
🔗 Analysis chain
Add test coverage for GetRecentFeeRate.
The static analysis indicates that the function lacks test coverage. Consider adding test cases for both success and error scenarios.
Also applies to: 281-281
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for existing test coverage
rg -l "func.*GetRecentFeeRate" --type go | xargs rg "Test.*GetRecentFeeRate"
Length of output: 369
Script:
#!/bin/bash
# Check the implementation details of both functions
echo "=== Implementation in bitcoin package ==="
ast-grep --pattern 'func GetRecentFeeRate($_) {
$$$
}'
echo -e "\n=== Implementation in rpc package ==="
ast-grep --pattern 'func LiveTest_GetRecentFeeRate($_) {
$$$
}'
echo -e "\n=== Looking for unit tests ==="
rg "func Test.*GetRecentFeeRate" --type go
Length of output: 1290
Script:
#!/bin/bash
# Get the implementation details of GetRecentFeeRate
echo "=== GetRecentFeeRate Implementation ==="
rg -A 20 "func GetRecentFeeRate" zetaclient/chains/bitcoin/fee.go
Length of output: 880
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 241-241: zetaclient/chains/bitcoin/fee.go#L241
Added line #L241 was not covered by tests
// WaitOutboundTracker wait for outbound tracker to be filled with 'hashCount' hashes | ||
func WaitOutboundTracker( | ||
ctx context.Context, | ||
client crosschaintypes.QueryClient, | ||
chainID int64, | ||
nonce uint64, | ||
hashCount int, | ||
logger infoLogger, | ||
timeout time.Duration, | ||
) []string { | ||
if timeout == 0 { | ||
timeout = DefaultCctxTimeout | ||
} | ||
|
||
t := TestingFromContext(ctx) | ||
startTime := time.Now() | ||
in := &crosschaintypes.QueryAllOutboundTrackerByChainRequest{Chain: chainID} | ||
|
||
for { | ||
require.False( | ||
t, | ||
time.Since(startTime) > timeout, | ||
fmt.Sprintf("waiting outbound tracker timeout, chainID: %d, nonce: %d", chainID, nonce), | ||
) | ||
time.Sleep(5 * time.Second) | ||
|
||
outboundTracker, err := client.OutboundTrackerAllByChain(ctx, in) | ||
require.NoError(t, err) | ||
|
||
// loop through all outbound trackers | ||
for i, tracker := range outboundTracker.OutboundTracker { | ||
if tracker.Nonce == nonce { | ||
logger.Info("Tracker[%d]:\n", i) | ||
logger.Info(" ChainId: %d\n", tracker.ChainId) | ||
logger.Info(" Nonce: %d\n", tracker.Nonce) | ||
logger.Info(" HashList:\n") | ||
|
||
hashes := []string{} | ||
for j, hash := range tracker.HashList { | ||
hashes = append(hashes, hash.TxHash) | ||
logger.Info(" hash[%d]: %s\n", j, hash.TxHash) | ||
} | ||
if len(hashes) >= hashCount { | ||
return hashes | ||
} | ||
} | ||
} | ||
} | ||
} |
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.
🛠️ Refactor suggestion
Improve error handling and logging.
The function could benefit from more structured error handling and detailed logging.
func WaitOutboundTracker(
ctx context.Context,
client crosschaintypes.QueryClient,
chainID int64,
nonce uint64,
hashCount int,
logger infoLogger,
timeout time.Duration,
) []string {
+ if hashCount <= 0 {
+ panic("hashCount must be positive")
+ }
if timeout == 0 {
timeout = DefaultCctxTimeout
}
t := TestingFromContext(ctx)
startTime := time.Now()
in := &crosschaintypes.QueryAllOutboundTrackerByChainRequest{Chain: chainID}
+ logger.Info("Starting outbound tracker wait",
+ "chainID", chainID,
+ "nonce", nonce,
+ "expectedHashes", hashCount,
+ "timeout", timeout)
for {
require.False(
t,
time.Since(startTime) > timeout,
fmt.Sprintf("waiting outbound tracker timeout, chainID: %d, nonce: %d", chainID, nonce),
)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// WaitOutboundTracker wait for outbound tracker to be filled with 'hashCount' hashes | |
func WaitOutboundTracker( | |
ctx context.Context, | |
client crosschaintypes.QueryClient, | |
chainID int64, | |
nonce uint64, | |
hashCount int, | |
logger infoLogger, | |
timeout time.Duration, | |
) []string { | |
if timeout == 0 { | |
timeout = DefaultCctxTimeout | |
} | |
t := TestingFromContext(ctx) | |
startTime := time.Now() | |
in := &crosschaintypes.QueryAllOutboundTrackerByChainRequest{Chain: chainID} | |
for { | |
require.False( | |
t, | |
time.Since(startTime) > timeout, | |
fmt.Sprintf("waiting outbound tracker timeout, chainID: %d, nonce: %d", chainID, nonce), | |
) | |
time.Sleep(5 * time.Second) | |
outboundTracker, err := client.OutboundTrackerAllByChain(ctx, in) | |
require.NoError(t, err) | |
// loop through all outbound trackers | |
for i, tracker := range outboundTracker.OutboundTracker { | |
if tracker.Nonce == nonce { | |
logger.Info("Tracker[%d]:\n", i) | |
logger.Info(" ChainId: %d\n", tracker.ChainId) | |
logger.Info(" Nonce: %d\n", tracker.Nonce) | |
logger.Info(" HashList:\n") | |
hashes := []string{} | |
for j, hash := range tracker.HashList { | |
hashes = append(hashes, hash.TxHash) | |
logger.Info(" hash[%d]: %s\n", j, hash.TxHash) | |
} | |
if len(hashes) >= hashCount { | |
return hashes | |
} | |
} | |
} | |
} | |
} | |
// WaitOutboundTracker wait for outbound tracker to be filled with 'hashCount' hashes | |
func WaitOutboundTracker( | |
ctx context.Context, | |
client crosschaintypes.QueryClient, | |
chainID int64, | |
nonce uint64, | |
hashCount int, | |
logger infoLogger, | |
timeout time.Duration, | |
) []string { | |
if hashCount <= 0 { | |
panic("hashCount must be positive") | |
} | |
if timeout == 0 { | |
timeout = DefaultCctxTimeout | |
} | |
t := TestingFromContext(ctx) | |
startTime := time.Now() | |
in := &crosschaintypes.QueryAllOutboundTrackerByChainRequest{Chain: chainID} | |
logger.Info("Starting outbound tracker wait", | |
"chainID", chainID, | |
"nonce", nonce, | |
"expectedHashes", hashCount, | |
"timeout", timeout) | |
for { | |
require.False( | |
t, | |
time.Since(startTime) > timeout, | |
fmt.Sprintf("waiting outbound tracker timeout, chainID: %d, nonce: %d", chainID, nonce), | |
) | |
time.Sleep(5 * time.Second) | |
outboundTracker, err := client.OutboundTrackerAllByChain(ctx, in) | |
require.NoError(t, err) | |
// loop through all outbound trackers | |
for i, tracker := range outboundTracker.OutboundTracker { | |
if tracker.Nonce == nonce { | |
logger.Info("Tracker[%d]:\n", i) | |
logger.Info(" ChainId: %d\n", tracker.ChainId) | |
logger.Info(" Nonce: %d\n", tracker.Nonce) | |
logger.Info(" HashList:\n") | |
hashes := []string{} | |
for j, hash := range tracker.HashList { | |
hashes = append(hashes, hash.TxHash) | |
logger.Info(" hash[%d]: %s\n", j, hash.TxHash) | |
} | |
if len(hashes) >= hashCount { | |
return hashes | |
} | |
} | |
} | |
} | |
} |
// LoadBTCTransaction loads archived Bitcoin transaction from file | ||
func LoadBTCTransaction(t *testing.T, dir string, chainID int64, txHash string) *btcjson.GetTransactionResult { | ||
name := path.Join(dir, TestDataPathBTC, FileNameBTCTransaction(chainID, txHash)) | ||
tx := &btcjson.GetTransactionResult{} | ||
LoadObjectFromJSONFile(t, tx, name) | ||
return tx | ||
} |
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.
🛠️ Refactor suggestion
Add validation for input parameters.
The function should validate the input parameters before attempting to load the transaction.
func LoadBTCTransaction(t *testing.T, dir string, chainID int64, txHash string) *btcjson.GetTransactionResult {
+ require.NotEmpty(t, dir, "directory path cannot be empty")
+ require.NotEmpty(t, txHash, "transaction hash cannot be empty")
+ require.Greater(t, chainID, int64(0), "chain ID must be positive")
+
name := path.Join(dir, TestDataPathBTC, FileNameBTCTransaction(chainID, txHash))
tx := &btcjson.GetTransactionResult{}
LoadObjectFromJSONFile(t, tx, name)
return tx
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// LoadBTCTransaction loads archived Bitcoin transaction from file | |
func LoadBTCTransaction(t *testing.T, dir string, chainID int64, txHash string) *btcjson.GetTransactionResult { | |
name := path.Join(dir, TestDataPathBTC, FileNameBTCTransaction(chainID, txHash)) | |
tx := &btcjson.GetTransactionResult{} | |
LoadObjectFromJSONFile(t, tx, name) | |
return tx | |
} | |
// LoadBTCTransaction loads archived Bitcoin transaction from file | |
func LoadBTCTransaction(t *testing.T, dir string, chainID int64, txHash string) *btcjson.GetTransactionResult { | |
require.NotEmpty(t, dir, "directory path cannot be empty") | |
require.NotEmpty(t, txHash, "transaction hash cannot be empty") | |
require.Greater(t, chainID, int64(0), "chain ID must be positive") | |
name := path.Join(dir, TestDataPathBTC, FileNameBTCTransaction(chainID, txHash)) | |
tx := &btcjson.GetTransactionResult{} | |
LoadObjectFromJSONFile(t, tx, name) | |
return tx | |
} |
func Test_CopyMsgTxNoWitness(t *testing.T) { | ||
chain := chains.BitcoinMainnet | ||
txid := "030cd813443f7b70cc6d8a544d320c6d8465e4528fc0f3410b599dc0b26753a0" | ||
msgTx := testutils.LoadBTCMsgTx(t, TestDataDir, chain.ChainId, txid) | ||
|
||
// make a non-witness copy | ||
copyTx := signer.CopyMsgTxNoWitness(msgTx) | ||
|
||
// make another copy and clear witness data manually | ||
newTx := msgTx.Copy() | ||
for idx := range newTx.TxIn { | ||
newTx.TxIn[idx].Witness = wire.TxWitness{} | ||
} | ||
|
||
// check | ||
require.Equal(t, newTx, copyTx) | ||
} |
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.
🛠️ Refactor suggestion
Add test case for nil input.
The test should include a case for handling nil input to ensure proper error handling.
func Test_CopyMsgTxNoWitness(t *testing.T) {
+ t.Run("should handle nil input", func(t *testing.T) {
+ require.Panics(t, func() {
+ signer.CopyMsgTxNoWitness(nil)
+ }, "should panic on nil input")
+ })
Committable suggestion skipped: line range outside the PR's diff.
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.
First review, haven't check in details the change in ZetaClient for now.
We need smaller PRs in the future.
This one could have been break down in a few PRs
@@ -317,6 +317,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { | |||
e2etests.TestBitcoinWithdrawP2WSHName, | |||
e2etests.TestBitcoinWithdrawMultipleName, | |||
e2etests.TestBitcoinWithdrawRestrictedName, | |||
//e2etests.TestBitcoinWithdrawRBFName, // leave it as the last BTC test |
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.
Why is it disabled for now?
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.
only RBF test requires setting minTxConfirmations = 1
to fake and simulate a pending outbound in mempool. All other tests are supposed to be ran under minTxConfirmations = 0
, which is the case for mainnet/testnet.
// wgDeposit is a wait group for deposit runner to finish | ||
var wgDepositRunner sync.WaitGroup | ||
|
||
func init() { |
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.
Let's not use init in e2e package why do we need this waitgroup?
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.
The TestBitcoinWithdrawRBFName
needs Bitcoin block generation to stop, so it has to run as the last Bitcoin E2E test after all other tests have completed (so height stop growing). This is to allow the broadcasted outbound tx to be pending in the mempool for a while. Otherwise, all mempool txs will be cleared on every new block and there is no chance to replace the pending tx.
@@ -0,0 +1,23 @@ | |||
package math |
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.
Can we add a package comment explaining why this package is needed?
x/crosschain/keeper/abci.go
Outdated
return math.ZeroUint(), math.ZeroUint(), nil | ||
} | ||
|
||
// GetCctxGasPriceUpdater returns the function to update gas price according to chain type |
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.
Need more details what does this function represent
x/crosschain/keeper/abci.go
Outdated
} | ||
|
||
// GetCctxGasPriceUpdater returns the function to update gas price according to chain type | ||
func GetCctxGasPriceUpdater(chainID int64, additionalChains []zetachains.Chain) (CheckAndUpdateCctxGasPriceFunc, bool) { |
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.
Why creating this getter function instead of a CheckAndUpdateCctxGasPrice
that dispatch to the correct function?
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.
That's a good idea. I can bring the method CheckAndUpdateCCTXGasPrice
back and wrap some common code (like below sanity check code) and dispatch it to CheckAndUpdateCCTXGasPriceEVM
and CheckAndUpdateCCTXGasRateBTC
) (math.Uint, math.Uint, error) { | ||
// skip if gas price or gas limit is not set | ||
if cctx.GetCurrentOutboundParam().GasPrice == "" || cctx.GetCurrentOutboundParam().CallOptions.GasLimit == 0 { | ||
return math.ZeroUint(), math.ZeroUint(), nil | ||
} | ||
|
||
// skip if retry interval is not reached | ||
lastUpdated := time.Unix(cctx.CctxStatus.LastUpdateTimestamp, 0) | ||
if ctx.BlockTime().Before(lastUpdated.Add(flags.RetryInterval)) { | ||
return math.ZeroUint(), math.ZeroUint(), nil | ||
} | ||
|
||
// compute gas price increase | ||
chainID := cctx.GetCurrentOutboundParam().ReceiverChainId | ||
medianGasPrice, _, isFound := k.GetMedianGasValues(ctx, chainID) | ||
if !isFound { | ||
return math.ZeroUint(), math.ZeroUint(), cosmoserrors.Wrap( | ||
types.ErrUnableToGetGasPrice, | ||
fmt.Sprintf("cannot get gas price for chain %d", chainID), | ||
) | ||
} |
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.
Can't this be put in common between BTC and EVM?
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.
Given recent incidents & Orchestrator V2, revamp, I think this PR requires 2 prerequisites before thorough review:
- Merging changes from
develop
as it has many conflicts - Implementing a custom rpc mentioned in zetaclient stops connecting to external chain RPCs after receiving HTTP error codes. #3328
I started work on (2). Will also take a closer look on this PR later today.
Description
The goals:
The general idea:
RBF
flag in every Bitcoin outbound.zetacore
feed latest fee rate (every 10 mins) to pending CCTXs in the newly created methodCheckAndUpdateCctxGasRateBTC
.WatchMempoolTxs
inzetaclient
to monitor pending outbound txs, so we know how long the txs have been sitting in the Bitcoin mempool.zetaclient
will mark the outbound status asstuck
and trigger tx replacement usingRBF
andCPFP
.gasRateCap = 100
zetaclient
will always use most recent fee rate (feed byzetacore
) to initiate new outbound transactions. The reason is that using an outdatedGasPrice
in CCTX struct is usually the root cause (not the Bitcoin network traffic) of stuck transactions, the low-fee problem needs to be solved at the first place to reduce the possibility of stuck txs.Some concepts and parameters:
LastPendingOutbound
:Bitcoin outbounds are sequentially chained transactions by nonce. Given
N
pending txs[TX1, TX2, TX3, TX4]
in the mempool,zetaclient
only need to watch and bump the fee ofTX4
to unblock all of them. According to BitcoinCPFP
strategy, the chained pending txs are treated as a package by miners. BumpingTX4
will increase the average fee rate of the whole txs package and make it more attractive to miners.minCPFPFeeBumpPercent
:It is set to
20%
as an exercise parameter designed to balance effectiveness in replacing stuck tx while avoiding excessive sensitivity to fee market fluctuations. For example, given apaidRate == 10
, RBF will not happen until the market rate goes up toliveRate==12
.gasRateCap
:It is the maximum average fee rate for fee bumping.
100 sat/vB
is a chosen heuristic based on Bitcoin mempool statistics to avoid excessive (or accidental) fees.reservedRBFFees
:It is the amount of BTC reserved in the outbound transaction for fee bumping. It is set to
0.01 BTC
by default, which can bmp 10 transactions (1KB each) by100 sat/vB
. Most of the time, we have just 1 or 2 stuck transactions in the mempool and the signers will automatically stop signing new transactions by design, so the number0.01 BTC
is good enough.How Has This Been Tested?
Summary by CodeRabbit
Based on the comprehensive summary, here are the release notes:
Release Notes
New Features
Improvements
Testing
Bug Fixes
Chores