github.com/trezor/blockbook@v0.4.1-0.20240328132726-e9a08582ee2c/bchain/coins/firo/firorpc.go (about)

     1  package firo
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  
     7  	"github.com/golang/glog"
     8  	"github.com/juju/errors"
     9  	"github.com/trezor/blockbook/bchain"
    10  	"github.com/trezor/blockbook/bchain/coins/btc"
    11  )
    12  
    13  type FiroRPC struct {
    14  	*btc.BitcoinRPC
    15  }
    16  
    17  func NewFiroRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
    18  	// init base implementation
    19  	bc, err := btc.NewBitcoinRPC(config, pushHandler)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  
    24  	// init firo implementation
    25  	zc := &FiroRPC{
    26  		BitcoinRPC: bc.(*btc.BitcoinRPC),
    27  	}
    28  
    29  	zc.ChainConfig.Parse = true
    30  	zc.ChainConfig.SupportsEstimateFee = true
    31  	zc.ChainConfig.SupportsEstimateSmartFee = false
    32  	zc.ParseBlocks = true
    33  	zc.RPCMarshaler = btc.JSONMarshalerV1{}
    34  
    35  	return zc, nil
    36  }
    37  
    38  func (zc *FiroRPC) Initialize() error {
    39  	ci, err := zc.GetChainInfo()
    40  	if err != nil {
    41  		return err
    42  	}
    43  	chainName := ci.Chain
    44  
    45  	params := GetChainParams(chainName)
    46  
    47  	// always create parser
    48  	zc.Parser = NewFiroParser(params, zc.ChainConfig)
    49  
    50  	// parameters for getInfo request
    51  	if params.Net == MainnetMagic {
    52  		zc.Testnet = false
    53  		zc.Network = "livenet"
    54  	} else {
    55  		zc.Testnet = true
    56  		zc.Network = "testnet"
    57  	}
    58  
    59  	glog.Info("rpc: block chain ", params.Name)
    60  
    61  	return nil
    62  }
    63  
    64  func (zc *FiroRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
    65  	var err error
    66  
    67  	if hash == "" {
    68  		hash, err = zc.GetBlockHash(height)
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  	}
    73  
    74  	// optimization
    75  	if height > 0 {
    76  		return zc.GetBlockWithoutHeader(hash, height)
    77  	}
    78  
    79  	header, err := zc.GetBlockHeader(hash)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	data, err := zc.GetBlockBytes(hash)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	block, err := zc.Parser.ParseBlock(data)
    90  	if err != nil {
    91  		return nil, errors.Annotatef(err, "hash %v", hash)
    92  	}
    93  
    94  	block.BlockHeader = *header
    95  
    96  	return block, nil
    97  }
    98  
    99  func (zc *FiroRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
   100  	glog.V(1).Info("rpc: getblock (verbosity=true) ", hash)
   101  
   102  	res := btc.ResGetBlockInfo{}
   103  	req := cmdGetBlock{Method: "getblock"}
   104  	req.Params.BlockHash = hash
   105  	req.Params.Verbosity = true
   106  	err := zc.Call(&req, &res)
   107  
   108  	if err != nil {
   109  		return nil, errors.Annotatef(err, "hash %v", hash)
   110  	}
   111  	if res.Error != nil {
   112  		if btc.IsErrBlockNotFound(res.Error) {
   113  			return nil, bchain.ErrBlockNotFound
   114  		}
   115  		return nil, errors.Annotatef(res.Error, "hash %v", hash)
   116  	}
   117  	return &res.Result, nil
   118  }
   119  
   120  func (zc *FiroRPC) GetBlockWithoutHeader(hash string, height uint32) (*bchain.Block, error) {
   121  	data, err := zc.GetBlockBytes(hash)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	block, err := zc.Parser.ParseBlock(data)
   127  	if err != nil {
   128  		return nil, errors.Annotatef(err, "%v %v", height, hash)
   129  	}
   130  
   131  	block.BlockHeader.Hash = hash
   132  	block.BlockHeader.Height = height
   133  
   134  	return block, nil
   135  }
   136  
   137  // GetBlockRaw returns block with given hash as hex string
   138  func (zc *FiroRPC) GetBlockRaw(hash string) (string, error) {
   139  	glog.V(1).Info("rpc: getblock (verbosity=false) ", hash)
   140  
   141  	res := btc.ResGetBlockRaw{}
   142  	req := cmdGetBlock{Method: "getblock"}
   143  	req.Params.BlockHash = hash
   144  	req.Params.Verbosity = false
   145  	err := zc.Call(&req, &res)
   146  
   147  	if err != nil {
   148  		return "", errors.Annotatef(err, "hash %v", hash)
   149  	}
   150  	if res.Error != nil {
   151  		if btc.IsErrBlockNotFound(res.Error) {
   152  			return "", bchain.ErrBlockNotFound
   153  		}
   154  		return "", errors.Annotatef(res.Error, "hash %v", hash)
   155  	}
   156  	return res.Result, nil
   157  }
   158  
   159  // GetBlockBytes returns block with given hash as bytes
   160  func (zc *FiroRPC) GetBlockBytes(hash string) ([]byte, error) {
   161  	block, err := zc.GetBlockRaw(hash)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	return hex.DecodeString(block)
   166  }
   167  
   168  func (zc *FiroRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
   169  	glog.V(1).Info("rpc: getrawtransaction nonverbose ", txid)
   170  
   171  	res := btc.ResGetRawTransactionNonverbose{}
   172  	req := cmdGetRawTransaction{Method: "getrawtransaction"}
   173  	req.Params.Txid = txid
   174  	req.Params.Verbose = 0
   175  	err := zc.Call(&req, &res)
   176  	if err != nil {
   177  		return nil, errors.Annotatef(err, "txid %v", txid)
   178  	}
   179  	if res.Error != nil {
   180  		if btc.IsMissingTx(res.Error) {
   181  			return nil, bchain.ErrTxNotFound
   182  		}
   183  		return nil, errors.Annotatef(res.Error, "txid %v", txid)
   184  	}
   185  	data, err := hex.DecodeString(res.Result)
   186  	if err != nil {
   187  		return nil, errors.Annotatef(err, "txid %v", txid)
   188  	}
   189  	tx, err := zc.Parser.ParseTx(data)
   190  	if err != nil {
   191  		return nil, errors.Annotatef(err, "txid %v", txid)
   192  	}
   193  	return tx, nil
   194  }
   195  
   196  func (zc *FiroRPC) GetTransaction(txid string) (*bchain.Tx, error) {
   197  	r, err := zc.getRawTransaction(txid)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	tx, err := zc.Parser.ParseTxFromJson(r)
   203  	tx.CoinSpecificData = r
   204  	if err != nil {
   205  		return nil, errors.Annotatef(err, "txid %v", txid)
   206  	}
   207  
   208  	return tx, nil
   209  }
   210  
   211  func (zc *FiroRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) {
   212  	if csd, ok := tx.CoinSpecificData.(json.RawMessage); ok {
   213  		return csd, nil
   214  	}
   215  	return zc.getRawTransaction(tx.Txid)
   216  }
   217  
   218  func (zc *FiroRPC) getRawTransaction(txid string) (json.RawMessage, error) {
   219  	glog.V(1).Info("rpc: getrawtransaction ", txid)
   220  
   221  	res := btc.ResGetRawTransaction{}
   222  	req := cmdGetRawTransaction{Method: "getrawtransaction"}
   223  	req.Params.Txid = txid
   224  	req.Params.Verbose = 1
   225  	err := zc.Call(&req, &res)
   226  
   227  	if err != nil {
   228  		return nil, errors.Annotatef(err, "txid %v", txid)
   229  	}
   230  	if res.Error != nil {
   231  		if btc.IsMissingTx(res.Error) {
   232  			return nil, bchain.ErrTxNotFound
   233  		}
   234  		return nil, errors.Annotatef(res.Error, "txid %v", txid)
   235  	}
   236  	return res.Result, nil
   237  }
   238  
   239  type cmdGetBlock struct {
   240  	Method string `json:"method"`
   241  	Params struct {
   242  		BlockHash string `json:"blockhash"`
   243  		Verbosity bool   `json:"verbosity"`
   244  	} `json:"params"`
   245  }
   246  
   247  type cmdGetRawTransaction struct {
   248  	Method string `json:"method"`
   249  	Params struct {
   250  		Txid    string `json:"txid"`
   251  		Verbose int    `json:"verbose"`
   252  	} `json:"params"`
   253  }