Skip to content

chore: use catalyst types instead of duplicated internal types #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 17 additions & 127 deletions activities/loadtest/loadtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/skip-mev/catalyst/pkg/types"
"sync"
"time"

Expand All @@ -22,132 +23,21 @@ import (
"gopkg.in/yaml.v3"
)

type MsgType string

// LoadTestResult represents the results of a load test
type LoadTestResult struct {
Overall OverallStats
ByMessage map[MsgType]MessageStats
ByNode map[string]NodeStats
ByBlock []BlockStat
Error string `json:"error,omitempty"`
}

// OverallStats represents the overall statistics of the load test
type OverallStats struct {
TotalTransactions int
SuccessfulTransactions int
FailedTransactions int
AvgGasPerTransaction int64
AvgBlockGasUtilization float64
Runtime time.Duration
StartTime time.Time
EndTime time.Time
BlocksProcessed int
}

// MessageStats represents statistics for a specific message type
type MessageStats struct {
Transactions TransactionStats
Gas GasStats
Errors ErrorStats
}

// TransactionStats represents transaction-related statistics
type TransactionStats struct {
Total int
Successful int
Failed int
}

// GasStats represents gas-related statistics
type GasStats struct {
Average int64
Min int64
Max int64
Total int64
}

// ErrorStats represents error-related statistics
type ErrorStats struct {
BroadcastErrors []BroadcastError
ErrorCounts map[string]int // Error type to count
}

// NodeStats represents statistics for a specific node
type NodeStats struct {
Address string
TransactionStats TransactionStats
MessageCounts map[MsgType]int
GasStats GasStats
}

// BlockStat represents statistics for a specific block
type BlockStat struct {
BlockHeight int64
Timestamp time.Time
GasLimit int
TotalGasUsed int64
MessageStats map[MsgType]MessageBlockStats
GasUtilization float64
}

// MessageBlockStats represents message-specific statistics within a block
type MessageBlockStats struct {
TransactionsSent int
SuccessfulTxs int
FailedTxs int
GasUsed int64
}

// BroadcastError represents errors during broadcasting transactions
type BroadcastError struct {
BlockHeight int64 // Block height where the error occurred (0 indicates tx did not make it to a block)
TxHash string // Hash of the transaction that failed
Error string // Error message
MsgType MsgType // Type of message that failed
NodeAddress string // Address of the node that returned the error
}

type PackagedState struct {
ProviderState []byte
ChainState []byte
Result LoadTestResult
}

type LoadTestConfig struct {
ChainID string `yaml:"chain_id"`
BlockGasLimitTarget float64 `yaml:"block_gas_limit_target,omitempty"`
NumOfTxs int `yaml:"num_of_txs,omitempty"`
NumOfBlocks int `yaml:"num_of_blocks"`
NodesAddresses []Node `yaml:"nodes_addresses"`
Mnemonics []string `yaml:"mnemonics"`
GasDenom string `yaml:"gas_denom"`
Bech32Prefix string `yaml:"bech32_prefix"`
Msgs []Message `yaml:"msgs"`
}

type Node struct {
GRPC string `yaml:"grpc"`
RPC string `yaml:"rpc"`
}

type Message struct {
Type string `yaml:"type" json:"type"`
Weight float64 `yaml:"weight" json:"weight"`
NumMsgs int `yaml:"num_msgs,omitempty" json:"NumMsgs,omitempty"`
ContainedType MsgType `yaml:"contained_type,omitempty" json:"ContainedType,omitempty"`
NumOfRecipients int `yaml:"num_of_recipients,omitempty" json:"NumOfRecipients,omitempty"` // Number of recipients to include for MsgMultiSend
Result types.LoadTestResult
}

type Activity struct {
DOToken string
TailscaleSettings digitalocean.TailscaleSettings
}

