Skip to content

Commit

Permalink
moved pure RPC methods to rpc package
Browse files Browse the repository at this point in the history
  • Loading branch information
ws4charlie committed Jun 20, 2024
1 parent cdbf149 commit 625e633
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 130 deletions.
54 changes: 20 additions & 34 deletions cmd/zetaclientd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ package main
import (
"fmt"

"github.com/btcsuite/btcd/rpcclient"
sdk "github.com/cosmos/cosmos-sdk/types"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"

"github.com/zeta-chain/zetacore/zetaclient/authz"
"github.com/zeta-chain/zetacore/zetaclient/chains/base"
btcobserver "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer"
btcrpc "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc"
btcsigner "github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/signer"
evmobserver "github.com/zeta-chain/zetacore/zetaclient/chains/evm/observer"
evmsigner "github.com/zeta-chain/zetacore/zetaclient/chains/evm/signer"
Expand Down Expand Up @@ -168,41 +168,27 @@ func CreateChainObserverMap(
// create BTC chain observer
btcChain, btcConfig, enabled := appContext.GetBTCChainAndConfig()
if enabled {
// create BTC client
connCfg := &rpcclient.ConnConfig{
Host: btcConfig.RPCHost,
User: btcConfig.RPCUsername,
Pass: btcConfig.RPCPassword,
HTTPPostMode: true,
DisableTLS: true,
Params: btcConfig.RPCParams,
}
btcClient, err := rpcclient.New(connCfg, nil)
if err != nil {
return nil, fmt.Errorf("error creating rpc client: %s", err)
}
err = btcClient.Ping()
btcClient, err := btcrpc.NewRPCClient(btcConfig)
if err != nil {
return nil, fmt.Errorf("error ping the bitcoin server: %s", err)
}

// create BTC chain observer
co, err := btcobserver.NewObserver(
btcChain,
btcClient,
*chainParams,
zetacoreContext,
zetacoreClient,
tss,
dbpath,
logger,
ts,
)
if err != nil {
logger.Std.Error().Err(err).Msgf("NewObserver error for bitcoin chain %s", btcChain.String())

logger.Std.Error().Err(err).Msgf("error creating rpc client for bitcoin chain %s", btcChain.String())
} else {
observerMap[btcChain.ChainId] = co
// create BTC chain observer
co, err := btcobserver.NewObserver(
btcChain,
btcClient,
*chainParams,
zetacoreContext,
zetacoreClient,
tss,
dbpath,
logger,
ts,
)
if err != nil {
logger.Std.Error().Err(err).Msgf("NewObserver error for bitcoin chain %s", btcChain.String())
} else {
observerMap[btcChain.ChainId] = co
}
}
}

Expand Down
73 changes: 0 additions & 73 deletions zetaclient/chains/bitcoin/observer/observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,79 +531,6 @@ func (ob *Observer) SaveBroadcastedTx(txHash string, nonce uint64) {
ob.logger.Outbound.Info().Msgf("SaveBroadcastedTx: saved broadcasted txHash %s for outbound %s", txHash, outboundID)
}

// GetTxResultByHash gets the transaction result by hash
func GetTxResultByHash(
rpcClient interfaces.BTCRPCClient,
txID string,
) (*chainhash.Hash, *btcjson.GetTransactionResult, error) {
hash, err := chainhash.NewHashFromStr(txID)
if err != nil {
return nil, nil, errors.Wrapf(err, "GetTxResultByHash: error NewHashFromStr: %s", txID)
}

// The Bitcoin node has to be configured to watch TSS address
txResult, err := rpcClient.GetTransaction(hash)
if err != nil {
return nil, nil, errors.Wrapf(err, "GetTxResultByHash: error GetTransaction %s", hash.String())
}
return hash, txResult, nil
}

// GetBlockHeightByHash gets the block height by block hash
func GetBlockHeightByHash(
rpcClient interfaces.BTCRPCClient,
hash string,
) (int64, error) {
// decode the block hash
var blockHash chainhash.Hash
err := chainhash.Decode(&blockHash, hash)
if err != nil {
return 0, errors.Wrapf(err, "GetBlockHeightByHash: error decoding block hash %s", hash)
}

// get block by hash
block, err := rpcClient.GetBlockVerbose(&blockHash)
if err != nil {
return 0, errors.Wrapf(err, "GetBlockHeightByHash: error GetBlockVerbose %s", hash)
}
return block.Height, nil
}

// GetRawTxResult gets the raw tx result
func GetRawTxResult(
rpcClient interfaces.BTCRPCClient,
hash *chainhash.Hash,
res *btcjson.GetTransactionResult,
) (btcjson.TxRawResult, error) {
if res.Confirmations == 0 { // for pending tx, we query the raw tx directly
rawResult, err := rpcClient.GetRawTransactionVerbose(hash) // for pending tx, we query the raw tx
if err != nil {
return btcjson.TxRawResult{}, errors.Wrapf(
err,
"getRawTxResult: error GetRawTransactionVerbose %s",
res.TxID,
)
}
return *rawResult, nil
} else if res.Confirmations > 0 { // for confirmed tx, we query the block
blkHash, err := chainhash.NewHashFromStr(res.BlockHash)
if err != nil {
return btcjson.TxRawResult{}, errors.Wrapf(err, "getRawTxResult: error NewHashFromStr for block hash %s", res.BlockHash)
}
block, err := rpcClient.GetBlockVerboseTx(blkHash)
if err != nil {
return btcjson.TxRawResult{}, errors.Wrapf(err, "getRawTxResult: error GetBlockVerboseTx %s", res.BlockHash)
}
if res.BlockIndex < 0 || res.BlockIndex >= int64(len(block.Tx)) {
return btcjson.TxRawResult{}, errors.Wrapf(err, "getRawTxResult: invalid outbound with invalid block index, TxID %s, BlockIndex %d", res.TxID, res.BlockIndex)
}
return block.Tx[res.BlockIndex], nil
}

// res.Confirmations < 0 (meaning not included)
return btcjson.TxRawResult{}, fmt.Errorf("getRawTxResult: tx %s not included yet", hash)
}

// GetBlockByNumberCached gets cached block (and header) by block number
func (ob *Observer) GetBlockByNumberCached(blockNumber int64) (*BTCBlockNHeader, error) {
if result, ok := ob.BlockCache().Get(blockNumber); ok {
Expand Down
9 changes: 5 additions & 4 deletions zetaclient/chains/bitcoin/observer/outbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/zeta-chain/zetacore/pkg/coin"
crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types"
"github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin"
"github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc"
"github.com/zeta-chain/zetacore/zetaclient/chains/interfaces"
"github.com/zeta-chain/zetacore/zetaclient/compliance"
"github.com/zeta-chain/zetacore/zetaclient/context"
Expand Down Expand Up @@ -163,7 +164,7 @@ func (ob *Observer) IsOutboundProcessed(cctx *crosschaintypes.CrossChainTx, logg
}

// Get outbound block height
blockHeight, err := GetBlockHeightByHash(ob.btcClient, res.BlockHash)
blockHeight, err := rpc.GetBlockHeightByHash(ob.btcClient, res.BlockHash)

Check warning on line 167 in zetaclient/chains/bitcoin/observer/outbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/observer/outbound.go#L167

Added line #L167 was not covered by tests
if err != nil {
return true, false, errors.Wrapf(
err,
Expand Down Expand Up @@ -346,7 +347,7 @@ func (ob *Observer) getOutboundIDByNonce(nonce uint64, test bool) (string, error
return "", fmt.Errorf("getOutboundIDByNonce: cannot find outbound txid for nonce %d", nonce)
}
// make sure it's a real Bitcoin txid
_, getTxResult, err := GetTxResultByHash(ob.btcClient, txid)
_, getTxResult, err := rpc.GetTxResultByHash(ob.btcClient, txid)

Check warning on line 350 in zetaclient/chains/bitcoin/observer/outbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/observer/outbound.go#L350

Added line #L350 was not covered by tests
if err != nil {
return "", errors.Wrapf(
err,
Expand Down Expand Up @@ -387,7 +388,7 @@ func (ob *Observer) checkIncludedTx(
txHash string,
) (*btcjson.GetTransactionResult, bool) {
outboundID := ob.GetTxID(cctx.GetCurrentOutboundParam().TssNonce)
hash, getTxResult, err := GetTxResultByHash(ob.btcClient, txHash)
hash, getTxResult, err := rpc.GetTxResultByHash(ob.btcClient, txHash)

Check warning on line 391 in zetaclient/chains/bitcoin/observer/outbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/observer/outbound.go#L391

Added line #L391 was not covered by tests
if err != nil {
ob.logger.Outbound.Error().Err(err).Msgf("checkIncludedTx: error GetTxResultByHash: %s", txHash)
return nil, false
Expand Down Expand Up @@ -471,7 +472,7 @@ func (ob *Observer) checkTssOutboundResult(
) error {
params := cctx.GetCurrentOutboundParam()
nonce := params.TssNonce
rawResult, err := GetRawTxResult(ob.btcClient, hash, res)
rawResult, err := rpc.GetRawTxResult(ob.btcClient, hash, res)

Check warning on line 475 in zetaclient/chains/bitcoin/observer/outbound.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/observer/outbound.go#L475

Added line #L475 was not covered by tests
if err != nil {
return errors.Wrapf(err, "checkTssOutboundResult: error GetRawTxResultByHash %s", hash.String())
}
Expand Down
109 changes: 109 additions & 0 deletions zetaclient/chains/bitcoin/rpc/rpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package rpc

import (
"fmt"

"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/rpcclient"
"github.com/pkg/errors"

"github.com/zeta-chain/zetacore/zetaclient/chains/interfaces"
"github.com/zeta-chain/zetacore/zetaclient/config"
)

// NewRPCClient creates a new RPC client by the given config.
func NewRPCClient(btcConfig config.BTCConfig) (*rpcclient.Client, error) {
connCfg := &rpcclient.ConnConfig{
Host: btcConfig.RPCHost,
User: btcConfig.RPCUsername,
Pass: btcConfig.RPCPassword,
HTTPPostMode: true,
DisableTLS: true,
Params: btcConfig.RPCParams,

Check warning on line 23 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L16-L23

Added lines #L16 - L23 were not covered by tests
}

rpcClient, err := rpcclient.New(connCfg, nil)
if err != nil {
return nil, fmt.Errorf("error creating rpc client: %s", err)

Check warning on line 28 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L26-L28

Added lines #L26 - L28 were not covered by tests
}

err = rpcClient.Ping()
if err != nil {
return nil, fmt.Errorf("error ping the bitcoin server: %s", err)

Check warning on line 33 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L31-L33

Added lines #L31 - L33 were not covered by tests
}
return rpcClient, nil

Check warning on line 35 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L35

Added line #L35 was not covered by tests
}

// GetTxResultByHash gets the transaction result by hash
func GetTxResultByHash(
rpcClient interfaces.BTCRPCClient,
txID string,
) (*chainhash.Hash, *btcjson.GetTransactionResult, error) {
hash, err := chainhash.NewHashFromStr(txID)
if err != nil {
return nil, nil, errors.Wrapf(err, "GetTxResultByHash: error NewHashFromStr: %s", txID)

Check warning on line 45 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L42-L45

Added lines #L42 - L45 were not covered by tests
}

// The Bitcoin node has to be configured to watch TSS address
txResult, err := rpcClient.GetTransaction(hash)
if err != nil {
return nil, nil, errors.Wrapf(err, "GetTxResultByHash: error GetTransaction %s", hash.String())

Check warning on line 51 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L49-L51

Added lines #L49 - L51 were not covered by tests
}
return hash, txResult, nil

Check warning on line 53 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L53

Added line #L53 was not covered by tests
}

// GetBlockHeightByHash gets the block height by block hash
func GetBlockHeightByHash(
rpcClient interfaces.BTCRPCClient,
hash string,
) (int64, error) {

Check warning on line 60 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L60

Added line #L60 was not covered by tests
// decode the block hash
var blockHash chainhash.Hash
err := chainhash.Decode(&blockHash, hash)
if err != nil {
return 0, errors.Wrapf(err, "GetBlockHeightByHash: error decoding block hash %s", hash)

Check warning on line 65 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L62-L65

Added lines #L62 - L65 were not covered by tests
}

// get block by hash
block, err := rpcClient.GetBlockVerbose(&blockHash)
if err != nil {
return 0, errors.Wrapf(err, "GetBlockHeightByHash: error GetBlockVerbose %s", hash)

Check warning on line 71 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L69-L71

Added lines #L69 - L71 were not covered by tests
}
return block.Height, nil

Check warning on line 73 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L73

Added line #L73 was not covered by tests
}

// GetRawTxResult gets the raw tx result
func GetRawTxResult(
rpcClient interfaces.BTCRPCClient,
hash *chainhash.Hash,
res *btcjson.GetTransactionResult,
) (btcjson.TxRawResult, error) {
if res.Confirmations == 0 { // for pending tx, we query the raw tx directly
rawResult, err := rpcClient.GetRawTransactionVerbose(hash) // for pending tx, we query the raw tx
if err != nil {
return btcjson.TxRawResult{}, errors.Wrapf(
err,
"GetRawTxResult: error GetRawTransactionVerbose %s",
res.TxID,
)

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

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L81-L89

Added lines #L81 - L89 were not covered by tests
}
return *rawResult, nil
} else if res.Confirmations > 0 { // for confirmed tx, we query the block
blkHash, err := chainhash.NewHashFromStr(res.BlockHash)
if err != nil {
return btcjson.TxRawResult{}, errors.Wrapf(err, "GetRawTxResult: error NewHashFromStr for block hash %s", res.BlockHash)

Check warning on line 95 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L91-L95

Added lines #L91 - L95 were not covered by tests
}
block, err := rpcClient.GetBlockVerboseTx(blkHash)
if err != nil {
return btcjson.TxRawResult{}, errors.Wrapf(err, "GetRawTxResult: error GetBlockVerboseTx %s", res.BlockHash)

Check warning on line 99 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L97-L99

Added lines #L97 - L99 were not covered by tests
}
if res.BlockIndex < 0 || res.BlockIndex >= int64(len(block.Tx)) {
return btcjson.TxRawResult{}, errors.Wrapf(err, "GetRawTxResult: invalid outbound with invalid block index, TxID %s, BlockIndex %d", res.TxID, res.BlockIndex)

Check warning on line 102 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L101-L102

Added lines #L101 - L102 were not covered by tests
}
return block.Tx[res.BlockIndex], nil

Check warning on line 104 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L104

Added line #L104 was not covered by tests
}

// res.Confirmations < 0 (meaning not included)
return btcjson.TxRawResult{}, fmt.Errorf("GetRawTxResult: tx %s not included yet", hash)

Check warning on line 108 in zetaclient/chains/bitcoin/rpc/rpc.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/chains/bitcoin/rpc/rpc.go#L108

Added line #L108 was not covered by tests
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package observer_test
package rpc_test

import (
"context"
Expand All @@ -24,6 +24,8 @@ import (
"github.com/zeta-chain/zetacore/zetaclient/chains/base"
"github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin"
"github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/observer"
"github.com/zeta-chain/zetacore/zetaclient/chains/bitcoin/rpc"
"github.com/zeta-chain/zetacore/zetaclient/config"
"github.com/zeta-chain/zetacore/zetaclient/testutils"
"github.com/zeta-chain/zetacore/zetaclient/testutils/mocks"
)
Expand Down Expand Up @@ -207,33 +209,37 @@ func (suite *BitcoinObserverTestSuite) Test2() {
suite.Require().Equal(0, len(inbounds))
}

func (suite *BitcoinObserverTestSuite) Test3() {
client := suite.rpcClient
res, err := client.EstimateSmartFee(1, &btcjson.EstimateModeConservative)
suite.Require().NoError(err)
suite.T().Logf("fee: %f", *res.FeeRate)
suite.T().Logf("blocks: %d", res.Blocks)
suite.T().Logf("errors: %s", res.Errors)
gasPrice := big.NewFloat(0)
gasPriceU64, _ := gasPrice.Mul(big.NewFloat(*res.FeeRate), big.NewFloat(1e8)).Uint64()
suite.T().Logf("gas price: %d", gasPriceU64)

bn, err := client.GetBlockCount()
suite.Require().NoError(err)
suite.T().Logf("block number %d", bn)
}

// TestBitcoinObserverLive is a phony test to run each live test individually
func TestBitcoinObserverLive(t *testing.T) {
// suite.Run(t, new(BitcoinClientTestSuite))

// LiveTestNewRPCClient(t)
// LiveTestGetBlockHeightByHash(t)
// LiveTestBitcoinFeeRate(t)
// LiveTestAvgFeeRateMainnetMempoolSpace(t)
// LiveTestAvgFeeRateTestnetMempoolSpace(t)
// LiveTestGetSenderByVin(t)
}

// LiveTestNewRPCClient creates a new Bitcoin RPC client
func LiveTestNewRPCClient(t *testing.T) {
btcConfig := config.BTCConfig{
RPCUsername: "user",
RPCPassword: "pass",
RPCHost: "bitcoin.rpc.zetachain.com/6315704c-49bc-4649-8b9d-e9278a1dfeb3",
RPCParams: "mainnet",
}

// create Bitcoin RPC client
client, err := rpc.NewRPCClient(btcConfig)
require.NoError(t, err)

// get block count
bn, err := client.GetBlockCount()
require.NoError(t, err)
require.Greater(t, bn, int64(0))
}

// LiveTestGetBlockHeightByHash queries Bitcoin block height by hash
func LiveTestGetBlockHeightByHash(t *testing.T) {
// setup Bitcoin client
Expand All @@ -246,11 +252,11 @@ func LiveTestGetBlockHeightByHash(t *testing.T) {
invalidHash := "invalidhash"

// get block by invalid hash
_, err = observer.GetBlockHeightByHash(client, invalidHash)
_, err = rpc.GetBlockHeightByHash(client, invalidHash)
require.ErrorContains(t, err, "error decoding block hash")

// get block height by block hash
height, err := observer.GetBlockHeightByHash(client, hash)
height, err := rpc.GetBlockHeightByHash(client, hash)
require.NoError(t, err)
require.Equal(t, expectedHeight, height)
}
Expand Down

0 comments on commit 625e633

Please sign in to comment.