Skip to content

Commit

Permalink
added live test for intx sender address parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
ws4charlie committed Apr 3, 2024
1 parent 11bd7ea commit d911f67
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@ import (
"testing"
"time"

"github.com/zeta-chain/zetacore/pkg/chains"
appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context"
clientcommon "github.com/zeta-chain/zetacore/zetaclient/common"
"github.com/zeta-chain/zetacore/zetaclient/config"
corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context"
"github.com/zeta-chain/zetacore/zetaclient/interfaces"

"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
Expand All @@ -25,12 +18,18 @@ import (
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/zeta-chain/zetacore/pkg/chains"
appcontext "github.com/zeta-chain/zetacore/zetaclient/app_context"
clientcommon "github.com/zeta-chain/zetacore/zetaclient/common"
"github.com/zeta-chain/zetacore/zetaclient/config"
corecontext "github.com/zeta-chain/zetacore/zetaclient/core_context"
"github.com/zeta-chain/zetacore/zetaclient/interfaces"
"github.com/zeta-chain/zetacore/zetaclient/testutils"
)

type BitcoinClientTestSuite struct {
suite.Suite
BitcoinChainClient *BTCChainClient
rpcClient *rpcclient.Client
}

func (suite *BitcoinClientTestSuite) SetupTest() {
Expand All @@ -50,7 +49,8 @@ func (suite *BitcoinClientTestSuite) SetupTest() {
client, err := NewBitcoinClient(appContext, chains.BtcRegtestChain(), nil, tss, tempSQLiteDbPath,
clientcommon.DefaultLoggers(), config.BTCConfig{}, nil)
suite.Require().NoError(err)
suite.BitcoinChainClient = client
suite.rpcClient, err = getRPCClient(18332)
suite.Require().NoError(err)
skBytes, err := hex.DecodeString(skHex)
suite.Require().NoError(err)
suite.T().Logf("skBytes: %d", len(skBytes))
Expand Down Expand Up @@ -127,10 +127,10 @@ func getFeeRate(client *rpcclient.Client, confTarget int64, estimateMode *btcjso
// All methods that begin with "Test" are run as tests within a
// suite.
func (suite *BitcoinClientTestSuite) Test1() {
feeResult, err := suite.BitcoinChainClient.rpcClient.EstimateSmartFee(1, nil)
feeResult, err := suite.rpcClient.EstimateSmartFee(1, nil)
suite.Require().NoError(err)
suite.T().Logf("fee result: %f", *feeResult.FeeRate)
bn, err := suite.BitcoinChainClient.rpcClient.GetBlockCount()
bn, err := suite.rpcClient.GetBlockCount()
suite.Require().NoError(err)
suite.T().Logf("block %d", bn)

Expand All @@ -139,14 +139,13 @@ func (suite *BitcoinClientTestSuite) Test1() {
err = chainhash.Decode(&hash, hashStr)
suite.Require().NoError(err)

//:= suite.BitcoinChainClient.rpcClient.GetBlock(&hash)
block, err := suite.BitcoinChainClient.rpcClient.GetBlockVerboseTx(&hash)
block, err := suite.rpcClient.GetBlockVerboseTx(&hash)
suite.Require().NoError(err)
suite.T().Logf("block confirmation %d", block.Confirmations)
suite.T().Logf("block txs len %d", len(block.Tx))

inTxs, err := FilterAndParseIncomingTx(
suite.BitcoinChainClient.rpcClient,
suite.rpcClient,
block.Tx,
uint64(block.Height),
"tb1qsa222mn2rhdq9cruxkz8p2teutvxuextx3ees2",
Expand Down Expand Up @@ -176,15 +175,14 @@ func (suite *BitcoinClientTestSuite) Test2() {
err := chainhash.Decode(&hash, hashStr)
suite.Require().NoError(err)

//:= suite.BitcoinChainClient.rpcClient.GetBlock(&hash)
block, err := suite.BitcoinChainClient.rpcClient.GetBlockVerboseTx(&hash)
block, err := suite.rpcClient.GetBlockVerboseTx(&hash)
suite.Require().NoError(err)
suite.T().Logf("block confirmation %d", block.Confirmations)
suite.T().Logf("block height %d", block.Height)
suite.T().Logf("block txs len %d", len(block.Tx))

inTxs, err := FilterAndParseIncomingTx(
suite.BitcoinChainClient.rpcClient,
suite.rpcClient,
block.Tx,
uint64(block.Height),
"tb1qsa222mn2rhdq9cruxkz8p2teutvxuextx3ees2",
Expand All @@ -197,7 +195,7 @@ func (suite *BitcoinClientTestSuite) Test2() {
}

func (suite *BitcoinClientTestSuite) Test3() {
client := suite.BitcoinChainClient.rpcClient
client := suite.rpcClient
res, err := client.EstimateSmartFee(1, &btcjson.EstimateModeConservative)
suite.Require().NoError(err)
suite.T().Logf("fee: %f", *res.FeeRate)
Expand All @@ -212,11 +210,18 @@ func (suite *BitcoinClientTestSuite) Test3() {
suite.T().Logf("block number %d", bn)
}

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

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

// Remove prefix "Live" to run this live test
// LiveTestBitcoinFeeRate query Bitcoin mainnet fee rate every 5 minutes
// and compares Conservative and Economical fee rates for different block targets (1 and 2)
func LiveTestBitcoinFeeRate(t *testing.T) {
// setup Bitcoin client
client, err := getRPCClient(8332)
Expand Down Expand Up @@ -321,7 +326,7 @@ func compareAvgFeeRate(t *testing.T, client *rpcclient.Client, startBlock int, e
}
}

// Remove prefix "Live" to run this live test
// LiveTestAvgFeeRateMainnetMempoolSpace compares calculated fee rate with mempool.space fee rate for mainnet
func LiveTestAvgFeeRateMainnetMempoolSpace(t *testing.T) {
// setup Bitcoin client
client, err := getRPCClient(8332)
Expand All @@ -335,7 +340,7 @@ func LiveTestAvgFeeRateMainnetMempoolSpace(t *testing.T) {
compareAvgFeeRate(t, client, startBlock, endBlock, false)
}

// Remove prefix "Live" to run this live test
// LiveTestAvgFeeRateTestnetMempoolSpace compares calculated fee rate with mempool.space fee rate for testnet
func LiveTestAvgFeeRateTestnetMempoolSpace(t *testing.T) {
// setup Bitcoin client
client, err := getRPCClient(18332)
Expand All @@ -348,3 +353,76 @@ func LiveTestAvgFeeRateTestnetMempoolSpace(t *testing.T) {

compareAvgFeeRate(t, client, startBlock, endBlock, true)
}

// LiveTestGetSenderByVin gets sender address for each vin and compares with mempool.space sender address
func LiveTestGetSenderByVin(t *testing.T) {
// setup Bitcoin client
chainID := int64(8332)
client, err := getRPCClient(chainID)
require.NoError(t, err)

// net params
net, err := chains.GetBTCChainParams(chainID)
require.NoError(t, err)
testnet := false
if chainID == chains.BtcTestNetChain().ChainId {
testnet = true
}

// calculates block range to test
startBlock, err := client.GetBlockCount()
require.NoError(t, err)
endBlock := startBlock - 5000

// loop through mempool.space blocks in descending order
BLOCKLOOP:
for bn := startBlock; bn >= endBlock; {
// get block hash
blkHash, err := client.GetBlockHash(int64(bn))
if err != nil {
fmt.Printf("error GetBlockHash for block %d: %s\n", bn, err)
time.Sleep(3 * time.Second)
continue
}

// get mempool.space txs for the block
mempoolTxs, err := testutils.GetBlockTxs(context.Background(), blkHash.String(), testnet)
if err != nil {
fmt.Printf("error GetBlockTxs %d: %s\n", bn, err)
time.Sleep(10 * time.Second)
continue
}

// loop through each tx in the block
for i, mptx := range mempoolTxs {
// sample 10 txs per block
if i >= 10 {
break
}
for _, mpvin := range mptx.Vin {
// skip coinbase tx
if mpvin.IsCoinbase {
continue
}
// get sender address for each vin
vin := btcjson.Vin{
Txid: mpvin.TxID,
Vout: mpvin.Vout,
}
senderAddr, err := GetSenderAddressByVin(client, vin, net)
if err != nil {
fmt.Printf("error GetSenderAddressByVin for block %d, tx %s vout %d: %s\n", bn, vin.Txid, vin.Vout, err)
time.Sleep(3 * time.Second)
continue BLOCKLOOP // retry the block
}
if senderAddr != mpvin.Prevout.ScriptpubkeyAddress {
panic(fmt.Sprintf("block %d, tx %s, vout %d: want %s, got %s\n", bn, vin.Txid, vin.Vout, mpvin.Prevout.ScriptpubkeyAddress, senderAddr))
} else {
fmt.Printf("block: %d sender address type: %s\n", bn, mpvin.Prevout.ScriptpubkeyType)
}
}
}
bn--
time.Sleep(500 * time.Millisecond)
}
}
55 changes: 52 additions & 3 deletions zetaclient/testutils/mempool_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import (
)

const (
APIURLBlocks = "https://mempool.space/api/v1/blocks"
APIUrlBlocksTestnet = "https://mempool.space/testnet/api/v1/blocks"
APIURLBlocks = "https://mempool.space/api/v1/blocks"
APIURLBlockTxs = "https://mempool.space/api/block/%s/txs"
APIURLBlocksTestnet = "https://mempool.space/testnet/api/v1/blocks"
APIURLBlockTxsTestnet = "https://mempool.space/testnet/api/block/%s/txs"
)

type MempoolBlock struct {
Expand All @@ -30,6 +32,39 @@ type MempoolBlock struct {
Extras BlockExtra `json:"extras"`
}

type Vin struct {
TxID string `json:"txid"`
Vout uint32 `json:"vout"`
Prevout struct {
Scriptpubkey string `json:"scriptpubkey"`
ScriptpubkeyAsm string `json:"scriptpubkey_asm"`
ScriptpubkeyType string `json:"scriptpubkey_type"`
ScriptpubkeyAddress string `json:"scriptpubkey_address"`
Value int64 `json:"value"`
} `json:"prevout"`
Scriptsig string `json:"scriptsig"`
IsCoinbase bool `json:"is_coinbase"`
Sequence uint32 `json:"sequence"`
}

type Vout struct {
Scriptpubkey string `json:"scriptpubkey"`
ScriptpubkeyAsm string `json:"scriptpubkey_asm"`
ScriptpubkeyType string `json:"scriptpubkey_type"`
Value int64 `json:"value"`
}

type MempoolTx struct {
TxID string `json:"txid"`
Version int `json:"version"`
LockTime int `json:"locktime"`
Vin []Vin `json:"vin"`
Vout []Vout `json:"vout"`
Size int `json:"size"`
Weight int `json:"weight"`
Fee int `json:"fee"`
}

type BlockExtra struct {
TotalFees int `json:"totalFees"`
MedianFee float64 `json:"medianFee"`
Expand Down Expand Up @@ -91,14 +126,28 @@ func Get(ctx context.Context, path string, v interface{}) error {
return json.NewDecoder(r.Body).Decode(v)
}

// GetBlocks returns return 15 mempool.space blocks [n-14, n] per request
func GetBlocks(ctx context.Context, n int, testnet bool) ([]MempoolBlock, error) {
path := fmt.Sprintf("%s/%d", APIURLBlocks, n)
if testnet {
path = fmt.Sprintf("%s/%d", APIUrlBlocksTestnet, n)
path = fmt.Sprintf("%s/%d", APIURLBlocksTestnet, n)
}
blocks := make([]MempoolBlock, 0)
if err := Get(ctx, path, &blocks); err != nil {
return nil, err
}
return blocks, nil
}

// GetBlockTxs a list of transactions in the block (up to 25 transactions beginning at index 0)
func GetBlockTxs(ctx context.Context, blockHash string, testnet bool) ([]MempoolTx, error) {
path := fmt.Sprintf(APIURLBlockTxs, blockHash)
if testnet {
path = fmt.Sprintf(APIURLBlockTxsTestnet, blockHash)
}
txs := make([]MempoolTx, 0)
if err := Get(ctx, path, &txs); err != nil {
return nil, err
}
return txs, nil
}

0 comments on commit d911f67

Please sign in to comment.