func generateLoadTestConfig(ctx context.Context, logger *zap.Logger, chain *chain.Chain, chainID string, loadTestConfig *LoadTestConfig) ([]byte, error) {
func generateLoadTestSpec(ctx context.Context, logger *zap.Logger, chain *chain.Chain, chainID string,
loadTestSpec *types.LoadTestSpec) ([]byte, error) {
validators := chain.GetValidators()
var nodes []Node
var nodes []types.NodeAddress
for _, v := range validators {
grpcAddr, err := v.GetIP(ctx)
grpcAddr = grpcAddr + ":9090"
Expand All @@ -161,7 +51,7 @@ func generateLoadTestConfig(ctx context.Context, logger *zap.Logger, chain *chai
return nil, err
}

nodes = append(nodes, Node{
nodes = append(nodes, types.NodeAddress{
GRPC: grpcAddr,
RPC: fmt.Sprintf("http://%s", rpcAddr),
})
Expand Down Expand Up @@ -227,20 +117,20 @@ func generateLoadTestConfig(ctx context.Context, logger *zap.Logger, chain *chai
}
time.Sleep(5 * time.Second)

config := LoadTestConfig{
config := types.LoadTestSpec{
ChainID: chainID,
NumOfBlocks: loadTestConfig.NumOfBlocks,
NumOfBlocks: loadTestSpec.NumOfBlocks,
NodesAddresses: nodes,
Mnemonics: mnemonics,
GasDenom: chain.GetConfig().Denom,
Bech32Prefix: chain.GetConfig().Bech32Prefix,
Msgs: loadTestConfig.Msgs,
Msgs: loadTestSpec.Msgs,
}

if loadTestConfig.NumOfTxs > 0 {
config.NumOfTxs = loadTestConfig.NumOfTxs
} else if loadTestConfig.BlockGasLimitTarget > 0 {
config.BlockGasLimitTarget = loadTestConfig.BlockGasLimitTarget
if loadTestSpec.NumOfTxs > 0 {
config.NumOfTxs = loadTestSpec.NumOfTxs
} else if loadTestSpec.BlockGasLimitTarget > 0 {
config.BlockGasLimitTarget = loadTestSpec.BlockGasLimitTarget
} else {
return nil, fmt.Errorf("failed to generate load test config, either BlockGasLimitTarget or NumOfTxs must be provided")
}
Expand All @@ -250,7 +140,7 @@ func generateLoadTestConfig(ctx context.Context, logger *zap.Logger, chain *chai
}

func (a *Activity) RunLoadTest(ctx context.Context, chainState []byte,
loadTestConfig *LoadTestConfig, runnerType string, providerState []byte) (PackagedState, error) {
loadTestSpec *types.LoadTestSpec, runnerType string, providerState []byte) (PackagedState, error) {
logger, _ := zap.NewDevelopment()

var p provider.ProviderI
Expand Down Expand Up @@ -280,7 +170,7 @@ func (a *Activity) RunLoadTest(ctx context.Context, chainState []byte,
return PackagedState{}, err
}

configBytes, err := generateLoadTestConfig(ctx, logger, chain, chain.GetConfig().ChainId, loadTestConfig)
configBytes, err := generateLoadTestSpec(ctx, logger, chain, chain.GetConfig().ChainId, loadTestSpec)
if err != nil {
return PackagedState{}, err
}
Expand All @@ -289,7 +179,7 @@ func (a *Activity) RunLoadTest(ctx context.Context, chainState []byte,
Name: "catalyst",
ContainerName: "catalyst",
Image: provider.ImageDefinition{
Image: "ghcr.io/skip-mev/catalyst:latest",
Image: "ghcr.io/skip-mev/catalyst:dev",
UID: "100",
GID: "100",
},
Expand Down Expand Up @@ -340,7 +230,7 @@ func (a *Activity) RunLoadTest(ctx context.Context, chainState []byte,
return PackagedState{}, fmt.Errorf("failed to read result file: %w", err)
}

var result LoadTestResult
var result types.LoadTestResult
if err := json.Unmarshal(resultBytes, &result); err != nil {
return PackagedState{}, fmt.Errorf("failed to parse result file: %w", err)
}
Expand Down
52 changes: 13 additions & 39 deletions app/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
catalyst_types "github.com/skip-mev/catalyst/pkg/types"
"regexp"
"strings"

Expand Down Expand Up @@ -90,7 +91,8 @@ func (a *App) generatedFailedCommandComment(command string, err error) (string,
return mdOut.String(), nil
}

func (a *App) generateStartedTestComment(chainConfig types.ChainsConfig, loadTestConfig types.LoadTestConfig, workflowId, runId string, runnerType testnettypes.RunnerType) (string, error) {
func (a *App) generateStartedTestComment(chainConfig types.ChainsConfig, loadTestSpec catalyst_types.LoadTestSpec,
workflowId, runId string, runnerType testnettypes.RunnerType) (string, error) {
var mdOut bytes.Buffer
var chainDetails bytes.Buffer
var loadTestDetails bytes.Buffer
Expand All @@ -107,14 +109,14 @@ func (a *App) generateStartedTestComment(chainConfig types.ChainsConfig, loadTes
}

loadTestMd := markdown.NewMarkdown(&loadTestDetails)
loadTestMd = loadTestMd.LF().PlainText(fmt.Sprintf("Load test config: `%v`", loadTestConfig)).LF()
loadTestMd = loadTestMd.LF().PlainText(fmt.Sprintf("Load test config: `%v`", loadTestSpec)).LF()

if err := loadTestMd.Build(); err != nil {
return "", err
}

md := markdown.NewMarkdown(&mdOut)
md = md.PlainText(fmt.Sprintf("Ironbird has started a testnet for chain `%s` using loadtest `%s` with runner `%s`", chainConfig.Name, loadTestConfig.Name, runnerType)).LF()
md = md.PlainText(fmt.Sprintf("Ironbird has started a testnet for chain `%s` using loadtest `%s` with runner `%s`", chainConfig.Name, loadTestSpec.Name, runnerType)).LF()
md = md.Details("Chain details", chainDetails.String())
md = md.Details("Load test details", loadTestDetails.String())

Expand Down Expand Up @@ -179,43 +181,15 @@ func (a *App) HandleCommand(ctx context.Context, comment *Comment, command strin
return nil
}

func (a *App) parseCustomLoadTestConfig(yamlStr string) (*types.LoadTestConfig, error) {
var customConfig types.LoadTestConfig
func (a *App) parseCustomLoadTestSpec(yamlStr string) (*catalyst_types.LoadTestSpec, error) {
var customConfig catalyst_types.LoadTestSpec
if err := yaml.Unmarshal([]byte(yamlStr), &customConfig); err != nil {
return nil, fmt.Errorf("failed to parse custom load test config: %w", err)
}

if customConfig.Name == "" {
customConfig.Name = "custom"
}

if customConfig.BlockGasLimitTarget <= 0 && customConfig.NumOfTxs <= 0 {
return nil, fmt.Errorf("either block_gas_limit_target or num_of_txs must be set")
}

if customConfig.BlockGasLimitTarget > 0 && customConfig.NumOfTxs > 0 {
return nil, fmt.Errorf("only one of block_gas_limit_target or num_of_txs should be set, not both")
}

if customConfig.BlockGasLimitTarget > 1 {
return nil, fmt.Errorf("block gas limit target must be between 0 and 1, got %f", customConfig.BlockGasLimitTarget)
}

if customConfig.NumOfBlocks <= 0 {
return nil, fmt.Errorf("num_of_blocks must be greater than 0")
}

if len(customConfig.Msgs) == 0 {
return nil, fmt.Errorf("at least one message type must be specified")
}

totalWeight := 0.0
for _, msg := range customConfig.Msgs {
totalWeight += msg.Weight
}

if totalWeight != 1.0 {
return nil, fmt.Errorf("message weights must sum to exactly 1.0 (got %.2f)", totalWeight)
err := customConfig.Validate()
if err != nil {
return nil, fmt.Errorf("failed to validate custom load test config: %w", err)
}

return &customConfig, nil
Expand Down Expand Up @@ -299,9 +273,9 @@ func (a *App) commandStart(ctx context.Context, comment *Comment, command string
return fmt.Errorf("unknown chain %s", chainName)
}

var loadTest types.LoadTestConfig
var loadTest catalyst_types.LoadTestSpec
if hasCustomConfig {
customLoadTest, err := a.parseCustomLoadTestConfig(customConfig[1])
customLoadTest, err := a.parseCustomLoadTestSpec(customConfig[1])
if err != nil {
return err
}
Expand All @@ -325,7 +299,7 @@ func (a *App) commandStart(ctx context.Context, comment *Comment, command string
Repo: comment.Issue.Repo,
SHA: *pr.Head.SHA,
ChainConfig: chain,
LoadTestConfig: &loadTest,
LoadTestSpec: &loadTest,
RunnerType: runnerType,
})

Expand Down
Loading
Loading