Skip to content
Merged
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
33 changes: 27 additions & 6 deletions cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"
"os"
"os/signal"
"strings"
"sync"
"syscall"

Expand Down Expand Up @@ -48,6 +49,15 @@ func NewApplication() *Application {
rpcHost = parsed.Host + parsed.Path
disableTLS = parsed.Scheme != "https"
}
rpcHost = strings.TrimSuffix(rpcHost, "/")
// Add default port if not specified
if !strings.Contains(rpcHost, ":") {
if disableTLS {
rpcHost = rpcHost + ":80"
} else {
rpcHost = rpcHost + ":443"
}
}
var extraHeaders map[string]string
if config.AppConfig.BTCRPCApiKey != "" {
extraHeaders = map[string]string{
Expand All @@ -58,10 +68,10 @@ func NewApplication() *Application {
rpcPass := config.AppConfig.BTCRPC_PASS
// Default user/pass for APIs that don't require auth (e.g., GetBlock)
if rpcUser == "" {
rpcUser = "goat"
rpcUser = "x"
}
if rpcPass == "" {
rpcPass = "goat"
rpcPass = "x"
}
// create bitcoin client using btc module connection
connConfig := &rpcclient.ConnConfig{
Expand All @@ -72,21 +82,32 @@ func NewApplication() *Application {
DisableTLS: disableTLS,
ExtraHeaders: extraHeaders,
}
btcClient, err := rpcclient.New(connConfig, nil)
bclient, err := rpcclient.New(connConfig, nil)
if err != nil {
log.Fatalf("Failed to start bitcoin client: %v", err)
}

dbm := db.NewDatabaseManager()
state := state.InitializeState(dbm)
libP2PService := p2p.NewLibP2PService(state)
btcClient := btc.NewBTCRPCService(bclient)

// Test BTC RPC connection first
log.Info("Testing BTC RPC connection...")
blockCount, err := btcClient.GetBlockCount()
if err != nil {
log.Fatalf("Failed to get block count from BTC RPC: %v", err)
}
log.Infof("✅ BTC RPC connection successful! Current block count: %d", blockCount)

layer2Listener := layer2.NewLayer2Listener(libP2PService, state, dbm, btcClient)
signer := bls.NewSigner(libP2PService, layer2Listener, state, btcClient)
httpServer := http.NewHTTPServer(libP2PService, state, dbm)
btcListener := btc.NewBTCListener(libP2PService, state, btcClient)
utxoService := rpc.NewUtxoServer(state, layer2Listener, btcClient)
btcRPCService := btc.NewBTCRPCService(btcClient)
walletService := wallet.NewWalletServer(libP2PService, state, signer, btcClient, btcRPCService)

// Initialize BLS signer last (requires RELAYER_BLS_SK)
signer := bls.NewSigner(libP2PService, layer2Listener, state, btcClient)
walletService := wallet.NewWalletServer(libP2PService, state, signer, btcClient)
voterProcessor := voter.NewVoterProcessor(libP2PService, state, signer)

return &Application{
Expand Down
4 changes: 2 additions & 2 deletions internal/bls/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
"sync"
"time"

"github.com/btcsuite/btcd/rpcclient"
log "github.com/sirupsen/logrus"

"github.com/goatnetwork/goat-relayer/internal/btc"
"github.com/goatnetwork/goat-relayer/internal/config"
"github.com/goatnetwork/goat-relayer/internal/layer2"
"github.com/goatnetwork/goat-relayer/internal/p2p"
Expand Down Expand Up @@ -39,7 +39,7 @@ type Signer struct {
sigMu sync.RWMutex
}

func NewSigner(libp2p *p2p.LibP2PService, layer2Listener *layer2.Layer2Listener, state *state.State, btcClient *rpcclient.Client) *Signer {
func NewSigner(libp2p *p2p.LibP2PService, layer2Listener *layer2.Layer2Listener, state *state.State, btcClient *btc.BTCRPCService) *Signer {
byt, err := hex.DecodeString(config.AppConfig.RelayerBlsSk)
if err != nil {
log.Fatalf("Decode bls sk error: %v", err)
Expand Down
7 changes: 3 additions & 4 deletions internal/btc/fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/rpcclient"
"github.com/goatnetwork/goat-relayer/internal/config"
"github.com/goatnetwork/goat-relayer/internal/types"
log "github.com/sirupsen/logrus"
Expand All @@ -28,11 +27,11 @@ type MempoolFeesResp struct {
}

type MemPoolFeeFetcher struct {
btcClient *rpcclient.Client
btcClient *BTCRPCService
httpClient *http.Client
}

func NewMemPoolFeeFetcher(btcClient *rpcclient.Client) *MemPoolFeeFetcher {
func NewMemPoolFeeFetcher(btcClient *BTCRPCService) *MemPoolFeeFetcher {
return &MemPoolFeeFetcher{btcClient: btcClient, httpClient: &http.Client{Timeout: 30 * time.Second}}
}

Expand Down Expand Up @@ -87,7 +86,7 @@ func (f *MemPoolFeeFetcher) getFeeRate(url string) (*types.BtcNetworkFee, error)
}

// get fee rate from btc node
func getFeeRateFromBtcNode(btcClient *rpcclient.Client) (*types.BtcNetworkFee, error) {
func getFeeRateFromBtcNode(btcClient *BTCRPCService) (*types.BtcNetworkFee, error) {
feeEstimate, err := btcClient.EstimateSmartFee(1, &btcjson.EstimateModeConservative)
if err != nil || feeEstimate == nil || feeEstimate.FeeRate == nil {
return nil, fmt.Errorf("failed to estimate smart fee 1: %v", err)
Expand Down
7 changes: 2 additions & 5 deletions internal/btc/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package btc
import (
"context"

"github.com/btcsuite/btcd/rpcclient"
"github.com/goatnetwork/goat-relayer/internal/p2p"
"github.com/goatnetwork/goat-relayer/internal/state"
log "github.com/sirupsen/logrus"
Expand All @@ -16,11 +15,9 @@ type BTCListener struct {
notifier *BTCNotifier
}

func NewBTCListener(libp2p *p2p.LibP2PService, state *state.State, btcClient *rpcclient.Client) *BTCListener {
rpcService := NewBTCRPCService(btcClient)
func NewBTCListener(libp2p *p2p.LibP2PService, state *state.State, rpcService *BTCRPCService) *BTCListener {
poller := NewBTCPoller(state, rpcService)

notifier := NewBTCNotifier(btcClient, poller)
notifier := NewBTCNotifier(rpcService, poller)

return &BTCListener{
libp2p: libp2p,
Expand Down
17 changes: 8 additions & 9 deletions internal/btc/notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ import (
"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/wire"
"github.com/goatnetwork/goat-relayer/internal/config"
log "github.com/sirupsen/logrus"
)

type BTCNotifier struct {
client *rpcclient.Client
rpcService *BTCRPCService
confirmations int64
currentHeight uint64
reindexBlocks []string
Expand All @@ -34,12 +33,12 @@ type BTCNotifier struct {
feeFetcher NetworkFeeFetcher
}

func NewBTCNotifier(client *rpcclient.Client, poller *BTCPoller) *BTCNotifier {
func NewBTCNotifier(rpcService *BTCRPCService, poller *BTCPoller) *BTCNotifier {
var maxBlockHeight int64 = -1
var reindexBlocks []string

// No longer retrieve the maximum block height from the cache database, switch to getting it from RPC
blockCount, err := client.GetBlockCount()
blockCount, err := rpcService.GetBlockCount()
if err != nil {
log.Fatalf("Failed to get block count from RPC, %v", err)
}
Expand Down Expand Up @@ -90,13 +89,13 @@ func NewBTCNotifier(client *rpcclient.Client, poller *BTCPoller) *BTCNotifier {
log.Infof("New btc notify sync status confirmed height is %d", syncStatus.ConfirmedHeight)

return &BTCNotifier{
client: client,
rpcService: rpcService,
confirmations: int64(config.AppConfig.BTCConfirmations),
currentHeight: uint64(maxBlockHeight + 1),
reindexBlocks: reindexBlocks,
syncStatus: syncStatus,
poller: poller,
feeFetcher: NewMemPoolFeeFetcher(client),
feeFetcher: NewMemPoolFeeFetcher(rpcService),
}
}

Expand All @@ -118,7 +117,7 @@ func (bn *BTCNotifier) checkConfirmations(ctx context.Context, blockDoneCh chan
return

case <-ticker.C:
bestHeight, err := bn.client.GetBlockCount()
bestHeight, err := bn.rpcService.GetBlockCount()
if err != nil {
log.Errorf("Error getting latest block height: %v", err)
continue
Expand Down Expand Up @@ -343,12 +342,12 @@ func (bn *BTCNotifier) handleP2WSHDeposit(tx *btcjson.TxRawResult, blockHeight i
}

func (bn *BTCNotifier) getBlockAtHeight(height int64) (*wire.MsgBlock, error) {
blockHash, err := bn.client.GetBlockHash(height)
blockHash, err := bn.rpcService.GetBlockHash(height)
if err != nil {
return nil, fmt.Errorf("error getting block hash at height %d: %v", height, err)
}

block, err := bn.client.GetBlock(blockHash)
block, err := bn.rpcService.GetBlock(blockHash)
if err != nil {
return nil, fmt.Errorf("error getting block at height %d: %v", height, err)
}
Expand Down
29 changes: 0 additions & 29 deletions internal/btc/poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import (
"github.com/goatnetwork/goat-relayer/internal/types"
"gorm.io/gorm"

"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
log "github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -121,33 +119,6 @@ func (p *BTCPoller) signLoop(ctx context.Context) {
}
}

func (p *BTCPoller) GetBlockHashForTx(txHash chainhash.Hash) (*chainhash.Hash, error) {
var btcTxOutput db.BtcTXOutput

if err := p.db.Where("tx_hash = ?", txHash.String()).First(&btcTxOutput).Error; err != nil {
return nil, fmt.Errorf("failed to find the block hash for the transaction: %v", err)
}

blockHashBytes := btcTxOutput.PkScript[:32] // Assuming the block hash is the first 32 bytes of PkScript
blockHash, err := chainhash.NewHash(blockHashBytes)
if err != nil {
return nil, fmt.Errorf("failed to create hash from block hash bytes: %v", err)
}

return blockHash, nil
}
func (p *BTCPoller) GetBlockHeader(blockHash *chainhash.Hash) (*wire.BlockHeader, error) {
return p.rpcService.GetBlockHeader(blockHash)
}

func (p *BTCPoller) GetTxHashes(blockHash *chainhash.Hash) ([]chainhash.Hash, error) {
return p.rpcService.GetTxHashes(blockHash)
}

func (p *BTCPoller) GetBlock(height uint64) (*db.BtcBlockData, error) {
return p.rpcService.GetBlockData(height)
}

func (p *BTCPoller) handleConfirmedBlock(block *types.BtcBlockExt) {
// Logic for handling confirmed blocks
blockHash := block.BlockHash()
Expand Down
72 changes: 48 additions & 24 deletions internal/btc/rpc_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"

"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/wire"
Expand All @@ -25,6 +26,53 @@ func NewBTCRPCService(client *rpcclient.Client) *BTCRPCService {
}
}

// GetBlockCount retrieves block count using RawRequest to avoid BackendVersion detection.
func (s *BTCRPCService) GetBlockCount() (int64, error) {
rawResp, err := s.client.RawRequest("getblockcount", nil)
if err != nil {
return 0, err
}
var blockCount int64
if err := json.Unmarshal(rawResp, &blockCount); err != nil {
return 0, fmt.Errorf("unmarshal blockcount: %w", err)
}
return blockCount, nil
}

func (s *BTCRPCService) RawRequest(method string, params []json.RawMessage) (json.RawMessage, error) {
return s.client.RawRequest(method, params)
}

func (s *BTCRPCService) SendRawTransaction(tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) {
return s.client.SendRawTransaction(tx, allowHighFees)
}

func (s *BTCRPCService) EstimateSmartFee(confTarget int64, mode *btcjson.EstimateSmartFeeMode) (*btcjson.EstimateSmartFeeResult, error) {
return s.client.EstimateSmartFee(confTarget, mode)
}

func (s *BTCRPCService) GetMempoolEntry(txid string) (*btcjson.GetMempoolEntryResult, error) {
return s.client.GetMempoolEntry(txid)
}

func (s *BTCRPCService) GetRawTransaction(txHash *chainhash.Hash) (*btcutil.Tx, error) {
return s.client.GetRawTransaction(txHash)
}

func (s *BTCRPCService) GetBlockVerboseTx(blockHash *chainhash.Hash) (*btcjson.GetBlockVerboseTxResult, error) {
return s.client.GetBlockVerboseTx(blockHash)
}

// GetBlockHash retrieves block hash by height.
func (s *BTCRPCService) GetBlockHash(height int64) (*chainhash.Hash, error) {
return s.client.GetBlockHash(height)
}

// GetBlock retrieves the block by hash.
func (s *BTCRPCService) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
return s.client.GetBlock(blockHash)
}

// GetBlockData retrieves block data via RPC
func (s *BTCRPCService) GetBlockData(height uint64) (*db.BtcBlockData, error) {
// Get block hash by height
Expand Down Expand Up @@ -92,25 +140,6 @@ func (s *BTCRPCService) GetTxHashes(blockHash *chainhash.Hash) ([]chainhash.Hash
return txHashes, nil
}

// GetBlockHashForTx retrieves the block hash by transaction hash
func (s *BTCRPCService) GetBlockHashForTx(txHash chainhash.Hash) (*chainhash.Hash, error) {
txRawResult, err := s.client.GetRawTransactionVerbose(&txHash)
if err != nil {
return nil, fmt.Errorf("failed to get raw transaction: %v", err)
}

if txRawResult.BlockHash == "" {
return nil, fmt.Errorf("transaction not in any block")
}

blockHash, err := chainhash.NewHashFromStr(txRawResult.BlockHash)
if err != nil {
return nil, fmt.Errorf("failed to parse block hash: %v", err)
}

return blockHash, nil
}

// GetBlockTimeFromTx retrieves the block time from transaction hash
func (s *BTCRPCService) GetBlockTimeFromTx(txHash chainhash.Hash) (blockTime int64, err error) {
txRawResult, err := s.client.GetRawTransactionVerbose(&txHash)
Expand All @@ -130,11 +159,6 @@ func (s *BTCRPCService) GetBlockVerbose(blockHash *chainhash.Hash) (*btcjson.Get
return s.client.GetBlockVerbose(blockHash)
}

// GetBlockVerboseTx retrieves detailed information of a block (including transactions)
func (s *BTCRPCService) GetBlockVerboseTx(blockHash *chainhash.Hash) (*btcjson.GetBlockVerboseTxResult, error) {
return s.client.GetBlockVerboseTx(blockHash)
}

// convertBlockToBlockData converts wire.MsgBlock to db.BtcBlockData
func (s *BTCRPCService) convertBlockToBlockData(block *wire.MsgBlock, height uint64) (*db.BtcBlockData, error) {
blockHash := block.BlockHash().String()
Expand Down
Loading
Loading