Add ERC20 transfer information to ethereum transactions

ethereum
Martin Boehm 2018-11-28 14:27:02 +01:00
parent 1f32a39d16
commit 8ac57a3d56
14 changed files with 385 additions and 84 deletions

View File

@ -2,6 +2,7 @@ package api
import (
"blockbook/bchain"
"blockbook/bchain/coins/eth"
"blockbook/common"
"blockbook/db"
"encoding/json"
@ -69,28 +70,49 @@ type Vout struct {
SpentHeight int `json:"spentHeight,omitempty"`
}
// Erc20Token contains info about ERC20 token held by an address
type Erc20Token struct {
Contract string `json:"contract"`
Txs int `json:"txs"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Balance string `json:"balance"`
}
// Erc20Transfer contains info about ERC20 transfer done in a transaction
type Erc20Transfer struct {
From string `json:"from"`
To string `json:"to"`
Contract string `json:"contract"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Tokens string `json:"tokens"`
}
// Tx holds information about a transaction
type Tx struct {
Txid string `json:"txid"`
Version int32 `json:"version,omitempty"`
Locktime uint32 `json:"locktime,omitempty"`
Vin []Vin `json:"vin"`
Vout []Vout `json:"vout"`
Blockhash string `json:"blockhash,omitempty"`
Blockheight int `json:"blockheight"`
Confirmations uint32 `json:"confirmations"`
Time int64 `json:"time,omitempty"`
Blocktime int64 `json:"blocktime"`
ValueOut string `json:"valueOut"`
ValueOutSat big.Int `json:"-"`
Size int `json:"size,omitempty"`
ValueIn string `json:"valueIn"`
ValueInSat big.Int `json:"-"`
Fees string `json:"fees"`
FeesSat big.Int `json:"-"`
Hex string `json:"hex"`
CoinSpecificData interface{} `json:"-"`
CoinSpecificJSON json.RawMessage `json:"-"`
Txid string `json:"txid"`
Version int32 `json:"version,omitempty"`
Locktime uint32 `json:"locktime,omitempty"`
Vin []Vin `json:"vin"`
Vout []Vout `json:"vout"`
Blockhash string `json:"blockhash,omitempty"`
Blockheight int `json:"blockheight"`
Confirmations uint32 `json:"confirmations"`
Time int64 `json:"time,omitempty"`
Blocktime int64 `json:"blocktime"`
ValueOut string `json:"valueOut"`
ValueOutSat big.Int `json:"-"`
Size int `json:"size,omitempty"`
ValueIn string `json:"valueIn"`
ValueInSat big.Int `json:"-"`
Fees string `json:"fees"`
FeesSat big.Int `json:"-"`
Hex string `json:"hex"`
CoinSpecificData interface{} `json:"-"`
CoinSpecificJSON json.RawMessage `json:"-"`
Erc20Transfers []Erc20Transfer `json:"erc20transfers,omitempty"`
EthereumSpecific *eth.EthereumTxData `json:"ethereumspecific,omitempty"`
}
// Paging contains information about paging for address, blocks and block
@ -100,14 +122,6 @@ type Paging struct {
ItemsOnPage int `json:"itemsOnPage"`
}
type Erc20Token struct {
Contract string `json:"contract"`
Txs int `json:"txs"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Value string `json:"value"`
}
// Address holds information about address and its transactions
type Address struct {
Paging

View File

@ -2,6 +2,7 @@ package api
import (
"blockbook/bchain"
"blockbook/bchain/coins/eth"
"blockbook/common"
"blockbook/db"
"bytes"
@ -106,6 +107,8 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool
return nil, NewAPIError(fmt.Sprintf("Tx not found, %v", err), true)
}
var ta *db.TxAddresses
var erc20t []Erc20Transfer
var ethSpecific *eth.EthereumTxData
var blockhash string
if bchainTx.Confirmations > 0 {
if w.chainType == bchain.ChainBitcoinType {
@ -204,10 +207,45 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool
}
}
}
// for coinbase transactions valIn is 0
feesSat.Sub(&valInSat, &valOutSat)
if feesSat.Sign() == -1 {
feesSat.SetUint64(0)
if w.chainType == bchain.ChainBitcoinType {
// for coinbase transactions valIn is 0
feesSat.Sub(&valInSat, &valOutSat)
if feesSat.Sign() == -1 {
feesSat.SetUint64(0)
}
} else if w.chainType == bchain.ChainEthereumType {
ets, err := eth.GetErc20FromTx(bchainTx)
if err != nil {
glog.Errorf("GetErc20FromTx error %v, %v", err, bchainTx)
}
erc20t = make([]Erc20Transfer, len(ets))
for i := range ets {
e := &ets[i]
cd, err := w.chainParser.GetAddrDescFromAddress(e.Contract)
if err != nil {
glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, e.Contract)
continue
}
erc20c, err := w.chain.EthereumTypeGetErc20ContractInfo(cd)
if err != nil {
glog.Errorf("GetErc20ContractInfo error %v, contract %v", err, e.Contract)
erc20c = &bchain.Erc20Contract{}
}
erc20t[i] = Erc20Transfer{
Contract: e.Contract,
From: e.From,
To: e.To,
Tokens: bchain.AmountToDecimalString(&e.Tokens, erc20c.Decimals),
Name: erc20c.Name,
Symbol: erc20c.Symbol,
}
}
ethSpecific = eth.GetEthereumTxData(bchainTx)
feesSat.Mul(ethSpecific.GasPriceNum, ethSpecific.GasUsed)
if len(bchainTx.Vout) > 0 {
valInSat = bchainTx.Vout[0].ValueSat
}
valOutSat = valInSat
}
// for now do not return size, we would have to compute vsize of segwit transactions
// size:=len(bchainTx.Hex) / 2
@ -238,6 +276,8 @@ func (w *Worker) GetTransaction(txid string, spendingTxs bool, specificJSON bool
Vout: vouts,
CoinSpecificData: bchainTx.CoinSpecificData,
CoinSpecificJSON: sj,
Erc20Transfers: erc20t,
EthereumSpecific: ethSpecific,
}
if spendingTxs {
glog.Info("GetTransaction ", txid, " finished in ", time.Since(start))

View File

@ -0,0 +1,45 @@
package bchain
import (
"errors"
"math/big"
)
// BaseChain is base type for bchain.BlockChain
type BaseChain struct {
Parser BlockChainParser
Testnet bool
Network string
}
// TODO more bchain.BlockChain methods
// GetChainParser returns BlockChainParser
func (b *BaseChain) GetChainParser() BlockChainParser {
return b.Parser
}
// IsTestnet returns true if the blockchain is testnet
func (b *BaseChain) IsTestnet() bool {
return b.Testnet
}
// GetNetworkName returns network name
func (b *BaseChain) GetNetworkName() string {
return b.Network
}
// EthereumTypeGetBalance is not supported
func (b *BaseChain) EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error) {
return nil, errors.New("Not supported")
}
// EthereumTypeGetErc20ContractInfo is not supported
func (b *BaseChain) EthereumTypeGetErc20ContractInfo(contractDesc AddressDescriptor) (*Erc20Contract, error) {
return nil, errors.New("Not supported")
}
// EthereumTypeGetErc20ContractBalance is not supported
func (b *BaseChain) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error) {
return nil, errors.New("Not supported")
}

View File

@ -34,10 +34,14 @@ func (p *BaseParser) AmountToBigInt(n json.Number) (big.Int, error) {
var r big.Int
s := string(n)
i := strings.IndexByte(s, '.')
d := p.AmountDecimalPoint
if d > len(zeros) {
d = len(zeros)
}
if i == -1 {
s = s + zeros[:p.AmountDecimalPoint]
s = s + zeros[:d]
} else {
z := p.AmountDecimalPoint - len(s) + i + 1
z := d - len(s) + i + 1
if z > 0 {
s = s[:i] + s[i+1:] + zeros[:z]
} else {
@ -58,6 +62,9 @@ func AmountToDecimalString(a *big.Int, d int) string {
n = n[1:]
s = "-"
}
if d > len(zeros) {
d = len(zeros)
}
if len(n) <= d {
n = zeros[:d-len(n)+1] + n
}

View File

@ -27,7 +27,8 @@ var amounts = []struct {
{big.NewInt(-8), "-0.00000008", 8, "!"},
{big.NewInt(-89012345678), "-890.12345678", 8, "!"},
{big.NewInt(-12345), "-0.00012345", 8, "!"},
{big.NewInt(12345678), "0.123456789012", 8, "0.12345678"}, // test of truncation of too many decimal places
{big.NewInt(12345678), "0.123456789012", 8, "0.12345678"}, // test of truncation of too many decimal places
{big.NewInt(12345678), "0.0000000000000000000000000000000012345678", 1234, "!"}, // test of too big number decimal places
}
func TestBaseParser_AmountToDecimalString(t *testing.T) {

View File

@ -239,3 +239,18 @@ func (c *blockChainWithMetrics) GetMempoolEntry(txid string) (v *bchain.MempoolE
func (c *blockChainWithMetrics) GetChainParser() bchain.BlockChainParser {
return c.b.GetChainParser()
}
func (c *blockChainWithMetrics) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) (v *big.Int, err error) {
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetBalance", s, err) }(time.Now())
return c.b.EthereumTypeGetBalance(addrDesc)
}
func (c *blockChainWithMetrics) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (v *bchain.Erc20Contract, err error) {
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetErc20ContractInfo", s, err) }(time.Now())
return c.b.EthereumTypeGetErc20ContractInfo(contractDesc)
}
func (c *blockChainWithMetrics) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (v *big.Int, err error) {
defer func(s time.Time) { c.observeRPCLatency("EthereumTypeGetErc20ContractInfo", s, err) }(time.Now())
return c.b.EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc)
}

View File

@ -11,6 +11,7 @@ import (
"math/big"
"net"
"net/http"
"runtime/debug"
"time"
"github.com/btcsuite/btcd/wire"
@ -20,13 +21,11 @@ import (
// BitcoinRPC is an interface to JSON-RPC bitcoind service.
type BitcoinRPC struct {
*bchain.BaseChain
client http.Client
rpcURL string
user string
password string
Parser bchain.BlockChainParser
Testnet bool
Network string
Mempool *bchain.MempoolBitcoinType
ParseBlocks bool
pushHandler func(bchain.NotificationType)
@ -84,6 +83,7 @@ func NewBitcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationT
}
s := &BitcoinRPC{
BaseChain: &bchain.BaseChain{},
client: http.Client{Timeout: time.Duration(c.RPCTimeout) * time.Second, Transport: transport},
rpcURL: c.RPCURL,
user: c.RPCUser,
@ -158,14 +158,6 @@ func (b *BitcoinRPC) Shutdown(ctx context.Context) error {
return nil
}
func (b *BitcoinRPC) IsTestnet() bool {
return b.Testnet
}
func (b *BitcoinRPC) GetNetworkName() string {
return b.Network
}
func (b *BitcoinRPC) GetCoinName() string {
return b.ChainConfig.CoinName
}
@ -837,6 +829,7 @@ func safeDecodeResponse(body io.ReadCloser, res interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
glog.Error("unmarshal json recovered from panic: ", r, "; data: ", string(data))
debug.PrintStack()
if len(data) > 0 && len(data) < 2048 {
err = errors.Errorf("Error: %v", string(data))
} else {
@ -881,8 +874,3 @@ func (b *BitcoinRPC) Call(req interface{}, res interface{}) error {
}
return safeDecodeResponse(httpRes.Body, &res)
}
// GetChainParser returns BlockChainParser
func (b *BitcoinRPC) GetChainParser() bchain.BlockChainParser {
return b.Parser
}

View File

@ -1,9 +1,15 @@
package eth
import (
"blockbook/bchain"
"context"
"encoding/hex"
"math/big"
"sync"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/glog"
"github.com/juju/errors"
)
@ -24,8 +30,12 @@ var erc20abi = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"
// doing the parsing/processing without using go-ethereum/accounts/abi library, it is simple to get data from Transfer event
const erc20EventTransferSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
const erc20NameSignature = "0x06fdde03"
const erc20SymbolSignature = "0x95d89b41"
const erc20DecimalsSignature = "0x313ce567"
const erc20BalanceOf = "0x70a08231"
// Erc20Transfer contains a single Erc20 token transfer
// Erc20Transfer contains a single ERC20 token transfer
type Erc20Transfer struct {
Contract string
From string
@ -33,6 +43,9 @@ type Erc20Transfer struct {
Tokens big.Int
}
var cachedContracts = make(map[string]bchain.Erc20Contract)
var cachedContractsMux sync.Mutex
func addressFromPaddedHex(s string) (string, error) {
var t big.Int
_, ok := t.SetString(s, 0)
@ -70,3 +83,107 @@ func erc20GetTransfersFromLog(logs []*rpcLog) ([]Erc20Transfer, error) {
}
return r, nil
}
func (b *EthereumRPC) ethCall(data, to string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
var r string
err := b.rpc.CallContext(ctx, &r, "eth_call", map[string]interface{}{
"data": data,
"to": to,
}, "latest")
if err != nil {
return "", err
}
return r, nil
}
func parseErc20NumericProperty(contractDesc bchain.AddressDescriptor, data string) *big.Int {
if has0xPrefix(data) {
data = data[2:]
}
if len(data) == 64 {
var n big.Int
_, ok := n.SetString(data, 16)
if ok {
return &n
}
}
glog.Warning("Cannot parse '", data, "' for contract ", contractDesc)
return nil
}
func parseErc20StringProperty(contractDesc bchain.AddressDescriptor, data string) string {
if has0xPrefix(data) {
data = data[2:]
}
if len(data) == 192 {
n := parseErc20NumericProperty(contractDesc, data[64:128])
if n != nil {
l := n.Uint64()
if l <= 32 {
b, err := hex.DecodeString(data[128 : 128+2*l])
if err == nil {
return string(b)
}
}
}
}
glog.Warning("Cannot parse '", data, "' for contract ", contractDesc)
return ""
}
// EthereumTypeGetErc20ContractInfo returns information about ERC20 contract
func (b *EthereumRPC) EthereumTypeGetErc20ContractInfo(contractDesc bchain.AddressDescriptor) (*bchain.Erc20Contract, error) {
cds := string(contractDesc)
cachedContractsMux.Lock()
contract, found := cachedContracts[cds]
cachedContractsMux.Unlock()
if !found {
address := hexutil.Encode(contractDesc)
data, err := b.ethCall(erc20NameSignature, address)
if err != nil {
return nil, err
}
name := parseErc20StringProperty(contractDesc, data)
data, err = b.ethCall(erc20SymbolSignature, address)
if err != nil {
return nil, err
}
symbol := parseErc20StringProperty(contractDesc, data)
data, err = b.ethCall(erc20DecimalsSignature, address)
if err != nil {
return nil, err
}
contract = bchain.Erc20Contract{
Name: name,
Symbol: symbol,
}
d := parseErc20NumericProperty(contractDesc, data)
if d != nil {
contract.Decimals = int(uint8(d.Uint64()))
} else {
contract.Decimals = EtherAmountDecimalPoint
}
cachedContractsMux.Lock()
cachedContracts[cds] = contract
cachedContractsMux.Unlock()
}
return &contract, nil
}
// EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address
func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) {
addr := hexutil.Encode(addrDesc)
contract := hexutil.Encode(contractDesc)
req := erc20BalanceOf + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:]
data, err := b.ethCall(req, contract)
if err != nil {
return nil, err
}
r := parseErc20NumericProperty(contractDesc, data)
if r == nil {
return nil, errors.New("Invalid balance")
}
return r, nil
}

View File

@ -1,4 +1,4 @@
// +build unittest
// build unittest
package eth
@ -108,5 +108,32 @@ func TestErc20_erc20GetTransfersFromLog(t *testing.T) {
}
})
}
}
func TestErc20_parseErc20StringProperty(t *testing.T) {
tests := []struct {
name string
args string
want string
}{
{
name: "1",
args: "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000758504c4f44444500000000000000000000000000000000000000000000000000",
want: "XPLODDE",
},
{
name: "1",
args: "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000758504c4f44444500000000000000000000000000000000000000000000000000",
want: "XPLODDE",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := parseErc20StringProperty(nil, tt.args)
// the addresses could have different case
if got != tt.want {
t.Errorf("parseErc20StringProperty = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -16,6 +16,9 @@ import (
// EthereumTypeAddressDescriptorLen - in case of EthereumType, the AddressDescriptor has fixed length
const EthereumTypeAddressDescriptorLen = 20
// EtherAmountDecimalPoint defines number of decimal points in Ether amounts
const EtherAmountDecimalPoint = 18
// EthereumParser handle
type EthereumParser struct {
*bchain.BaseParser
@ -25,7 +28,7 @@ type EthereumParser struct {
func NewEthereumParser(b int) *EthereumParser {
return &EthereumParser{&bchain.BaseParser{
BlockAddressesToKeep: b,
AmountDecimalPoint: 18,
AmountDecimalPoint: EtherAmountDecimalPoint,
}}
}
@ -435,3 +438,36 @@ func GetErc20FromTx(tx *bchain.Tx) ([]Erc20Transfer, error) {
}
return r, nil
}
// EthereumTxData contains ethereum specific transaction data
type EthereumTxData struct {
Status int `json:"status"` // 1 OK, 0 Fail, -1 pending
Nonce uint64 `json:"nonce"`
GasLimit *big.Int `json:"gaslimit"`
GasUsed *big.Int `json:"gasused"`
GasPrice string `json:"gasprice"`
GasPriceNum *big.Int `json:"-"`
}
// GetEthereumTxData returns EthereumTxData from bchain.Tx
func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData {
etd := EthereumTxData{Status: -1}
csd, ok := tx.CoinSpecificData.(completeTransaction)
if ok {
if csd.Tx != nil {
etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce)
etd.GasLimit, _ = hexutil.DecodeBig(csd.Tx.GasLimit)
etd.GasPriceNum, _ = hexutil.DecodeBig(csd.Tx.GasPrice)
etd.GasPrice = bchain.AmountToDecimalString(etd.GasPriceNum, EtherAmountDecimalPoint)
}
if csd.Receipt != nil {
if csd.Receipt.Status == "0x1" {
etd.Status = 1
} else {
etd.Status = 0
}
etd.GasUsed, _ = hexutil.DecodeBig(csd.Receipt.GasUsed)
}
}
return &etd
}

View File

@ -40,12 +40,11 @@ type Configuration struct {
// EthereumRPC is an interface to JSON-RPC eth service.
type EthereumRPC struct {
*bchain.BaseChain
client *ethclient.Client
rpc *rpc.Client
timeout time.Duration
Parser *EthereumParser
Testnet bool
Network string
Mempool *bchain.MempoolEthereumType
bestHeaderMu sync.Mutex
bestHeader *ethtypes.Header
@ -77,6 +76,7 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification
ec := ethclient.NewClient(rc)
s := &EthereumRPC{
BaseChain: &bchain.BaseChain{},
client: ec,
rpc: rc,
ChainConfig: &c,
@ -250,16 +250,6 @@ func (b *EthereumRPC) Shutdown(ctx context.Context) error {
return nil
}
// IsTestnet returns true if the network is testnet
func (b *EthereumRPC) IsTestnet() bool {
return b.Testnet
}
// GetNetworkName returns network name
func (b *EthereumRPC) GetNetworkName() string {
return b.Network
}
// GetCoinName returns coin name
func (b *EthereumRPC) GetCoinName() string {
return b.ChainConfig.CoinName
@ -652,6 +642,13 @@ func (b *EthereumRPC) SendRawTransaction(hex string) (string, error) {
return result, nil
}
// EthereumTypeGetBalance returns current balance of an address
func (b *EthereumRPC) EthereumTypeGetBalance(addrDesc bchain.AddressDescriptor) (*big.Int, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
return b.client.BalanceAt(ctx, ethcommon.BytesToAddress(addrDesc), nil)
}
// ResyncMempool gets mempool transactions and maps output scripts to transactions.
// ResyncMempool is not reentrant, it should be called from a single thread.
// Return value is number of transactions in mempool

View File

@ -68,7 +68,7 @@ func (m *MempoolEthereumType) Resync(onNewTxAddr OnNewTxAddrFunc) (int, error) {
if !exists {
tx, err := m.chain.GetTransactionForMempool(txid)
if err != nil {
glog.Error("cannot get transaction ", txid, ": ", err)
glog.Warning("cannot get transaction ", txid, ": ", err)
continue
}
io = make([]addrIndex, 0, len(tx.Vout)+len(tx.Vin))

View File

@ -159,6 +159,16 @@ func (ad AddressDescriptor) String() string {
return "ad:" + hex.EncodeToString(ad)
}
// EthereumType specific
// Erc20Contract contains info about ERC20 contract
type Erc20Contract struct {
Contract string
Name string
Symbol string
Decimals int
}
// OnNewBlockFunc is used to send notification about a new block
type OnNewBlockFunc func(hash string, height uint32)
@ -197,6 +207,10 @@ type BlockChain interface {
GetMempoolEntry(txid string) (*MempoolEntry, error)
// parser
GetChainParser() BlockChainParser
// EthereumType specific
EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error)
EthereumTypeGetErc20ContractInfo(contractDesc AddressDescriptor) (*Erc20Contract, error)
EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc AddressDescriptor) (*big.Int, error)
}
// BlockChainParser defines common interface to parsing and conversions of block chain data

View File

@ -9,11 +9,11 @@ import (
)
type fakeBlockChain struct {
parser bchain.BlockChainParser
*bchain.BaseChain
}
func NewFakeBlockChain(parser bchain.BlockChainParser) (*fakeBlockChain, error) {
return &fakeBlockChain{parser: parser}, nil
return &fakeBlockChain{&bchain.BaseChain{Parser: parser}}, nil
}
func (c *fakeBlockChain) Initialize() error {
@ -45,26 +45,26 @@ func (c *fakeBlockChain) GetChainInfo() (v *bchain.ChainInfo, err error) {
Chain: c.GetNetworkName(),
Blocks: 2,
Headers: 2,
Bestblockhash: GetTestBitcoinTypeBlock2(c.parser).BlockHeader.Hash,
Bestblockhash: GetTestBitcoinTypeBlock2(c.Parser).BlockHeader.Hash,
Version: "001001",
Subversion: c.GetSubversion(),
}, nil
}
func (c *fakeBlockChain) GetBestBlockHash() (v string, err error) {
return GetTestBitcoinTypeBlock2(c.parser).BlockHeader.Hash, nil
return GetTestBitcoinTypeBlock2(c.Parser).BlockHeader.Hash, nil
}
func (c *fakeBlockChain) GetBestBlockHeight() (v uint32, err error) {
return GetTestBitcoinTypeBlock2(c.parser).BlockHeader.Height, nil
return GetTestBitcoinTypeBlock2(c.Parser).BlockHeader.Height, nil
}
func (c *fakeBlockChain) GetBlockHash(height uint32) (v string, err error) {
b1 := GetTestBitcoinTypeBlock1(c.parser)
b1 := GetTestBitcoinTypeBlock1(c.Parser)
if height == b1.BlockHeader.Height {
return b1.BlockHeader.Hash, nil
}
b2 := GetTestBitcoinTypeBlock2(c.parser)
b2 := GetTestBitcoinTypeBlock2(c.Parser)
if height == b2.BlockHeader.Height {
return b2.BlockHeader.Hash, nil
}
@ -72,11 +72,11 @@ func (c *fakeBlockChain) GetBlockHash(height uint32) (v string, err error) {
}
func (c *fakeBlockChain) GetBlockHeader(hash string) (v *bchain.BlockHeader, err error) {
b1 := GetTestBitcoinTypeBlock1(c.parser)
b1 := GetTestBitcoinTypeBlock1(c.Parser)
if hash == b1.BlockHeader.Hash {
return &b1.BlockHeader, nil
}
b2 := GetTestBitcoinTypeBlock2(c.parser)
b2 := GetTestBitcoinTypeBlock2(c.Parser)
if hash == b2.BlockHeader.Hash {
return &b2.BlockHeader, nil
}
@ -84,11 +84,11 @@ func (c *fakeBlockChain) GetBlockHeader(hash string) (v *bchain.BlockHeader, err
}
func (c *fakeBlockChain) GetBlock(hash string, height uint32) (v *bchain.Block, err error) {
b1 := GetTestBitcoinTypeBlock1(c.parser)
b1 := GetTestBitcoinTypeBlock1(c.Parser)
if hash == b1.BlockHeader.Hash || height == b1.BlockHeader.Height {
return b1, nil
}
b2 := GetTestBitcoinTypeBlock2(c.parser)
b2 := GetTestBitcoinTypeBlock2(c.Parser)
if hash == b2.BlockHeader.Hash || height == b2.BlockHeader.Height {
return b2, nil
}
@ -106,11 +106,11 @@ func getBlockInfo(b *bchain.Block) *bchain.BlockInfo {
}
func (c *fakeBlockChain) GetBlockInfo(hash string) (v *bchain.BlockInfo, err error) {
b1 := GetTestBitcoinTypeBlock1(c.parser)
b1 := GetTestBitcoinTypeBlock1(c.Parser)
if hash == b1.BlockHeader.Hash {
return getBlockInfo(b1), nil
}
b2 := GetTestBitcoinTypeBlock2(c.parser)
b2 := GetTestBitcoinTypeBlock2(c.Parser)
if hash == b2.BlockHeader.Hash {
return getBlockInfo(b2), nil
}
@ -131,9 +131,9 @@ func getTxInBlock(b *bchain.Block, txid string) *bchain.Tx {
}
func (c *fakeBlockChain) GetTransaction(txid string) (v *bchain.Tx, err error) {
v = getTxInBlock(GetTestBitcoinTypeBlock1(c.parser), txid)
v = getTxInBlock(GetTestBitcoinTypeBlock1(c.Parser), txid)
if v == nil {
v = getTxInBlock(GetTestBitcoinTypeBlock2(c.parser), txid)
v = getTxInBlock(GetTestBitcoinTypeBlock2(c.Parser), txid)
}
if v != nil {
return v, nil
@ -195,5 +195,5 @@ func (c *fakeBlockChain) GetMempoolEntry(txid string) (v *bchain.MempoolEntry, e
}
func (c *fakeBlockChain) GetChainParser() bchain.BlockChainParser {
return c.parser
return c.Parser
}