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: WIP (DON'T review, code will change a lot) bitcoin transaction replace by fee #3306

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ require (
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/edwards/v2 v2.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
Expand Down
16 changes: 14 additions & 2 deletions x/crosschain/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ func (k Keeper) IterateAndUpdateCctxGasPrice(

IterateChains:
for _, chain := range chains {
// support only external evm chains
if zetachains.IsEVMChain(chain.ChainId, additionalChains) && !zetachains.IsZetaChain(chain.ChainId, additionalChains) {
// support only external evm chains and bitcoin chain
if IsGasStabilityPoolEnabledChain(chain.ChainId, additionalChains) {
res, err := k.ListPendingCctx(sdk.UnwrapSDKContext(ctx), &types.QueryListPendingCctxRequest{
ChainId: chain.ChainId,
Limit: gasPriceIncreaseFlags.MaxPendingCctxs,
Expand Down Expand Up @@ -175,3 +175,15 @@ func CheckAndUpdateCctxGasPrice(

return gasPriceIncrease, additionalFees, nil
}

// IsGasStabilityPoolEnabledChain returns true if given chainID is enabled for gas stability pool
func IsGasStabilityPoolEnabledChain(chainID int64, additionalChains []zetachains.Chain) bool {
switch {
case zetachains.IsEVMChain(chainID, additionalChains):
return !zetachains.IsZetaChain(chainID, additionalChains)
case zetachains.IsBitcoinChain(chainID, additionalChains):
return true
default:
return false
}
}
46 changes: 46 additions & 0 deletions x/crosschain/keeper/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/zeta-chain/node/pkg/chains"
zetachains "github.com/zeta-chain/node/pkg/chains"
testkeeper "github.com/zeta-chain/node/testutil/keeper"
"github.com/zeta-chain/node/testutil/sample"
"github.com/zeta-chain/node/x/crosschain/keeper"
Expand Down Expand Up @@ -449,3 +450,48 @@ func TestCheckAndUpdateCctxGasPrice(t *testing.T) {
})
}
}

func TestIsGasStabilityPoolEnabledChain(t *testing.T) {
tests := []struct {
name string
chainID int64
expected bool
}{
{
name: "Ethereum is enabled",
chainID: chains.Ethereum.ChainId,
expected: true,
},
{
name: "Binance Smart Chain is enabled",
chainID: chains.BscMainnet.ChainId,
expected: true,
},
{
name: "Bitcoin is enabled",
chainID: chains.BitcoinMainnet.ChainId,
expected: true,
},
{
name: "ZetaChain is not enabled",
chainID: chains.ZetaChainMainnet.ChainId,
expected: false,
},
{
name: "Solana is not enabled",
chainID: chains.SolanaMainnet.ChainId,
expected: false,
},
{
name: "TON is not enabled",
chainID: chains.TONMainnet.ChainId,
expected: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.expected, keeper.IsGasStabilityPoolEnabledChain(tt.chainID, []zetachains.Chain{}))
})
}
}
2 changes: 1 addition & 1 deletion x/observer/types/crosschain_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import "time"

var DefaultGasPriceIncreaseFlags = GasPriceIncreaseFlags{
// EpochLength is the number of blocks in an epoch before triggering a gas price increase

EpochLength: 100,

// RetryInterval is the number of blocks to wait before incrementing the gas price again
RetryInterval: time.Minute * 10,

Expand Down
2 changes: 1 addition & 1 deletion zetaclient/chains/bitcoin/fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ const (
bytesPerWitness = 108 // each additional witness incurs about 108 bytes and it may vary
OutboundBytesMin = uint64(239) // 239vB == EstimateSegWitTxSize(2, 2, toP2WPKH)
OutboundBytesMax = uint64(1543) // 1543v == EstimateSegWitTxSize(21, 2, toP2TR)
OutboundBytesAvg = uint64(245) // 245vB is a suggested gas limit for zetacore

// defaultDepositorFeeRate is the default fee rate for depositor fee, 20 sat/vB
defaultDepositorFeeRate = 20
Expand All @@ -49,6 +48,7 @@ var (
BtcOutboundBytesDepositor = OutboundSizeDepositor()

// BtcOutboundBytesWithdrawer is the outbound size incurred by the withdrawer: 177vB
// This will be the suggested gas limit used for zetacore
BtcOutboundBytesWithdrawer = OutboundSizeWithdrawer()

// DefaultDepositorFee is the default depositor fee is 0.00001360 BTC (20 * 68vB / 100000000)
Expand Down
3 changes: 1 addition & 2 deletions zetaclient/chains/bitcoin/fee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ func TestOutboundSizeBreakdown(t *testing.T) {
txSizeTotal += sizeOutput
}

// calculate the average outbound size
// calculate the average outbound size (245 vByte)
// #nosec G115 always in range
txSizeAverage := uint64((float64(txSizeTotal))/float64(len(payees)) + 0.5)

Expand All @@ -433,7 +433,6 @@ func TestOutboundSizeBreakdown(t *testing.T) {
require.Equal(t, uint64(177), txSizeWithdrawer)

// total outbound size == (deposit fee + withdrawer fee), 245 = 68 + 177
require.Equal(t, OutboundBytesAvg, txSizeAverage)
require.Equal(t, txSizeAverage, txSizeDepositor+txSizeWithdrawer)

// check default depositor fee
Expand Down
2 changes: 1 addition & 1 deletion zetaclient/chains/bitcoin/observer/observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ func (ob *Observer) PostGasPrice(ctx context.Context) error {
return errors.Wrap(err, "GetBlockCount error")
}

// UTXO has no concept of priority fee (like eth)
// Bitcoin has no concept of priority fee (like eth)
const priorityFee = 0

// #nosec G115 always positive
Expand Down
104 changes: 42 additions & 62 deletions zetaclient/chains/bitcoin/rpc/rpc_live_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ func createRPCClient(chainID int64) (*rpcclient.Client, error) {
var connCfg *rpcclient.ConnConfig
rpcMainnet := os.Getenv(common.EnvBtcRPCMainnet)
rpcTestnet := os.Getenv(common.EnvBtcRPCTestnet)
rpcTestnet4 := "localhost:48332" // os.Getenv(common.EnvBtcRPCTestnet4)

// mainnet
if chainID == chains.BitcoinMainnet.ChainId {
switch chainID {
case chains.BitcoinMainnet.ChainId:
connCfg = &rpcclient.ConnConfig{
Host: rpcMainnet, // mainnet endpoint goes here
User: "user",
Expand All @@ -44,9 +45,7 @@ func createRPCClient(chainID int64) (*rpcclient.Client, error) {
HTTPPostMode: true,
DisableTLS: true,
}
}
// testnet3
if chainID == chains.BitcoinTestnet.ChainId {
case chains.BitcoinTestnet.ChainId:
connCfg = &rpcclient.ConnConfig{
Host: rpcTestnet, // testnet endpoint goes here
User: "user",
Expand All @@ -55,7 +54,19 @@ func createRPCClient(chainID int64) (*rpcclient.Client, error) {
HTTPPostMode: true,
DisableTLS: true,
}
case chains.BitcoinTestnet4.ChainId:
connCfg = &rpcclient.ConnConfig{
Host: rpcTestnet4, // testnet endpoint goes here
User: "admin",
Pass: "admin",
Params: "testnet3", // testnet4 uses testnet3 network name
HTTPPostMode: true,
DisableTLS: true,
}
default:
return nil, errors.New("unsupported chain")
}

return rpcclient.New(connCfg, nil)
}

Expand Down Expand Up @@ -101,19 +112,31 @@ func getMempoolSpaceTxsByBlock(
return blkHash, mempoolTxs, nil
}

// Test_BitcoinLive is a phony test to run each live test individually
// Test_BitcoinLive is a test to run all Bitcoin live tests
func Test_BitcoinLive(t *testing.T) {
// LiveTest_FilterAndParseIncomingTx(t)
// LiveTest_FilterAndParseIncomingTx_Nop(t)
// LiveTest_NewRPCClient(t)
// LiveTest_GetBlockHeightByHash(t)
// LiveTest_BitcoinFeeRate(t)
// LiveTest_AvgFeeRateMainnetMempoolSpace(t)
// LiveTest_AvgFeeRateTestnetMempoolSpace(t)
// LiveTest_GetRecentFeeRate(t)
// LiveTest_GetSenderByVin(t)
// LiveTest_GetTransactionFeeAndRate(t)
// LiveTest_CalcDepositorFeeV2(t)
if !common.LiveTestEnabled() {
return
}

LiveTest_NewRPCClient(t)
LiveTest_CheckRPCStatus(t)
LiveTest_FilterAndParseIncomingTx(t)
LiveTest_GetBlockHeightByHash(t)
LiveTest_GetSenderByVin(t)
}

// Test_BitcoinFeeLive is a test to run all Bitcoin fee related live tests
func Test_BitcoinFeeLive(t *testing.T) {
if !common.LiveTestEnabled() {
return
}

LiveTest_BitcoinFeeRate(t)
LiveTest_AvgFeeRateMainnetMempoolSpace(t)
LiveTest_AvgFeeRateTestnetMempoolSpace(t)
LiveTest_GetRecentFeeRate(t)
LiveTest_GetTransactionFeeAndRate(t)
LiveTest_CalcDepositorFee(t)
}

func LiveTest_FilterAndParseIncomingTx(t *testing.T) {
Expand All @@ -140,7 +163,7 @@ func LiveTest_FilterAndParseIncomingTx(t *testing.T) {
)
require.NoError(t, err)
require.Len(t, inbounds, 1)
require.Equal(t, inbounds[0].Value, 0.0001)
require.Equal(t, inbounds[0].Value+inbounds[0].DepositorFee, 0.0001)
require.Equal(t, inbounds[0].ToAddress, "tb1qsa222mn2rhdq9cruxkz8p2teutvxuextx3ees2")

// the text memo is base64 std encoded string:DSRR1RmDCwWmxqY201/TMtsJdmA=
Expand All @@ -153,49 +176,6 @@ func LiveTest_FilterAndParseIncomingTx(t *testing.T) {
require.Equal(t, inbounds[0].TxHash, "889bfa69eaff80a826286d42ec3f725fd97c3338357ddc3a1f543c2d6266f797")
}

func LiveTest_FilterAndParseIncomingTx_Nop(t *testing.T) {
// setup Bitcoin client
client, err := createRPCClient(chains.BitcoinTestnet.ChainId)
require.NoError(t, err)

// get a block that contains no incoming tx
hashStr := "000000000000002fd8136dbf91708898da9d6ae61d7c354065a052568e2f2888"
hash, err := chainhash.NewHashFromStr(hashStr)
require.NoError(t, err)

block, err := client.GetBlockVerboseTx(hash)
require.NoError(t, err)

// filter incoming tx
inbounds, err := observer.FilterAndParseIncomingTx(
client,
block.Tx,
uint64(block.Height),
"tb1qsa222mn2rhdq9cruxkz8p2teutvxuextx3ees2",
log.Logger,
&chaincfg.TestNet3Params,
)

require.NoError(t, err)
require.Empty(t, inbounds)
}

// TestBitcoinObserverLive is a phony test to run each live test individually
func TestBitcoinObserverLive(t *testing.T) {
if !common.LiveTestEnabled() {
return
}

LiveTest_NewRPCClient(t)
LiveTest_CheckRPCStatus(t)
LiveTest_GetBlockHeightByHash(t)
LiveTest_BitcoinFeeRate(t)
LiveTest_AvgFeeRateMainnetMempoolSpace(t)
LiveTest_AvgFeeRateTestnetMempoolSpace(t)
LiveTest_GetRecentFeeRate(t)
LiveTest_GetSenderByVin(t)
}

// LiveTestNewRPCClient creates a new Bitcoin RPC client
func LiveTest_NewRPCClient(t *testing.T) {
btcConfig := config.BTCConfig{
Expand Down Expand Up @@ -500,7 +480,7 @@ func LiveTest_GetTransactionFeeAndRate(t *testing.T) {
// calculates block range to test
startBlock, err := client.GetBlockCount()
require.NoError(t, err)
endBlock := startBlock - 100 // go back whatever blocks as needed
endBlock := startBlock - 1 // go back whatever blocks as needed

// loop through mempool.space blocks backwards
for bn := startBlock; bn >= endBlock; {
Expand Down
Loading
Loading