From 3fc378b2b451d14ea95d2519516049aa00be953b Mon Sep 17 00:00:00 2001 From: Patrick Schork <354473+pschork@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:50:30 -0700 Subject: [PATCH 1/4] Ejections report cli Adds second table ordered by ejection txn Sort results in descending order Consolidate table logic Add stake percentage calculation to ejection report Add stake percentage to operator ejections data api Update swagger Fix id->address mapping Lint --- disperser/dataapi/docs/docs.go | 6 + disperser/dataapi/docs/swagger.json | 6 + disperser/dataapi/docs/swagger.yaml | 4 + .../dataapi/queried_operators_handlers.go | 53 ++++++ disperser/dataapi/server.go | 12 +- tools/ejections/Makefile | 9 + tools/ejections/cmd/main.go | 166 ++++++++++++++++++ tools/ejections/config.go | 46 +++++ tools/ejections/flags/flags.go | 84 +++++++++ 9 files changed, 381 insertions(+), 5 deletions(-) create mode 100644 tools/ejections/Makefile create mode 100644 tools/ejections/cmd/main.go create mode 100644 tools/ejections/config.go create mode 100644 tools/ejections/flags/flags.go diff --git a/disperser/dataapi/docs/docs.go b/disperser/dataapi/docs/docs.go index ab30ff667..6c8a7c487 100644 --- a/disperser/dataapi/docs/docs.go +++ b/disperser/dataapi/docs/docs.go @@ -992,12 +992,18 @@ const docTemplate = `{ "block_timestamp": { "type": "string" }, + "operator_address": { + "type": "string" + }, "operator_id": { "type": "string" }, "quorum": { "type": "integer" }, + "stake_percentage": { + "type": "number" + }, "transaction_hash": { "type": "string" } diff --git a/disperser/dataapi/docs/swagger.json b/disperser/dataapi/docs/swagger.json index b425b25ec..654ac8073 100644 --- a/disperser/dataapi/docs/swagger.json +++ b/disperser/dataapi/docs/swagger.json @@ -988,12 +988,18 @@ "block_timestamp": { "type": "string" }, + "operator_address": { + "type": "string" + }, "operator_id": { "type": "string" }, "quorum": { "type": "integer" }, + "stake_percentage": { + "type": "number" + }, "transaction_hash": { "type": "string" } diff --git a/disperser/dataapi/docs/swagger.yaml b/disperser/dataapi/docs/swagger.yaml index 8844b4f6f..337ea4172 100644 --- a/disperser/dataapi/docs/swagger.yaml +++ b/disperser/dataapi/docs/swagger.yaml @@ -166,10 +166,14 @@ definitions: type: integer block_timestamp: type: string + operator_address: + type: string operator_id: type: string quorum: type: integer + stake_percentage: + type: number transaction_hash: type: string type: object diff --git a/disperser/dataapi/queried_operators_handlers.go b/disperser/dataapi/queried_operators_handlers.go index c983bdf72..030d5ffe2 100644 --- a/disperser/dataapi/queried_operators_handlers.go +++ b/disperser/dataapi/queried_operators_handlers.go @@ -3,8 +3,10 @@ package dataapi import ( "context" "fmt" + "math/big" "net" "sort" + "strings" "time" "github.com/Layr-Labs/eigenda/core" @@ -106,6 +108,57 @@ func (s *server) getOperatorEjections(ctx context.Context, days int32, operatorI return nil, err } + stateCache := make(map[uint64]*core.OperatorState) + ejectedOperatorIds := make(map[core.OperatorID]struct{}) + for _, ejection := range operatorEjections { + previouseBlock := ejection.BlockNumber - 1 + if _, exists := stateCache[previouseBlock]; !exists { + state, err := s.chainState.GetOperatorState(context.Background(), uint(previouseBlock), []uint8{0, 1}) + if err != nil { + return nil, err + } + stateCache[previouseBlock] = state + } + + // construct a set of ejected operator ids for later batch address lookup + opID, err := core.OperatorIDFromHex(ejection.OperatorId) + if err != nil { + return nil, err + } + ejectedOperatorIds[opID] = struct{}{} + } + + // resolve operator id to operator addresses mapping + operatorIDs := make([]core.OperatorID, 0, len(ejectedOperatorIds)) + for opID := range ejectedOperatorIds { + operatorIDs = append(operatorIDs, opID) + } + operatorAddresses, err := s.transactor.BatchOperatorIDToAddress(ctx, operatorIDs) + if err != nil { + return nil, err + } + operatorIdToAddress := make(map[string]string) + for i := range operatorAddresses { + operatorIdToAddress["0x"+operatorIDs[i].Hex()] = strings.ToLower(operatorAddresses[i].Hex()) + } + + for _, ejection := range operatorEjections { + state := stateCache[ejection.BlockNumber-1] + opID, err := core.OperatorIDFromHex(ejection.OperatorId) + if err != nil { + return nil, err + } + + stakePercentage := float64(0) + if stake, ok := state.Operators[ejection.Quorum][opID]; ok { + totalStake := new(big.Float).SetInt(state.Totals[ejection.Quorum].Stake) + operatorStake := new(big.Float).SetInt(stake.Stake) + stakePercentage, _ = new(big.Float).Mul(big.NewFloat(100), new(big.Float).Quo(operatorStake, totalStake)).Float64() + } + ejection.StakePercentage = stakePercentage + ejection.OperatorAddress = operatorIdToAddress[ejection.OperatorId] + } + s.logger.Info("Get operator ejections", "days", days, "operatorId", operatorId, "len", len(operatorEjections), "duration", time.Since(startTime)) return operatorEjections, nil } diff --git a/disperser/dataapi/server.go b/disperser/dataapi/server.go index a4113d44c..9a622f19a 100644 --- a/disperser/dataapi/server.go +++ b/disperser/dataapi/server.go @@ -147,11 +147,13 @@ type ( } QueriedOperatorEjections struct { - OperatorId string `json:"operator_id"` - Quorum uint8 `json:"quorum"` - BlockNumber uint64 `json:"block_number"` - BlockTimestamp string `json:"block_timestamp"` - TransactionHash string `json:"transaction_hash"` + OperatorId string `json:"operator_id"` + OperatorAddress string `json:"operator_address"` + Quorum uint8 `json:"quorum"` + BlockNumber uint64 `json:"block_number"` + BlockTimestamp string `json:"block_timestamp"` + TransactionHash string `json:"transaction_hash"` + StakePercentage float64 `json:"stake_percentage"` } QueriedOperatorEjectionsResponse struct { Ejections []*QueriedOperatorEjections `json:"ejections"` diff --git a/tools/ejections/Makefile b/tools/ejections/Makefile new file mode 100644 index 000000000..f971cf35f --- /dev/null +++ b/tools/ejections/Makefile @@ -0,0 +1,9 @@ +build: clean + go mod tidy + go build -o ./bin/ejections ./cmd + +clean: + rm -rf ./bin + +run: build + ./bin/ejections --help diff --git a/tools/ejections/cmd/main.go b/tools/ejections/cmd/main.go new file mode 100644 index 000000000..295c631f9 --- /dev/null +++ b/tools/ejections/cmd/main.go @@ -0,0 +1,166 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log" + "math/big" + "os" + "sort" + "strings" + + "github.com/Layr-Labs/eigenda/common" + "github.com/Layr-Labs/eigenda/common/geth" + "github.com/Layr-Labs/eigenda/core" + "github.com/Layr-Labs/eigenda/core/eth" + "github.com/Layr-Labs/eigenda/disperser/dataapi" + "github.com/Layr-Labs/eigenda/disperser/dataapi/subgraph" + "github.com/Layr-Labs/eigenda/tools/ejections" + "github.com/Layr-Labs/eigenda/tools/ejections/flags" + "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" + "github.com/urfave/cli" + + gethcommon "github.com/ethereum/go-ethereum/common" +) + +var ( + version = "1.0.0" + gitCommit = "" + gitDate = "" +) + +func main() { + app := cli.NewApp() + app.Version = fmt.Sprintf("%s,%s,%s", version, gitCommit, gitDate) + app.Name = "ejections report" + app.Description = "operator ejections report" + app.Usage = "" + app.Flags = flags.Flags + app.Action = RunScan + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } +} + +func RunScan(ctx *cli.Context) error { + config, err := ejections.NewConfig(ctx) + if err != nil { + return err + } + + logger, err := common.NewLogger(config.LoggerConfig) + if err != nil { + return err + } + + client, err := geth.NewMultiHomingClient(config.EthClientConfig, gethcommon.Address{}, logger) + if err != nil { + return err + } + + tx, err := eth.NewTransactor(logger, client, config.BLSOperatorStateRetrieverAddr, config.EigenDAServiceManagerAddr) + if err != nil { + return err + } + + chainState := eth.NewChainState(tx, client) + if chainState == nil { + return errors.New("failed to create chain state") + } + subgraphApi := subgraph.NewApi(config.SubgraphEndpoint, config.SubgraphEndpoint) + subgraphClient := dataapi.NewSubgraphClient(subgraphApi, logger) + + ejections, err := subgraphClient.QueryOperatorEjectionsForTimeWindow(context.Background(), int32(config.Days), config.OperatorId, config.First, config.Skip) + if err != nil { + logger.Warn("failed to fetch operator ejections", "operatorId", config.OperatorId, "error", err) + return errors.New("operator ejections not found") + } + + rowConfigAutoMerge := table.RowConfig{AutoMerge: true} + + operators := table.NewWriter() + operators.AppendHeader(table.Row{"Operator Address", "Quorum", "Stake %", "Timestamp", "Txn"}, rowConfigAutoMerge) + txns := table.NewWriter() + txns.AppendHeader(table.Row{"Txn", "Timestamp", "Operator Address", "Quorum", "Stake %"}, rowConfigAutoMerge) + + sort.Slice(ejections, func(i, j int) bool { + return ejections[i].BlockTimestamp > ejections[j].BlockTimestamp + }) + + stateCache := make(map[uint64]*core.OperatorState) + ejectedOperatorIds := make(map[core.OperatorID]struct{}) + for _, ejection := range ejections { + previouseBlock := ejection.BlockNumber - 1 + if _, exists := stateCache[previouseBlock]; !exists { + state, err := chainState.GetOperatorState(context.Background(), uint(previouseBlock), []uint8{0, 1}) + if err != nil { + return err + } + stateCache[previouseBlock] = state + } + + // construct a set of ejected operator ids for later batch address lookup + opID, err := core.OperatorIDFromHex(ejection.OperatorId) + if err != nil { + return err + } + ejectedOperatorIds[opID] = struct{}{} + } + + // resolve operator id to operator addresses mapping + operatorIDs := make([]core.OperatorID, 0, len(ejectedOperatorIds)) + for opID := range ejectedOperatorIds { + operatorIDs = append(operatorIDs, opID) + } + operatorAddresses, err := tx.BatchOperatorIDToAddress(context.Background(), operatorIDs) + if err != nil { + return err + } + operatorIdToAddress := make(map[string]string) + for i := range operatorAddresses { + operatorIdToAddress["0x"+operatorIDs[i].Hex()] = strings.ToLower(operatorAddresses[i].Hex()) + } + + for _, ejection := range ejections { + state := stateCache[ejection.BlockNumber-1] + opID, err := core.OperatorIDFromHex(ejection.OperatorId) + if err != nil { + return err + } + + stakePercentage := float64(0) + if stake, ok := state.Operators[ejection.Quorum][opID]; ok { + totalStake := new(big.Float).SetInt(state.Totals[ejection.Quorum].Stake) + operatorStake := new(big.Float).SetInt(stake.Stake) + stakePercentage, _ = new(big.Float).Mul(big.NewFloat(100), new(big.Float).Quo(operatorStake, totalStake)).Float64() + } + + operatorAddress := operatorIdToAddress[ejection.OperatorId] + operators.AppendRow(table.Row{operatorAddress, ejection.Quorum, stakePercentage, ejection.BlockTimestamp, ejection.TransactionHash}, rowConfigAutoMerge) + txns.AppendRow(table.Row{ejection.TransactionHash, ejection.BlockTimestamp, operatorAddress, ejection.Quorum, stakePercentage}, rowConfigAutoMerge) + } + + operators.SetAutoIndex(true) + operators.SetColumnConfigs([]table.ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, Align: text.AlignCenter}, + }) + operators.SetStyle(table.StyleLight) + operators.Style().Options.SeparateRows = true + + txns.SetAutoIndex(true) + txns.SetColumnConfigs([]table.ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, AutoMerge: true}, + {Number: 3, AutoMerge: true}, + {Number: 4, Align: text.AlignCenter}, + }) + txns.SetStyle(table.StyleLight) + txns.Style().Options.SeparateRows = true + + fmt.Println(operators.Render()) + fmt.Println(txns.Render()) + return nil +} diff --git a/tools/ejections/config.go b/tools/ejections/config.go new file mode 100644 index 000000000..4ea7b3b6f --- /dev/null +++ b/tools/ejections/config.go @@ -0,0 +1,46 @@ +package ejections + +import ( + "github.com/Layr-Labs/eigenda/common" + "github.com/Layr-Labs/eigenda/common/geth" + "github.com/Layr-Labs/eigenda/tools/ejections/flags" + "github.com/urfave/cli" +) + +type Config struct { + LoggerConfig common.LoggerConfig + Days int + OperatorId string + SubgraphEndpoint string + First uint + Skip uint + + EthClientConfig geth.EthClientConfig + BLSOperatorStateRetrieverAddr string + EigenDAServiceManagerAddr string +} + +func ReadConfig(ctx *cli.Context) *Config { + return &Config{ + Days: ctx.Int(flags.DaysFlag.Name), + OperatorId: ctx.String(flags.OperatorIdFlag.Name), + SubgraphEndpoint: ctx.String(flags.SubgraphEndpointFlag.Name), + First: ctx.Uint(flags.FirstFlag.Name), + Skip: ctx.Uint(flags.SkipFlag.Name), + EthClientConfig: geth.ReadEthClientConfig(ctx), + BLSOperatorStateRetrieverAddr: ctx.GlobalString(flags.BlsOperatorStateRetrieverFlag.Name), + EigenDAServiceManagerAddr: ctx.GlobalString(flags.EigenDAServiceManagerFlag.Name), + } +} + +func NewConfig(ctx *cli.Context) (*Config, error) { + loggerConfig, err := common.ReadLoggerCLIConfig(ctx, flags.FlagPrefix) + if err != nil { + return nil, err + } + + config := ReadConfig(ctx) + config.LoggerConfig = *loggerConfig + + return config, nil +} diff --git a/tools/ejections/flags/flags.go b/tools/ejections/flags/flags.go new file mode 100644 index 000000000..9cbbb37bd --- /dev/null +++ b/tools/ejections/flags/flags.go @@ -0,0 +1,84 @@ +package flags + +import ( + "github.com/Layr-Labs/eigenda/common" + "github.com/Layr-Labs/eigenda/common/geth" + "github.com/urfave/cli" +) + +const ( + FlagPrefix = "" + envPrefix = "" +) + +var ( + /* Required Flags*/ + SubgraphEndpointFlag = cli.StringFlag{ + Name: common.PrefixFlag(FlagPrefix, "subgraph"), + Usage: "Subgraph URL to query operator state", + Required: true, + EnvVar: common.PrefixEnvVar(envPrefix, "SUBGRAPH"), + } + OperatorIdFlag = cli.StringFlag{ + Name: common.PrefixFlag(FlagPrefix, "operator_id"), + Usage: "Query operator id", + Required: false, + EnvVar: common.PrefixEnvVar(envPrefix, "OPERATOR_ID"), + Value: "", + } + DaysFlag = cli.UintFlag{ + Name: common.PrefixFlag(FlagPrefix, "days"), + Usage: "Lookback days", + Required: false, + EnvVar: common.PrefixEnvVar(envPrefix, "DAYS"), + Value: 1, + } + FirstFlag = cli.UintFlag{ + Name: common.PrefixFlag(FlagPrefix, "first"), + Usage: "Return first n records (default 1000, max 10000)", + Required: false, + EnvVar: common.PrefixEnvVar(envPrefix, "FIRST"), + Value: 1000, + } + SkipFlag = cli.UintFlag{ + Name: common.PrefixFlag(FlagPrefix, "skip"), + Usage: "Skip first n records (default 0, max 1000000)", + Required: false, + EnvVar: common.PrefixEnvVar(envPrefix, "SKIP"), + Value: 0, + } + BlsOperatorStateRetrieverFlag = cli.StringFlag{ + Name: common.PrefixFlag(FlagPrefix, "bls-operator-state-retriever"), + Usage: "Address of the BLS Operator State Retriever", + Required: true, + EnvVar: common.PrefixEnvVar(envPrefix, "BLS_OPERATOR_STATE_RETRIVER"), + } + EigenDAServiceManagerFlag = cli.StringFlag{ + Name: common.PrefixFlag(FlagPrefix, "eigenda-service-manager"), + Usage: "Address of the EigenDA Service Manager", + Required: true, + EnvVar: common.PrefixEnvVar(envPrefix, "EIGENDA_SERVICE_MANAGER"), + } +) + +var requiredFlags = []cli.Flag{ + SubgraphEndpointFlag, + BlsOperatorStateRetrieverFlag, + EigenDAServiceManagerFlag, +} + +var optionalFlags = []cli.Flag{ + OperatorIdFlag, + DaysFlag, + FirstFlag, + SkipFlag, +} + +// Flags contains the list of configuration options available to the binary. +var Flags []cli.Flag + +func init() { + Flags = append(requiredFlags, optionalFlags...) + Flags = append(Flags, common.LoggerCLIFlags(envPrefix, FlagPrefix)...) + Flags = append(Flags, geth.EthClientFlags(envPrefix)...) +} From 385d07d7476a5405fb3141367494fd3cbf40ceab Mon Sep 17 00:00:00 2001 From: Patrick Schork <354473+pschork@users.noreply.github.com> Date: Thu, 24 Oct 2024 18:07:46 -0700 Subject: [PATCH 2/4] Fix rebase --- tools/ejections/cmd/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ejections/cmd/main.go b/tools/ejections/cmd/main.go index 295c631f9..31754e029 100644 --- a/tools/ejections/cmd/main.go +++ b/tools/ejections/cmd/main.go @@ -60,7 +60,7 @@ func RunScan(ctx *cli.Context) error { return err } - tx, err := eth.NewTransactor(logger, client, config.BLSOperatorStateRetrieverAddr, config.EigenDAServiceManagerAddr) + tx, err := eth.NewReader(logger, client, config.BLSOperatorStateRetrieverAddr, config.EigenDAServiceManagerAddr) if err != nil { return err } From 6fb398a657d5fbac0c611c5cf20cd0360ed2fcab Mon Sep 17 00:00:00 2001 From: Patrick Schork <354473+pschork@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:09:23 -0700 Subject: [PATCH 3/4] Add aggregate quorum stake % per ejection transaction --- tools/ejections/cmd/main.go | 64 +++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/tools/ejections/cmd/main.go b/tools/ejections/cmd/main.go index 31754e029..288ea5343 100644 --- a/tools/ejections/cmd/main.go +++ b/tools/ejections/cmd/main.go @@ -31,6 +31,14 @@ var ( gitDate = "" ) +type EjectionTransaction struct { + BlockNumber uint64 `json:"block_number"` + BlockTimestamp string `json:"block_timestamp"` + TransactionHash string `json:"transaction_hash"` + QuorumStakePercentage map[uint8]float64 `json:"stake_percentage"` + QuorumEjections map[uint8]uint8 `json:"ejections"` +} + func main() { app := cli.NewApp() app.Version = fmt.Sprintf("%s,%s,%s", version, gitCommit, gitDate) @@ -78,13 +86,6 @@ func RunScan(ctx *cli.Context) error { return errors.New("operator ejections not found") } - rowConfigAutoMerge := table.RowConfig{AutoMerge: true} - - operators := table.NewWriter() - operators.AppendHeader(table.Row{"Operator Address", "Quorum", "Stake %", "Timestamp", "Txn"}, rowConfigAutoMerge) - txns := table.NewWriter() - txns.AppendHeader(table.Row{"Txn", "Timestamp", "Operator Address", "Quorum", "Stake %"}, rowConfigAutoMerge) - sort.Slice(ejections, func(i, j int) bool { return ejections[i].BlockTimestamp > ejections[j].BlockTimestamp }) @@ -123,6 +124,16 @@ func RunScan(ctx *cli.Context) error { operatorIdToAddress["0x"+operatorIDs[i].Hex()] = strings.ToLower(operatorAddresses[i].Hex()) } + rowConfigAutoMerge := table.RowConfig{AutoMerge: true} + rowConfigNoAutoMerge := table.RowConfig{AutoMerge: false} + operators := table.NewWriter() + operators.AppendHeader(table.Row{"Operator Address", "Quorum", "Stake %", "Timestamp", "Txn"}, rowConfigAutoMerge) + txns := table.NewWriter() + txns.AppendHeader(table.Row{"Txn", "Timestamp", "Operator Address", "Quorum", "Stake %"}, rowConfigAutoMerge) + txnQuorums := table.NewWriter() + txnQuorums.AppendHeader(table.Row{"Txn", "Timestamp", "Quorum", "Stake %", "Operators"}, rowConfigNoAutoMerge) + + ejectionTransactions := make(map[string]*EjectionTransaction) for _, ejection := range ejections { state := stateCache[ejection.BlockNumber-1] opID, err := core.OperatorIDFromHex(ejection.OperatorId) @@ -137,11 +148,39 @@ func RunScan(ctx *cli.Context) error { stakePercentage, _ = new(big.Float).Mul(big.NewFloat(100), new(big.Float).Quo(operatorStake, totalStake)).Float64() } + if _, exists := ejectionTransactions[ejection.TransactionHash]; !exists { + ejectionTransactions[ejection.TransactionHash] = &EjectionTransaction{ + BlockNumber: ejection.BlockNumber, + BlockTimestamp: ejection.BlockTimestamp, + TransactionHash: ejection.TransactionHash, + QuorumStakePercentage: make(map[uint8]float64), + QuorumEjections: make(map[uint8]uint8), + } + ejectionTransactions[ejection.TransactionHash].QuorumStakePercentage[ejection.Quorum] = stakePercentage + ejectionTransactions[ejection.TransactionHash].QuorumEjections[ejection.Quorum] = 1 + } else { + ejectionTransactions[ejection.TransactionHash].QuorumStakePercentage[ejection.Quorum] += stakePercentage + ejectionTransactions[ejection.TransactionHash].QuorumEjections[ejection.Quorum] += 1 + } + operatorAddress := operatorIdToAddress[ejection.OperatorId] operators.AppendRow(table.Row{operatorAddress, ejection.Quorum, stakePercentage, ejection.BlockTimestamp, ejection.TransactionHash}, rowConfigAutoMerge) txns.AppendRow(table.Row{ejection.TransactionHash, ejection.BlockTimestamp, operatorAddress, ejection.Quorum, stakePercentage}, rowConfigAutoMerge) } + orderedEjectionTransactions := make([]*EjectionTransaction, 0, len(ejectionTransactions)) + for _, txn := range ejectionTransactions { + orderedEjectionTransactions = append(orderedEjectionTransactions, txn) + } + sort.Slice(orderedEjectionTransactions, func(i, j int) bool { + return orderedEjectionTransactions[i].BlockNumber > orderedEjectionTransactions[j].BlockNumber + }) + for _, txn := range orderedEjectionTransactions { + for quorum, ejections := range txn.QuorumEjections { + txnQuorums.AppendRow(table.Row{txn.TransactionHash, txn.BlockTimestamp, quorum, txn.QuorumStakePercentage[quorum], ejections}, rowConfigAutoMerge) + } + } + operators.SetAutoIndex(true) operators.SetColumnConfigs([]table.ColumnConfig{ {Number: 1, AutoMerge: true}, @@ -160,7 +199,18 @@ func RunScan(ctx *cli.Context) error { txns.SetStyle(table.StyleLight) txns.Style().Options.SeparateRows = true + txnQuorums.SetAutoIndex(true) + txnQuorums.SetColumnConfigs([]table.ColumnConfig{ + {Number: 1, AutoMerge: true}, + {Number: 2, AutoMerge: true, Align: text.AlignCenter}, + {Number: 3, Align: text.AlignCenter}, + {Number: 5, Align: text.AlignCenter}, + }) + txnQuorums.SetStyle(table.StyleLight) + txnQuorums.Style().Options.SeparateRows = true + fmt.Println(operators.Render()) fmt.Println(txns.Render()) + fmt.Println(txnQuorums.Render()) return nil } From 6cf0f9114657fba4afa0fefc2004b26e82c9f9bb Mon Sep 17 00:00:00 2001 From: Patrick Schork <354473+pschork@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:37:28 -0700 Subject: [PATCH 4/4] Derive and sort quorum slice from ejections --- .../dataapi/queried_operators_handlers.go | 15 ++++++++++++- tools/ejections/cmd/main.go | 21 ++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/disperser/dataapi/queried_operators_handlers.go b/disperser/dataapi/queried_operators_handlers.go index 030d5ffe2..a5e7bd5c5 100644 --- a/disperser/dataapi/queried_operators_handlers.go +++ b/disperser/dataapi/queried_operators_handlers.go @@ -108,12 +108,25 @@ func (s *server) getOperatorEjections(ctx context.Context, days int32, operatorI return nil, err } + // create a sorted slice from the set of quorums + quorumSet := make(map[uint8]struct{}) + for _, ejection := range operatorEjections { + quorumSet[ejection.Quorum] = struct{}{} + } + quorums := make([]uint8, 0, len(quorumSet)) + for quorum := range quorumSet { + quorums = append(quorums, quorum) + } + sort.Slice(quorums, func(i, j int) bool { + return quorums[i] < quorums[j] + }) + stateCache := make(map[uint64]*core.OperatorState) ejectedOperatorIds := make(map[core.OperatorID]struct{}) for _, ejection := range operatorEjections { previouseBlock := ejection.BlockNumber - 1 if _, exists := stateCache[previouseBlock]; !exists { - state, err := s.chainState.GetOperatorState(context.Background(), uint(previouseBlock), []uint8{0, 1}) + state, err := s.chainState.GetOperatorState(context.Background(), uint(previouseBlock), quorums) if err != nil { return nil, err } diff --git a/tools/ejections/cmd/main.go b/tools/ejections/cmd/main.go index 288ea5343..a38ac6460 100644 --- a/tools/ejections/cmd/main.go +++ b/tools/ejections/cmd/main.go @@ -90,12 +90,25 @@ func RunScan(ctx *cli.Context) error { return ejections[i].BlockTimestamp > ejections[j].BlockTimestamp }) + // Create a sorted slice from the set of quorums + quorumSet := make(map[uint8]struct{}) + for _, ejection := range ejections { + quorumSet[ejection.Quorum] = struct{}{} + } + quorums := make([]uint8, 0, len(quorumSet)) + for quorum := range quorumSet { + quorums = append(quorums, quorum) + } + sort.Slice(quorums, func(i, j int) bool { + return quorums[i] < quorums[j] + }) + stateCache := make(map[uint64]*core.OperatorState) ejectedOperatorIds := make(map[core.OperatorID]struct{}) for _, ejection := range ejections { previouseBlock := ejection.BlockNumber - 1 if _, exists := stateCache[previouseBlock]; !exists { - state, err := chainState.GetOperatorState(context.Background(), uint(previouseBlock), []uint8{0, 1}) + state, err := chainState.GetOperatorState(context.Background(), uint(previouseBlock), quorums) if err != nil { return err } @@ -176,8 +189,10 @@ func RunScan(ctx *cli.Context) error { return orderedEjectionTransactions[i].BlockNumber > orderedEjectionTransactions[j].BlockNumber }) for _, txn := range orderedEjectionTransactions { - for quorum, ejections := range txn.QuorumEjections { - txnQuorums.AppendRow(table.Row{txn.TransactionHash, txn.BlockTimestamp, quorum, txn.QuorumStakePercentage[quorum], ejections}, rowConfigAutoMerge) + for _, quorum := range quorums { + if _, exists := txn.QuorumEjections[quorum]; exists { + txnQuorums.AppendRow(table.Row{txn.TransactionHash, txn.BlockTimestamp, quorum, txn.QuorumStakePercentage[quorum], txn.QuorumEjections[quorum]}, rowConfigAutoMerge) + } } }