This repository has been archived by the owner on Apr 10, 2024. It is now read-only.
forked from erigontech/erigon
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added reverse beacon changeset for beacon state rewind (erigontech#7185)
Added changesets for beacon chain to implement memory efficient fork choice
- Loading branch information
1 parent
9c3cb6e
commit 1879a4c
Showing
32 changed files
with
1,034 additions
and
202 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package beacon_changeset | ||
|
||
import ( | ||
"sort" | ||
) | ||
|
||
type ListChangeSet[T any] struct { | ||
list []*listElementChangeset[T] | ||
listLength int | ||
nextId int | ||
compact bool | ||
} | ||
|
||
type listElementChangeset[T any] struct { | ||
value T | ||
listIndex int | ||
id int | ||
} | ||
|
||
// NewListChangeSet creates new list with given length. | ||
func NewListChangeSet[T any](length int) *ListChangeSet[T] { | ||
return &ListChangeSet[T]{listLength: length} | ||
} | ||
|
||
// AddChange appens to a new change to the changeset of the list. | ||
func (l *ListChangeSet[T]) AddChange(index int, elem T) { | ||
l.compact = false | ||
l.list = append(l.list, &listElementChangeset[T]{ | ||
value: elem, | ||
listIndex: index, | ||
id: l.nextId, | ||
}) | ||
l.nextId++ | ||
} | ||
|
||
// CompactChanges removes duplicates from a list using QuickSort and linear scan. | ||
// duplicates may appear if one state parameter is changed more than once. | ||
func (l *ListChangeSet[T]) CompactChanges() { | ||
if l.compact { | ||
return | ||
} | ||
l.compact = true | ||
// Check if there are any duplicates to remove. | ||
if len(l.list) < 2 { | ||
return | ||
} | ||
|
||
// Sort the list using QuickSort. | ||
sort.Slice(l.list, func(i, j int) bool { | ||
if l.list[i].listIndex == l.list[j].listIndex { | ||
return l.list[i].id < l.list[j].id | ||
} | ||
return l.list[i].listIndex < l.list[j].listIndex | ||
}) | ||
|
||
// Create a new list buffer for the compacted list. | ||
compactList := []*listElementChangeset[T]{} | ||
|
||
// Do a linear scan through the sorted list and remove duplicates. | ||
previousIndexElement := l.list[0] | ||
for _, listElement := range l.list { | ||
if listElement.listIndex != previousIndexElement.listIndex { | ||
compactList = append(compactList, previousIndexElement) | ||
} | ||
previousIndexElement = listElement | ||
} | ||
compactList = append(compactList, previousIndexElement) | ||
|
||
// Update the original list with the compacted list. | ||
l.list = compactList | ||
} | ||
|
||
// CompactChangesReverse removes duplicates from a list using QuickSort and linear scan. | ||
// duplicates may appear if one state parameter is changed more than once. | ||
// Difference with CompactChanges is that the sorting is reversed. | ||
func (l *ListChangeSet[T]) CompactChangesReverse() { | ||
if l.compact { | ||
return | ||
} | ||
l.compact = true | ||
// Check if there are any duplicates to remove. | ||
if len(l.list) < 2 { | ||
return | ||
} | ||
|
||
// Sort the list using QuickSort. | ||
sort.Slice(l.list, func(i, j int) bool { | ||
if l.list[i].listIndex == l.list[j].listIndex { | ||
return l.list[i].id > l.list[j].id | ||
} | ||
return l.list[i].listIndex < l.list[j].listIndex | ||
}) | ||
|
||
// Create a new list buffer for the compacted list. | ||
compactList := []*listElementChangeset[T]{} | ||
|
||
// Do a linear scan through the sorted list and remove duplicates. | ||
previousIndexElement := l.list[0] | ||
for _, listElement := range l.list { | ||
if listElement.listIndex != previousIndexElement.listIndex { | ||
compactList = append(compactList, previousIndexElement) | ||
} | ||
previousIndexElement = listElement | ||
} | ||
compactList = append(compactList, previousIndexElement) | ||
// Update the original list with the compacted list. | ||
l.list = compactList | ||
} | ||
|
||
// ApplyChanges Apply changes without any mercy. if it is reverse, you need to call CompactChangesReverse before. | ||
func (l *ListChangeSet[T]) ApplyChanges(input []T) (output []T, changed bool) { | ||
if len(l.list) == 0 && l.listLength == len(input) { | ||
output = input | ||
return | ||
} | ||
changed = true | ||
// Re-adjust list size. | ||
output = make([]T, l.listLength) | ||
copy(output, input) | ||
// Now apply changes to the given list | ||
for _, elem := range l.list { | ||
if elem.listIndex >= len(output) { | ||
continue | ||
} | ||
output[elem.listIndex] = elem.value | ||
} | ||
return | ||
} | ||
|
||
// ChangesWithHandler uses custom handler to handle changes. | ||
func (l *ListChangeSet[T]) ChangesWithHandler(fn func(value T, index int)) { | ||
// Now apply changes to the given list | ||
for _, elem := range l.list { | ||
fn(elem.value, elem.listIndex) | ||
} | ||
} | ||
|
||
// ListLength return full list length | ||
func (l *ListChangeSet[T]) ListLength() int { | ||
return l.listLength | ||
} | ||
|
||
// Empty return whether current list diff is empty | ||
func (l *ListChangeSet[T]) Empty() bool { | ||
return len(l.list) == 0 | ||
} |
45 changes: 45 additions & 0 deletions
45
cmd/erigon-cl/core/beacon_changeset/list_changeset_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package beacon_changeset | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestListChangeset(t *testing.T) { | ||
pre := []int{6, 8, 9} | ||
changeset := NewListChangeSet[int](3) | ||
changeset.AddChange(1, 45) | ||
changeset.AddChange(1, 1) | ||
changeset.AddChange(2, 45) | ||
changeset.CompactChanges() | ||
require.Equal(t, len(changeset.list), 2) | ||
post, changed := changeset.ApplyChanges(pre) | ||
require.Equal(t, post, []int{6, 1, 45}) | ||
require.Equal(t, changed, true) | ||
} | ||
|
||
func TestListChangesetWithReverse(t *testing.T) { | ||
pre := []int{6, 8, 9} | ||
changeset := NewListChangeSet[int](3) | ||
changeset.AddChange(1, 45) | ||
changeset.AddChange(1, 1) | ||
changeset.AddChange(2, 45) | ||
changeset.CompactChangesReverse() | ||
require.Equal(t, len(changeset.list), 2) | ||
post, changed := changeset.ApplyChanges(pre) | ||
require.Equal(t, post, []int{6, 45, 45}) | ||
require.Equal(t, changed, true) | ||
} | ||
|
||
func TestListChangesetWithoutCompact(t *testing.T) { | ||
pre := []int{6, 8, 9} | ||
changeset := NewListChangeSet[int](3) | ||
changeset.AddChange(1, 45) | ||
changeset.AddChange(1, 1) | ||
changeset.AddChange(2, 45) | ||
require.Equal(t, len(changeset.list), 3) | ||
post, changed := changeset.ApplyChanges(pre) | ||
require.Equal(t, post, []int{6, 1, 45}) | ||
require.Equal(t, changed, true) | ||
} |
Oops, something went wrong.