Commit 32780542 authored by Hamdi Allam's avatar Hamdi Allam

index specified contract events

parent 2fdcbd5a
......@@ -3,9 +3,12 @@ package database
import (
"errors"
"gorm.io/gorm"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"gorm.io/gorm"
"github.com/google/uuid"
)
/**
......@@ -26,7 +29,7 @@ type TokenPair struct {
}
type Deposit struct {
GUID string `gorm:"primaryKey"`
GUID uuid.UUID `gorm:"primaryKey"`
InitiatedL1EventGUID string
Tx Transaction `gorm:"embedded"`
......@@ -39,7 +42,7 @@ type DepositWithTransactionHash struct {
}
type Withdrawal struct {
GUID string `gorm:"primaryKey"`
GUID uuid.UUID `gorm:"primaryKey"`
InitiatedL2EventGUID string
WithdrawalHash common.Hash `gorm:"serializer:json"`
......
package database
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"gorm.io/gorm"
"github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"
)
/**
......@@ -11,11 +13,11 @@ import (
*/
type ContractEvent struct {
GUID string `gorm:"primaryKey"`
GUID uuid.UUID `gorm:"primaryKey"`
BlockHash common.Hash `gorm:"serializer:json"`
TransactionHash common.Hash `gorm:"serializer:json"`
EventSignature hexutil.Bytes `gorm:"serializer:json"`
EventSignature common.Hash `gorm:"serializer:json"`
LogIndex uint64
Timestamp uint64
}
......
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/indexer/processor"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
......@@ -72,22 +73,30 @@ func NewIndexer(ctx *cli.Context) (*Indexer, error) {
return nil, err
}
// L1 Processor
// L1 Processor (hardhat devnet contracts). Make this configurable
l1Contracts := processor.L1Contracts{
OptimismPortal: common.HexToAddress("0x6900000000000000000000000000000000000000"),
L2OutputOracle: common.HexToAddress("0x6900000000000000000000000000000000000001"),
L1CrossDomainMessenger: common.HexToAddress("0x6900000000000000000000000000000000000002"),
L1StandardBridge: common.HexToAddress("0x6900000000000000000000000000000000000003"),
L1ERC721Bridge: common.HexToAddress("0x6900000000000000000000000000000000000004"),
}
l1EthClient, err := node.NewEthClient(ctx.GlobalString(flags.L1EthRPCFlag.Name))
if err != nil {
return nil, err
}
l1Processor, err := processor.NewL1Processor(l1EthClient, db)
l1Processor, err := processor.NewL1Processor(l1EthClient, db, l1Contracts)
if err != nil {
return nil, err
}
// L2Processor
l2Contracts := processor.L2ContractPredeploys() // Make this configurable
l2EthClient, err := node.NewEthClient(ctx.GlobalString(flags.L2EthRPCFlag.Name))
if err != nil {
return nil, err
}
l2Processor, err := processor.NewL2Processor(l2EthClient, db)
l2Processor, err := processor.NewL2Processor(l2EthClient, db, l2Contracts)
if err != nil {
return nil, err
}
......
package processor
import (
"context"
"errors"
"reflect"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/google/uuid"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
type L1Contracts struct {
OptimismPortal common.Address
L2OutputOracle common.Address
L1CrossDomainMessenger common.Address
L1StandardBridge common.Address
L1ERC721Bridge common.Address
// Some more contracts -- ProxyAdmin, SystemConfig, etcc
// Ignore the auxilliary contracts?
// Legacy contracts? We'll add this in to index the legacy chain.
// Remove afterwards?
}
func (c L1Contracts) toArray() []common.Address {
fields := reflect.VisibleFields(reflect.TypeOf(c))
v := reflect.ValueOf(c)
contracts := make([]common.Address, len(fields))
for i, field := range fields {
contracts[i] = (v.FieldByName(field.Name).Interface()).(common.Address)
}
return contracts
}
type L1Processor struct {
processor
}
func NewL1Processor(ethClient node.EthClient, db *database.DB) (*L1Processor, error) {
func NewL1Processor(ethClient node.EthClient, db *database.DB, l1Contracts L1Contracts) (*L1Processor, error) {
l1ProcessLog := log.New("processor", "l1")
l1ProcessLog.Info("initializing processor")
......@@ -41,7 +75,7 @@ func NewL1Processor(ethClient node.EthClient, db *database.DB) (*L1Processor, er
processor: processor{
fetcher: node.NewFetcher(ethClient, fromL1Header),
db: db,
processFn: l1ProcessFn(ethClient),
processFn: l1ProcessFn(l1ProcessLog, ethClient, l1Contracts),
processLog: l1ProcessLog,
},
}
......@@ -49,22 +83,79 @@ func NewL1Processor(ethClient node.EthClient, db *database.DB) (*L1Processor, er
return l1Processor, nil
}
func l1ProcessFn(ethClient node.EthClient) func(db *database.DB, headers []*types.Header) error {
func l1ProcessFn(processLog log.Logger, ethClient node.EthClient, l1Contracts L1Contracts) func(db *database.DB, headers []*types.Header) error {
rawEthClient := ethclient.NewClient(ethClient.RawRpcClient())
contractAddrs := l1Contracts.toArray()
processLog.Info("processor configured with contracts", "contracts", l1Contracts)
return func(db *database.DB, headers []*types.Header) error {
numHeaders := len(headers)
// index all l2 blocks for now
l1Headers := make([]*database.L1BlockHeader, len(headers))
/** Index Blocks **/
l1Headers := make([]*database.L1BlockHeader, numHeaders)
l1HeaderMap := make(map[common.Hash]*types.Header)
for i, header := range headers {
blockHash := header.Hash()
l1Headers[i] = &database.L1BlockHeader{
BlockHeader: database.BlockHeader{
Hash: header.Hash(),
Hash: blockHash,
ParentHash: header.ParentHash,
Number: database.U256{Int: header.Number},
Timestamp: header.Time,
},
}
l1HeaderMap[blockHash] = header
}
/** Index Contract Events **/
logFilter := ethereum.FilterQuery{FromBlock: headers[0].Number, ToBlock: headers[numHeaders-1].Number, Addresses: contractAddrs}
logs, err := rawEthClient.FilterLogs(context.Background(), logFilter)
if err != nil {
return err
}
numLogs := len(logs)
l1ContractEvents := make([]*database.L1ContractEvent, numLogs)
for i, log := range logs {
header, ok := l1HeaderMap[log.BlockHash]
if !ok {
// Log the individual headers in the batch?
processLog.Crit("contract event found with associated header not in the batch", "header", header, "log_index", log.Index)
return errors.New("parsed log with a block hash not in this batch")
}
l1ContractEvents[i] = &database.L1ContractEvent{
ContractEvent: database.ContractEvent{
GUID: uuid.New(),
BlockHash: log.BlockHash,
TransactionHash: log.TxHash,
EventSignature: log.Topics[0],
LogIndex: uint64(log.Index),
Timestamp: header.Time,
},
}
}
/** Update Database **/
err = db.Blocks.StoreL1BlockHeaders(l1Headers)
if err != nil {
return err
}
if numLogs > 0 {
processLog.Info("detected new contract logs", "size", numLogs)
err = db.ContractEvents.StoreL1ContractEvents(l1ContractEvents)
if err != nil {
return err
}
}
return db.Blocks.StoreL1BlockHeaders(l1Headers)
// a-ok!
return nil
}
}
package processor
import (
"context"
"errors"
"reflect"
"github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/node"
"github.com/google/uuid"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
type L2Contracts struct {
L2CrossDomainMessenger common.Address
L2StandardBridge common.Address
L2ERC721Bridge common.Address
L2ToL1MessagePasser common.Address
// Some more contracts -- ProxyAdmin, SystemConfig, etcc
// Ignore the auxilliary contracts?
// Legacy Contracts? We'll add this in to index the legacy chain.
// Remove afterwards?
}
func L2ContractPredeploys() L2Contracts {
return L2Contracts{
L2CrossDomainMessenger: common.HexToAddress("0x4200000000000000000000000000000000000007"),
L2StandardBridge: common.HexToAddress("0x4200000000000000000000000000000000000010"),
L2ERC721Bridge: common.HexToAddress("0x4200000000000000000000000000000000000014"),
L2ToL1MessagePasser: common.HexToAddress("0x4200000000000000000000000000000000000016"),
}
}
func (c L2Contracts) toArray() []common.Address {
fields := reflect.VisibleFields(reflect.TypeOf(c))
v := reflect.ValueOf(c)
contracts := make([]common.Address, len(fields))
for i, field := range fields {
contracts[i] = (v.FieldByName(field.Name).Interface()).(common.Address)
}
return contracts
}
type L2Processor struct {
processor
}
func NewL2Processor(ethClient node.EthClient, db *database.DB) (*L2Processor, error) {
func NewL2Processor(ethClient node.EthClient, db *database.DB, l2Contracts L2Contracts) (*L2Processor, error) {
l2ProcessLog := log.New("processor", "l2")
l2ProcessLog.Info("initializing processor")
......@@ -40,7 +82,7 @@ func NewL2Processor(ethClient node.EthClient, db *database.DB) (*L2Processor, er
processor: processor{
fetcher: node.NewFetcher(ethClient, fromL2Header),
db: db,
processFn: l2ProcessFn(ethClient),
processFn: l2ProcessFn(l2ProcessLog, ethClient, l2Contracts),
processLog: l2ProcessLog,
},
}
......@@ -48,22 +90,78 @@ func NewL2Processor(ethClient node.EthClient, db *database.DB) (*L2Processor, er
return l2Processor, nil
}
func l2ProcessFn(ethClient node.EthClient) func(db *database.DB, headers []*types.Header) error {
func l2ProcessFn(processLog log.Logger, ethClient node.EthClient, l2Contracts L2Contracts) func(db *database.DB, headers []*types.Header) error {
rawEthClient := ethclient.NewClient(ethClient.RawRpcClient())
contractAddrs := l2Contracts.toArray()
processLog.Info("processor configured with contracts", "contracts", l2Contracts)
return func(db *database.DB, headers []*types.Header) error {
numHeaders := len(headers)
/** Index Blocks **/
// index all l2 blocks for now
l2Headers := make([]*database.L2BlockHeader, len(headers))
l2HeaderMap := make(map[common.Hash]*types.Header)
for i, header := range headers {
blockHash := header.Hash()
l2Headers[i] = &database.L2BlockHeader{
BlockHeader: database.BlockHeader{
Hash: header.Hash(),
Hash: blockHash,
ParentHash: header.ParentHash,
Number: database.U256{Int: header.Number},
Timestamp: header.Time,
},
}
l2HeaderMap[blockHash] = header
}
/** Index Contract Events **/
logFilter := ethereum.FilterQuery{FromBlock: headers[0].Number, ToBlock: headers[numHeaders-1].Number, Addresses: contractAddrs}
logs, err := rawEthClient.FilterLogs(context.Background(), logFilter)
if err != nil {
return err
}
numLogs := len(logs)
l2ContractEvents := make([]*database.L2ContractEvent, numLogs)
for i, log := range logs {
header, ok := l2HeaderMap[log.BlockHash]
if !ok {
// Log the individual headers in the batch?
processLog.Crit("contract event found with associated header not in the batch", "header", header, "log_index", log.Index)
return errors.New("parsed log with a block hash not in this batch")
}
l2ContractEvents[i] = &database.L2ContractEvent{
ContractEvent: database.ContractEvent{
GUID: uuid.New(),
BlockHash: log.BlockHash,
TransactionHash: log.TxHash,
EventSignature: log.Topics[0],
LogIndex: uint64(log.Index),
Timestamp: header.Time,
},
}
}
/** Update Database **/
err = db.Blocks.StoreL2BlockHeaders(l2Headers)
if err != nil {
return err
}
if numLogs > 0 {
processLog.Info("detected new contract logs", "size", numLogs)
err = db.ContractEvents.StoreL2ContractEvents(l2ContractEvents)
if err != nil {
return err
}
}
return db.Blocks.StoreL2BlockHeaders(l2Headers)
// a-ok!
return nil
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment