Process tx receipts and ERC20 tokens WIP

ethereum
Martin Boehm 2018-11-09 13:08:43 +01:00
parent fac728bc51
commit 4a05757321
7 changed files with 513 additions and 187 deletions

View File

@ -0,0 +1,71 @@
package eth
import (
"math/big"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/juju/errors"
)
var erc20abi = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x06fdde03"},
{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x95d89b41"},
{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function","signature":"0x313ce567"},
{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function","signature":"0x18160ddd"},
{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function","signature":"0x70a08231"},
{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function","signature":"0xa9059cbb"},
{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function","signature":"0x23b872dd"},
{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function","signature":"0x095ea7b3"},
{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"type":"function","signature":"0xdd62ed3e"},
{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event","signature":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},
{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event","signature":"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"},
{"inputs":[{"name":"_initialAmount","type":"uint256"},{"name":"_tokenName","type":"string"},{"name":"_decimalUnits","type":"uint8"},{"name":"_tokenSymbol","type":"string"}],"payable":false,"type":"constructor"},
{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"type":"function","signature":"0xcae9ca51"},
{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x54fd4d50"}]`
// doing the parsing/processing without using go-ethereum/accounts/abi library, it is simple to get data from Transfer event
const erc20EventTransferSignature = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
type erc20Transfer struct {
Contract ethcommon.Address
From ethcommon.Address
To ethcommon.Address
Tokens big.Int
}
func addressFromPaddedHex(s string) (*ethcommon.Address, error) {
var t big.Int
_, ok := t.SetString(s, 0)
if !ok {
return nil, errors.New("Data is not a number")
}
a := ethcommon.BigToAddress(&t)
return &a, nil
}
func erc20GetTransfersFromLog(logs []*rpcLog) ([]erc20Transfer, error) {
var r []erc20Transfer
for _, l := range logs {
if len(l.Topics) == 3 && l.Topics[0] == erc20EventTransferSignature {
var t big.Int
_, ok := t.SetString(l.Data, 0)
if !ok {
return nil, errors.New("Data is not a number")
}
from, err := addressFromPaddedHex(l.Topics[1])
if err != nil {
return nil, err
}
to, err := addressFromPaddedHex(l.Topics[2])
if err != nil {
return nil, err
}
r = append(r, erc20Transfer{
Contract: l.Address,
From: *from,
To: *to,
Tokens: t,
})
}
}
return r, nil
}

View File

@ -0,0 +1,112 @@
// build unittest
package eth
import (
"math/big"
"reflect"
"testing"
ethcommon "github.com/ethereum/go-ethereum/common"
)
func TestErc20_erc20GetTransfersFromLog(t *testing.T) {
tests := []struct {
name string
args []*rpcLog
want []erc20Transfer
wantErr bool
}{
{
name: "1",
args: []*rpcLog{
&rpcLog{
Address: ethcommon.HexToAddress("0x76a45e8976499ab9ae223cc584019341d5a84e96"),
Topics: []string{
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000002aacf811ac1a60081ea39f7783c0d26c500871a8",
"0x000000000000000000000000e9a5216ff992cfa01594d43501a56e12769eb9d2",
},
Data: "0x0000000000000000000000000000000000000000000000000000000000000123",
},
},
want: []erc20Transfer{
{
Contract: ethcommon.HexToAddress("0x76a45e8976499ab9ae223cc584019341d5a84e96"),
From: ethcommon.HexToAddress("0x2aacf811ac1a60081ea39f7783c0d26c500871a8"),
To: ethcommon.HexToAddress("0xe9a5216ff992cfa01594d43501a56e12769eb9d2"),
Tokens: *big.NewInt(0x123),
},
},
},
{
name: "2",
args: []*rpcLog{
&rpcLog{ // Transfer
Address: ethcommon.HexToAddress("0x0d0f936ee4c93e25944694d6c121de94d9760f11"),
Topics: []string{
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000006f44cceb49b4a5812d54b6f494fc2febf25511ed",
"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d",
},
Data: "0x0000000000000000000000000000000000000000000000006a8313d60b1f606b",
},
&rpcLog{ // Transfer
Address: ethcommon.HexToAddress("0xc778417e063141139fce010982780140aa0cd5ab"),
Topics: []string{
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d",
"0x0000000000000000000000006f44cceb49b4a5812d54b6f494fc2febf25511ed",
},
Data: "0x000000000000000000000000000000000000000000000000000308fd0e798ac0",
},
&rpcLog{ // not Transfer
Address: ethcommon.HexToAddress("0x479cc461fecd078f766ecc58533d6f69580cf3ac"),
Topics: []string{
"0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3",
"0x0000000000000000000000006f44cceb49b4a5812d54b6f494fc2febf25511ed",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x5af266c0a89a07c1917deaa024414577e6c3c31c8907d079e13eb448c082594f",
},
Data: "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d0000000000000",
},
&rpcLog{ // not Transfer
Address: ethcommon.HexToAddress("0x0d0f936ee4c93e25944694d6c121de94d9760f11"),
Topics: []string{
"0x0d0b9391970d9a25552f37d436d2aae2925e2bfe1b2a923754bada030c498cb3",
"0x0000000000000000000000007b62eb7fe80350dc7ec945c0b73242cb9877fb1b",
"0xb0b69dad58df6032c3b266e19b1045b19c87acd2c06fb0c598090f44b8e263aa",
},
Data: "0x0000000000000000000000004bda106325c335df99eab7fe363cac8a0ba2a24d000000000000000000000000c778417e063141139fce010982780140aa0cd5ab0000000000000000000000000d0f936ee4c93e25944694d6c121de94d9760f1100000000000000000000000000000000000000000000000000031855667df7a80000000000000000000000000000000000000000000000006a8313d60b1f800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
},
},
want: []erc20Transfer{
{
Contract: ethcommon.HexToAddress("0x0d0f936ee4c93e25944694d6c121de94d9760f11"),
From: ethcommon.HexToAddress("0x6f44cceb49b4a5812d54b6f494fc2febf25511ed"),
To: ethcommon.HexToAddress("0x4bda106325c335df99eab7fe363cac8a0ba2a24d"),
Tokens: *big.NewInt(0x6a8313d60b1f606b),
},
{
Contract: ethcommon.HexToAddress("0xc778417e063141139fce010982780140aa0cd5ab"),
From: ethcommon.HexToAddress("0x4bda106325c335df99eab7fe363cac8a0ba2a24d"),
To: ethcommon.HexToAddress("0x6f44cceb49b4a5812d54b6f494fc2febf25511ed"),
Tokens: *big.NewInt(0x308fd0e798ac0),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := erc20GetTransfersFromLog(tt.args)
if (err != nil) != tt.wantErr {
t.Errorf("erc20GetTransfersFromLog error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("erc20GetTransfersFromLog = %+v, want %+v", got, tt.want)
}
})
}
}

View File

@ -9,6 +9,7 @@ import (
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/glog"
"github.com/golang/protobuf/proto"
"github.com/juju/errors"
)
@ -38,16 +39,33 @@ type rpcTransaction struct {
BlockHash *ethcommon.Hash `json:"blockHash,omitempty"`
From string `json:"from"`
TransactionIndex string `json:"transactionIndex"`
// Signature values
V string `json:"v" gencodec:"required"`
R string `json:"r" gencodec:"required"`
S string `json:"s" gencodec:"required"`
// Signature values - ignored
// V string `json:"v" gencodec:"required"`
// R string `json:"r" gencodec:"required"`
// S string `json:"s" gencodec:"required"`
}
type rpcLog struct {
Address ethcommon.Address `json:"address" gencodec:"required"`
Topics []string `json:"topics" gencodec:"required"`
Data string `json:"data" gencodec:"required"`
}
type rpcReceipt struct {
GasUsed string `json:"gasUsed" gencodec:"required"`
Status string `json:"status"`
Logs []*rpcLog `json:"logs" gencodec:"required"`
}
type completeTransaction struct {
Tx *rpcTransaction `json:"tx"`
Receipt *rpcReceipt `json:"receipt,omitempty"`
}
type rpcBlock struct {
Hash ethcommon.Hash `json:"hash"`
Size string `json:"size"`
Transactions []rpcTransaction `json:"transactions"`
UncleHashes []ethcommon.Hash `json:"uncles"`
}
func ethHashToHash(h ethcommon.Hash) string {
@ -61,7 +79,7 @@ func ethNumber(n string) (int64, error) {
return 0, errors.Errorf("Not a number: '%v'", n)
}
func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirmations uint32) (*bchain.Tx, error) {
func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, blocktime int64, confirmations uint32) (*bchain.Tx, error) {
txid := ethHashToHash(tx.Hash)
var (
fa, ta []string
@ -73,15 +91,21 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
if len(tx.To) > 2 {
ta = []string{tx.To}
}
// temporarily, the complete rpcTransaction without BlockHash is marshalled and hex encoded to bchain.Tx.Hex
// completeTransaction without BlockHash is marshalled and hex encoded to bchain.Tx.Hex
bh := tx.BlockHash
tx.BlockHash = nil
b, err := json.Marshal(tx)
ct := completeTransaction{
Tx: tx,
Receipt: receipt,
}
b, err := json.Marshal(ct)
if err != nil {
return nil, err
}
tx.BlockHash = bh
h := hex.EncodeToString(b)
glog.Info(h)
vs, err := hexutil.DecodeBig(tx.Value)
if err != nil {
return nil, err
@ -181,79 +205,96 @@ func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) (
if err != nil {
return nil, err
}
var r rpcTransaction
var r completeTransaction
var n uint64
err = json.Unmarshal(b, &r)
if err != nil {
return nil, err
}
pt := &ProtoTransaction{}
if pt.AccountNonce, err = hexutil.DecodeUint64(r.AccountNonce); err != nil {
return nil, errors.Annotatef(err, "AccountNonce %v", r.AccountNonce)
pt := &ProtoCompleteTransaction{}
if pt.Tx.AccountNonce, err = hexutil.DecodeUint64(r.Tx.AccountNonce); err != nil {
return nil, errors.Annotatef(err, "AccountNonce %v", r.Tx.AccountNonce)
}
if n, err = hexutil.DecodeUint64(r.BlockNumber); err != nil {
return nil, errors.Annotatef(err, "BlockNumber %v", r.BlockNumber)
if n, err = hexutil.DecodeUint64(r.Tx.BlockNumber); err != nil {
return nil, errors.Annotatef(err, "BlockNumber %v", r.Tx.BlockNumber)
}
pt.BlockNumber = uint32(n)
pt.BlockTime = uint64(blockTime)
if pt.From, err = hexDecode(r.From); err != nil {
return nil, errors.Annotatef(err, "From %v", r.From)
if pt.Tx.From, err = hexDecode(r.Tx.From); err != nil {
return nil, errors.Annotatef(err, "From %v", r.Tx.From)
}
if pt.GasLimit, err = hexutil.DecodeUint64(r.GasLimit); err != nil {
return nil, errors.Annotatef(err, "GasLimit %v", r.GasLimit)
if pt.Tx.GasLimit, err = hexutil.DecodeUint64(r.Tx.GasLimit); err != nil {
return nil, errors.Annotatef(err, "GasLimit %v", r.Tx.GasLimit)
}
pt.Hash = r.Hash.Bytes()
if pt.Payload, err = hexDecode(r.Payload); err != nil {
return nil, errors.Annotatef(err, "Payload %v", r.Payload)
pt.Tx.Hash = r.Tx.Hash.Bytes()
if pt.Tx.Payload, err = hexDecode(r.Tx.Payload); err != nil {
return nil, errors.Annotatef(err, "Payload %v", r.Tx.Payload)
}
if pt.Price, err = hexDecodeBig(r.Price); err != nil {
return nil, errors.Annotatef(err, "Price %v", r.Price)
if pt.Tx.Price, err = hexDecodeBig(r.Tx.Price); err != nil {
return nil, errors.Annotatef(err, "Price %v", r.Tx.Price)
}
if pt.R, err = hexDecodeBig(r.R); err != nil {
return nil, errors.Annotatef(err, "R %v", r.R)
// if pt.R, err = hexDecodeBig(r.R); err != nil {
// return nil, errors.Annotatef(err, "R %v", r.R)
// }
// if pt.S, err = hexDecodeBig(r.S); err != nil {
// return nil, errors.Annotatef(err, "S %v", r.S)
// }
// if pt.V, err = hexDecodeBig(r.V); err != nil {
// return nil, errors.Annotatef(err, "V %v", r.V)
// }
if pt.Tx.To, err = hexDecode(r.Tx.To); err != nil {
return nil, errors.Annotatef(err, "To %v", r.Tx.To)
}
if pt.S, err = hexDecodeBig(r.S); err != nil {
return nil, errors.Annotatef(err, "S %v", r.S)
if n, err = hexutil.DecodeUint64(r.Tx.TransactionIndex); err != nil {
return nil, errors.Annotatef(err, "TransactionIndex %v", r.Tx.TransactionIndex)
}
if pt.V, err = hexDecodeBig(r.V); err != nil {
return nil, errors.Annotatef(err, "V %v", r.V)
}
if pt.To, err = hexDecode(r.To); err != nil {
return nil, errors.Annotatef(err, "To %v", r.To)
}
if n, err = hexutil.DecodeUint64(r.TransactionIndex); err != nil {
return nil, errors.Annotatef(err, "TransactionIndex %v", r.TransactionIndex)
}
pt.TransactionIndex = uint32(n)
if pt.Value, err = hexDecodeBig(r.Value); err != nil {
return nil, errors.Annotatef(err, "Value %v", r.Value)
pt.Tx.TransactionIndex = uint32(n)
if pt.Tx.Value, err = hexDecodeBig(r.Tx.Value); err != nil {
return nil, errors.Annotatef(err, "Value %v", r.Tx.Value)
}
return proto.Marshal(pt)
}
// UnpackTx unpacks transaction from byte array
func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
var pt ProtoTransaction
var pt ProtoCompleteTransaction
err := proto.Unmarshal(buf, &pt)
if err != nil {
return nil, 0, err
}
r := rpcTransaction{
AccountNonce: hexutil.EncodeUint64(pt.AccountNonce),
BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)),
From: hexutil.Encode(pt.From),
GasLimit: hexutil.EncodeUint64(pt.GasLimit),
Hash: ethcommon.BytesToHash(pt.Hash),
Payload: hexutil.Encode(pt.Payload),
Price: hexEncodeBig(pt.Price),
R: hexEncodeBig(pt.R),
S: hexEncodeBig(pt.S),
V: hexEncodeBig(pt.V),
To: hexutil.Encode(pt.To),
TransactionIndex: hexutil.EncodeUint64(uint64(pt.TransactionIndex)),
Value: hexEncodeBig(pt.Value),
rt := rpcTransaction{
AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce),
BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)),
From: hexutil.Encode(pt.Tx.From),
GasLimit: hexutil.EncodeUint64(pt.Tx.GasLimit),
Hash: ethcommon.BytesToHash(pt.Tx.Hash),
Payload: hexutil.Encode(pt.Tx.Payload),
Price: hexEncodeBig(pt.Tx.Price),
// R: hexEncodeBig(pt.R),
// S: hexEncodeBig(pt.S),
// V: hexEncodeBig(pt.V),
To: hexutil.Encode(pt.Tx.To),
TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)),
Value: hexEncodeBig(pt.Tx.Value),
}
tx, err := p.ethTxToTx(&r, int64(pt.BlockTime), 0)
logs := make([]*rpcLog, len(pt.Receipt.Log))
for i, l := range pt.Receipt.Log {
topics := make([]string, len(l.Topics))
for j, t := range l.Topics {
topics[j] = hexutil.Encode(t)
}
logs[i] = &rpcLog{
Address: ethcommon.BytesToAddress(l.Address),
Data: hexutil.Encode(l.Data),
Topics: topics,
}
}
rr := rpcReceipt{
GasUsed: hexEncodeBig(pt.Receipt.GasUsed),
Status: hexEncodeBig(pt.Receipt.Status),
Logs: logs,
}
tx, err := p.ethTxToTx(&rt, &rr, int64(pt.BlockTime), 0)
if err != nil {
return nil, 0, err
}
@ -303,14 +344,14 @@ func GetHeightFromTx(tx *bchain.Tx) (uint32, error) {
if err != nil {
return 0, err
}
var r rpcTransaction
var ct completeTransaction
var n uint64
err = json.Unmarshal(b, &r)
err = json.Unmarshal(b, &ct)
if err != nil {
return 0, err
}
if n, err = hexutil.DecodeUint64(r.BlockNumber); err != nil {
return 0, errors.Annotatef(err, "BlockNumber %v", r.BlockNumber)
if n, err = hexutil.DecodeUint64(ct.Tx.BlockNumber); err != nil {
return 0, errors.Annotatef(err, "BlockNumber %v", ct.Tx.BlockNumber)
}
return uint32(n), nil
}

View File

@ -1,4 +1,4 @@
// +build unittest
// build unittest
package eth

View File

@ -12,6 +12,7 @@ import (
ethereum "github.com/ethereum/go-ethereum"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
@ -423,12 +424,6 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
}
// Quick-verify transaction and uncle lists. This mostly helps with debugging the server.
if head.UncleHash == ethtypes.EmptyUncleHash && len(body.UncleHashes) > 0 {
return nil, errors.Annotatef(fmt.Errorf("server returned non-empty uncle list but block header indicates no uncles"), "hash %v, height %v", hash, height)
}
if head.UncleHash != ethtypes.EmptyUncleHash && len(body.UncleHashes) == 0 {
return nil, errors.Annotatef(fmt.Errorf("server returned empty uncle list but block header indicates uncles"), "hash %v, height %v", hash, height)
}
if head.TxHash == ethtypes.EmptyRootHash && len(body.Transactions) > 0 {
return nil, errors.Annotatef(fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions"), "hash %v, height %v", hash, height)
}
@ -439,11 +434,16 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
}
// TODO - this is probably not the correct size
bbh.Size = len(raw)
bigSize, err := hexutil.DecodeBig(body.Size)
if err != nil {
glog.Error("invalid size of block ", body.Hash, ": ", body.Size)
} else {
bbh.Size = int(bigSize.Int64())
}
// TODO - get ERC20 events
btxs := make([]bchain.Tx, len(body.Transactions))
for i, tx := range body.Transactions {
btx, err := b.Parser.ethTxToTx(&tx, int64(head.Time.Uint64()), uint32(bbh.Confirmations))
btx, err := b.Parser.ethTxToTx(&tx, nil, int64(head.Time.Uint64()), uint32(bbh.Confirmations))
if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash.String())
}
@ -473,32 +473,38 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel()
var tx *rpcTransaction
err := b.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", ethcommon.HexToHash(txid))
hash := ethcommon.HexToHash(txid)
err := b.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", hash)
if err != nil {
return nil, err
} else if tx == nil {
return nil, ethereum.NotFound
} else if tx.R == "" {
if !b.isETC {
return nil, errors.Annotatef(fmt.Errorf("server returned transaction without signature"), "txid %v", txid)
} else {
glog.Warning("server returned transaction without signature, txid ", txid)
}
}
// else if tx.R == "" {
// if !b.isETC {
// return nil, errors.Annotatef(fmt.Errorf("server returned transaction without signature"), "txid %v", txid)
// }
// glog.Warning("server returned transaction without signature, txid ", txid)
// }
var btx *bchain.Tx
if tx.BlockNumber == "" {
// mempool tx
btx, err = b.Parser.ethTxToTx(tx, 0, 0)
btx, err = b.Parser.ethTxToTx(tx, nil, 0, 0)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
} else {
// non mempool tx - we must read the block header to get the block time
n, err := ethNumber(tx.BlockNumber)
h, err := b.client.HeaderByHash(ctx, *tx.BlockHash)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
h, err := b.client.HeaderByHash(ctx, *tx.BlockHash)
var receipt rpcReceipt
err = b.rpc.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
n, err := ethNumber(tx.BlockNumber)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
@ -506,7 +512,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
btx, err = b.Parser.ethTxToTx(tx, h.Time.Int64(), confirmations)
btx, err = b.Parser.ethTxToTx(tx, &receipt, h.Time.Int64(), confirmations)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}

View File

@ -8,7 +8,7 @@ It is generated from these files:
tx.proto
It has these top-level messages:
ProtoTransaction
ProtoCompleteTransaction
*/
package eth
@ -27,149 +27,234 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type ProtoTransaction struct {
AccountNonce uint64 `protobuf:"varint,1,opt,name=AccountNonce" json:"AccountNonce,omitempty"`
Price []byte `protobuf:"bytes,2,opt,name=Price,proto3" json:"Price,omitempty"`
GasLimit uint64 `protobuf:"varint,3,opt,name=GasLimit" json:"GasLimit,omitempty"`
Value []byte `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"`
Payload []byte `protobuf:"bytes,5,opt,name=Payload,proto3" json:"Payload,omitempty"`
Hash []byte `protobuf:"bytes,6,opt,name=Hash,proto3" json:"Hash,omitempty"`
BlockNumber uint32 `protobuf:"varint,7,opt,name=BlockNumber" json:"BlockNumber,omitempty"`
BlockTime uint64 `protobuf:"varint,8,opt,name=BlockTime" json:"BlockTime,omitempty"`
To []byte `protobuf:"bytes,9,opt,name=To,proto3" json:"To,omitempty"`
From []byte `protobuf:"bytes,10,opt,name=From,proto3" json:"From,omitempty"`
TransactionIndex uint32 `protobuf:"varint,11,opt,name=TransactionIndex" json:"TransactionIndex,omitempty"`
V []byte `protobuf:"bytes,12,opt,name=V,proto3" json:"V,omitempty"`
R []byte `protobuf:"bytes,13,opt,name=R,proto3" json:"R,omitempty"`
S []byte `protobuf:"bytes,14,opt,name=S,proto3" json:"S,omitempty"`
type ProtoCompleteTransaction struct {
BlockNumber uint32 `protobuf:"varint,1,opt,name=BlockNumber" json:"BlockNumber,omitempty"`
BlockTime uint64 `protobuf:"varint,2,opt,name=BlockTime" json:"BlockTime,omitempty"`
Tx *ProtoCompleteTransaction_TxType `protobuf:"bytes,3,opt,name=Tx" json:"Tx,omitempty"`
Receipt *ProtoCompleteTransaction_ReceiptType `protobuf:"bytes,4,opt,name=Receipt" json:"Receipt,omitempty"`
}
func (m *ProtoTransaction) Reset() { *m = ProtoTransaction{} }
func (m *ProtoTransaction) String() string { return proto.CompactTextString(m) }
func (*ProtoTransaction) ProtoMessage() {}
func (*ProtoTransaction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *ProtoCompleteTransaction) Reset() { *m = ProtoCompleteTransaction{} }
func (m *ProtoCompleteTransaction) String() string { return proto.CompactTextString(m) }
func (*ProtoCompleteTransaction) ProtoMessage() {}
func (*ProtoCompleteTransaction) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *ProtoTransaction) GetAccountNonce() uint64 {
if m != nil {
return m.AccountNonce
}
return 0
}
func (m *ProtoTransaction) GetPrice() []byte {
if m != nil {
return m.Price
}
return nil
}
func (m *ProtoTransaction) GetGasLimit() uint64 {
if m != nil {
return m.GasLimit
}
return 0
}
func (m *ProtoTransaction) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
func (m *ProtoTransaction) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
func (m *ProtoTransaction) GetHash() []byte {
if m != nil {
return m.Hash
}
return nil
}
func (m *ProtoTransaction) GetBlockNumber() uint32 {
func (m *ProtoCompleteTransaction) GetBlockNumber() uint32 {
if m != nil {
return m.BlockNumber
}
return 0
}
func (m *ProtoTransaction) GetBlockTime() uint64 {
func (m *ProtoCompleteTransaction) GetBlockTime() uint64 {
if m != nil {
return m.BlockTime
}
return 0
}
func (m *ProtoTransaction) GetTo() []byte {
func (m *ProtoCompleteTransaction) GetTx() *ProtoCompleteTransaction_TxType {
if m != nil {
return m.Tx
}
return nil
}
func (m *ProtoCompleteTransaction) GetReceipt() *ProtoCompleteTransaction_ReceiptType {
if m != nil {
return m.Receipt
}
return nil
}
type ProtoCompleteTransaction_TxType struct {
AccountNonce uint64 `protobuf:"varint,1,opt,name=AccountNonce" json:"AccountNonce,omitempty"`
Price []byte `protobuf:"bytes,2,opt,name=Price,proto3" json:"Price,omitempty"`
GasLimit uint64 `protobuf:"varint,3,opt,name=GasLimit" json:"GasLimit,omitempty"`
Value []byte `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"`
Payload []byte `protobuf:"bytes,5,opt,name=Payload,proto3" json:"Payload,omitempty"`
Hash []byte `protobuf:"bytes,6,opt,name=Hash,proto3" json:"Hash,omitempty"`
To []byte `protobuf:"bytes,7,opt,name=To,proto3" json:"To,omitempty"`
From []byte `protobuf:"bytes,8,opt,name=From,proto3" json:"From,omitempty"`
TransactionIndex uint32 `protobuf:"varint,9,opt,name=TransactionIndex" json:"TransactionIndex,omitempty"`
}
func (m *ProtoCompleteTransaction_TxType) Reset() { *m = ProtoCompleteTransaction_TxType{} }
func (m *ProtoCompleteTransaction_TxType) String() string { return proto.CompactTextString(m) }
func (*ProtoCompleteTransaction_TxType) ProtoMessage() {}
func (*ProtoCompleteTransaction_TxType) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{0, 0}
}
func (m *ProtoCompleteTransaction_TxType) GetAccountNonce() uint64 {
if m != nil {
return m.AccountNonce
}
return 0
}
func (m *ProtoCompleteTransaction_TxType) GetPrice() []byte {
if m != nil {
return m.Price
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetGasLimit() uint64 {
if m != nil {
return m.GasLimit
}
return 0
}
func (m *ProtoCompleteTransaction_TxType) GetValue() []byte {
if m != nil {
return m.Value
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetHash() []byte {
if m != nil {
return m.Hash
}
return nil
}
func (m *ProtoCompleteTransaction_TxType) GetTo() []byte {
if m != nil {
return m.To
}
return nil
}
func (m *ProtoTransaction) GetFrom() []byte {
func (m *ProtoCompleteTransaction_TxType) GetFrom() []byte {
if m != nil {
return m.From
}
return nil
}
func (m *ProtoTransaction) GetTransactionIndex() uint32 {
func (m *ProtoCompleteTransaction_TxType) GetTransactionIndex() uint32 {
if m != nil {
return m.TransactionIndex
}
return 0
}
func (m *ProtoTransaction) GetV() []byte {
type ProtoCompleteTransaction_ReceiptType struct {
GasUsed []byte `protobuf:"bytes,1,opt,name=GasUsed,proto3" json:"GasUsed,omitempty"`
Status []byte `protobuf:"bytes,2,opt,name=Status,proto3" json:"Status,omitempty"`
Log []*ProtoCompleteTransaction_ReceiptType_LogType `protobuf:"bytes,3,rep,name=Log" json:"Log,omitempty"`
}
func (m *ProtoCompleteTransaction_ReceiptType) Reset() { *m = ProtoCompleteTransaction_ReceiptType{} }
func (m *ProtoCompleteTransaction_ReceiptType) String() string { return proto.CompactTextString(m) }
func (*ProtoCompleteTransaction_ReceiptType) ProtoMessage() {}
func (*ProtoCompleteTransaction_ReceiptType) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{0, 1}
}
func (m *ProtoCompleteTransaction_ReceiptType) GetGasUsed() []byte {
if m != nil {
return m.V
return m.GasUsed
}
return nil
}
func (m *ProtoTransaction) GetR() []byte {
func (m *ProtoCompleteTransaction_ReceiptType) GetStatus() []byte {
if m != nil {
return m.R
return m.Status
}
return nil
}
func (m *ProtoTransaction) GetS() []byte {
func (m *ProtoCompleteTransaction_ReceiptType) GetLog() []*ProtoCompleteTransaction_ReceiptType_LogType {
if m != nil {
return m.S
return m.Log
}
return nil
}
type ProtoCompleteTransaction_ReceiptType_LogType struct {
Address []byte `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=Data,proto3" json:"Data,omitempty"`
Topics [][]byte `protobuf:"bytes,3,rep,name=Topics,proto3" json:"Topics,omitempty"`
}
func (m *ProtoCompleteTransaction_ReceiptType_LogType) Reset() {
*m = ProtoCompleteTransaction_ReceiptType_LogType{}
}
func (m *ProtoCompleteTransaction_ReceiptType_LogType) String() string {
return proto.CompactTextString(m)
}
func (*ProtoCompleteTransaction_ReceiptType_LogType) ProtoMessage() {}
func (*ProtoCompleteTransaction_ReceiptType_LogType) Descriptor() ([]byte, []int) {
return fileDescriptor0, []int{0, 1, 0}
}
func (m *ProtoCompleteTransaction_ReceiptType_LogType) GetAddress() []byte {
if m != nil {
return m.Address
}
return nil
}
func (m *ProtoCompleteTransaction_ReceiptType_LogType) GetData() []byte {
if m != nil {
return m.Data
}
return nil
}
func (m *ProtoCompleteTransaction_ReceiptType_LogType) GetTopics() [][]byte {
if m != nil {
return m.Topics
}
return nil
}
func init() {
proto.RegisterType((*ProtoTransaction)(nil), "eth.ProtoTransaction")
proto.RegisterType((*ProtoCompleteTransaction)(nil), "eth.ProtoCompleteTransaction")
proto.RegisterType((*ProtoCompleteTransaction_TxType)(nil), "eth.ProtoCompleteTransaction.TxType")
proto.RegisterType((*ProtoCompleteTransaction_ReceiptType)(nil), "eth.ProtoCompleteTransaction.ReceiptType")
proto.RegisterType((*ProtoCompleteTransaction_ReceiptType_LogType)(nil), "eth.ProtoCompleteTransaction.ReceiptType.LogType")
}
func init() { proto.RegisterFile("tx.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 262 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xbd, 0x6a, 0xeb, 0x40,
0x10, 0x85, 0x59, 0x59, 0xb6, 0xe5, 0xb1, 0x6c, 0xcc, 0x70, 0x8b, 0xe1, 0x92, 0x42, 0xb8, 0x12,
0x29, 0xd2, 0xe4, 0x09, 0x92, 0x22, 0x3f, 0x10, 0x8c, 0x90, 0x85, 0xfa, 0xf5, 0x7a, 0xc1, 0x22,
0x92, 0x26, 0x48, 0x2b, 0x70, 0x5e, 0x38, 0xcf, 0x11, 0x76, 0x44, 0x12, 0x87, 0x74, 0xf3, 0x7d,
0x70, 0xf6, 0x2c, 0x07, 0x22, 0x77, 0xbe, 0x79, 0xeb, 0xd8, 0x31, 0x4e, 0xac, 0x3b, 0x6d, 0x3f,
0x02, 0xd8, 0x64, 0x1e, 0x8b, 0x4e, 0xb7, 0xbd, 0x36, 0xae, 0xe2, 0x16, 0xb7, 0x10, 0xdf, 0x19,
0xc3, 0x43, 0xeb, 0x76, 0xdc, 0x1a, 0x4b, 0x2a, 0x51, 0x69, 0x98, 0xff, 0x72, 0xf8, 0x0f, 0xa6,
0x59, 0x57, 0x19, 0x4b, 0x41, 0xa2, 0xd2, 0x38, 0x1f, 0x01, 0xff, 0x43, 0xf4, 0xa8, 0xfb, 0x97,
0xaa, 0xa9, 0x1c, 0x4d, 0x24, 0xf5, 0xcd, 0x3e, 0x51, 0xea, 0x7a, 0xb0, 0x14, 0x8e, 0x09, 0x01,
0x24, 0x98, 0x67, 0xfa, 0xbd, 0x66, 0x7d, 0xa4, 0xa9, 0xf8, 0x2f, 0x44, 0x84, 0xf0, 0x49, 0xf7,
0x27, 0x9a, 0x89, 0x96, 0x1b, 0x13, 0x58, 0xde, 0xd7, 0x6c, 0x5e, 0x77, 0x43, 0x73, 0xb0, 0x1d,
0xcd, 0x13, 0x95, 0xae, 0xf2, 0x4b, 0x85, 0x57, 0xb0, 0x10, 0x2c, 0xaa, 0xc6, 0x52, 0x24, 0x5f,
0xf8, 0x11, 0xb8, 0x86, 0xa0, 0x60, 0x5a, 0xc8, 0x8b, 0x41, 0xc1, 0xbe, 0xe3, 0xa1, 0xe3, 0x86,
0x60, 0xec, 0xf0, 0x37, 0x5e, 0xc3, 0xe6, 0x62, 0x8c, 0xe7, 0xf6, 0x68, 0xcf, 0xb4, 0x94, 0xa2,
0x3f, 0x1e, 0x63, 0x50, 0x25, 0xc5, 0x12, 0x56, 0xa5, 0xa7, 0x9c, 0x56, 0x23, 0xe5, 0x9e, 0xf6,
0xb4, 0x1e, 0x69, 0x7f, 0x98, 0xc9, 0xe8, 0xb7, 0x9f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x15, 0xc8,
0xe4, 0x30, 0x80, 0x01, 0x00, 0x00,
// 393 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdf, 0x8a, 0xd4, 0x30,
0x14, 0xc6, 0xe9, 0x9f, 0x69, 0x67, 0x4f, 0xab, 0x48, 0x10, 0x09, 0xc5, 0x8b, 0xb2, 0x78, 0x51,
0xbd, 0x28, 0xb8, 0xfa, 0x02, 0xeb, 0x88, 0xab, 0x30, 0xac, 0x43, 0x8c, 0xde, 0x67, 0xd3, 0xb0,
0x53, 0x6c, 0x9b, 0xd2, 0xa4, 0xd0, 0x7d, 0x1d, 0xdf, 0xc9, 0x17, 0xf1, 0x09, 0x24, 0xa7, 0xad,
0x8e, 0x88, 0xb2, 0x77, 0xe7, 0xf7, 0x71, 0xbe, 0xc9, 0xf7, 0x9d, 0x29, 0x6c, 0xed, 0x54, 0xf6,
0x83, 0xb6, 0x9a, 0x04, 0xca, 0x1e, 0xcf, 0xbf, 0x6d, 0x80, 0x1e, 0x1c, 0xee, 0x74, 0xdb, 0x37,
0xca, 0x2a, 0x3e, 0x88, 0xce, 0x08, 0x69, 0x6b, 0xdd, 0x91, 0x1c, 0x92, 0x37, 0x8d, 0x96, 0x5f,
0xaf, 0xc7, 0xf6, 0x46, 0x0d, 0xd4, 0xcb, 0xbd, 0xe2, 0x01, 0x3b, 0x95, 0xc8, 0x53, 0x38, 0x43,
0xe4, 0x75, 0xab, 0xa8, 0x9f, 0x7b, 0x45, 0xc8, 0x7e, 0x0b, 0xe4, 0x35, 0xf8, 0x7c, 0xa2, 0x41,
0xee, 0x15, 0xc9, 0xc5, 0xb3, 0x52, 0xd9, 0x63, 0xf9, 0xaf, 0xa7, 0x4a, 0x3e, 0xf1, 0xbb, 0x5e,
0x31, 0x9f, 0x4f, 0x64, 0x07, 0x31, 0x53, 0x52, 0xd5, 0xbd, 0xa5, 0x21, 0x5a, 0x9f, 0xff, 0xdf,
0xba, 0x2c, 0xa3, 0x7f, 0x75, 0x66, 0x3f, 0x3c, 0x88, 0xe6, 0xdf, 0x24, 0xe7, 0x90, 0x5e, 0x4a,
0xa9, 0xc7, 0xce, 0x5e, 0xeb, 0x4e, 0x2a, 0xac, 0x11, 0xb2, 0x3f, 0x34, 0xf2, 0x18, 0x36, 0x87,
0xa1, 0x96, 0x73, 0x87, 0x94, 0xcd, 0x40, 0x32, 0xd8, 0x5e, 0x09, 0xb3, 0xaf, 0xdb, 0xda, 0x62,
0x8b, 0x90, 0xfd, 0x62, 0xe7, 0xf8, 0x22, 0x9a, 0x51, 0x61, 0xc6, 0x94, 0xcd, 0x40, 0x28, 0xc4,
0x07, 0x71, 0xd7, 0x68, 0x51, 0xd1, 0x0d, 0xea, 0x2b, 0x12, 0x02, 0xe1, 0x7b, 0x61, 0x8e, 0x34,
0x42, 0x19, 0x67, 0xf2, 0x10, 0x7c, 0xae, 0x69, 0x8c, 0x8a, 0xcf, 0xb5, 0xdb, 0x79, 0x37, 0xe8,
0x96, 0x6e, 0xe7, 0x1d, 0x37, 0x93, 0x17, 0xf0, 0xe8, 0xa4, 0xec, 0x87, 0xae, 0x52, 0x13, 0x3d,
0xc3, 0x3f, 0xe2, 0x2f, 0x3d, 0xfb, 0xee, 0x41, 0x72, 0x72, 0x0d, 0x97, 0xe6, 0x4a, 0x98, 0xcf,
0x46, 0x55, 0x58, 0x3a, 0x65, 0x2b, 0x92, 0x27, 0x10, 0x7d, 0xb2, 0xc2, 0x8e, 0x66, 0x29, 0xbc,
0x10, 0xd9, 0x41, 0xb0, 0xd7, 0xb7, 0x34, 0xc8, 0x83, 0x22, 0xb9, 0x78, 0x79, 0xef, 0xbb, 0x97,
0x7b, 0x7d, 0x8b, 0xf7, 0x77, 0xee, 0xec, 0x23, 0xc4, 0x0b, 0xbb, 0x04, 0x97, 0x55, 0x35, 0x28,
0x63, 0xd6, 0x04, 0x0b, 0xba, 0xae, 0x6f, 0x85, 0x15, 0xcb, 0xfb, 0x38, 0xbb, 0x54, 0x5c, 0xf7,
0xb5, 0x34, 0x18, 0x20, 0x65, 0x0b, 0xdd, 0x44, 0xf8, 0xc1, 0xbe, 0xfa, 0x19, 0x00, 0x00, 0xff,
0xff, 0x84, 0x73, 0x4b, 0xa3, 0xbc, 0x02, 0x00, 0x00,
}

View File

@ -1,19 +1,30 @@
syntax = "proto3";
package eth;
message ProtoTransaction {
uint64 AccountNonce = 1;
bytes Price = 2;
uint64 GasLimit = 3;
bytes Value = 4;
bytes Payload = 5;
bytes Hash = 6;
uint32 BlockNumber = 7;
uint64 BlockTime = 8;
bytes To = 9;
bytes From = 10;
uint32 TransactionIndex = 11;
bytes V = 12;
bytes R = 13;
bytes S = 14;
message ProtoCompleteTransaction {
message TxType {
uint64 AccountNonce = 1;
bytes Price = 2;
uint64 GasLimit = 3;
bytes Value = 4;
bytes Payload = 5;
bytes Hash = 6;
bytes To = 7;
bytes From = 8;
uint32 TransactionIndex = 9;
}
message ReceiptType {
message LogType {
bytes Address = 1;
bytes Data = 2;
repeated bytes Topics = 3;
}
bytes GasUsed = 1;
bytes Status = 2;
repeated LogType Log = 3;
}
uint32 BlockNumber = 1;
uint64 BlockTime = 2;
TxType Tx = 3;
ReceiptType Receipt = 4;
}