Update types in preparation for eth internal transactions,

bump dbVersion to 6
This commit is contained in:
Martin Boehm 2021-12-10 00:30:27 +01:00 committed by Martin
parent 5818ce8aa2
commit c374ef86fd
13 changed files with 185 additions and 154 deletions

View File

@ -173,12 +173,14 @@ type TokenTransfer struct {
// EthereumSpecific contains ethereum specific transaction data // EthereumSpecific contains ethereum specific transaction data
type EthereumSpecific struct { type EthereumSpecific struct {
Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending TxType string `json:"txType,omitempty"`
Nonce uint64 `json:"nonce"` Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending
GasLimit *big.Int `json:"gasLimit"` Nonce uint64 `json:"nonce"`
GasUsed *big.Int `json:"gasUsed"` GasLimit *big.Int `json:"gasLimit"`
GasPrice *Amount `json:"gasPrice"` GasUsed *big.Int `json:"gasUsed"`
Data string `json:"data,omitempty"` GasPrice *Amount `json:"gasPrice"`
Data string `json:"data,omitempty"`
InternalTransfers []bchain.EthereumInternalTransfer `json:"internalTransfers,omitempty"`
} }
// Tx holds information about a transaction // Tx holds information about a transaction

View File

@ -56,7 +56,7 @@ func addressFromPaddedHex(s string) (string, error) {
return a.String(), nil return a.String(), nil
} }
func erc20GetTransfersFromLog(logs []*rpcLog) ([]bchain.Erc20Transfer, error) { func erc20GetTransfersFromLog(logs []*bchain.RpcLog) ([]bchain.Erc20Transfer, error) {
var r []bchain.Erc20Transfer var r []bchain.Erc20Transfer
for _, l := range logs { for _, l := range logs {
if len(l.Topics) == 3 && l.Topics[0] == erc20TransferEventSignature { if len(l.Topics) == 3 && l.Topics[0] == erc20TransferEventSignature {
@ -84,7 +84,7 @@ func erc20GetTransfersFromLog(logs []*rpcLog) ([]bchain.Erc20Transfer, error) {
return r, nil return r, nil
} }
func erc20GetTransfersFromTx(tx *rpcTransaction) ([]bchain.Erc20Transfer, error) { func erc20GetTransfersFromTx(tx *bchain.RpcTransaction) ([]bchain.Erc20Transfer, error) {
var r []bchain.Erc20Transfer var r []bchain.Erc20Transfer
if len(tx.Payload) == 128+len(erc20TransferMethodSignature) && strings.HasPrefix(tx.Payload, erc20TransferMethodSignature) { if len(tx.Payload) == 128+len(erc20TransferMethodSignature) && strings.HasPrefix(tx.Payload, erc20TransferMethodSignature) {
to, err := addressFromPaddedHex(tx.Payload[len(erc20TransferMethodSignature) : 64+len(erc20TransferMethodSignature)]) to, err := addressFromPaddedHex(tx.Payload[len(erc20TransferMethodSignature) : 64+len(erc20TransferMethodSignature)])

View File

@ -15,13 +15,13 @@ import (
func TestErc20_erc20GetTransfersFromLog(t *testing.T) { func TestErc20_erc20GetTransfersFromLog(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args []*rpcLog args []*bchain.RpcLog
want []bchain.Erc20Transfer want []bchain.Erc20Transfer
wantErr bool wantErr bool
}{ }{
{ {
name: "1", name: "1",
args: []*rpcLog{ args: []*bchain.RpcLog{
{ {
Address: "0x76a45e8976499ab9ae223cc584019341d5a84e96", Address: "0x76a45e8976499ab9ae223cc584019341d5a84e96",
Topics: []string{ Topics: []string{
@ -43,7 +43,7 @@ func TestErc20_erc20GetTransfersFromLog(t *testing.T) {
}, },
{ {
name: "2", name: "2",
args: []*rpcLog{ args: []*bchain.RpcLog{
{ // Transfer { // Transfer
Address: "0x0d0f936ee4c93e25944694d6c121de94d9760f11", Address: "0x0d0f936ee4c93e25944694d6c121de94d9760f11",
Topics: []string{ Topics: []string{
@ -167,17 +167,17 @@ func TestErc20_erc20GetTransfersFromTx(t *testing.T) {
bn, _ := new(big.Int).SetString("21e19e0c9bab2400000", 16) bn, _ := new(big.Int).SetString("21e19e0c9bab2400000", 16)
tests := []struct { tests := []struct {
name string name string
args *rpcTransaction args *bchain.RpcTransaction
want []bchain.Erc20Transfer want []bchain.Erc20Transfer
}{ }{
{ {
name: "0", name: "0",
args: (b.Txs[0].CoinSpecificData.(completeTransaction)).Tx, args: (b.Txs[0].CoinSpecificData.(bchain.EthereumSpecificData)).Tx,
want: []bchain.Erc20Transfer{}, want: []bchain.Erc20Transfer{},
}, },
{ {
name: "1", name: "1",
args: (b.Txs[1].CoinSpecificData.(completeTransaction)).Tx, args: (b.Txs[1].CoinSpecificData.(bchain.EthereumSpecificData)).Tx,
want: []bchain.Erc20Transfer{ want: []bchain.Erc20Transfer{
{ {
Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2", Contract: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2",

View File

@ -41,49 +41,13 @@ type rpcHeader struct {
Nonce string `json:"nonce"` Nonce string `json:"nonce"`
} }
type rpcTransaction struct {
AccountNonce string `json:"nonce"`
GasPrice string `json:"gasPrice"`
GasLimit string `json:"gas"`
To string `json:"to"` // nil means contract creation
Value string `json:"value"`
Payload string `json:"input"`
Hash string `json:"hash"`
BlockNumber string `json:"blockNumber"`
BlockHash string `json:"blockHash,omitempty"`
From string `json:"from"`
TransactionIndex string `json:"transactionIndex"`
// Signature values - ignored
// V string `json:"v"`
// R string `json:"r"`
// S string `json:"s"`
}
type rpcLog struct {
Address string `json:"address"`
Topics []string `json:"topics"`
Data string `json:"data"`
}
type rpcLogWithTxHash struct { type rpcLogWithTxHash struct {
rpcLog bchain.RpcLog
Hash string `json:"transactionHash"` Hash string `json:"transactionHash"`
} }
type rpcReceipt struct {
GasUsed string `json:"gasUsed"`
Status string `json:"status"`
Logs []*rpcLog `json:"logs"`
}
type completeTransaction struct {
Tx *rpcTransaction `json:"tx"`
InternalData *bchain.EthereumInternalData `json:"internalData,omitempty"`
Receipt *rpcReceipt `json:"receipt,omitempty"`
}
type rpcBlockTransactions struct { type rpcBlockTransactions struct {
Transactions []rpcTransaction `json:"transactions"` Transactions []bchain.RpcTransaction `json:"transactions"`
} }
type rpcBlockTxids struct { type rpcBlockTxids struct {
@ -97,7 +61,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, receipt *rpcReceipt, internalData *bchain.EthereumInternalData, blocktime int64, confirmations uint32, fixEIP55 bool) (*bchain.Tx, error) { func (p *EthereumParser) ethTxToTx(tx *bchain.RpcTransaction, receipt *bchain.RpcReceipt, internalData *bchain.EthereumInternalData, blocktime int64, confirmations uint32, fixEIP55 bool) (*bchain.Tx, error) {
txid := tx.Hash txid := tx.Hash
var ( var (
fa, ta []string fa, ta []string
@ -136,7 +100,7 @@ func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, inte
} }
} }
} }
ct := completeTransaction{ ct := bchain.EthereumSpecificData{
Tx: tx, Tx: tx,
InternalData: internalData, InternalData: internalData,
Receipt: receipt, Receipt: receipt,
@ -274,7 +238,7 @@ func hexEncodeBig(b []byte) string {
func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) { func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
var err error var err error
var n uint64 var n uint64
r, ok := tx.CoinSpecificData.(completeTransaction) r, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData)
if !ok { if !ok {
return nil, errors.New("Missing CoinSpecificData") return nil, errors.New("Missing CoinSpecificData")
} }
@ -373,7 +337,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
rt := rpcTransaction{ rt := bchain.RpcTransaction{
AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce), AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce),
BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)), BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)),
From: EIP55Address(pt.Tx.From), From: EIP55Address(pt.Tx.From),
@ -388,15 +352,15 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)), TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)),
Value: hexEncodeBig(pt.Tx.Value), Value: hexEncodeBig(pt.Tx.Value),
} }
var rr *rpcReceipt var rr *bchain.RpcReceipt
if pt.Receipt != nil { if pt.Receipt != nil {
logs := make([]*rpcLog, len(pt.Receipt.Log)) logs := make([]*bchain.RpcLog, len(pt.Receipt.Log))
for i, l := range pt.Receipt.Log { for i, l := range pt.Receipt.Log {
topics := make([]string, len(l.Topics)) topics := make([]string, len(l.Topics))
for j, t := range l.Topics { for j, t := range l.Topics {
topics[j] = hexutil.Encode(t) topics[j] = hexutil.Encode(t)
} }
logs[i] = &rpcLog{ logs[i] = &bchain.RpcLog{
Address: EIP55Address(l.Address), Address: EIP55Address(l.Address),
Data: hexutil.Encode(l.Data), Data: hexutil.Encode(l.Data),
Topics: topics, Topics: topics,
@ -407,7 +371,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
if len(pt.Receipt.Status) != 1 || pt.Receipt.Status[0] != 'U' { if len(pt.Receipt.Status) != 1 || pt.Receipt.Status[0] != 'U' {
status = hexEncodeBig(pt.Receipt.Status) status = hexEncodeBig(pt.Receipt.Status)
} }
rr = &rpcReceipt{ rr = &bchain.RpcReceipt{
GasUsed: hexEncodeBig(pt.Receipt.GasUsed), GasUsed: hexEncodeBig(pt.Receipt.GasUsed),
Status: status, Status: status,
Logs: logs, Logs: logs,
@ -460,7 +424,7 @@ func (p *EthereumParser) GetChainType() bchain.ChainType {
// GetHeightFromTx returns ethereum specific data from bchain.Tx // GetHeightFromTx returns ethereum specific data from bchain.Tx
func GetHeightFromTx(tx *bchain.Tx) (uint32, error) { func GetHeightFromTx(tx *bchain.Tx) (uint32, error) {
var bn string var bn string
csd, ok := tx.CoinSpecificData.(completeTransaction) csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData)
if !ok { if !ok {
return 0, errors.New("Missing CoinSpecificData") return 0, errors.New("Missing CoinSpecificData")
} }
@ -476,7 +440,7 @@ func GetHeightFromTx(tx *bchain.Tx) (uint32, error) {
func (p *EthereumParser) EthereumTypeGetErc20FromTx(tx *bchain.Tx) ([]bchain.Erc20Transfer, error) { func (p *EthereumParser) EthereumTypeGetErc20FromTx(tx *bchain.Tx) ([]bchain.Erc20Transfer, error) {
var r []bchain.Erc20Transfer var r []bchain.Erc20Transfer
var err error var err error
csd, ok := tx.CoinSpecificData.(completeTransaction) csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData)
if ok { if ok {
if csd.Receipt != nil { if csd.Receipt != nil {
r, err = erc20GetTransfersFromLog(csd.Receipt.Logs) r, err = erc20GetTransfersFromLog(csd.Receipt.Logs)
@ -519,7 +483,7 @@ func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData {
// GetEthereumTxDataFromSpecificData returns EthereumTxData from coinSpecificData // GetEthereumTxDataFromSpecificData returns EthereumTxData from coinSpecificData
func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTxData { func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTxData {
etd := EthereumTxData{Status: TxStatusPending} etd := EthereumTxData{Status: TxStatusPending}
csd, ok := coinSpecificData.(completeTransaction) csd, ok := coinSpecificData.(bchain.EthereumSpecificData)
if ok { if ok {
if csd.Tx != nil { if csd.Tx != nil {
etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce) etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce)

View File

@ -89,8 +89,8 @@ func init() {
}, },
}, },
}, },
CoinSpecificData: completeTransaction{ CoinSpecificData: bchain.EthereumSpecificData{
Tx: &rpcTransaction{ Tx: &bchain.RpcTransaction{
AccountNonce: "0xb26c", AccountNonce: "0xb26c",
GasPrice: "0x430e23400", GasPrice: "0x430e23400",
GasLimit: "0x5208", GasLimit: "0x5208",
@ -102,10 +102,10 @@ func init() {
From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97", From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97",
TransactionIndex: "0xa", TransactionIndex: "0xa",
}, },
Receipt: &rpcReceipt{ Receipt: &bchain.RpcReceipt{
GasUsed: "0x5208", GasUsed: "0x5208",
Status: "0x1", Status: "0x1",
Logs: []*rpcLog{}, Logs: []*bchain.RpcLog{},
}, },
}, },
} }
@ -127,8 +127,8 @@ func init() {
}, },
}, },
}, },
CoinSpecificData: completeTransaction{ CoinSpecificData: bchain.EthereumSpecificData{
Tx: &rpcTransaction{ Tx: &bchain.RpcTransaction{
AccountNonce: "0xd0", AccountNonce: "0xd0",
GasPrice: "0x9502f9000", GasPrice: "0x9502f9000",
GasLimit: "0x130d5", GasLimit: "0x130d5",
@ -139,10 +139,10 @@ func init() {
BlockNumber: "0x41eee8", BlockNumber: "0x41eee8",
From: "0x20cD153de35D469BA46127A0C8F18626b59a256A", From: "0x20cD153de35D469BA46127A0C8F18626b59a256A",
TransactionIndex: "0x0"}, TransactionIndex: "0x0"},
Receipt: &rpcReceipt{ Receipt: &bchain.RpcReceipt{
GasUsed: "0xcb39", GasUsed: "0xcb39",
Status: "0x1", Status: "0x1",
Logs: []*rpcLog{ Logs: []*bchain.RpcLog{
{ {
Address: "0x4af4114F73d1c1C903aC9E0361b379D1291808A2", Address: "0x4af4114F73d1c1C903aC9E0361b379D1291808A2",
Data: "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", Data: "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000",
@ -174,8 +174,8 @@ func init() {
}, },
}, },
}, },
CoinSpecificData: completeTransaction{ CoinSpecificData: bchain.EthereumSpecificData{
Tx: &rpcTransaction{ Tx: &bchain.RpcTransaction{
AccountNonce: "0xb26c", AccountNonce: "0xb26c",
GasPrice: "0x430e23400", GasPrice: "0x430e23400",
GasLimit: "0x5208", GasLimit: "0x5208",
@ -187,10 +187,10 @@ func init() {
From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97", From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97",
TransactionIndex: "0xa", TransactionIndex: "0xa",
}, },
Receipt: &rpcReceipt{ Receipt: &bchain.RpcReceipt{
GasUsed: "0x5208", GasUsed: "0x5208",
Status: "0x0", Status: "0x0",
Logs: []*rpcLog{}, Logs: []*bchain.RpcLog{},
}, },
}, },
} }
@ -212,8 +212,8 @@ func init() {
}, },
}, },
}, },
CoinSpecificData: completeTransaction{ CoinSpecificData: bchain.EthereumSpecificData{
Tx: &rpcTransaction{ Tx: &bchain.RpcTransaction{
AccountNonce: "0xb26c", AccountNonce: "0xb26c",
GasPrice: "0x430e23400", GasPrice: "0x430e23400",
GasLimit: "0x5208", GasLimit: "0x5208",
@ -225,10 +225,10 @@ func init() {
From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97", From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97",
TransactionIndex: "0xa", TransactionIndex: "0xa",
}, },
Receipt: &rpcReceipt{ Receipt: &bchain.RpcReceipt{
GasUsed: "0x5208", GasUsed: "0x5208",
Status: "", Status: "",
Logs: []*rpcLog{}, Logs: []*bchain.RpcLog{},
}, },
}, },
} }
@ -351,8 +351,8 @@ func TestEthereumParser_UnpackTx(t *testing.T) {
return return
} }
// DeepEqual has problems with pointers in completeTransaction // DeepEqual has problems with pointers in completeTransaction
gs := got.CoinSpecificData.(completeTransaction) gs := got.CoinSpecificData.(bchain.EthereumSpecificData)
ws := tt.want.CoinSpecificData.(completeTransaction) ws := tt.want.CoinSpecificData.(bchain.EthereumSpecificData)
gc := *got gc := *got
wc := *tt.want wc := *tt.want
gc.CoinSpecificData = nil gc.CoinSpecificData = nil

View File

@ -490,7 +490,7 @@ func (b *EthereumRPC) getBlockRaw(hash string, height uint32, fullTxs bool) (jso
return raw, nil return raw, nil
} }
func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]*rpcLog, error) { func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]*bchain.RpcLog, error) {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout) ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
defer cancel() defer cancel()
var logs []rpcLogWithTxHash var logs []rpcLogWithTxHash
@ -502,10 +502,10 @@ func (b *EthereumRPC) getERC20EventsForBlock(blockNumber string) (map[string][]*
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "blockNumber %v", blockNumber) return nil, errors.Annotatef(err, "blockNumber %v", blockNumber)
} }
r := make(map[string][]*rpcLog) r := make(map[string][]*bchain.RpcLog)
for i := range logs { for i := range logs {
l := &logs[i] l := &logs[i]
r[l.Hash] = append(r[l.Hash], &l.rpcLog) r[l.Hash] = append(r[l.Hash], &l.RpcLog)
} }
return r, nil return r, nil
} }
@ -555,7 +555,7 @@ func (b *EthereumRPC) processCallTrace(call rpcCallTrace, d *bchain.EthereumInte
// getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts // getInternalDataForBlock fetches debug trace using callTracer, extracts internal transfers and creations and destructions of contracts
// by design, it never returns error so that missing internal transactions do not stop the rest of the blockchain import // by design, it never returns error so that missing internal transactions do not stop the rest of the blockchain import
func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []rpcTransaction) ([]bchain.EthereumInternalData, error) { func (b *EthereumRPC) getInternalDataForBlock(blockHash string, transactions []bchain.RpcTransaction) ([]bchain.EthereumInternalData, error) {
data := make([]bchain.EthereumInternalData, len(transactions)) data := make([]bchain.EthereumInternalData, len(transactions))
if b.ChainConfig.ProcessInternalTransactions { if b.ChainConfig.ProcessInternalTransactions {
ctx, cancel := context.WithTimeout(context.Background(), b.timeout) ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
@ -619,7 +619,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
btxs := make([]bchain.Tx, len(body.Transactions)) btxs := make([]bchain.Tx, len(body.Transactions))
for i := range body.Transactions { for i := range body.Transactions {
tx := &body.Transactions[i] tx := &body.Transactions[i]
btx, err := b.Parser.ethTxToTx(tx, &rpcReceipt{Logs: logs[tx.Hash]}, &internalData[i], bbh.Time, uint32(bbh.Confirmations), true) btx, err := b.Parser.ethTxToTx(tx, &bchain.RpcReceipt{Logs: logs[tx.Hash]}, &internalData[i], bbh.Time, uint32(bbh.Confirmations), true)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash) return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash)
} }
@ -671,7 +671,7 @@ func (b *EthereumRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error)
func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) { 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 *bchain.RpcTransaction
hash := ethcommon.HexToHash(txid) hash := ethcommon.HexToHash(txid)
err := b.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) err := b.rpc.CallContext(ctx, &tx, "eth_getTransactionByHash", hash)
if err != nil { if err != nil {
@ -705,7 +705,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if time, err = ethNumber(ht.Time); err != nil { if time, err = ethNumber(ht.Time); err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
var receipt rpcReceipt var receipt bchain.RpcReceipt
err = b.rpc.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash) err = b.rpc.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
@ -733,13 +733,13 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
// GetTransactionSpecific returns json as returned by backend, with all coin specific data // GetTransactionSpecific returns json as returned by backend, with all coin specific data
func (b *EthereumRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) { func (b *EthereumRPC) GetTransactionSpecific(tx *bchain.Tx) (json.RawMessage, error) {
csd, ok := tx.CoinSpecificData.(completeTransaction) csd, ok := tx.CoinSpecificData.(bchain.EthereumSpecificData)
if !ok { if !ok {
ntx, err := b.GetTransaction(tx.Txid) ntx, err := b.GetTransaction(tx.Txid)
if err != nil { if err != nil {
return nil, err return nil, err
} }
csd, ok = ntx.CoinSpecificData.(completeTransaction) csd, ok = ntx.CoinSpecificData.(bchain.EthereumSpecificData)
if !ok { if !ok {
return nil, errors.New("Cannot get CoinSpecificData") return nil, errors.New("Cannot get CoinSpecificData")
} }

View File

@ -200,49 +200,6 @@ func AddressDescriptorFromString(s string) (AddressDescriptor, error) {
return nil, errors.New("invalid address descriptor") return nil, errors.New("invalid address descriptor")
} }
// EthereumType specific
// EthereumInternalTransfer contains data about internal transfer
type EthereumInternalTransfer struct {
Type EthereumInternalTransactionType `json:"type"`
From string `json:"from"`
To string `json:"to"`
Value big.Int `json:"value"`
}
// EthereumInternalTransactionType - type of ethereum transaction from internal data
type EthereumInternalTransactionType int
// EthereumInternalTransactionType enumeration
const (
CALL = EthereumInternalTransactionType(iota)
CREATE
SELFDESTRUCT
)
// EthereumInternalTransaction contains internal transfers
type EthereumInternalData struct {
Type EthereumInternalTransactionType `json:"type"`
Contract string `json:"contract,omitempty"`
Transfers []EthereumInternalTransfer `json:"transfers,omitempty"`
}
// Erc20Contract contains info about ERC20 contract
type Erc20Contract struct {
Contract string `json:"contract"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals int `json:"decimals"`
}
// Erc20Transfer contains a single ERC20 token transfer
type Erc20Transfer struct {
Contract string
From string
To string
Tokens big.Int
}
// MempoolTxidEntry contains mempool txid with first seen time // MempoolTxidEntry contains mempool txid with first seen time
type MempoolTxidEntry struct { type MempoolTxidEntry struct {
Txid string Txid string

View File

@ -0,0 +1,85 @@
package bchain
import "math/big"
// EthereumType specific
// EthereumInternalTransfer contains data about internal transfer
type EthereumInternalTransfer struct {
Type EthereumInternalTransactionType `json:"type"`
From string `json:"from"`
To string `json:"to"`
Value big.Int `json:"value"`
}
// EthereumInternalTransactionType - type of ethereum transaction from internal data
type EthereumInternalTransactionType int
// EthereumInternalTransactionType enumeration
const (
CALL = EthereumInternalTransactionType(iota)
CREATE
SELFDESTRUCT
)
// EthereumInternalTransaction contains internal transfers
type EthereumInternalData struct {
Type EthereumInternalTransactionType `json:"type"`
Contract string `json:"contract,omitempty"`
Transfers []EthereumInternalTransfer `json:"transfers,omitempty"`
}
// Erc20Contract contains info about ERC20 contract
type Erc20Contract struct {
Contract string `json:"contract"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Decimals int `json:"decimals"`
}
// Erc20Transfer contains a single ERC20 token transfer
type Erc20Transfer struct {
Contract string
From string
To string
Tokens big.Int
}
// RpcTransaction is returned by eth_getTransactionByHash
type RpcTransaction struct {
AccountNonce string `json:"nonce"`
GasPrice string `json:"gasPrice"`
GasLimit string `json:"gas"`
To string `json:"to"` // nil means contract creation
Value string `json:"value"`
Payload string `json:"input"`
Hash string `json:"hash"`
BlockNumber string `json:"blockNumber"`
BlockHash string `json:"blockHash,omitempty"`
From string `json:"from"`
TransactionIndex string `json:"transactionIndex"`
// Signature values - ignored
// V string `json:"v"`
// R string `json:"r"`
// S string `json:"s"`
}
// RpcLog is returned by eth_getLogs
type RpcLog struct {
Address string `json:"address"`
Topics []string `json:"topics"`
Data string `json:"data"`
}
// RpcLog is returned by eth_getTransactionReceipt
type RpcReceipt struct {
GasUsed string `json:"gasUsed"`
Status string `json:"status"`
Logs []*RpcLog `json:"logs"`
}
type EthereumSpecificData struct {
Tx *RpcTransaction `json:"tx"`
InternalData *EthereumInternalData `json:"internalData,omitempty"`
Receipt *RpcReceipt `json:"receipt,omitempty"`
}

View File

@ -22,7 +22,7 @@ import (
"github.com/trezor/blockbook/common" "github.com/trezor/blockbook/common"
) )
const dbVersion = 5 const dbVersion = 6
const packedHeightBytes = 4 const packedHeightBytes = 4
const maxAddrDescLen = 1024 const maxAddrDescLen = 1024
@ -112,6 +112,8 @@ const (
cfTxAddresses cfTxAddresses
// EthereumType // EthereumType
cfAddressContracts = cfAddressBalance cfAddressContracts = cfAddressBalance
cfInternalData
cfContracts
) )
// common columns // common columns
@ -120,7 +122,7 @@ var cfBaseNames = []string{"default", "height", "addresses", "blockTxs", "transa
// type specific columns // type specific columns
var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"} var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"}
var cfNamesEthereumType = []string{"addressContracts"} var cfNamesEthereumType = []string{"addressContracts", "internalData", "contracts"}
func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*gorocksdb.ColumnFamilyHandle, error) { func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*gorocksdb.ColumnFamilyHandle, error) {
// opts with bloom filter // opts with bloom filter

View File

@ -22,6 +22,7 @@ type AddrContract struct {
type AddrContracts struct { type AddrContracts struct {
TotalTxs uint TotalTxs uint
NonContractTxs uint NonContractTxs uint
InternalTxs uint
Contracts []AddrContract Contracts []AddrContract
} }
@ -30,7 +31,7 @@ func (d *RocksDB) storeAddressContracts(wb *gorocksdb.WriteBatch, acm map[string
varBuf := make([]byte, vlq.MaxLen64) varBuf := make([]byte, vlq.MaxLen64)
for addrDesc, acs := range acm { for addrDesc, acs := range acm {
// address with 0 contracts is removed from db - happens on disconnect // address with 0 contracts is removed from db - happens on disconnect
if acs == nil || (acs.NonContractTxs == 0 && len(acs.Contracts) == 0) { if acs == nil || (acs.NonContractTxs == 0 && acs.InternalTxs == 0 && len(acs.Contracts) == 0) {
wb.DeleteCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc)) wb.DeleteCF(d.cfh[cfAddressContracts], bchain.AddressDescriptor(addrDesc))
} else { } else {
buf = buf[:0] buf = buf[:0]
@ -38,6 +39,8 @@ func (d *RocksDB) storeAddressContracts(wb *gorocksdb.WriteBatch, acm map[string
buf = append(buf, varBuf[:l]...) buf = append(buf, varBuf[:l]...)
l = packVaruint(acs.NonContractTxs, varBuf) l = packVaruint(acs.NonContractTxs, varBuf)
buf = append(buf, varBuf[:l]...) buf = append(buf, varBuf[:l]...)
l = packVaruint(acs.InternalTxs, varBuf)
buf = append(buf, varBuf[:l]...)
for _, ac := range acs.Contracts { for _, ac := range acs.Contracts {
buf = append(buf, ac.Contract...) buf = append(buf, ac.Contract...)
l = packVaruint(ac.Txs, varBuf) l = packVaruint(ac.Txs, varBuf)
@ -64,6 +67,8 @@ func (d *RocksDB) GetAddrDescContracts(addrDesc bchain.AddressDescriptor) (*Addr
buf = buf[l:] buf = buf[l:]
nct, l := unpackVaruint(buf) nct, l := unpackVaruint(buf)
buf = buf[l:] buf = buf[l:]
ict, l := unpackVaruint(buf)
buf = buf[l:]
c := make([]AddrContract, 0, 4) c := make([]AddrContract, 0, 4)
for len(buf) > 0 { for len(buf) > 0 {
if len(buf) < eth.EthereumTypeAddressDescriptorLen { if len(buf) < eth.EthereumTypeAddressDescriptorLen {
@ -80,6 +85,7 @@ func (d *RocksDB) GetAddrDescContracts(addrDesc bchain.AddressDescriptor) (*Addr
return &AddrContracts{ return &AddrContracts{
TotalTxs: tt, TotalTxs: tt,
NonContractTxs: nct, NonContractTxs: nct,
InternalTxs: ict,
Contracts: c, Contracts: c,
}, nil }, nil
} }

View File

@ -44,10 +44,10 @@ func verifyAfterEthereumTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect boo
} }
if err := checkColumn(d, cfAddressContracts, []keyPair{ if err := checkColumn(d, cfAddressContracts, []keyPair{
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "0101", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "010100", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "0201" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "020100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "0101" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "0101", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010100", nil},
}); err != nil { }); err != nil {
{ {
t.Fatal(err) t.Fatal(err)
@ -113,14 +113,14 @@ func verifyAfterEthereumTypeBlock2(t *testing.T, d *RocksDB) {
} }
if err := checkColumn(d, cfAddressContracts, []keyPair{ if err := checkColumn(d, cfAddressContracts, []keyPair{
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "0101", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr3e, d.chainParser), "010100", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "0402" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr55, d.chainParser), "040200" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "0101" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr20, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "0101", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser), "010100", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "0101", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr9f, d.chainParser), "010100", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "0101" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr4b, d.chainParser), "010100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "02" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "02", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), "0100" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddr7b, d.chainParser), "010000" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract4a, d.chainParser) + "01" + dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract0d, d.chainParser) + "01", nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser), "0101", nil}, {dbtestdata.AddressToPubKeyHex(dbtestdata.EthAddrContract47, d.chainParser), "010100", nil},
}); err != nil { }); err != nil {
{ {
t.Fatal(err) t.Fatal(err)

View File

@ -84,9 +84,9 @@ Column families used only by **Ethereum type** coins:
- **addressContracts** (used only by Ethereum type coins) - **addressContracts** (used only by Ethereum type coins)
Maps *addrDesc* to *total number of transactions*, *number of non contract transactions* and array of *contracts* with *number of transfers* of given address. Maps *addrDesc* to *total number of transactions*, *number of non contract transactions*, *number of internal transactions* and array of *contracts* with *number of transfers* of given address.
``` ```
(addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+[]((contractAddrDesc []byte)+(nr_transfers vuint)) (addrDesc []byte) -> (total_txs vuint)+(non-contract_txs vuint)+(internal_txs vuint)+[]((contractAddrDesc []byte)+(nr_transfers vuint))
``` ```
- **blockTxs** - **blockTxs**

File diff suppressed because one or more lines are too long