Skip to content
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: DON'T review before merging previous PRs, (BitcoinRBF-Step3) zetaclient watches BTC mempool stuck txs and bump fee #3396

Draft
wants to merge 56 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
8fae53a
minimum code refactor for bitcoin RBF
ws4charlie Jan 21, 2025
2481654
add changelog entry
ws4charlie Jan 21, 2025
111d703
add unit test for FeeRateToSatPerByte
ws4charlie Jan 21, 2025
37f4ebb
make changelog descriptive; rename specialHandleFeeRate as GetFeeRate…
ws4charlie Jan 21, 2025
e3f2bc6
renaming sample function; make switch case in PostGasPrice to estimat…
ws4charlie Jan 22, 2025
28bd97c
Merge branch 'develop' of https://github.com/zeta-chain/node into fea…
ws4charlie Jan 22, 2025
009b55b
remove redundant variable description
ws4charlie Jan 22, 2025
09fb0ac
make AddWithdrawTxOutputs a one-line call
ws4charlie Jan 23, 2025
aeceb26
implementation of Bitcoin RBF transaction
ws4charlie Jan 23, 2025
6c8f9f4
switch to bitcoin-core image with mempool RPCs enabled
ws4charlie Jan 23, 2025
6803a7c
add changelog entry
ws4charlie Jan 23, 2025
c501cf2
Merge branch 'develop' of https://github.com/zeta-chain/node into fea…
ws4charlie Jan 25, 2025
1d2afef
rename sample function BTCAddressP2WPKH
ws4charlie Jan 25, 2025
1033a91
remove unused test and comment
ws4charlie Jan 27, 2025
bccf7e9
Merge branch 'develop' into feat-bitcoin-RBF-zetaclient-refactor-minimum
ws4charlie Jan 28, 2025
25cdfc7
Merge branch 'develop' of https://github.com/zeta-chain/node into fea…
ws4charlie Jan 28, 2025
199bf2e
use 1.5 as multiplier to send outbound txs so that we slightly make p…
ws4charlie Jan 28, 2025
c156321
Merge branch 'develop' into feat-bitcoin-RBF-zetaclient-implementation
ws4charlie Jan 28, 2025
f4ab7a2
Merge branch 'develop' into feat-bitcoin-RBF-zetaclient-refactor-minimum
ws4charlie Jan 28, 2025
3f5087b
include coin type to error message; make code cleaner
ws4charlie Jan 28, 2025
c31020b
return error if failed to get signer address; add BTCPayToAddrScript …
ws4charlie Jan 28, 2025
58fd340
Update zetaclient/chains/bitcoin/signer/signer.go
ws4charlie Jan 28, 2025
0f0b9aa
remove redundant test functions; use testlog for unit test
ws4charlie Jan 28, 2025
54bb4de
create observer in test suite; use testlog package
ws4charlie Jan 28, 2025
93b3a38
add description to fee estimation formula
ws4charlie Jan 28, 2025
4f83fdb
use structured logs
ws4charlie Jan 29, 2025
6fd51dd
make AddTxInputs independent method
ws4charlie Jan 29, 2025
a69d58b
add comments to explain function arguments; improve error wrapping; c…
ws4charlie Jan 29, 2025
5c09d39
replace ifs with switch case; return original err without overwriting
ws4charlie Jan 29, 2025
c19fa2b
seems safe to remove panic recovery in FetchUTXOs
ws4charlie Jan 29, 2025
0d41413
move Telemetry update to the line before acquiring observer lock
ws4charlie Jan 29, 2025
c0a2a23
use testlog package
ws4charlie Jan 29, 2025
8644a33
use retry package for Bitcoin tx broadcasting; let SaveBroadcastedTx …
ws4charlie Jan 29, 2025
bf9b4c4
use named return values to make GetEstimatedFeeRate more readable
ws4charlie Jan 29, 2025
9600442
move utxo unit tests to utxos.go and improved unit tests
ws4charlie Jan 30, 2025
8040ac1
wrap RPC error in LoadLastBlockScanned
ws4charlie Jan 30, 2025
a64f738
move last scanned block to log field; use Opt function for test suite
ws4charlie Jan 30, 2025
bb95935
move values to log fields
ws4charlie Jan 30, 2025
84f4b5c
Merge branch 'develop' of https://github.com/zeta-chain/node into fea…
ws4charlie Jan 30, 2025
13efedc
add unit test for FetchUTXOs
ws4charlie Jan 30, 2025
416bd03
add unit for SignWithdrawTx; use structured log
ws4charlie Jan 30, 2025
1b369ca
Merge branch 'develop' into feat-bitcoin-RBF-zetaclient-refactor-minimum
ws4charlie Jan 30, 2025
79db16e
avoid creating log field map and add log fields right on the logger
ws4charlie Jan 30, 2025
0349246
fix client.GetEstimatedFeeRate
swift1337 Jan 31, 2025
6e8f900
Fix loadBroadcastedTxMap
swift1337 Jan 31, 2025
8cddb38
Fix SelectedUTXOs
swift1337 Jan 31, 2025
01e50df
Fix log naming
swift1337 Jan 31, 2025
d556b7e
fix e2e logging
swift1337 Jan 31, 2025
4c6b5bb
Fix setPendingNonce
swift1337 Jan 31, 2025
ed986e0
Merge branch 'develop' of https://github.com/zeta-chain/node into fea…
ws4charlie Jan 31, 2025
1df7b53
Merge branch 'feat-bitcoin-RBF-zetaclient-refactor-minimum' of https:…
ws4charlie Jan 31, 2025
3ab710e
don't use GasPriorityFee as it's always empty
ws4charlie Jan 31, 2025
f7e72cc
Merge branch 'develop' of https://github.com/zeta-chain/node into fea…
ws4charlie Jan 31, 2025
3925b6e
Merge branch 'feat-bitcoin-RBF-zetaclient-refactor-minimum' of https:…
ws4charlie Feb 3, 2025
f3cfdb6
Merge branch 'develop' into feat-bitcoin-RBF-zetaclient-implementation
ws4charlie Feb 3, 2025
03d366c
check if fee rate is bumped or not in zetaclient
ws4charlie Feb 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
* [3368](https://github.com/zeta-chain/node/pull/3368) - cli command to fetch inbound ballot from inbound hash added to zetatools.
* [3425](https://github.com/zeta-chain/node/pull/3425) - enable inscription parsing on Bitcoin mainnet

### Features

* [3396](https://github.com/zeta-chain/node/pull/3396) - add support for Bitcoin RBF (Replace-By-Fee) in zetaclient

### Refactor

* [3332](https://github.com/zeta-chain/node/pull/3332) - implement orchestrator V2. Move BTC observer-signer to V2
Expand Down
2 changes: 1 addition & 1 deletion contrib/localnet/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ services:
ipv4_address: 172.20.0.102

bitcoin:
image: ghcr.io/zeta-chain/bitcoin-core-docker:a94b52f
image: ghcr.io/zeta-chain/bitcoin-core-docker:28.0-zeta6
container_name: bitcoin
hostname: bitcoin
networks:
Expand Down
40 changes: 40 additions & 0 deletions pkg/math/integer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Package implements helper functions for integer math operations.
package math

import (
"math"
"math/big"
)

// IncreaseIntByPercent is a function that increases integer by a percentage.
// Example1: IncreaseIntByPercent(10, 15) = 10 * 1.15 = 11
// Example2: IncreaseIntByPercent(-10, 15) = -10 * 1.15 = -11
//
// Note: use with caution if passing negative values.
func IncreaseIntByPercent(value int64, percent uint32) int64 {
if percent == 0 {
return value
}

if value < 0 {
return -IncreaseIntByPercent(-value, percent)
}

bigValue := big.NewInt(value)
bigPercent := big.NewInt(int64(percent))

// product = value * percent
product := new(big.Int).Mul(bigValue, bigPercent)

// dividing product by 100
product.Div(product, big.NewInt(100))

// result = original value + product
result := new(big.Int).Add(bigValue, product)

// be mindful if result > MaxInt64
if result.Cmp(big.NewInt(math.MaxInt64)) > 0 {
return math.MaxInt64
}
return result.Int64()
}
31 changes: 31 additions & 0 deletions pkg/math/integer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package math

import (
"fmt"
"math"
"testing"

"github.com/stretchr/testify/assert"
)

func Test_IncreaseIntByPercent(t *testing.T) {
for i, tt := range []struct {
value int64
percent uint32
expected int64
}{
{value: 10, percent: 0, expected: 10},
{value: 10, percent: 15, expected: 11},
{value: 10, percent: 225, expected: 32},
{value: math.MaxInt64 / 2, percent: 101, expected: math.MaxInt64},
{value: -10, percent: 0, expected: -10},
{value: -10, percent: 15, expected: -11},
{value: -10, percent: 225, expected: -32},
{value: -math.MaxInt64 / 2, percent: 101, expected: -math.MaxInt64},
} {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
result := IncreaseIntByPercent(tt.value, tt.percent)
assert.Equal(t, tt.expected, result)
})
}
}
19 changes: 14 additions & 5 deletions testutil/sample/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import (
"strconv"
"testing"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/cometbft/cometbft/crypto/secp256k1"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
secp "github.com/decred/dcrd/dcrec/secp256k1/v4"
ethcommon "github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -90,16 +91,24 @@ func EthAddressFromRand(r *rand.Rand) ethcommon.Address {
return ethcommon.BytesToAddress(sdk.AccAddress(PubKey(r).Address()).Bytes())
}

// BtcAddressP2WPKH returns a sample btc P2WPKH address
func BtcAddressP2WPKH(t *testing.T, net *chaincfg.Params) string {
privateKey, err := btcec.NewPrivateKey()
// BTCAddressP2WPKH returns a sample Bitcoin Pay-to-Witness-Public-Key-Hash (P2WPKH) address
func BTCAddressP2WPKH(t *testing.T, r *rand.Rand, net *chaincfg.Params) *btcutil.AddressWitnessPubKeyHash {
privateKey, err := secp.GeneratePrivateKeyFromRand(r)
require.NoError(t, err)

pubKeyHash := btcutil.Hash160(privateKey.PubKey().SerializeCompressed())
addr, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, net)
require.NoError(t, err)

return addr.String()
return addr
}

// BtcAddressP2WPKH returns a pkscript for a sample btc P2WPKH address
func BTCAddressP2WPKHScript(t *testing.T, r *rand.Rand, net *chaincfg.Params) []byte {
addr := BTCAddressP2WPKH(t, r, net)
script, err := txscript.PayToAddrScript(addr)
require.NoError(t, err)
return script
}

// SolanaPrivateKey returns a sample solana private key
Expand Down
3 changes: 2 additions & 1 deletion x/crosschain/types/cctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,11 @@ func Test_SetRevertOutboundValues(t *testing.T) {
})

t.Run("successfully set BTC revert address V1", func(t *testing.T) {
r := sample.Rand()
cctx := sample.CrossChainTx(t, "test")
cctx.InboundParams.SenderChainId = chains.BitcoinTestnet.ChainId
cctx.OutboundParams = cctx.OutboundParams[:1]
cctx.RevertOptions.RevertAddress = sample.BtcAddressP2WPKH(t, &chaincfg.TestNet3Params)
cctx.RevertOptions.RevertAddress = sample.BTCAddressP2WPKH(t, r, &chaincfg.TestNet3Params).String()

err := cctx.AddRevertOutbound(100)
require.NoError(t, err)
Expand Down
3 changes: 2 additions & 1 deletion x/crosschain/types/revert_options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ func TestRevertOptions_GetEVMRevertAddress(t *testing.T) {

func TestRevertOptions_GetBTCRevertAddress(t *testing.T) {
t.Run("valid Bitcoin revert address", func(t *testing.T) {
addr := sample.BtcAddressP2WPKH(t, &chaincfg.TestNet3Params)
r := sample.Rand()
addr := sample.BTCAddressP2WPKH(t, r, &chaincfg.TestNet3Params).String()
actualAddr, valid := types.RevertOptions{
RevertAddress: addr,
}.GetBTCRevertAddress(chains.BitcoinTestnet.ChainId)
Expand Down
4 changes: 4 additions & 0 deletions zetaclient/chains/bitcoin/bitcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/zeta-chain/node/zetaclient/chains/base"
"github.com/zeta-chain/node/zetaclient/chains/bitcoin/observer"
"github.com/zeta-chain/node/zetaclient/chains/bitcoin/signer"
"github.com/zeta-chain/node/zetaclient/common"
zctx "github.com/zeta-chain/node/zetaclient/context"
)

Expand Down Expand Up @@ -62,6 +63,8 @@ func (b *Bitcoin) Start(ctx context.Context) error {
return ticker.DurationFromUint64Seconds(b.observer.ChainParams().WatchUtxoTicker)
})

optMempoolInterval := scheduler.Interval(common.BTCMempoolStuckTxCheckInterval)

optOutboundInterval := scheduler.IntervalUpdater(func() time.Duration {
return ticker.DurationFromUint64Seconds(b.observer.ChainParams().OutboundTicker)
})
Expand Down Expand Up @@ -91,6 +94,7 @@ func (b *Bitcoin) Start(ctx context.Context) error {
register(b.observer.ObserveInbound, "observe_inbound", optInboundInterval, optInboundSkipper)
register(b.observer.ProcessInboundTrackers, "process_inbound_trackers", optInboundInterval, optInboundSkipper)
register(b.observer.FetchUTXOs, "fetch_utxos", optUTXOInterval, optGenericSkipper)
register(b.observer.WatchMempoolTxs, "watch_mempool_txs", optMempoolInterval, optGenericSkipper)
register(b.observer.PostGasPrice, "post_gas_price", optGasInterval, optGenericSkipper)
register(b.observer.CheckRPCStatus, "check_rpc_status")
register(b.observer.ProcessOutboundTrackers, "process_outbound_trackers", optOutboundInterval, optOutboundSkipper)
Expand Down
8 changes: 7 additions & 1 deletion zetaclient/chains/bitcoin/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"github.com/rs/zerolog"
"github.com/tendermint/btcd/chaincfg"

pkgchains "github.com/zeta-chain/node/pkg/chains"
"github.com/zeta-chain/node/zetaclient/config"
"github.com/zeta-chain/node/zetaclient/logs"
"github.com/zeta-chain/node/zetaclient/metrics"
Expand All @@ -48,6 +49,7 @@
hostURL string
client *http.Client
clientName string
isRegnet bool
config config.BTCConfig
params chains.Params
logger zerolog.Logger
Expand Down Expand Up @@ -81,14 +83,18 @@
return nil, errors.Wrap(err, "unable to resolve chain params")
}

clientName := fmt.Sprintf("btc:%d", chainID)
var (
clientName = fmt.Sprintf("btc:%d", chainID)
isRegnet = pkgchains.IsBitcoinRegnet(chainID)
)

Check warning on line 89 in zetaclient/chains/bitcoin/client/client.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/client/client.go#L86-L89

Added lines #L86 - L89 were not covered by tests

c := &Client{
hostURL: normalizeHostURL(cfg.RPCHost, true),
client: defaultHTTPClient(),
config: cfg,
params: params,
clientName: clientName,
isRegnet: isRegnet,

Check warning on line 97 in zetaclient/chains/bitcoin/client/client.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/client/client.go#L97

Added line #L97 was not covered by tests
logger: logger.With().
Str(logs.FieldModule, "btc_client").
Int64(logs.FieldChain, chainID).
Expand Down
Loading