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:
parent
dd27b854e8
commit
40b6b3da13
|
@ -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"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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: ðlog,
|
||||
}, 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.
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"`
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue