Simplify transfer object (#1521)

* Store tx and receipt in db and cast it to TransferView on read

* Store Log instead of log index

* Use contract from log and bring back address field

* Add tx status and id fields
This commit is contained in:
Dmitry Shulyak 2019-07-15 14:16:07 +03:00 committed by GitHub
parent dd27b854e8
commit 40b6b3da13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 165 additions and 96 deletions

View File

@ -46,37 +46,44 @@ Returns avaiable transfers in a given range.
List of objects like:
```json
```
[
{
"id": "0xac14e5fb9a81fd7d0517e51e23c4f3a8040459bfe0c4bee97b813db2d0438e2e",
"type": "eth",
"address": "0xd448dbe70b62304fc157319e00a041eea238c5eb",
"blockNumber": "0x1",
"blockhash": "0x2c8f84bc61572e82b39c7dc6bf067d7e71e5d53e745b1174e1b7df4938df0053",
"timestamp": "0x2332",
"transaction": {
"blockhash": "0x1471b02682f2308ce74314d89009251afb1f2d5dedc6835d069b1ad6edf98257",
"timestamp": "0x5d25a873",
"gasPrice": "0xa",
"gasLimit": "0xf4240",
"gasUsed": "0x5208",
"nonce": "0x0",
"gasPrice": "0x1",
"gas": "0x5208",
"to": "0xd448dbe70b62304fc157319e00a041eea238c5eb",
"value": "0x16345785d8a0000",
"input": "0x",
"v": "0xa95",
"r": "0x73159b07b55d810b3898b60a0e3aed87e59e097be6bcae508a9b60e3e1f0ec3a",
"s": "0x2b58524c9b96228e1e996a1e236a52e4a10beb54aad7c9ee1bf36b613f4d9cfb",
"hash": "0x23da761563d8aa59398649df43a89a9ae3a7497861313674c401481b7400e8f9"
"txStatus": "0x1",
"txHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"value": "0xde0b6b3a7640000",
"from": "0xd1c9bfa31ae8c085ba4672b165151245b9bfc25e",
"to": "0x9dfc85106d84405a83271c2fe0cdfc1ca311a1f5",
"contract": "0x0000000000000000000000000000000000000000"
},
"from": "0x27bc544041e129501a6e6fb3c54cf6f12970b1e3",
"receipt": {
"root": "0x",
"status": "0x1",
"cumulativeGasUsed": "0x5208",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"logs": [],
"transactionHash": "0x23da761563d8aa59398649df43a89a9ae3a7497861313674c401481b7400e8f9",
"contractAddress": "0x0000000000000000000000000000000000000000",
"gasUsed": "0x5208"
}
{
"id": "0x2629ee5f443d558ee4ae9e1cf202d76c04e262051b8d8acde7b766bb9d95068e",
"type": "erc20",
"blockNumber": "0x2",
"blockhash": "0x046ad915b86a5eaa6026c8cdd09ea2f09fd3e603dd6e1ea86e8318f4a4b7d4e0",
"timestamp": "0x5d25a88a",
"gasPrice": "0x1",
"gasLimit": "0xb0b8",
"gasUsed": "0xb0b8",
"nonce": "0x1",
"txStatus": "0x1",
"input": "0xa9059cbb000000000000000000000000f759c6683dfc5dad899eb86529dfaf4d0b25af1b0000000000000000000000000000000000000000000000000000000000000064",
"txHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"value": "0x64",
"from": "0xbd691e87d65b2857de55ac44598161ea135f73f6",
"to": "0xf759c6683dfc5dad899eb86529dfaf4d0b25af1b",
"contract": "0xd2439b0e20823e1e4c08df2d19c3b6a4c5f8f2d1"
}
]
```

View File

@ -27,7 +27,7 @@ type API struct {
// GetTransfers returns transfers in range of blocks. If `end` is nil all transfers from `start` will be returned.
// TODO(dshulyak) benchmark loading many transfers from database. We can avoid json unmarshal/marshal if we will
// read header, tx and receipt as a raw json.
func (api *API) GetTransfers(ctx context.Context, start, end *hexutil.Big) ([]Transfer, error) {
func (api *API) GetTransfers(ctx context.Context, start, end *hexutil.Big) ([]TransferView, error) {
log.Debug("call to get transfers", "start", start, "end", end)
if start == nil {
return nil, errors.New("start of the query must be provided. use 0 if you want to load all transfers")
@ -40,11 +40,11 @@ func (api *API) GetTransfers(ctx context.Context, start, end *hexutil.Big) ([]Tr
return nil, err
}
log.Debug("result from database for transfers", "start", start, "end", end, "len", len(rst))
return rst, nil
return castToTransferViews(rst), nil
}
// GetTransfersByAddress returns transfers for a single address between two blocks.
func (api *API) GetTransfersByAddress(ctx context.Context, address common.Address, start, end *hexutil.Big) ([]Transfer, error) {
func (api *API) GetTransfersByAddress(ctx context.Context, address common.Address, start, end *hexutil.Big) ([]TransferView, error) {
log.Debug("call to get transfers for an address", "address", address, "start", start, "end", end)
if start == nil {
return nil, errors.New("start of the query must be provided. use 0 if you want to load all transfers")
@ -57,7 +57,7 @@ func (api *API) GetTransfersByAddress(ctx context.Context, address common.Addres
return nil, err
}
log.Debug("result from database for address", "address", address, "start", start, "end", end, "len", len(rst))
return rst, nil
return castToTransferViews(rst), nil
}
// GetTokensBalances return mapping of token balances for every account.

View File

@ -110,7 +110,7 @@ func (c *erc20HistoricalCommand) Run(ctx context.Context) (err error) {
transfers, err := c.iterator.Next(ctx)
if err != nil {
log.Error("failed to get next batch", "error", err)
break
return err
}
headers := headersFromTransfers(transfers)
headers = append(headers, c.iterator.Header())

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
@ -88,6 +89,9 @@ type JSONBlob struct {
// Scan implements interface.
func (blob *JSONBlob) Scan(value interface{}) error {
if value == nil || reflect.ValueOf(blob.data).IsNil() {
return nil
}
bytes, ok := value.([]byte)
if !ok {
return errors.New("not a byte slice")
@ -101,6 +105,9 @@ func (blob *JSONBlob) Scan(value interface{}) error {
// Value implements interface.
func (blob *JSONBlob) Value() (driver.Value, error) {
if blob.data == nil || reflect.ValueOf(blob.data).IsNil() {
return nil, nil
}
return json.Marshal(blob.data)
}
@ -310,12 +317,12 @@ func insertHeaders(creator statementCreator, headers []*DBHeader) error {
}
func insertTransfers(creator statementCreator, transfers []Transfer) error {
insert, err := creator.Prepare("INSERT OR IGNORE INTO transfers(hash, blk_hash, address, tx, sender, receipt, type) VALUES (?, ?, ?, ?, ?, ?, ?)")
insert, err := creator.Prepare("INSERT OR IGNORE INTO transfers(hash, blk_hash, address, tx, sender, receipt, log, type) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
if err != nil {
return err
}
for _, t := range transfers {
_, err = insert.Exec(t.ID, t.BlockHash, t.Address, &JSONBlob{t.Transaction}, t.From, &JSONBlob{t.Receipt}, t.Type)
_, err = insert.Exec(t.ID, t.BlockHash, t.Address, &JSONBlob{t.Transaction}, t.From, &JSONBlob{t.Receipt}, &JSONBlob{t.Log}, t.Type)
if err != nil {
return err
}

View File

@ -104,10 +104,10 @@ func TestDBReorgTransfers(t *testing.T) {
originalTX := types.NewTransaction(1, common.Address{1}, nil, 10, big.NewInt(10), nil)
replacedTX := types.NewTransaction(2, common.Address{1}, nil, 10, big.NewInt(10), nil)
require.NoError(t, db.ProcessTranfers([]Transfer{
{ethTransfer, common.Hash{1}, *originalTX.To(), original.Number, original.Hash, 100, originalTX, common.Address{1}, rcpt},
{ethTransfer, common.Hash{1}, *originalTX.To(), original.Number, original.Hash, 100, originalTX, common.Address{1}, rcpt, nil},
}, nil, []*DBHeader{original}, nil, 0))
require.NoError(t, db.ProcessTranfers([]Transfer{
{ethTransfer, common.Hash{2}, *replacedTX.To(), replaced.Number, replaced.Hash, 100, replacedTX, common.Address{1}, rcpt},
{ethTransfer, common.Hash{2}, *replacedTX.To(), replaced.Number, replaced.Hash, 100, replacedTX, common.Address{1}, rcpt, nil},
}, nil, []*DBHeader{replaced}, []*DBHeader{original}, 0))
all, err := db.GetTransfers(big.NewInt(0), nil)

View File

@ -3,14 +3,12 @@ package wallet
import (
"context"
"encoding/binary"
"encoding/json"
"errors"
"math/big"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
@ -45,50 +43,8 @@ type Transfer struct {
// From is derived from tx signature in order to offload this computation from UI component.
From common.Address `json:"from"`
Receipt *types.Receipt `json:"receipt"`
}
func (t Transfer) MarshalJSON() ([]byte, error) {
m := transferMarshaling{}
m.Type = t.Type
m.Address = t.Address
m.BlockNumber = (*hexutil.Big)(t.BlockNumber)
m.BlockHash = t.BlockHash
m.Timestamp = hexutil.Uint64(t.Timestamp)
m.Transaction = t.Transaction
m.From = t.From
m.Receipt = t.Receipt
return json.Marshal(m)
}
func (t *Transfer) UnmarshalJSON(input []byte) error {
m := transferMarshaling{}
err := json.Unmarshal(input, &m)
if err != nil {
return err
}
t.Type = m.Type
t.Address = m.Address
t.BlockNumber = (*big.Int)(m.BlockNumber)
t.BlockHash = m.BlockHash
t.Timestamp = uint64(m.Timestamp)
t.Transaction = m.Transaction
m.From = t.From
m.Receipt = t.Receipt
return nil
}
// transferMarshaling ensures that all integers will be marshalled with hexutil
// to be consistent with types.Transaction and types.Receipt.
type transferMarshaling struct {
Type TransferType `json:"type"`
Address common.Address `json:"address"`
BlockNumber *hexutil.Big `json:"blockNumber"`
BlockHash common.Hash `json:"blockhash"`
Timestamp hexutil.Uint64 `json:"timestamp"`
Transaction *types.Transaction `json:"transaction"`
// From is derived from tx signature in order to offload this computation from UI component.
From common.Address `json:"from"`
Receipt *types.Receipt `json:"receipt"`
// Log that was used to generate erc20 transfer. Nil for eth transfer.
Log *types.Log `json:"log"`
}
// ETHTransferDownloader downloads regular eth transfers.
@ -218,9 +174,9 @@ func (d *ERC20TransfersDownloader) outboundTopics(address common.Address) [][]co
return [][]common.Hash{{d.signature}, {d.paddedAddress(address)}, {}}
}
func (d *ERC20TransfersDownloader) transferFromLog(parent context.Context, log types.Log, address common.Address) (Transfer, error) {
func (d *ERC20TransfersDownloader) transferFromLog(parent context.Context, ethlog types.Log, address common.Address) (Transfer, error) {
ctx, cancel := context.WithTimeout(parent, 3*time.Second)
tx, _, err := d.client.TransactionByHash(ctx, log.TxHash)
tx, _, err := d.client.TransactionByHash(ctx, ethlog.TxHash)
cancel()
if err != nil {
return Transfer{}, err
@ -230,31 +186,31 @@ func (d *ERC20TransfersDownloader) transferFromLog(parent context.Context, log t
return Transfer{}, err
}
ctx, cancel = context.WithTimeout(parent, 3*time.Second)
receipt, err := d.client.TransactionReceipt(ctx, log.TxHash)
receipt, err := d.client.TransactionReceipt(ctx, ethlog.TxHash)
cancel()
if err != nil {
return Transfer{}, err
}
ctx, cancel = context.WithTimeout(parent, 3*time.Second)
blk, err := d.client.BlockByHash(ctx, log.BlockHash)
blk, err := d.client.BlockByHash(ctx, ethlog.BlockHash)
cancel()
if err != nil {
return Transfer{}, err
}
// TODO(dshulyak) what is the max number of logs?
index := [4]byte{}
binary.BigEndian.PutUint32(index[:], uint32(log.Index))
id := crypto.Keccak256Hash(log.TxHash.Bytes(), index[:])
binary.BigEndian.PutUint32(index[:], uint32(ethlog.Index))
id := crypto.Keccak256Hash(ethlog.TxHash.Bytes(), index[:])
return Transfer{
Address: address,
ID: id,
Type: erc20Transfer,
BlockNumber: new(big.Int).SetUint64(log.BlockNumber),
BlockHash: log.BlockHash,
BlockNumber: new(big.Int).SetUint64(ethlog.BlockNumber),
BlockHash: ethlog.BlockHash,
Transaction: tx,
From: from,
Receipt: receipt,
Timestamp: blk.Time(),
Log: &ethlog,
}, nil
}
@ -262,6 +218,9 @@ func (d *ERC20TransfersDownloader) transfersFromLogs(parent context.Context, log
concurrent := NewConcurrentDownloader(parent)
for i := range logs {
l := logs[i]
if l.Removed {
continue
}
concurrent.Add(func(ctx context.Context) error {
transfer, err := d.transferFromLog(ctx, l, address)
if err != nil {
@ -276,7 +235,7 @@ func (d *ERC20TransfersDownloader) transfersFromLogs(parent context.Context, log
case <-parent.Done():
return nil, errors.New("logs downloader stuck")
}
return concurrent.Get(), nil
return concurrent.Get(), concurrent.Error()
}
// GetTransfers for erc20 uses eth_getLogs rpc with Transfer event signature and our address acount.

View File

@ -34,7 +34,7 @@ func _0001_transfers_down_db_sql() ([]byte, error) {
)
}
var __0001_transfers_up_db_sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x91\xcf\x8e\x9b\x30\x10\xc6\xef\x7e\x8a\x39\x06\x89\x37\xe8\xc9\xc0\x90\x58\x75\xed\xd6\x98\xa6\x39\x21\x02\x6e\x83\x12\x0c\xc5\x20\x6d\xde\x7e\x45\x80\xfc\xd9\x8d\xa2\xbd\xce\x7c\x33\xdf\x6f\xe6\x0b\x15\x52\x8d\xa0\x69\xc0\x11\x58\x0c\x42\x6a\xc0\x3f\x2c\xd1\x09\xf4\x5d\x6e\xdd\x5f\xd3\x39\x58\x91\x43\xee\x0e\xf0\x9b\xaa\x70\x43\x15\xa4\x82\xfd\x4a\xd1\x27\x79\x59\x76\xc6\xb9\x6b\x7d\x9c\x15\x29\xe7\x3e\xd9\x9f\x8e\xd9\xc3\xc8\xad\xd5\xbf\x41\xc0\x65\xe0\x13\x67\x6c\x69\xba\x27\x8a\xce\x14\xa6\x6a\xfb\x59\xd6\x9f\x5b\xf3\x44\x14\x4b\x85\x6c\x2d\xe0\x3b\xee\x56\x8b\x9b\x07\x0a\x63\x54\x28\x42\x4c\x60\x7f\x6a\x8a\xa3\x5b\x4d\x75\x29\x20\x42\x8e\x1a\x21\xa4\x49\x48\x23\xf4\x49\x28\x45\xa2\x15\x65\x42\xc3\x60\xab\xff\x83\xc9\x96\x7b\xb3\xc6\x5e\xd6\x65\xcb\x7d\xd3\xbd\x70\xd9\xe5\xcf\x45\x8f\x78\xdf\x08\x79\xf1\xbd\xc9\xff\xe3\xeb\x7e\x2a\xf6\x83\xaa\xdd\x88\xed\x13\x3b\xd4\x7b\xd3\x41\xc0\xd6\x23\xc5\xec\x72\xf7\xa9\xaa\x36\xae\xcf\xeb\x16\x52\x91\xb0\xb5\xc0\x68\x91\xde\x34\x07\x93\x97\x10\x48\xc9\x21\xc2\x98\xa6\x5c\x43\x4c\x79\x82\xc4\x83\x2d\xd3\x1b\x99\x6a\x50\x72\xcb\xa2\xd7\xa8\x79\x51\x34\x83\xed\x5d\xd6\x37\xd9\x15\xfb\x75\xb8\x8f\xe8\xb7\x9e\x3b\xdb\x02\x98\xd0\x9f\x03\x9a\x26\x9e\x45\xb4\x74\xbe\x14\x52\x9d\xb7\x6d\x65\xff\x8d\x19\xcd\x84\x13\xf2\x42\xb4\x64\x35\x37\xfd\x3b\xeb\x31\xb1\xf7\x00\x00\x00\xff\xff\x76\x37\x2b\x31\xef\x02\x00\x00")
var __0001_transfers_up_db_sql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x91\xcd\xae\x9b\x30\x10\x85\xf7\x7e\x8a\x59\x06\x89\x37\xe8\xca\xc0\x90\x58\x75\xed\xd6\x98\xa6\x59\x21\x02\x6e\x82\x12\x0c\xc5\x20\x35\x6f\x5f\x11\x20\x3f\xbd\x51\x74\x77\xd6\xcc\x19\x9f\x6f\xe6\x84\x0a\xa9\x46\xd0\x34\xe0\x08\x2c\x06\x21\x35\xe0\x2f\x96\xe8\x04\xfa\x2e\xb7\xee\xb7\xe9\x1c\xac\xc8\x31\x77\x47\xf8\x49\x55\xb8\xa1\x0a\x52\xc1\x7e\xa4\xe8\x93\xbc\x2c\x3b\xe3\xdc\xad\x3e\xce\x8a\x94\x73\x9f\xec\xcf\xa7\xec\x69\xe4\xde\xea\xff\x42\xc0\x65\xe0\x13\x67\x6c\x69\xba\x17\x8a\xce\x14\xa6\x6a\xfb\x59\x76\x6e\x0e\xf3\xab\xbf\xb4\xe6\x85\x3c\x96\x0a\xd9\x5a\xc0\x57\xdc\xad\x16\x5f\x0f\x14\xc6\xa8\x50\x84\x98\xc0\xfe\xdc\x14\x27\xb7\x9a\xea\x52\x40\x84\x1c\x35\x42\x48\x93\x90\x46\xe8\x93\x50\x8a\x44\x2b\xca\x84\x86\xc1\x56\x7f\x06\x93\x2d\x9b\x67\x8d\xbd\x7e\x97\x2d\x9b\x4e\x9b\xc3\xf5\x2f\x7f\x2e\x7a\xc4\xfb\x42\xc8\x9b\x3b\x4e\xfe\xff\x1f\xf1\xbb\x62\xdf\xa8\xda\x8d\xd8\x3e\xb1\x43\xbd\x37\x1d\x04\x6c\x3d\x52\xcc\x2e\x0f\x37\xab\x6a\xe3\xfa\xbc\x6e\x21\x15\x09\x5b\x0b\x8c\x16\xe9\x5d\x73\x34\x79\x09\x81\x94\x1c\x22\x8c\x69\xca\x35\xc4\x94\x27\x48\x3c\xd8\x32\xbd\x91\xa9\x06\x25\xb7\x2c\x7a\x8f\x9a\x17\x45\x33\xd8\xde\x65\x7d\x93\xdd\xb0\xdf\xc7\xfc\x8c\x7e\xef\xb9\x8b\x2d\x80\x09\xfd\x31\xa0\x69\xe2\x55\x44\x4b\xe7\x53\x21\xd5\x79\xdb\x56\xf6\x30\x66\x34\x13\x4e\xc8\x0b\xd1\x92\xd5\xdc\xf4\x1f\xac\xc7\xc4\xfe\x05\x00\x00\xff\xff\x69\x7c\xa5\x4c\xf9\x02\x00\x00")
func _0001_transfers_up_db_sql() ([]byte, error) {
return bindata_read(

View File

@ -5,6 +5,7 @@ blk_hash VARCHAR NOT NULL,
tx BLOB,
sender VARCHAR NOT NULL,
receipt BLOB,
log BLOB,
type VARCHAR NOT NULL,
FOREIGN KEY(blk_hash) REFERENCES blocks(hash) ON DELETE CASCADE,
CONSTRAINT unique_transfer_on_hash_address UNIQUE (hash,address)

View File

@ -0,0 +1,94 @@
package wallet
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
func castToTransferViews(transfers []Transfer) []TransferView {
views := make([]TransferView, len(transfers))
for i := range transfers {
views[i] = castToTransferView(transfers[i])
}
return views
}
func castToTransferView(t Transfer) TransferView {
view := TransferView{}
view.ID = t.ID
view.Type = t.Type
view.Address = t.Address
view.BlockNumber = (*hexutil.Big)(t.BlockNumber)
view.BlockHash = t.BlockHash
view.Timestamp = hexutil.Uint64(t.Timestamp)
view.GasPrice = (*hexutil.Big)(t.Transaction.GasPrice())
view.GasLimit = hexutil.Uint64(t.Transaction.Gas())
view.GasUsed = hexutil.Uint64(t.Receipt.GasUsed)
view.Nonce = hexutil.Uint64(t.Transaction.Nonce())
view.TxStatus = hexutil.Uint64(t.Receipt.Status)
view.Input = hexutil.Bytes(t.Transaction.Data())
view.TxHash = t.Transaction.Hash()
switch t.Type {
case ethTransfer:
view.From = t.From
if t.Transaction.To() != nil {
view.To = *t.Transaction.To()
}
view.Value = (*hexutil.Big)(t.Transaction.Value())
view.Contract = t.Receipt.ContractAddress
case erc20Transfer:
view.Contract = t.Log.Address
from, to, amount := parseLog(t.Log)
view.From, view.To, view.Value = from, to, (*hexutil.Big)(amount)
}
return view
}
func parseLog(ethlog *types.Log) (from, to common.Address, amount *big.Int) {
if len(ethlog.Topics) < 3 {
log.Warn("not enough topics for erc20 transfer", "topics", ethlog.Topics)
return
}
if len(ethlog.Topics[1]) != 32 {
log.Warn("second topic is not padded to 32 byte address", "topic", ethlog.Topics[1])
return
}
if len(ethlog.Topics[2]) != 32 {
log.Warn("third topic is not padded to 32 byte address", "topic", ethlog.Topics[2])
return
}
copy(from[:], ethlog.Topics[1][12:])
copy(to[:], ethlog.Topics[2][12:])
if len(ethlog.Data) != 32 {
log.Warn("data is not padded to 32 byts big int", "data", ethlog.Data)
return
}
amount = new(big.Int).SetBytes(ethlog.Data)
return
}
// TransferView stores only fields used by a client and ensures that all relevant fields are
// encoded in hex.
type TransferView struct {
ID common.Hash `json:"id"`
Type TransferType `json:"type"`
Address common.Address `json:"address"`
BlockNumber *hexutil.Big `json:"blockNumber"`
BlockHash common.Hash `json:"blockhash"`
Timestamp hexutil.Uint64 `json:"timestamp"`
GasPrice *hexutil.Big `json:"gasPrice"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
GasUsed hexutil.Uint64 `json:"gasUsed"`
Nonce hexutil.Uint64 `json:"nonce"`
TxStatus hexutil.Uint64 `json:"txStatus"`
Input hexutil.Bytes `json:"input"`
TxHash common.Hash `json:"txHash"`
Value *hexutil.Big `json:"value"`
From common.Address `json:"from"`
To common.Address `json:"to"`
Contract common.Address `json:"contract"`
}

View File

@ -9,7 +9,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
const baseTransfersQuery = "SELECT transfers.hash, type, blocks.hash, blocks.number, blocks.timestamp, address, tx, sender, receipt FROM transfers JOIN blocks ON blk_hash = blocks.hash"
const baseTransfersQuery = "SELECT transfers.hash, type, blocks.hash, blocks.number, blocks.timestamp, address, tx, sender, receipt, log FROM transfers JOIN blocks ON blk_hash = blocks.hash"
func newTransfersQuery() *transfersQuery {
buf := bytes.NewBuffer(nil)
@ -73,11 +73,12 @@ func (q *transfersQuery) Scan(rows *sql.Rows) (rst []Transfer, err error) {
BlockNumber: &big.Int{},
Transaction: &types.Transaction{},
Receipt: &types.Receipt{},
Log: &types.Log{},
}
err = rows.Scan(
&transfer.ID, &transfer.Type, &transfer.BlockHash,
(*SQLBigInt)(transfer.BlockNumber), &transfer.Timestamp, &transfer.Address,
&JSONBlob{transfer.Transaction}, &transfer.From, &JSONBlob{transfer.Receipt})
&JSONBlob{transfer.Transaction}, &transfer.From, &JSONBlob{transfer.Receipt}, &JSONBlob{transfer.Log})
if err != nil {
return nil, err
}

View File

@ -49,7 +49,7 @@ func (s *TransfersSuite) TearDownTest() {
s.DevNodeSuite.TearDownTest()
}
func (s *TransfersSuite) getAllTranfers() (rst []wallet.Transfer, err error) {
func (s *TransfersSuite) getAllTranfers() (rst []wallet.TransferView, err error) {
return rst, s.Local.Call(&rst, "wallet_getTransfersByAddress", s.Address, (*hexutil.Big)(big.NewInt(0)))
}