erigon-pulse/cmd/devnet/scenarios/scenario.go
Mark Holt 529d359ca6
Bor span testing (#7897)
An update to the devnet to introduce a local heimdall to facilitate
multiple validators without the need for an external process, and hence
validator registration/staking etc.

In this initial release only span generation is supported.  

It has the following changes:

* Introduction of a local grpc heimdall interface
* Allocation of accounts via a devnet account generator ()
* Introduction on 'Services' for the network config

"--chain bor-devnet --bor.localheimdall" will run a 2 validator network
with a local service
"--chain bor-devnet --bor.withoutheimdall" will sun a single validator
with no heimdall service as before

---------

Co-authored-by: Alex Sharp <alexsharp@Alexs-MacBook-Pro-2.local>
2023-07-18 09:47:04 +01:00

179 lines
3.9 KiB
Go

package scenarios
import (
"context"
"errors"
"fmt"
"path"
"reflect"
"regexp"
"runtime"
"unicode"
"github.com/ledgerwatch/erigon/cmd/devnet/devnet"
"github.com/ledgerwatch/log/v3"
)
var (
ErrUnmatchedStepArgumentNumber = errors.New("func received more arguments than expected")
ErrCannotConvert = errors.New("cannot convert argument")
ErrUnsupportedArgumentType = errors.New("unsupported argument type")
)
var stepRunnerRegistry = map[reflect.Value]*stepRunner{}
type stepHandler struct {
handler reflect.Value
matchExpressions []string
}
func RegisterStepHandlers(handlers ...stepHandler) error {
for _, h := range handlers {
var exprs []*regexp.Regexp
if kind := h.handler.Kind(); kind != reflect.Func {
return fmt.Errorf("Can't register non-function %s as step handler", kind)
}
if len(h.matchExpressions) == 0 {
name := path.Ext(runtime.FuncForPC(h.handler.Pointer()).Name())[1:]
if unicode.IsLower(rune(name[0])) {
return fmt.Errorf("Can't register unexported function %s as step handler", name)
}
h.matchExpressions = []string{
name,
}
}
for _, e := range h.matchExpressions {
exp, err := regexp.Compile(e)
if err != nil {
return err
}
exprs = append(exprs, exp)
}
stepRunnerRegistry[h.handler] = &stepRunner{
Handler: h.handler,
Exprs: exprs,
}
}
return nil
}
func MustRegisterStepHandlers(handlers ...stepHandler) {
if err := RegisterStepHandlers(handlers...); err != nil {
panic(fmt.Errorf("Step handler registration failed: %w", err))
}
}
func StepHandler(handler interface{}, matchExpressions ...string) stepHandler {
return stepHandler{reflect.ValueOf(handler), matchExpressions}
}
type Scenario struct {
Context devnet.Context `json:"-"`
Id string `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Steps []*Step `json:"steps"`
}
type Step struct {
Id string `json:"id"`
Args []interface{} `json:"args,omitempty"`
Text string `json:"text"`
Description string `json:"description,omitempty"`
}
type stepRunner struct {
Exprs []*regexp.Regexp
Handler reflect.Value
// multistep related
Nested bool
Undefined []string
}
var typeOfBytes = reflect.TypeOf([]byte(nil))
var typeOfContext = reflect.TypeOf((*context.Context)(nil)).Elem()
func (c *stepRunner) Run(ctx context.Context, text string, args []interface{}, logger log.Logger) (context.Context, interface{}) {
var values = make([]reflect.Value, 0, len(args))
typ := c.Handler.Type()
numIn := typ.NumIn()
hasCtxIn := numIn > 0 && typ.In(0).Implements(typeOfContext)
if hasCtxIn {
values = append(values, reflect.ValueOf(ctx))
numIn--
}
if len(args) < numIn {
return ctx, fmt.Errorf("Expected %d arguments, matched %d from step", typ.NumIn(), len(args))
}
for _, arg := range args {
values = append(values, reflect.ValueOf(arg))
}
logger.Info("Calling step: "+text, "handler", c.Handler, "args", args)
res := c.Handler.Call(values)
if len(res) == 0 {
return ctx, nil
}
r := res[0].Interface()
if rctx, ok := r.(context.Context); ok {
if len(res) == 1 {
return rctx, nil
}
res = res[1:]
ctx = rctx
}
if len(res) == 1 {
return ctx, res[0].Interface()
}
var results = make([]interface{}, 0, len(res))
for _, value := range res {
results = append(results, value.Interface())
}
return ctx, results
}
type Scenarios map[string]*Scenario
func (s Scenarios) Run(ctx context.Context, scenarioNames ...string) error {
var scenarios []*Scenario
if len(scenarioNames) == 0 {
for name, scenario := range s {
scenario.Name = name
scenarios = append(scenarios, scenario)
}
} else {
for _, name := range scenarioNames {
if scenario, ok := s[name]; ok {
scenario.Name = name
scenarios = append(scenarios, scenario)
}
}
}
return Run(ctx, scenarios...)
}