Implement and test eth GetBlock and GetTransaction
This commit is contained in:
parent
e8eca5c6ea
commit
a5c4dd0d70
@ -3,6 +3,7 @@ package eth
|
|||||||
import (
|
import (
|
||||||
"blockbook/bchain"
|
"blockbook/bchain"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -197,15 +198,21 @@ func (b *EthRPC) computeConfirmations(n uint64) (uint32, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type rpcTransaction struct {
|
type rpcTransaction struct {
|
||||||
tx *ethtypes.Transaction
|
AccountNonce string `json:"nonce" gencodec:"required"`
|
||||||
txExtraInfo
|
Price string `json:"gasPrice" gencodec:"required"`
|
||||||
}
|
GasLimit string `json:"gas" gencodec:"required"`
|
||||||
|
To string `json:"to" rlp:"nil"` // nil means contract creation
|
||||||
type txExtraInfo struct {
|
Value string `json:"value" gencodec:"required"`
|
||||||
BlockNumber *string
|
Payload string `json:"input" gencodec:"required"`
|
||||||
|
Hash ethcommon.Hash `json:"hash" rlp:"-"`
|
||||||
|
BlockNumber string
|
||||||
BlockHash ethcommon.Hash
|
BlockHash ethcommon.Hash
|
||||||
From ethcommon.Address
|
From string
|
||||||
TransactionIndex string `json:"transactionIndex"`
|
TransactionIndex string `json:"transactionIndex"`
|
||||||
|
// Signature values
|
||||||
|
V string `json:"v" gencodec:"required"`
|
||||||
|
R string `json:"r" gencodec:"required"`
|
||||||
|
S string `json:"s" gencodec:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type rpcBlock struct {
|
type rpcBlock struct {
|
||||||
@ -214,22 +221,39 @@ type rpcBlock struct {
|
|||||||
UncleHashes []ethcommon.Hash `json:"uncles"`
|
UncleHashes []ethcommon.Hash `json:"uncles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ethTxToTx(rtx *rpcTransaction, blocktime int64, confirmations uint32) (*bchain.Tx, error) {
|
func ethNumber(n string) (int64, error) {
|
||||||
txid := ethHashToHash(rtx.tx.Hash())
|
if len(n) > 2 {
|
||||||
n, err := strconv.ParseInt(rtx.TransactionIndex, 16, 64)
|
return strconv.ParseInt(n[2:], 16, 64)
|
||||||
|
}
|
||||||
|
return 0, errors.Errorf("Not a number: '%v'", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ethTxToTx(tx *rpcTransaction, blocktime int64, confirmations uint32) (*bchain.Tx, error) {
|
||||||
|
txid := ethHashToHash(tx.Hash)
|
||||||
|
n, err := ethNumber(tx.TransactionIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var from, to string
|
var from, to string
|
||||||
ethTo := rtx.tx.To()
|
if len(tx.To) > 2 {
|
||||||
if ethTo != nil {
|
to = tx.To[2:]
|
||||||
to = ethTo.Hex()[2:]
|
|
||||||
}
|
}
|
||||||
from = rtx.From.Hex()[2:]
|
if len(tx.From) > 2 {
|
||||||
|
from = tx.From[2:]
|
||||||
|
}
|
||||||
|
v, err := ethNumber(tx.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
h := hex.EncodeToString(b)
|
||||||
return &bchain.Tx{
|
return &bchain.Tx{
|
||||||
Blocktime: blocktime,
|
Blocktime: blocktime,
|
||||||
Confirmations: confirmations,
|
Confirmations: confirmations,
|
||||||
// Hex
|
Hex: h,
|
||||||
// LockTime
|
// LockTime
|
||||||
Time: blocktime,
|
Time: blocktime,
|
||||||
Txid: txid,
|
Txid: txid,
|
||||||
@ -246,7 +270,7 @@ func ethTxToTx(rtx *rpcTransaction, blocktime int64, confirmations uint32) (*bch
|
|||||||
Vout: []bchain.Vout{
|
Vout: []bchain.Vout{
|
||||||
{
|
{
|
||||||
N: uint32(n),
|
N: uint32(n),
|
||||||
Value: float64(rtx.tx.Value().Int64()),
|
Value: float64(v),
|
||||||
ScriptPubKey: bchain.ScriptPubKey{
|
ScriptPubKey: bchain.ScriptPubKey{
|
||||||
// Hex
|
// Hex
|
||||||
Addresses: []string{to},
|
Addresses: []string{to},
|
||||||
@ -307,29 +331,29 @@ func (b *EthRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
|
|||||||
func (b *EthRPC) GetTransaction(txid string) (*bchain.Tx, error) {
|
func (b *EthRPC) GetTransaction(txid string) (*bchain.Tx, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
var json *rpcTransaction
|
var tx *rpcTransaction
|
||||||
err := b.rpc.CallContext(ctx, &json, "eth_getTransactionByHash", ethcommon.HexToHash(txid))
|
err := b.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", ethcommon.HexToHash(txid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if json == nil {
|
} else if tx == nil {
|
||||||
return nil, ethereum.NotFound
|
return nil, ethereum.NotFound
|
||||||
} else if _, r, _ := json.tx.RawSignatureValues(); r == nil {
|
} else if tx.R == "" {
|
||||||
return nil, fmt.Errorf("server returned transaction without signature")
|
return nil, fmt.Errorf("server returned transaction without signature")
|
||||||
}
|
}
|
||||||
var btx *bchain.Tx
|
var btx *bchain.Tx
|
||||||
if json.BlockNumber == nil {
|
if tx.BlockNumber == "" {
|
||||||
// mempool tx
|
// mempool tx
|
||||||
btx, err = ethTxToTx(json, 0, 0)
|
btx, err = ethTxToTx(tx, 0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// non mempool tx - we must read the block header to get the block time
|
// non mempool tx - we must read the block header to get the block time
|
||||||
n, err := strconv.ParseInt((*json.BlockNumber)[2:], 16, 64)
|
n, err := ethNumber(tx.BlockNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
h, err := b.client.HeaderByHash(ctx, json.BlockHash)
|
h, err := b.client.HeaderByHash(ctx, tx.BlockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -337,7 +361,7 @@ func (b *EthRPC) GetTransaction(txid string) (*bchain.Tx, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
btx, err = ethTxToTx(json, h.Time.Int64(), confirmations)
|
btx, err = ethTxToTx(tx, h.Time.Int64(), confirmations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,21 +8,26 @@ import (
|
|||||||
|
|
||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rpcURL = "ws://10.34.3.4:18546"
|
var rpcURL = "ws://10.34.3.4:18546"
|
||||||
var client *ethclient.Client
|
var ethClient *ethclient.Client
|
||||||
|
var ethRpcClient *rpc.Client
|
||||||
|
|
||||||
func setupEthRPC() *EthRPC {
|
func setupEthRPC() *EthRPC {
|
||||||
if client == nil {
|
if ethClient == nil {
|
||||||
ec, err := ethclient.Dial(rpcURL)
|
rc, err := rpc.Dial(rpcURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
client = ec
|
ec := ethclient.NewClient(rc)
|
||||||
|
ethRpcClient = rc
|
||||||
|
ethClient = ec
|
||||||
}
|
}
|
||||||
return &EthRPC{
|
return &EthRPC{
|
||||||
client: client,
|
client: ethClient,
|
||||||
|
rpc: ethRpcClient,
|
||||||
timeout: time.Duration(25) * time.Second,
|
timeout: time.Duration(25) * time.Second,
|
||||||
rpcURL: "ws://10.34.3.4:18546",
|
rpcURL: "ws://10.34.3.4:18546",
|
||||||
}
|
}
|
||||||
@ -231,11 +236,12 @@ func TestEthRPC_GetBlock(t *testing.T) {
|
|||||||
height uint32
|
height uint32
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fields fields
|
fields fields
|
||||||
args args
|
args args
|
||||||
want *bchain.Block
|
want *bchain.Block
|
||||||
wantErr bool
|
wantTxCount int
|
||||||
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "2870000",
|
name: "2870000",
|
||||||
@ -252,6 +258,7 @@ func TestEthRPC_GetBlock(t *testing.T) {
|
|||||||
Confirmations: int(uint32(bh.Number.Uint64()) - 2870000),
|
Confirmations: int(uint32(bh.Number.Uint64()) - 2870000),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
wantTxCount: 12,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@ -261,8 +268,114 @@ func TestEthRPC_GetBlock(t *testing.T) {
|
|||||||
t.Errorf("EthRPC.GetBlock() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("EthRPC.GetBlock() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if got.Hash != tt.want.Hash {
|
||||||
t.Errorf("EthRPC.GetBlock() = %v, want %v", got, tt.want)
|
t.Errorf("EthRPC.GetBlock().Hash = %v, want %v", got.Hash, tt.want.Hash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got.Height != tt.want.Height {
|
||||||
|
t.Errorf("EthRPC.GetBlock().Height = %v, want %v", got.Height, tt.want.Height)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got.Confirmations != tt.want.Confirmations {
|
||||||
|
t.Errorf("EthRPC.GetBlock().Confirmations = %v, want %v", got.Confirmations, tt.want.Confirmations)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(got.Txs) != tt.wantTxCount {
|
||||||
|
t.Errorf("EthRPC.GetBlock().Txs = %v, want %v", len(got.Txs), tt.wantTxCount)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEthRPC_GetTransaction(t *testing.T) {
|
||||||
|
bh, err := setupEthRPC().getBestHeader()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
type fields struct {
|
||||||
|
b *EthRPC
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
txid string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want *bchain.Tx
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "1",
|
||||||
|
fields: fields{
|
||||||
|
b: setupEthRPC(),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
txid: "e6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
|
||||||
|
},
|
||||||
|
want: &bchain.Tx{
|
||||||
|
Blocktime: 1521515026,
|
||||||
|
Confirmations: uint32(bh.Number.Uint64()) - 2870000,
|
||||||
|
Hex: "7b226e6f6e6365223a2230783239666165222c226761735072696365223a223078313261303566323030222c22676173223a2230786462626130222c22746f223a22307836383262373930336131313039386366373730633761656634616130326138356233663336303161222c2276616c7565223a22307830222c22696e707574223a223078663032356361616630303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030323235222c2268617368223a22307865366231363864366262336438656437386530336462663832386236626664316662363133663665313239636261363234393634393834353533373234633564222c22426c6f636b4e756d626572223a223078326263616630222c22426c6f636b48617368223a22307865636364366230303331303135613139636237643465313066323835393062613635613661353461643162616133323262353066653561643136393033383935222c2246726f6d223a22307864616363396336313735346130633436313666633533323364633934366538396562323732333032222c227472616e73616374696f6e496e646578223a22307831222c2276223a2230783162222c2272223a22307831626434306133313132326330333931386466366431363664373430613661336132326630386132353933346365623136383863363239373736363163383063222c2273223a22307836303766626331356331663739393561343235386635613962636363363362303430333632643139393164356566653133363163353632323265346361383966227d",
|
||||||
|
Time: 1521515026,
|
||||||
|
Txid: "e6b168d6bb3d8ed78e03dbf828b6bfd1fb613f6e129cba624964984553724c5d",
|
||||||
|
Vin: []bchain.Vin{
|
||||||
|
{
|
||||||
|
Addresses: []string{"dacc9c61754a0c4616fc5323dc946e89eb272302"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Vout: []bchain.Vout{
|
||||||
|
{
|
||||||
|
N: uint32(1),
|
||||||
|
ScriptPubKey: bchain.ScriptPubKey{
|
||||||
|
Addresses: []string{"682b7903a11098cf770c7aef4aa02a85b3f3601a"},
|
||||||
|
},
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "1",
|
||||||
|
fields: fields{
|
||||||
|
b: setupEthRPC(),
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
txid: "cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
|
||||||
|
},
|
||||||
|
want: &bchain.Tx{
|
||||||
|
Blocktime: 1521533434,
|
||||||
|
Confirmations: uint32(bh.Number.Uint64()) - 2871048,
|
||||||
|
Hex: "7b226e6f6e6365223a22307862323663222c226761735072696365223a223078343330653233343030222c22676173223a22307835323038222c22746f223a22307835353565653131666264646330653439613962616233353861383934316164393566666462343866222c2276616c7565223a22307831626330313539643533306536303030222c22696e707574223a223078222c2268617368223a22307863643634373135313535326235313332623261656637633962653030646336663733616663353930316464653135376161623133313333356261616138353362222c22426c6f636b4e756d626572223a223078326263663038222c22426c6f636b48617368223a22307863303266396632623736633265393537643464656139643030366263643636356239303462613866383461653466343836373561383662373536326461366239222c2246726f6d223a22307833653361336436396463363662613130373337663533316564303838393534613965633839643937222c227472616e73616374696f6e496e646578223a22307861222c2276223a2230783239222c2272223a22307866373136316331373064343335373361643963386437303163646166373134666632613534386135363262306463363339323330643137383839666364343035222c2273223a22307833633439373766633930333835613237656661303033326531376234396664353735623238323663623536653364316563663231353234663261393466393135227d",
|
||||||
|
Time: 1521533434,
|
||||||
|
Txid: "cd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
|
||||||
|
Vin: []bchain.Vin{
|
||||||
|
{
|
||||||
|
Addresses: []string{"3e3a3d69dc66ba10737f531ed088954a9ec89d97"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Vout: []bchain.Vout{
|
||||||
|
{
|
||||||
|
N: uint32(10),
|
||||||
|
ScriptPubKey: bchain.ScriptPubKey{
|
||||||
|
Addresses: []string{"555ee11fbddc0e49a9bab358a8941ad95ffdb48f"},
|
||||||
|
},
|
||||||
|
Value: 1.999622e+18,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := tt.fields.b.GetTransaction(tt.args.txid)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("EthRPC.GetTransaction() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("EthRPC.GetTransaction() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user