Process tx receipts and ERC20 tokens WIP

This commit is contained in:
Martin Boehm 2018-11-09 13:08:43 +01:00
parent 1ac7a7abca
commit ef03abcd1c
7 changed files with 513 additions and 187 deletions

71
bchain/coins/eth/erc20.go Normal file
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" ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/glog"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/juju/errors" "github.com/juju/errors"
) )
@ -38,16 +39,33 @@ type rpcTransaction struct {
BlockHash *ethcommon.Hash `json:"blockHash,omitempty"` BlockHash *ethcommon.Hash `json:"blockHash,omitempty"`
From string `json:"from"` From string `json:"from"`
TransactionIndex string `json:"transactionIndex"` TransactionIndex string `json:"transactionIndex"`
// Signature values // Signature values - ignored
V string `json:"v" gencodec:"required"` // V string `json:"v" gencodec:"required"`
R string `json:"r" gencodec:"required"` // R string `json:"r" gencodec:"required"`
S string `json:"s" 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 { type rpcBlock struct {
Hash ethcommon.Hash `json:"hash"` Hash ethcommon.Hash `json:"hash"`
Size string `json:"size"`
Transactions []rpcTransaction `json:"transactions"` Transactions []rpcTransaction `json:"transactions"`
UncleHashes []ethcommon.Hash `json:"uncles"`
} }
func ethHashToHash(h ethcommon.Hash) string { 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) 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) txid := ethHashToHash(tx.Hash)
var ( var (
fa, ta []string fa, ta []string
@ -73,15 +91,21 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, blocktime int64, confirma
if len(tx.To) > 2 { if len(tx.To) > 2 {
ta = []string{tx.To} 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 bh := tx.BlockHash
tx.BlockHash = nil tx.BlockHash = nil
b, err := json.Marshal(tx) ct := completeTransaction{
Tx: tx,
Receipt: receipt,
}
b, err := json.Marshal(ct)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tx.BlockHash = bh tx.BlockHash = bh
h := hex.EncodeToString(b) h := hex.EncodeToString(b)
glog.Info(h)
vs, err := hexutil.DecodeBig(tx.Value) vs, err := hexutil.DecodeBig(tx.Value)
if err != nil { if err != nil {
return nil, err return nil, err
@ -181,79 +205,96 @@ func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
var r rpcTransaction var r completeTransaction
var n uint64 var n uint64
err = json.Unmarshal(b, &r) err = json.Unmarshal(b, &r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
pt := &ProtoTransaction{} pt := &ProtoCompleteTransaction{}
if pt.AccountNonce, err = hexutil.DecodeUint64(r.AccountNonce); err != nil { if pt.Tx.AccountNonce, err = hexutil.DecodeUint64(r.Tx.AccountNonce); err != nil {
return nil, errors.Annotatef(err, "AccountNonce %v", r.AccountNonce) return nil, errors.Annotatef(err, "AccountNonce %v", r.Tx.AccountNonce)
} }
if n, err = hexutil.DecodeUint64(r.BlockNumber); err != nil { if n, err = hexutil.DecodeUint64(r.Tx.BlockNumber); err != nil {
return nil, errors.Annotatef(err, "BlockNumber %v", r.BlockNumber) return nil, errors.Annotatef(err, "BlockNumber %v", r.Tx.BlockNumber)
} }
pt.BlockNumber = uint32(n) pt.BlockNumber = uint32(n)
pt.BlockTime = uint64(blockTime) pt.BlockTime = uint64(blockTime)
if pt.From, err = hexDecode(r.From); err != nil { if pt.Tx.From, err = hexDecode(r.Tx.From); err != nil {
return nil, errors.Annotatef(err, "From %v", r.From) return nil, errors.Annotatef(err, "From %v", r.Tx.From)
} }
if pt.GasLimit, err = hexutil.DecodeUint64(r.GasLimit); err != nil { if pt.Tx.GasLimit, err = hexutil.DecodeUint64(r.Tx.GasLimit); err != nil {
return nil, errors.Annotatef(err, "GasLimit %v", r.GasLimit) return nil, errors.Annotatef(err, "GasLimit %v", r.Tx.GasLimit)
} }
pt.Hash = r.Hash.Bytes() pt.Tx.Hash = r.Tx.Hash.Bytes()
if pt.Payload, err = hexDecode(r.Payload); err != nil { if pt.Tx.Payload, err = hexDecode(r.Tx.Payload); err != nil {
return nil, errors.Annotatef(err, "Payload %v", r.Payload) return nil, errors.Annotatef(err, "Payload %v", r.Tx.Payload)
} }
if pt.Price, err = hexDecodeBig(r.Price); err != nil { if pt.Tx.Price, err = hexDecodeBig(r.Tx.Price); err != nil {
return nil, errors.Annotatef(err, "Price %v", r.Price) return nil, errors.Annotatef(err, "Price %v", r.Tx.Price)
} }
if pt.R, err = hexDecodeBig(r.R); err != nil { // if pt.R, err = hexDecodeBig(r.R); err != nil {
return nil, errors.Annotatef(err, "R %v", r.R) // 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 { if n, err = hexutil.DecodeUint64(r.Tx.TransactionIndex); err != nil {
return nil, errors.Annotatef(err, "S %v", r.S) return nil, errors.Annotatef(err, "TransactionIndex %v", r.Tx.TransactionIndex)
} }
if pt.V, err = hexDecodeBig(r.V); err != nil { pt.Tx.TransactionIndex = uint32(n)
return nil, errors.Annotatef(err, "V %v", r.V) if pt.Tx.Value, err = hexDecodeBig(r.Tx.Value); err != nil {
} return nil, errors.Annotatef(err, "Value %v", r.Tx.Value)
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)
} }
return proto.Marshal(pt) return proto.Marshal(pt)
} }
// UnpackTx unpacks transaction from byte array // UnpackTx unpacks transaction from byte array
func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) { func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
var pt ProtoTransaction var pt ProtoCompleteTransaction
err := proto.Unmarshal(buf, &pt) err := proto.Unmarshal(buf, &pt)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
r := rpcTransaction{ rt := rpcTransaction{
AccountNonce: hexutil.EncodeUint64(pt.AccountNonce), AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce),
BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)), BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)),
From: hexutil.Encode(pt.From), From: hexutil.Encode(pt.Tx.From),
GasLimit: hexutil.EncodeUint64(pt.GasLimit), GasLimit: hexutil.EncodeUint64(pt.Tx.GasLimit),
Hash: ethcommon.BytesToHash(pt.Hash), Hash: ethcommon.BytesToHash(pt.Tx.Hash),
Payload: hexutil.Encode(pt.Payload), Payload: hexutil.Encode(pt.Tx.Payload),
Price: hexEncodeBig(pt.Price), Price: hexEncodeBig(pt.Tx.Price),
R: hexEncodeBig(pt.R), // R: hexEncodeBig(pt.R),
S: hexEncodeBig(pt.S), // S: hexEncodeBig(pt.S),
V: hexEncodeBig(pt.V), // V: hexEncodeBig(pt.V),
To: hexutil.Encode(pt.To), To: hexutil.Encode(pt.Tx.To),
TransactionIndex: hexutil.EncodeUint64(uint64(pt.TransactionIndex)), TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)),
Value: hexEncodeBig(pt.Value), 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 { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -303,14 +344,14 @@ func GetHeightFromTx(tx *bchain.Tx) (uint32, error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
var r rpcTransaction var ct completeTransaction
var n uint64 var n uint64
err = json.Unmarshal(b, &r) err = json.Unmarshal(b, &ct)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if n, err = hexutil.DecodeUint64(r.BlockNumber); err != nil { if n, err = hexutil.DecodeUint64(ct.Tx.BlockNumber); err != nil {
return 0, errors.Annotatef(err, "BlockNumber %v", r.BlockNumber) return 0, errors.Annotatef(err, "BlockNumber %v", ct.Tx.BlockNumber)
} }
return uint32(n), nil return uint32(n), nil
} }

View File

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

View File

@ -12,6 +12,7 @@ import (
ethereum "github.com/ethereum/go-ethereum" ethereum "github.com/ethereum/go-ethereum"
ethcommon "github.com/ethereum/go-ethereum/common" ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
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" "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) return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
} }
// Quick-verify transaction and uncle lists. This mostly helps with debugging the server. // 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 { 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) 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 { if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v", hash, height) return nil, errors.Annotatef(err, "hash %v, height %v", hash, height)
} }
// TODO - this is probably not the correct size bigSize, err := hexutil.DecodeBig(body.Size)
bbh.Size = len(raw) 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)) btxs := make([]bchain.Tx, len(body.Transactions))
for i, tx := range 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 { if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash.String()) 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) ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel() defer cancel()
var tx *rpcTransaction 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 { if err != nil {
return nil, err return nil, err
} else if tx == nil { } else if tx == nil {
return nil, ethereum.NotFound 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 var btx *bchain.Tx
if tx.BlockNumber == "" { if tx.BlockNumber == "" {
// mempool tx // mempool tx
btx, err = b.Parser.ethTxToTx(tx, 0, 0) btx, err = b.Parser.ethTxToTx(tx, nil, 0, 0)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
} 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 := ethNumber(tx.BlockNumber) h, err := b.client.HeaderByHash(ctx, *tx.BlockHash)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) 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 { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
@ -506,7 +512,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) 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 { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }

View File

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