Skip to content

Commit 977b5cb

Browse files
authored
feat(eventindexer): introduce disperser (#16694)
1 parent ca45aa6 commit 977b5cb

File tree

17 files changed

+8331
-5
lines changed

17 files changed

+8331
-5
lines changed

packages/eventindexer/SgxVerifier.json

Lines changed: 932 additions & 0 deletions
Large diffs are not rendered by default.

packages/eventindexer/TaikoToken.json

Lines changed: 1155 additions & 0 deletions
Large diffs are not rendered by default.

packages/eventindexer/abigen.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ if [ ! -d "../protocol/out" ]; then
55
exit 1
66
fi
77

8-
paths=("TaikoL1.sol" "Bridge.sol" "AssignmentHook.sol")
8+
paths=("TaikoL1.sol" "Bridge.sol" "AssignmentHook.sol" "SgxVerifier.sol" "TaikoToken.sol")
99

10-
names=("TaikoL1" "Bridge" "AssignmentHook")
10+
names=("TaikoL1" "Bridge" "AssignmentHook" "SgxVerifier" "TaikoToken")
1111

1212

1313
for (( i = 0; i < ${#paths[@]}; ++i ));

packages/eventindexer/cmd/flags/common.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var (
88
commonCategory = "COMMON"
99
indexerCategory = "INDEXER"
1010
generatorCategory = "GENERATOR"
11+
disperserCategory = "DISPERSER"
1112
)
1213

1314
var (
@@ -71,7 +72,6 @@ var (
7172
Value: 6061,
7273
EnvVars: []string{"METRICS_HTTP_PORT"},
7374
}
74-
7575
Layer = &cli.StringFlag{
7676
Name: "layer",
7777
Usage: "Which layer indexing is occurring on",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package flags
2+
3+
import "github.com/urfave/cli/v2"
4+
5+
var (
6+
DisperserPrivateKey = &cli.StringFlag{
7+
Name: "disperserPrivateKey",
8+
Usage: "Disperser private key which contains TTKO",
9+
Required: true,
10+
Category: disperserCategory,
11+
EnvVars: []string{"DISPERSER_PRIVATE_KEY"},
12+
}
13+
TaikoTokenAddress = &cli.StringFlag{
14+
Name: "taikoTokenAddress",
15+
Usage: "Address of the TaikoToken contract",
16+
Required: true,
17+
Category: disperserCategory,
18+
EnvVars: []string{"TAIKO_TOKEN_ADDRESS"},
19+
}
20+
DispersalAmount = &cli.StringFlag{
21+
Name: "taikoTokenAddress",
22+
Usage: "Dispersal amount in wei",
23+
Required: true,
24+
Category: disperserCategory,
25+
EnvVars: []string{"DISPERSAL_AMOUNT"},
26+
}
27+
RPCUrl = &cli.StringFlag{
28+
Name: "rpcUrl",
29+
Usage: "RPC URL for the source chain",
30+
Required: true,
31+
Category: commonCategory,
32+
EnvVars: []string{"RPC_URL"},
33+
}
34+
)
35+
36+
var DisperserFlags = MergeFlags(CommonFlags, []cli.Flag{
37+
DisperserPrivateKey,
38+
TaikoTokenAddress,
39+
DispersalAmount,
40+
RPCUrl,
41+
})

packages/eventindexer/cmd/flags/indexer.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ var (
5151
Category: indexerCategory,
5252
EnvVars: []string{"ASSIGNMENT_HOOK_ADDRESS"},
5353
}
54+
SgxVerifierAddress = &cli.StringFlag{
55+
Name: "sgxVerifierAddress",
56+
Usage: "Address of the SGXVerifier contract",
57+
Required: false,
58+
Category: indexerCategory,
59+
EnvVars: []string{"SGX_VERIFIER_ADDRESS"},
60+
}
5461
BlockBatchSize = &cli.Uint64Flag{
5562
Name: "blockBatchSize",
5663
Usage: "Block batch size when iterating through blocks",
@@ -90,6 +97,7 @@ var IndexerFlags = MergeFlags(CommonFlags, []cli.Flag{
9097
L1TaikoAddress,
9198
BridgeAddress,
9299
SwapAddresses,
100+
SgxVerifierAddress,
93101
AssignmentHookAddress,
94102
BlockBatchSize,
95103
SubscriptionBackoff,

packages/eventindexer/cmd/main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/taikoxyz/taiko-mono/packages/eventindexer/api"
1010
"github.com/taikoxyz/taiko-mono/packages/eventindexer/cmd/flags"
1111
"github.com/taikoxyz/taiko-mono/packages/eventindexer/cmd/utils"
12+
"github.com/taikoxyz/taiko-mono/packages/eventindexer/disperser"
1213
"github.com/taikoxyz/taiko-mono/packages/eventindexer/generator"
1314
"github.com/taikoxyz/taiko-mono/packages/eventindexer/indexer"
1415
"github.com/urfave/cli/v2"
@@ -58,6 +59,13 @@ func main() {
5859
Description: "Taiko time-series data generator",
5960
Action: utils.SubcommandAction(new(generator.Generator)),
6061
},
62+
{
63+
Name: "disperser",
64+
Flags: flags.DisperserFlags,
65+
Usage: "Starts the disperser software",
66+
Description: "Taiko TTKO disperser",
67+
Action: utils.SubcommandAction(new(disperser.Disperser)),
68+
},
6169
}
6270

6371
if err := app.Run(os.Args); err != nil {

packages/eventindexer/contracts/sgxverifier/SgxVerifier.go

Lines changed: 2371 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/eventindexer/contracts/taikotoken/TaikoToken.go

Lines changed: 3455 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package disperser
2+
3+
import (
4+
"crypto/ecdsa"
5+
"database/sql"
6+
"fmt"
7+
"math/big"
8+
9+
"github.com/ethereum-optimism/optimism/op-service/txmgr"
10+
"github.com/ethereum/go-ethereum/common"
11+
"github.com/ethereum/go-ethereum/crypto"
12+
"github.com/taikoxyz/taiko-mono/packages/eventindexer/cmd/flags"
13+
"github.com/taikoxyz/taiko-mono/packages/eventindexer/pkg/db"
14+
pkgFlags "github.com/taikoxyz/taiko-mono/packages/relayer/pkg/flags"
15+
"github.com/urfave/cli/v2"
16+
"gorm.io/driver/mysql"
17+
"gorm.io/gorm"
18+
"gorm.io/gorm/logger"
19+
)
20+
21+
type DB interface {
22+
DB() (*sql.DB, error)
23+
GormDB() *gorm.DB
24+
}
25+
26+
type Config struct {
27+
// db configs
28+
DatabaseUsername string
29+
DatabasePassword string
30+
DatabaseName string
31+
DatabaseHost string
32+
DatabaseMaxIdleConns uint64
33+
DatabaseMaxOpenConns uint64
34+
DatabaseMaxConnLifetime uint64
35+
MetricsHTTPPort uint64
36+
DisperserPrivateKey *ecdsa.PrivateKey
37+
DispersalAmount *big.Int
38+
TaikoTokenAddress common.Address
39+
TxmgrConfigs *txmgr.CLIConfig
40+
RPCURL string
41+
OpenDBFunc func() (DB, error)
42+
}
43+
44+
// NewConfigFromCliContext creates a new config instance from command line flags.
45+
func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
46+
disperserPrivateKey, err := crypto.ToECDSA(
47+
common.Hex2Bytes(c.String(flags.DisperserPrivateKey.Name)),
48+
)
49+
if err != nil {
50+
return nil, fmt.Errorf("invalid disperserPrivateKey: %w", err)
51+
}
52+
53+
dispersalAmount, ok := new(big.Int).SetString(c.String(flags.DispersalAmount.Name), 10)
54+
if !ok {
55+
return nil, fmt.Errorf("Invalid dispersal amount")
56+
}
57+
58+
return &Config{
59+
DatabaseUsername: c.String(flags.DatabaseUsername.Name),
60+
DatabasePassword: c.String(flags.DatabasePassword.Name),
61+
DatabaseName: c.String(flags.DatabaseName.Name),
62+
DatabaseHost: c.String(flags.DatabaseHost.Name),
63+
DatabaseMaxIdleConns: c.Uint64(flags.DatabaseMaxIdleConns.Name),
64+
DatabaseMaxOpenConns: c.Uint64(flags.DatabaseMaxOpenConns.Name),
65+
DatabaseMaxConnLifetime: c.Uint64(flags.DatabaseConnMaxLifetime.Name),
66+
MetricsHTTPPort: c.Uint64(flags.MetricsHTTPPort.Name),
67+
DisperserPrivateKey: disperserPrivateKey,
68+
RPCURL: c.String(flags.RPCUrl.Name),
69+
DispersalAmount: dispersalAmount,
70+
TaikoTokenAddress: common.HexToAddress(c.String(flags.TaikoTokenAddress.Name)),
71+
TxmgrConfigs: pkgFlags.InitTxmgrConfigsFromCli(
72+
c.String(flags.RPCUrl.Name),
73+
disperserPrivateKey,
74+
c,
75+
),
76+
OpenDBFunc: func() (DB, error) {
77+
return db.OpenDBConnection(db.DBConnectionOpts{
78+
Name: c.String(flags.DatabaseUsername.Name),
79+
Password: c.String(flags.DatabasePassword.Name),
80+
Database: c.String(flags.DatabaseName.Name),
81+
Host: c.String(flags.DatabaseHost.Name),
82+
MaxIdleConns: c.Uint64(flags.DatabaseMaxIdleConns.Name),
83+
MaxOpenConns: c.Uint64(flags.DatabaseMaxOpenConns.Name),
84+
MaxConnLifetime: c.Uint64(flags.DatabaseConnMaxLifetime.Name),
85+
OpenFunc: func(dsn string) (*db.DB, error) {
86+
gormDB, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
87+
Logger: logger.Default.LogMode(logger.Silent),
88+
})
89+
if err != nil {
90+
return nil, err
91+
}
92+
93+
return db.New(gormDB), nil
94+
},
95+
})
96+
},
97+
}, nil
98+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package disperser
2+
3+
import (
4+
"context"
5+
"log/slog"
6+
"math/big"
7+
8+
txmgrMetrics "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
9+
"github.com/taikoxyz/taiko-mono/packages/eventindexer/encoding"
10+
11+
"github.com/ethereum-optimism/optimism/op-service/txmgr"
12+
"github.com/ethereum/go-ethereum/common"
13+
"github.com/ethereum/go-ethereum/log"
14+
"github.com/urfave/cli/v2"
15+
)
16+
17+
var (
18+
ZeroAddress = common.HexToAddress("0x0000000000000000000000000000000000000000")
19+
)
20+
21+
// Disperser is a subcommand which is intended to be run on an interval, like
22+
// a cronjob, to parse the indexed data from the database, and generate
23+
// time series data that can easily be displayed via charting libraries.
24+
type Disperser struct {
25+
db DB
26+
dispersalAmount *big.Int
27+
taikoTokenAddress common.Address
28+
txmgr txmgr.TxManager
29+
}
30+
31+
func (d *Disperser) InitFromCli(ctx context.Context, c *cli.Context) error {
32+
config, err := NewConfigFromCliContext(c)
33+
if err != nil {
34+
return err
35+
}
36+
37+
return InitFromConfig(ctx, d, config)
38+
}
39+
40+
func InitFromConfig(ctx context.Context, d *Disperser, cfg *Config) error {
41+
db, err := cfg.OpenDBFunc()
42+
if err != nil {
43+
return err
44+
}
45+
46+
if d.txmgr, err = txmgr.NewSimpleTxManager(
47+
"disperser",
48+
log.Root(),
49+
new(txmgrMetrics.NoopTxMetrics),
50+
*cfg.TxmgrConfigs,
51+
); err != nil {
52+
return err
53+
}
54+
55+
d.db = db
56+
57+
d.dispersalAmount = cfg.DispersalAmount
58+
d.taikoTokenAddress = cfg.TaikoTokenAddress
59+
60+
return nil
61+
}
62+
63+
func (d *Disperser) Name() string {
64+
return "disperser"
65+
}
66+
67+
func (d *Disperser) Start() error {
68+
addresses, err := d.findAllAddresses()
69+
if err != nil {
70+
return err
71+
}
72+
73+
for _, address := range addresses {
74+
slog.Info("dispersing to", "address", address)
75+
76+
data, err := encoding.TaikoTokenABI.Pack("transfer", common.HexToAddress(address), d.dispersalAmount)
77+
if err != nil {
78+
return err
79+
}
80+
81+
candidate := txmgr.TxCandidate{
82+
TxData: data,
83+
Blobs: nil,
84+
To: &d.taikoTokenAddress,
85+
}
86+
87+
receipt, err := d.txmgr.Send(context.Background(), candidate)
88+
if err != nil {
89+
slog.Warn("Failed to send transfer transaction", "error", err.Error())
90+
return err
91+
}
92+
93+
slog.Info("sent tx", "tx", receipt.TxHash.Hex())
94+
}
95+
96+
return nil
97+
}
98+
99+
func (d *Disperser) findAllAddresses() ([]string, error) {
100+
var addresses []string
101+
// Execute raw SQL query to find distinct addresses where event is 'InstanceAdded'
102+
err := d.db.GormDB().Raw("SELECT DISTINCT address FROM events WHERE event = ?", "InstanceAdded").Scan(&addresses).Error
103+
104+
if err != nil {
105+
return nil, err
106+
}
107+
108+
return addresses, nil
109+
}
110+
111+
func (d *Disperser) Close(ctx context.Context) {
112+
sqlDB, err := d.db.DB()
113+
if err != nil {
114+
slog.Error("error getting sqldb when closing Disperser", "err", err.Error())
115+
}
116+
117+
if err := sqlDB.Close(); err != nil {
118+
slog.Error("error closing sqlbd connection", "err", err.Error())
119+
}
120+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package encoding
2+
3+
import (
4+
"github.com/ethereum/go-ethereum/accounts/abi"
5+
6+
"github.com/ethereum/go-ethereum/log"
7+
"github.com/taikoxyz/taiko-mono/packages/eventindexer/contracts/taikotoken"
8+
)
9+
10+
var TaikoTokenABI *abi.ABI
11+
12+
var err error
13+
14+
func init() {
15+
if TaikoTokenABI, err = taikotoken.TaikoTokenMetaData.GetAbi(); err != nil {
16+
log.Crit("Get TaikoTokenABI ABI error", "error", err)
17+
}
18+
}

packages/eventindexer/event.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var (
2222
EventNameSwap = "Swap"
2323
EventNameMint = "Mint"
2424
EventNameNFTTransfer = "Transfer"
25+
EventNameInstanceAdded = "InstanceAdded"
2526
)
2627

2728
// Event represents a stored EVM event. The fields will be serialized

packages/eventindexer/indexer/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type Config struct {
3333
L1TaikoAddress common.Address
3434
BridgeAddress common.Address
3535
AssignmentHookAddress common.Address
36+
SgxVerifierAddress common.Address
3637
SwapAddresses []common.Address
3738
BlockBatchSize uint64
3839
SubscriptionBackoff uint64
@@ -67,6 +68,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
6768
L1TaikoAddress: common.HexToAddress(c.String(flags.L1TaikoAddress.Name)),
6869
BridgeAddress: common.HexToAddress(c.String(flags.BridgeAddress.Name)),
6970
AssignmentHookAddress: common.HexToAddress(c.String(flags.AssignmentHookAddress.Name)),
71+
SgxVerifierAddress: common.HexToAddress(flags.SgxVerifierAddress.Name),
7072
SwapAddresses: swaps,
7173
BlockBatchSize: c.Uint64(flags.BlockBatchSize.Name),
7274
SubscriptionBackoff: c.Uint64(flags.SubscriptionBackoff.Name),

packages/eventindexer/indexer/filter.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,22 @@ func filterFunc(
156156
}
157157
}
158158

159+
if i.sgxVerifier != nil {
160+
wg.Go(func() error {
161+
instancesAdded, err := i.sgxVerifier.FilterInstanceAdded(filterOpts, nil, nil)
162+
if err != nil {
163+
return errors.Wrap(err, "i.sgxVerifier.FilterInstanceAdded")
164+
}
165+
166+
err = i.saveInstanceAddedEvents(ctx, chainID, instancesAdded)
167+
if err != nil {
168+
return errors.Wrap(err, "i.saveInstanceAddedEvents")
169+
}
170+
171+
return nil
172+
})
173+
}
174+
159175
wg.Go(func() error {
160176
if err := i.indexRawBlockData(ctx, chainID, filterOpts.Start, *filterOpts.End); err != nil {
161177
return errors.Wrap(err, "i.indexRawBlockData")

0 commit comments

Comments
 (0)