erigon-pulse/cmd/devnet/scenarios/run.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

125 lines
2.3 KiB
Go

package scenarios
import (
"context"
"sync"
"github.com/ledgerwatch/erigon/cmd/devnet/devnetutils"
)
type SimulationInitializer func(*SimulationContext)
func Run(ctx context.Context, scenarios ...*Scenario) error {
if len(scenarios) == 0 {
return nil
}
return runner{scenarios: scenarios}.runWithOptions(ctx, getDefaultOptions())
}
type runner struct {
randomize bool
stopOnFailure bool
scenarios []*Scenario
simulationInitializer SimulationInitializer
}
func (r *runner) concurrent(ctx context.Context, rate int) (err error) {
var copyLock sync.Mutex
queue := make(chan int, rate)
scenarios := make([]*Scenario, len(r.scenarios))
if r.randomize {
for i := range r.scenarios {
j := devnetutils.RandomInt(i + 1)
scenarios[i] = r.scenarios[j]
}
} else {
copy(scenarios, r.scenarios)
}
simulationContext := SimulationContext{
suite: &suite{
randomize: r.randomize,
defaultContext: ctx,
stepRunners: stepRunners(ctx),
},
}
for i, s := range scenarios {
scenario := *s
queue <- i // reserve space in queue
runScenario := func(err *error, Scenario *Scenario) {
defer func() {
<-queue // free a space in queue
}()
if r.stopOnFailure && *err != nil {
return
}
// Copy base suite.
suite := *simulationContext.suite
if r.simulationInitializer != nil {
sc := SimulationContext{suite: &suite}
r.simulationInitializer(&sc)
}
_, serr := suite.runScenario(&scenario)
if suite.shouldFail(serr) {
copyLock.Lock()
*err = serr
copyLock.Unlock()
}
}
if rate == 1 {
// Running within the same goroutine for concurrency 1
// to preserve original stacks and simplify debugging.
runScenario(&err, &scenario)
} else {
go runScenario(&err, &scenario)
}
}
// wait until last are processed
for i := 0; i < rate; i++ {
queue <- i
}
close(queue)
return err
}
func (runner runner) runWithOptions(ctx context.Context, opt *Options) error {
//var output io.Writer = os.Stdout
//if nil != opt.Output {
// output = opt.Output
//}
if opt.Concurrency < 1 {
opt.Concurrency = 1
}
return runner.concurrent(ctx, opt.Concurrency)
}
type Options struct {
Concurrency int
}
func getDefaultOptions() *Options {
opt := &Options{
Concurrency: 1,
}
return opt
}