Add receipts (#119)
* Conversion between Geth Receipt and core.Receipt * Add receipt to DB * Insert receipts with transactions * Update Travis CI to use dep for dependencies
This commit is contained in:
parent
13748a92e5
commit
4fabe3e917
10
.travis.yml
10
.travis.yml
@ -6,6 +6,12 @@ services:
|
|||||||
- postgresql
|
- postgresql
|
||||||
addons:
|
addons:
|
||||||
postgresql: "9.6"
|
postgresql: "9.6"
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get -u github.com/golang/dep/cmd/dep
|
||||||
|
- dep ensure
|
||||||
|
- go get -u github.com/onsi/ginkgo/ginkgo
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.7.2-1db4ecdc.tar.gz
|
- wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.7.2-1db4ecdc.tar.gz
|
||||||
- tar -xzf geth-linux-amd64-1.7.2-1db4ecdc.tar.gz
|
- tar -xzf geth-linux-amd64-1.7.2-1db4ecdc.tar.gz
|
||||||
@ -14,5 +20,9 @@ before_script:
|
|||||||
- nohup ./scripts/start_private_blockchain </dev/null &
|
- nohup ./scripts/start_private_blockchain </dev/null &
|
||||||
- createdb vulcanize_private
|
- createdb vulcanize_private
|
||||||
- psql vulcanize_private < db/schema.sql
|
- psql vulcanize_private < db/schema.sql
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ginkgo -r
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
2
Gopkg.lock
generated
2
Gopkg.lock
generated
@ -208,6 +208,6 @@
|
|||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "9b993b03db46de97fde5cfe8022a60d1654172dcb7d63c2c4b876308ffd1f73e"
|
inputs-digest = "2d7b9c5c88a94f3384b0cd754d35a3d7822a5858f439aaafe8c6477fb7c24f63"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
2
db/migrations/1514564898_create_receipts_table.down.sql
Normal file
2
db/migrations/1514564898_create_receipts_table.down.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DROP TABLE receipts;
|
||||||
|
|
16
db/migrations/1514564898_create_receipts_table.up.sql
Normal file
16
db/migrations/1514564898_create_receipts_table.up.sql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
CREATE TABLE receipts
|
||||||
|
(
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
transaction_id INTEGER NOT NULL,
|
||||||
|
contract_address VARCHAR(42),
|
||||||
|
cumulative_gas_used NUMERIC,
|
||||||
|
gas_used NUMERIC,
|
||||||
|
state_root VARCHAR(66),
|
||||||
|
status INTEGER,
|
||||||
|
tx_hash VARCHAR(66),
|
||||||
|
CONSTRAINT transaction_fk FOREIGN KEY (transaction_id)
|
||||||
|
REFERENCES transactions (id)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
DROP INDEX transaction_id_index;
|
@ -0,0 +1 @@
|
|||||||
|
CREATE INDEX transaction_id_index ON receipts (transaction_id);
|
@ -145,6 +145,41 @@ CREATE SEQUENCE nodes_id_seq
|
|||||||
ALTER SEQUENCE nodes_id_seq OWNED BY nodes.id;
|
ALTER SEQUENCE nodes_id_seq OWNED BY nodes.id;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: receipts; Type: TABLE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE TABLE receipts (
|
||||||
|
id integer NOT NULL,
|
||||||
|
transaction_id integer NOT NULL,
|
||||||
|
contract_address character varying(42),
|
||||||
|
cumulative_gas_used numeric,
|
||||||
|
gas_used numeric,
|
||||||
|
state_root character varying(66),
|
||||||
|
status integer,
|
||||||
|
tx_hash character varying(66)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: receipts_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE SEQUENCE receipts_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: receipts_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER SEQUENCE receipts_id_seq OWNED BY receipts.id;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: -
|
-- Name: schema_migrations; Type: TABLE; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -243,6 +278,13 @@ ALTER TABLE ONLY logs ALTER COLUMN id SET DEFAULT nextval('logs_id_seq'::regclas
|
|||||||
ALTER TABLE ONLY nodes ALTER COLUMN id SET DEFAULT nextval('nodes_id_seq'::regclass);
|
ALTER TABLE ONLY nodes ALTER COLUMN id SET DEFAULT nextval('nodes_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: receipts id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY receipts ALTER COLUMN id SET DEFAULT nextval('receipts_id_seq'::regclass);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: transactions id; Type: DEFAULT; Schema: public; Owner: -
|
-- Name: transactions id; Type: DEFAULT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -313,6 +355,14 @@ ALTER TABLE ONLY nodes
|
|||||||
ADD CONSTRAINT nodes_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT nodes_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: receipts receipts_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY receipts
|
||||||
|
ADD CONSTRAINT receipts_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
-- Name: schema_migrations schema_migrations_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -358,6 +408,13 @@ CREATE INDEX block_number_index ON blocks USING btree (block_number);
|
|||||||
CREATE INDEX node_id_index ON blocks USING btree (node_id);
|
CREATE INDEX node_id_index ON blocks USING btree (node_id);
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: transaction_id_index; Type: INDEX; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE INDEX transaction_id_index ON receipts USING btree (transaction_id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: tx_from_index; Type: INDEX; Schema: public; Owner: -
|
-- Name: tx_from_index; Type: INDEX; Schema: public; Owner: -
|
||||||
--
|
--
|
||||||
@ -388,6 +445,14 @@ ALTER TABLE ONLY blocks
|
|||||||
ADD CONSTRAINT node_fk FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE;
|
ADD CONSTRAINT node_fk FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: receipts transaction_fk; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY receipts
|
||||||
|
ADD CONSTRAINT transaction_fk FOREIGN KEY (transaction_id) REFERENCES transactions(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- PostgreSQL database dump complete
|
-- PostgreSQL database dump complete
|
||||||
--
|
--
|
||||||
|
12
pkg/core/receipts.go
Normal file
12
pkg/core/receipts.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
type Receipt struct {
|
||||||
|
Bloom string
|
||||||
|
ContractAddress string
|
||||||
|
CumulativeGasUsed int64
|
||||||
|
GasUsed int64
|
||||||
|
Logs []Log
|
||||||
|
StateRoot string
|
||||||
|
Status int
|
||||||
|
TxHash string
|
||||||
|
}
|
@ -8,5 +8,6 @@ type Transaction struct {
|
|||||||
From string
|
From string
|
||||||
GasLimit int64
|
GasLimit int64
|
||||||
GasPrice int64
|
GasPrice int64
|
||||||
Value int64
|
Receipt
|
||||||
|
Value int64
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ package geth
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"log"
|
||||||
|
|
||||||
"github.com/8thlight/vulcanizedb/pkg/core"
|
"github.com/8thlight/vulcanizedb/pkg/core"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
@ -16,12 +18,7 @@ type GethClient interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GethBlockToCoreBlock(gethBlock *types.Block, client GethClient) core.Block {
|
func GethBlockToCoreBlock(gethBlock *types.Block, client GethClient) core.Block {
|
||||||
transactions := []core.Transaction{}
|
transactions := convertGethTransactionsToCore(gethBlock, client)
|
||||||
for i, gethTransaction := range gethBlock.Transactions() {
|
|
||||||
from, _ := client.TransactionSender(context.Background(), gethTransaction, gethBlock.Hash(), uint(i))
|
|
||||||
transaction := gethTransToCoreTrans(gethTransaction, &from)
|
|
||||||
transactions = append(transactions, transaction)
|
|
||||||
}
|
|
||||||
blockReward := CalcBlockReward(gethBlock, client)
|
blockReward := CalcBlockReward(gethBlock, client)
|
||||||
uncleReward := CalcUnclesReward(gethBlock)
|
uncleReward := CalcUnclesReward(gethBlock)
|
||||||
return core.Block{
|
return core.Block{
|
||||||
@ -43,6 +40,30 @@ func GethBlockToCoreBlock(gethBlock *types.Block, client GethClient) core.Block
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convertGethTransactionsToCore(gethBlock *types.Block, client GethClient) []core.Transaction {
|
||||||
|
transactions := make([]core.Transaction, 0)
|
||||||
|
for i, gethTransaction := range gethBlock.Transactions() {
|
||||||
|
from, err := client.TransactionSender(context.Background(), gethTransaction, gethBlock.Hash(), uint(i))
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
transaction := gethTransToCoreTrans(gethTransaction, &from)
|
||||||
|
transaction, err = appendReceiptToTransaction(client, transaction)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
transactions = append(transactions, transaction)
|
||||||
|
}
|
||||||
|
return transactions
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendReceiptToTransaction(client GethClient, transaction core.Transaction) (core.Transaction, error) {
|
||||||
|
gethReceipt, err := client.TransactionReceipt(context.Background(), common.HexToHash(transaction.Hash))
|
||||||
|
receipt := GethReceiptToCoreReceipt(gethReceipt)
|
||||||
|
transaction.Receipt = receipt
|
||||||
|
return transaction, err
|
||||||
|
}
|
||||||
|
|
||||||
func gethTransToCoreTrans(transaction *types.Transaction, from *common.Address) core.Transaction {
|
func gethTransToCoreTrans(transaction *types.Transaction, from *common.Address) core.Transaction {
|
||||||
data := hexutil.Encode(transaction.Data())
|
data := hexutil.Encode(transaction.Data())
|
||||||
return core.Transaction{
|
return core.Transaction{
|
||||||
|
@ -197,42 +197,86 @@ var _ = Describe("Conversion of GethBlock to core.Block", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("converts a single transaction", func() {
|
It("converts a single transaction", func() {
|
||||||
nonce := uint64(10000)
|
gethTransaction := types.NewTransaction(
|
||||||
header := types.Header{}
|
uint64(10000), common.Address{1},
|
||||||
to := common.Address{1}
|
big.NewInt(10),
|
||||||
amount := big.NewInt(10)
|
big.NewInt(5000),
|
||||||
gasLimit := big.NewInt(5000)
|
big.NewInt(3),
|
||||||
gasPrice := big.NewInt(3)
|
hexutil.MustDecode("0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14"),
|
||||||
input := "0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14"
|
)
|
||||||
payload, _ := hexutil.Decode(input)
|
|
||||||
|
|
||||||
gethTransaction := types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, payload)
|
gethReceipt := &types.Receipt{
|
||||||
|
Bloom: types.BytesToBloom(hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
|
||||||
|
ContractAddress: common.HexToAddress("x123"),
|
||||||
|
CumulativeGasUsed: big.NewInt(7996119),
|
||||||
|
GasUsed: big.NewInt(21000),
|
||||||
|
Logs: []*types.Log{},
|
||||||
|
Status: uint(1),
|
||||||
|
TxHash: gethTransaction.Hash(),
|
||||||
|
}
|
||||||
|
|
||||||
client := NewFakeClient()
|
client := NewFakeClient()
|
||||||
|
client.AddReceipts([]*types.Receipt{gethReceipt})
|
||||||
|
|
||||||
gethBlock := types.NewBlock(&header, []*types.Transaction{gethTransaction}, []*types.Header{}, []*types.Receipt{})
|
header := types.Header{}
|
||||||
|
gethBlock := types.NewBlock(
|
||||||
|
&header,
|
||||||
|
[]*types.Transaction{gethTransaction},
|
||||||
|
[]*types.Header{},
|
||||||
|
[]*types.Receipt{gethReceipt},
|
||||||
|
)
|
||||||
coreBlock := geth.GethBlockToCoreBlock(gethBlock, client)
|
coreBlock := geth.GethBlockToCoreBlock(gethBlock, client)
|
||||||
|
|
||||||
Expect(len(coreBlock.Transactions)).To(Equal(1))
|
Expect(len(coreBlock.Transactions)).To(Equal(1))
|
||||||
coreTransaction := coreBlock.Transactions[0]
|
coreTransaction := coreBlock.Transactions[0]
|
||||||
Expect(coreTransaction.Data).To(Equal(input))
|
Expect(coreTransaction.Data).To(Equal("0xf7d8c8830000000000000000000000000000000000000000000000000000000000037788000000000000000000000000000000000000000000000000000000000003bd14"))
|
||||||
Expect(coreTransaction.To).To(Equal(gethTransaction.To().Hex()))
|
Expect(coreTransaction.To).To(Equal(gethTransaction.To().Hex()))
|
||||||
Expect(coreTransaction.From).To(Equal("0x0000000000000000000000000000000000000123"))
|
Expect(coreTransaction.From).To(Equal("0x0000000000000000000000000000000000000123"))
|
||||||
Expect(coreTransaction.GasLimit).To(Equal(gethTransaction.Gas().Int64()))
|
Expect(coreTransaction.GasLimit).To(Equal(gethTransaction.Gas().Int64()))
|
||||||
Expect(coreTransaction.GasPrice).To(Equal(gethTransaction.GasPrice().Int64()))
|
Expect(coreTransaction.GasPrice).To(Equal(gethTransaction.GasPrice().Int64()))
|
||||||
Expect(coreTransaction.Value).To(Equal(gethTransaction.Value().Int64()))
|
Expect(coreTransaction.Value).To(Equal(gethTransaction.Value().Int64()))
|
||||||
Expect(coreTransaction.Nonce).To(Equal(gethTransaction.Nonce()))
|
Expect(coreTransaction.Nonce).To(Equal(gethTransaction.Nonce()))
|
||||||
|
|
||||||
|
coreReceipt := coreTransaction.Receipt
|
||||||
|
expectedReceipt := geth.GethReceiptToCoreReceipt(gethReceipt)
|
||||||
|
Expect(coreReceipt).To(Equal(expectedReceipt))
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
It("has an empty to field when transaction creates a new contract", func() {
|
It("has an empty 'To' field when transaction creates a new contract", func() {
|
||||||
gethTransaction := types.NewContractCreation(uint64(10000), big.NewInt(10), big.NewInt(5000), big.NewInt(3), []byte("1234"))
|
gethTransaction := types.NewContractCreation(
|
||||||
gethBlock := types.NewBlock(&types.Header{}, []*types.Transaction{gethTransaction}, []*types.Header{}, []*types.Receipt{})
|
uint64(10000),
|
||||||
|
big.NewInt(10),
|
||||||
|
big.NewInt(5000),
|
||||||
|
big.NewInt(3),
|
||||||
|
[]byte("1234"),
|
||||||
|
)
|
||||||
|
|
||||||
|
gethReceipt := &types.Receipt{
|
||||||
|
CumulativeGasUsed: big.NewInt(1),
|
||||||
|
GasUsed: big.NewInt(1),
|
||||||
|
TxHash: gethTransaction.Hash(),
|
||||||
|
ContractAddress: common.HexToAddress("0x1023342345"),
|
||||||
|
}
|
||||||
|
|
||||||
client := NewFakeClient()
|
client := NewFakeClient()
|
||||||
|
client.AddReceipts([]*types.Receipt{gethReceipt})
|
||||||
|
|
||||||
|
gethBlock := types.NewBlock(
|
||||||
|
&types.Header{},
|
||||||
|
[]*types.Transaction{gethTransaction},
|
||||||
|
[]*types.Header{},
|
||||||
|
[]*types.Receipt{gethReceipt},
|
||||||
|
)
|
||||||
|
|
||||||
coreBlock := geth.GethBlockToCoreBlock(gethBlock, client)
|
coreBlock := geth.GethBlockToCoreBlock(gethBlock, client)
|
||||||
|
|
||||||
coreTransaction := coreBlock.Transactions[0]
|
coreTransaction := coreBlock.Transactions[0]
|
||||||
Expect(coreTransaction.To).To(Equal(""))
|
Expect(coreTransaction.To).To(Equal(""))
|
||||||
|
|
||||||
|
coreReceipt := coreTransaction.Receipt
|
||||||
|
expectedReceipt := geth.GethReceiptToCoreReceipt(gethReceipt)
|
||||||
|
Expect(coreReceipt).To(Equal(expectedReceipt))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
62
pkg/geth/geth_receipt_to_core_receipt.go
Normal file
62
pkg/geth/geth_receipt_to_core_receipt.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package geth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/8thlight/vulcanizedb/pkg/core"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BigTo64(n *big.Int) int64 {
|
||||||
|
if n != nil {
|
||||||
|
return n.Int64()
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func GethReceiptToCoreReceipt(gethReceipt *types.Receipt) core.Receipt {
|
||||||
|
bloom := hexutil.Encode(gethReceipt.Bloom.Bytes())
|
||||||
|
var postState string
|
||||||
|
var status int
|
||||||
|
postState, status = postStateOrStatus(gethReceipt)
|
||||||
|
logs := dereferenceLogs(gethReceipt)
|
||||||
|
contractAddress := setContractAddress(gethReceipt)
|
||||||
|
|
||||||
|
return core.Receipt{
|
||||||
|
Bloom: bloom,
|
||||||
|
ContractAddress: contractAddress,
|
||||||
|
CumulativeGasUsed: gethReceipt.CumulativeGasUsed.Int64(),
|
||||||
|
GasUsed: gethReceipt.GasUsed.Int64(),
|
||||||
|
Logs: logs,
|
||||||
|
StateRoot: postState,
|
||||||
|
TxHash: gethReceipt.TxHash.Hex(),
|
||||||
|
Status: status,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setContractAddress(gethReceipt *types.Receipt) string {
|
||||||
|
emptyAddress := common.Address{}.Bytes()
|
||||||
|
if bytes.Equal(gethReceipt.ContractAddress.Bytes(), emptyAddress) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return gethReceipt.ContractAddress.Hex()
|
||||||
|
}
|
||||||
|
|
||||||
|
func dereferenceLogs(gethReceipt *types.Receipt) []core.Log {
|
||||||
|
logs := []core.Log{}
|
||||||
|
for _, log := range gethReceipt.Logs {
|
||||||
|
logs = append(logs, GethLogToCoreLog(*log))
|
||||||
|
}
|
||||||
|
return logs
|
||||||
|
}
|
||||||
|
|
||||||
|
func postStateOrStatus(gethReceipts *types.Receipt) (string, int) {
|
||||||
|
if len(gethReceipts.PostState) != 0 {
|
||||||
|
return hexutil.Encode(gethReceipts.PostState), -99
|
||||||
|
}
|
||||||
|
return "", int(gethReceipts.Status)
|
||||||
|
}
|
85
pkg/geth/geth_receipt_to_core_receipt_test.go
Normal file
85
pkg/geth/geth_receipt_to_core_receipt_test.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package geth_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/8thlight/vulcanizedb/pkg/core"
|
||||||
|
"github.com/8thlight/vulcanizedb/pkg/geth"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("Conversion of GethReceipt to core.Receipt", func() {
|
||||||
|
|
||||||
|
It(`converts geth receipt to internal receipt format (pre Byzantium has post-transaction stateroot)`, func() {
|
||||||
|
receipt := types.Receipt{
|
||||||
|
Bloom: types.BytesToBloom(hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
|
||||||
|
ContractAddress: common.Address{},
|
||||||
|
CumulativeGasUsed: big.NewInt(21000),
|
||||||
|
GasUsed: big.NewInt(21000),
|
||||||
|
Logs: []*types.Log{},
|
||||||
|
PostState: hexutil.MustDecode("0x88abf7e73128227370aa7baa3dd4e18d0af70e92ef1f9ef426942fbe2dddb733"),
|
||||||
|
TxHash: common.HexToHash("0x97d99bc7729211111a21b12c933c949d4f31684f1d6954ff477d0477538ff017"),
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := core.Receipt{
|
||||||
|
Bloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
ContractAddress: "",
|
||||||
|
CumulativeGasUsed: 21000,
|
||||||
|
GasUsed: 21000,
|
||||||
|
Logs: []core.Log{},
|
||||||
|
StateRoot: "0x88abf7e73128227370aa7baa3dd4e18d0af70e92ef1f9ef426942fbe2dddb733",
|
||||||
|
Status: -99,
|
||||||
|
TxHash: receipt.TxHash.Hex(),
|
||||||
|
}
|
||||||
|
|
||||||
|
coreReceipt := geth.GethReceiptToCoreReceipt(&receipt)
|
||||||
|
Expect(coreReceipt.Bloom).To(Equal(expected.Bloom))
|
||||||
|
Expect(coreReceipt.ContractAddress).To(Equal(expected.ContractAddress))
|
||||||
|
Expect(coreReceipt.CumulativeGasUsed).To(Equal(expected.CumulativeGasUsed))
|
||||||
|
Expect(coreReceipt.GasUsed).To(Equal(expected.GasUsed))
|
||||||
|
Expect(coreReceipt.Logs).To(Equal(expected.Logs))
|
||||||
|
Expect(coreReceipt.StateRoot).To(Equal(expected.StateRoot))
|
||||||
|
Expect(coreReceipt.Status).To(Equal(expected.Status))
|
||||||
|
Expect(coreReceipt.TxHash).To(Equal(expected.TxHash))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
It("converts geth receipt to internal receipt format (post Byzantium has status", func() {
|
||||||
|
receipt := types.Receipt{
|
||||||
|
Bloom: types.BytesToBloom(hexutil.MustDecode("0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
|
||||||
|
ContractAddress: common.HexToAddress("x0123"),
|
||||||
|
CumulativeGasUsed: big.NewInt(7996119),
|
||||||
|
GasUsed: big.NewInt(21000),
|
||||||
|
Logs: []*types.Log{},
|
||||||
|
Status: uint(1),
|
||||||
|
TxHash: common.HexToHash("0xe340558980f89d5f86045ac11e5cc34e4bcec20f9f1e2a427aa39d87114e8223"),
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := core.Receipt{
|
||||||
|
Bloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
ContractAddress: receipt.ContractAddress.Hex(),
|
||||||
|
CumulativeGasUsed: 7996119,
|
||||||
|
GasUsed: 21000,
|
||||||
|
Logs: []core.Log{},
|
||||||
|
StateRoot: "",
|
||||||
|
Status: 1,
|
||||||
|
TxHash: receipt.TxHash.Hex(),
|
||||||
|
}
|
||||||
|
|
||||||
|
coreReceipt := geth.GethReceiptToCoreReceipt(&receipt)
|
||||||
|
Expect(coreReceipt.Bloom).To(Equal(expected.Bloom))
|
||||||
|
Expect(coreReceipt.ContractAddress).To(Equal(""))
|
||||||
|
Expect(coreReceipt.CumulativeGasUsed).To(Equal(expected.CumulativeGasUsed))
|
||||||
|
Expect(coreReceipt.GasUsed).To(Equal(expected.GasUsed))
|
||||||
|
Expect(coreReceipt.Logs).To(Equal(expected.Logs))
|
||||||
|
Expect(coreReceipt.StateRoot).To(Equal(expected.StateRoot))
|
||||||
|
Expect(coreReceipt.Status).To(Equal(expected.Status))
|
||||||
|
Expect(coreReceipt.TxHash).To(Equal(expected.TxHash))
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -8,11 +8,29 @@ import (
|
|||||||
|
|
||||||
type InMemory struct {
|
type InMemory struct {
|
||||||
blocks map[int64]core.Block
|
blocks map[int64]core.Block
|
||||||
|
receipts map[string]core.Receipt
|
||||||
contracts map[string]core.Contract
|
contracts map[string]core.Contract
|
||||||
logs map[string][]core.Log
|
logs map[string][]core.Log
|
||||||
HandleBlockCallCount int
|
HandleBlockCallCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewInMemory() *InMemory {
|
||||||
|
return &InMemory{
|
||||||
|
HandleBlockCallCount: 0,
|
||||||
|
blocks: make(map[int64]core.Block),
|
||||||
|
receipts: make(map[string]core.Receipt),
|
||||||
|
contracts: make(map[string]core.Contract),
|
||||||
|
logs: make(map[string][]core.Log),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository *InMemory) FindReceipt(txHash string) (core.Receipt, error) {
|
||||||
|
if receipt, ok := repository.receipts[txHash]; ok {
|
||||||
|
return receipt, nil
|
||||||
|
}
|
||||||
|
return core.Receipt{}, ErrReceiptDoesNotExist(txHash)
|
||||||
|
}
|
||||||
|
|
||||||
func (repository *InMemory) SetBlocksStatus(chainHead int64) {
|
func (repository *InMemory) SetBlocksStatus(chainHead int64) {
|
||||||
for key, block := range repository.blocks {
|
for key, block := range repository.blocks {
|
||||||
if key < (chainHead - blocksFromHeadBeforeFinal) {
|
if key < (chainHead - blocksFromHeadBeforeFinal) {
|
||||||
@ -79,18 +97,12 @@ func (repository *InMemory) MissingBlockNumbers(startingBlockNumber int64, endin
|
|||||||
return missingNumbers
|
return missingNumbers
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInMemory() *InMemory {
|
|
||||||
return &InMemory{
|
|
||||||
HandleBlockCallCount: 0,
|
|
||||||
blocks: make(map[int64]core.Block),
|
|
||||||
contracts: make(map[string]core.Contract),
|
|
||||||
logs: make(map[string][]core.Log),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (repository *InMemory) CreateOrUpdateBlock(block core.Block) error {
|
func (repository *InMemory) CreateOrUpdateBlock(block core.Block) error {
|
||||||
repository.HandleBlockCallCount++
|
repository.HandleBlockCallCount++
|
||||||
repository.blocks[block.Number] = block
|
repository.blocks[block.Number] = block
|
||||||
|
for _, transaction := range block.Transactions {
|
||||||
|
repository.receipts[transaction.Hash] = transaction.Receipt
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ import (
|
|||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BlockStatus int
|
|
||||||
|
|
||||||
type Postgres struct {
|
type Postgres struct {
|
||||||
Db *sqlx.DB
|
Db *sqlx.DB
|
||||||
node core.Node
|
node core.Node
|
||||||
@ -30,6 +28,10 @@ var (
|
|||||||
ErrUnableToSetNode = errors.New("postgres: unable to set node")
|
ErrUnableToSetNode = errors.New("postgres: unable to set node")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrReceiptDoesNotExist = func(txHash string) error {
|
||||||
|
return errors.New(fmt.Sprintf("Receipt for tx: %v does not exist", txHash))
|
||||||
|
}
|
||||||
|
|
||||||
var ErrContractDoesNotExist = func(contractHash string) error {
|
var ErrContractDoesNotExist = func(contractHash string) error {
|
||||||
return errors.New(fmt.Sprintf("Contract %v does not exist", contractHash))
|
return errors.New(fmt.Sprintf("Contract %v does not exist", contractHash))
|
||||||
}
|
}
|
||||||
@ -290,13 +292,31 @@ func (repository Postgres) removeBlock(blockNumber int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repository Postgres) FindReceipt(txHash string) (core.Receipt, error) {
|
||||||
|
row := repository.Db.QueryRow(
|
||||||
|
`SELECT contract_address,
|
||||||
|
tx_hash,
|
||||||
|
cumulative_gas_used,
|
||||||
|
gas_used,
|
||||||
|
state_root,
|
||||||
|
status
|
||||||
|
FROM receipts
|
||||||
|
WHERE tx_hash = $1`, txHash)
|
||||||
|
receipt, err := loadReceipt(row)
|
||||||
|
if err != nil {
|
||||||
|
switch err {
|
||||||
|
case sql.ErrNoRows:
|
||||||
|
return core.Receipt{}, ErrReceiptDoesNotExist(txHash)
|
||||||
|
default:
|
||||||
|
return core.Receipt{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return receipt, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (repository Postgres) createTransactions(tx *sql.Tx, blockId int64, transactions []core.Transaction) error {
|
func (repository Postgres) createTransactions(tx *sql.Tx, blockId int64, transactions []core.Transaction) error {
|
||||||
for _, transaction := range transactions {
|
for _, transaction := range transactions {
|
||||||
_, err := tx.Exec(
|
err := repository.createTransaction(tx, blockId, transaction)
|
||||||
`INSERT INTO transactions
|
|
||||||
(block_id, tx_hash, tx_nonce, tx_to, tx_from, tx_gaslimit, tx_gasprice, tx_value, tx_input_data)
|
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
|
||||||
blockId, transaction.Hash, transaction.Nonce, transaction.To, transaction.From, transaction.GasLimit, transaction.GasPrice, transaction.Value, transaction.Data)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -304,6 +324,59 @@ func (repository Postgres) createTransactions(tx *sql.Tx, blockId int64, transac
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repository Postgres) createTransaction(tx *sql.Tx, blockId int64, transaction core.Transaction) error {
|
||||||
|
var transactionId int
|
||||||
|
err := tx.QueryRow(
|
||||||
|
`INSERT INTO transactions
|
||||||
|
(block_id, tx_hash, tx_nonce, tx_to, tx_from, tx_gaslimit, tx_gasprice, tx_value, tx_input_data)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||||
|
RETURNING id`,
|
||||||
|
blockId, transaction.Hash, transaction.Nonce, transaction.To, transaction.From, transaction.GasLimit, transaction.GasPrice, transaction.Value, transaction.Data).
|
||||||
|
Scan(&transactionId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if transaction.Receipt.TxHash != "" {
|
||||||
|
err = repository.createReceipt(tx, transactionId, transaction.Receipt)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repository Postgres) createReceipt(tx *sql.Tx, transactionId int, receipt core.Receipt) error {
|
||||||
|
//Not currently persisting log bloom filters
|
||||||
|
_, err := tx.Exec(
|
||||||
|
`INSERT INTO receipts
|
||||||
|
(contract_address, tx_hash, cumulative_gas_used, gas_used, state_root, status, transaction_id)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
||||||
|
receipt.ContractAddress, receipt.TxHash, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.StateRoot, receipt.Status, transactionId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadReceipt(receiptsRow *sql.Row) (core.Receipt, error) {
|
||||||
|
var contractAddress string
|
||||||
|
var txHash string
|
||||||
|
var cumulativeGasUsed int64
|
||||||
|
var gasUsed int64
|
||||||
|
var stateRoot string
|
||||||
|
var status int
|
||||||
|
|
||||||
|
err := receiptsRow.Scan(&contractAddress, &txHash, &cumulativeGasUsed, &gasUsed, &stateRoot, &status)
|
||||||
|
return core.Receipt{
|
||||||
|
TxHash: txHash,
|
||||||
|
ContractAddress: contractAddress,
|
||||||
|
CumulativeGasUsed: cumulativeGasUsed,
|
||||||
|
GasUsed: gasUsed,
|
||||||
|
StateRoot: stateRoot,
|
||||||
|
Status: status,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
func (repository Postgres) loadBlock(blockRows *sql.Row) (core.Block, error) {
|
func (repository Postgres) loadBlock(blockRows *sql.Row) (core.Block, error) {
|
||||||
var blockId int64
|
var blockId int64
|
||||||
var blockHash string
|
var blockHash string
|
||||||
|
@ -12,6 +12,7 @@ type Repository interface {
|
|||||||
FindBlockByNumber(blockNumber int64) (core.Block, error)
|
FindBlockByNumber(blockNumber int64) (core.Block, error)
|
||||||
MaxBlockNumber() int64
|
MaxBlockNumber() int64
|
||||||
MissingBlockNumbers(startingBlockNumber int64, endingBlockNumber int64) []int64
|
MissingBlockNumbers(startingBlockNumber int64, endingBlockNumber int64) []int64
|
||||||
|
FindReceipt(txHash string) (core.Receipt, error)
|
||||||
CreateContract(contract core.Contract) error
|
CreateContract(contract core.Contract) error
|
||||||
ContractExists(contractHash string) bool
|
ContractExists(contractHash string) bool
|
||||||
FindContract(contractHash string) (core.Contract, error)
|
FindContract(contractHash string) (core.Contract, error)
|
||||||
|
@ -15,6 +15,7 @@ func ClearData(postgres repositories.Postgres) {
|
|||||||
postgres.Db.MustExec("DELETE FROM transactions")
|
postgres.Db.MustExec("DELETE FROM transactions")
|
||||||
postgres.Db.MustExec("DELETE FROM blocks")
|
postgres.Db.MustExec("DELETE FROM blocks")
|
||||||
postgres.Db.MustExec("DELETE FROM logs")
|
postgres.Db.MustExec("DELETE FROM logs")
|
||||||
|
postgres.Db.MustExec("DELETE FROM receipts")
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssertRepositoryBehavior(buildRepository func(node core.Node) repositories.Repository) {
|
func AssertRepositoryBehavior(buildRepository func(node core.Node) repositories.Repository) {
|
||||||
@ -497,4 +498,43 @@ func AssertRepositoryBehavior(buildRepository func(node core.Node) repositories.
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("Saving receipts", func() {
|
||||||
|
It("returns the receipt when it exists", func() {
|
||||||
|
expected := core.Receipt{
|
||||||
|
ContractAddress: "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
|
||||||
|
CumulativeGasUsed: 7996119,
|
||||||
|
GasUsed: 21000,
|
||||||
|
Logs: []core.Log{},
|
||||||
|
StateRoot: "0x88abf7e73128227370aa7baa3dd4e18d0af70e92ef1f9ef426942fbe2dddb733",
|
||||||
|
Status: 1,
|
||||||
|
TxHash: "0xe340558980f89d5f86045ac11e5cc34e4bcec20f9f1e2a427aa39d87114e8223",
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction := core.Transaction{
|
||||||
|
Hash: expected.TxHash,
|
||||||
|
Receipt: expected,
|
||||||
|
}
|
||||||
|
|
||||||
|
block := core.Block{Transactions: []core.Transaction{transaction}}
|
||||||
|
repository.CreateOrUpdateBlock(block)
|
||||||
|
receipt, err := repository.FindReceipt("0xe340558980f89d5f86045ac11e5cc34e4bcec20f9f1e2a427aa39d87114e8223")
|
||||||
|
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
//Not currently serializing bloom logs
|
||||||
|
Expect(receipt.Bloom).To(Equal(core.Receipt{}.Bloom))
|
||||||
|
Expect(receipt.TxHash).To(Equal(expected.TxHash))
|
||||||
|
Expect(receipt.CumulativeGasUsed).To(Equal(expected.CumulativeGasUsed))
|
||||||
|
Expect(receipt.GasUsed).To(Equal(expected.GasUsed))
|
||||||
|
Expect(receipt.StateRoot).To(Equal(expected.StateRoot))
|
||||||
|
Expect(receipt.Status).To(Equal(expected.Status))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns ErrReceiptDoesNotExist when receipt does not exist", func() {
|
||||||
|
receipt, err := repository.FindReceipt("DOES NOT EXIST")
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
Expect(receipt).To(BeZero())
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user