diff --git a/params/forks/forkreg.go b/params/forks/forkreg.go index 5598e6ff64..d5b140e046 100644 --- a/params/forks/forkreg.go +++ b/params/forks/forkreg.go @@ -75,6 +75,43 @@ func (f *Fork) MarshalText() ([]byte, error) { return []byte(f.configName), nil } +// DependencyOrder computes an ordering of the given forks, according to their dependencies. +// Note the resulting ordering may not be unique! +func DependencyOrder(list []Fork) []Fork { + var ( + inList = make(map[Fork]bool, len(list)) + visiting = make(map[Fork]bool, len(list)) + mark = make(map[Fork]bool, len(list)) + result = make([]Fork, 0, len(list)) + ) + for _, f := range list { + inList[f] = true + } + + var visit func(Fork) + visit = func(f Fork) { + if mark[f] { + return + } + if visiting[f] { + // This can't happen because we check for cycles at definition time. + panic("fork dependency cycle") + } + visiting[f] = true + for _, dep := range f.directDeps { + visit(dep) + } + mark[f] = true + if inList[f] { + result = append(result, f) + } + } + for _, l := range list { + visit(l) + } + return result +} + type forkProperties struct { name string configName string diff --git a/params/forks/forkreg_test.go b/params/forks/forkreg_test.go index 1bbe47ff66..850296e299 100644 --- a/params/forks/forkreg_test.go +++ b/params/forks/forkreg_test.go @@ -17,6 +17,7 @@ package forks import ( + "slices" "testing" "github.com/stretchr/testify/assert" @@ -48,3 +49,29 @@ func TestForkName(t *testing.T) { t.Fatal("wrong fork found by name cancun") } } + +func TestForkDependencyOrder(t *testing.T) { + tests := []struct { + list, result []Fork + }{ + { + list: []Fork{}, + result: []Fork{}, + }, + { + list: []Fork{BPO2, Homestead, Cancun, London, Paris}, + result: []Fork{Homestead, London, Paris, Cancun, BPO2}, + }, + { + list: []Fork{BPO3, Osaka, Cancun, Prague, BPO1, BPO2}, + result: []Fork{Cancun, Prague, Osaka, BPO1, BPO2, BPO3}, + }, + } + + for _, test := range tests { + res := DependencyOrder(test.list) + if !slices.Equal(res, test.result) { + t.Errorf("DependencyOrder(%v) -> %v\n want: %v", test.list, res, test.result) + } + } +}