Skip to content
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

refactor(committee): using generic list for validators #667

Merged
merged 5 commits into from
Aug 26, 2023
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
49 changes: 25 additions & 24 deletions committee/committee.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
package committee

import (
"container/list"
"fmt"
"sort"
"strings"

"github.com/pactus-project/pactus/crypto"
"github.com/pactus-project/pactus/types/validator"
"github.com/pactus-project/pactus/util/linkedlist"
)

var _ Committee = &committee{}

type committee struct {
committeeSize int
validatorList *list.List
proposerPos *list.Element
validatorList *linkedlist.LinkedList[*validator.Validator]
proposerPos *linkedlist.Element[*validator.Validator]
}

func cloneValidator(val *validator.Validator) *validator.Validator {
Expand All @@ -27,11 +27,11 @@ func cloneValidator(val *validator.Validator) *validator.Validator {
func NewCommittee(validators []*validator.Validator, committeeSize int,
proposerAddress crypto.Address,
) (Committee, error) {
validatorList := list.New()
var proposerPos *list.Element
validatorList := linkedlist.New[*validator.Validator]()
var proposerPos *linkedlist.Element[*validator.Validator]

for _, val := range validators {
el := validatorList.PushBack(cloneValidator(val))
el := validatorList.InsertAtTail(cloneValidator(val))
if val.Address().EqualsTo(proposerAddress) {
proposerPos = el
}
Expand Down Expand Up @@ -66,6 +66,8 @@ func (c *committee) Update(lastRound int16, joined []*validator.Validator) {
for _, val := range joined {
committeeVal := c.find(val.Address())
if committeeVal == nil {
fmt.Println("c.porposerPos is:")
fmt.Println(c.proposerPos)
c.validatorList.InsertBefore(cloneValidator(val), c.proposerPos)
} else {
committeeVal.UpdateLastSortitionHeight(val.LastSortitionHeight())
Expand All @@ -82,41 +84,40 @@ func (c *committee) Update(lastRound int16, joined []*validator.Validator) {
}

// Now, adjust the list
oldestFirst := make([]*list.Element, c.validatorList.Len())
oldestFirst := make([]*linkedlist.Element[*validator.Validator], c.validatorList.Length())
i := 0
for e := c.validatorList.Front(); e != nil; e = e.Next() {
for e := c.validatorList.Head; e != nil; e = e.Next {
oldestFirst[i] = e
i++
}

sort.SliceStable(oldestFirst, func(i, j int) bool {
return oldestFirst[i].Value.(*validator.Validator).LastSortitionHeight() <
oldestFirst[j].Value.(*validator.Validator).LastSortitionHeight()
return oldestFirst[i].Data.LastSortitionHeight() < oldestFirst[j].Data.LastSortitionHeight()
})

for i := 0; i <= int(lastRound); i++ {
c.proposerPos = c.proposerPos.Next()
c.proposerPos = c.proposerPos.Next
if c.proposerPos == nil {
c.proposerPos = c.validatorList.Front()
c.proposerPos = c.validatorList.Head
}
}

adjust := c.validatorList.Len() - c.committeeSize
adjust := c.validatorList.Length() - c.committeeSize
for i := 0; i < adjust; i++ {
if oldestFirst[i] == c.proposerPos {
c.proposerPos = c.proposerPos.Next()
c.proposerPos = c.proposerPos.Next
if c.proposerPos == nil {
c.proposerPos = c.validatorList.Front()
c.proposerPos = c.validatorList.Head
}
}
c.validatorList.Remove(oldestFirst[i])
c.validatorList.Delete(oldestFirst[i])
}
}

// Validators retrieves a list of all validators in the committee.
// A cloned instance of each validator is returned to avoid modification of the original objects.
func (c *committee) Validators() []*validator.Validator {
vals := make([]*validator.Validator, c.validatorList.Len())
vals := make([]*validator.Validator, c.validatorList.Length())
i := 0
c.iterate(func(v *validator.Validator) (stop bool) {
vals[i] = cloneValidator(v)
Expand Down Expand Up @@ -158,17 +159,17 @@ func (c *committee) Proposer(round int16) *validator.Validator {
func (c *committee) proposer(round int16) *validator.Validator {
pos := c.proposerPos
for i := 0; i < int(round); i++ {
pos = pos.Next()
pos = pos.Next
if pos == nil {
pos = c.validatorList.Front()
pos = c.validatorList.Head
}
}

return pos.Value.(*validator.Validator)
return pos.Data
}

func (c *committee) Committers() []int32 {
committers := make([]int32, c.validatorList.Len())
committers := make([]int32, c.validatorList.Length())
i := 0
c.iterate(func(v *validator.Validator) (stop bool) {
committers[i] = v.Number()
Expand All @@ -180,7 +181,7 @@ func (c *committee) Committers() []int32 {
}

func (c *committee) Size() int {
return c.validatorList.Len()
return c.validatorList.Length()
}

func (c *committee) String() string {
Expand All @@ -203,8 +204,8 @@ func (c *committee) String() string {

// iterate uses for easy iteration over validators in list.
func (c *committee) iterate(consumer func(*validator.Validator) (stop bool)) {
for e := c.validatorList.Front(); e != nil; e = e.Next() {
if consumer(e.Value.(*validator.Validator)) {
for e := c.validatorList.Head; e != nil; e = e.Next {
if consumer(e.Data) {
return
}
}
Expand Down
3 changes: 2 additions & 1 deletion txpool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/pactus-project/pactus/types/block"
"github.com/pactus-project/pactus/types/tx"
"github.com/pactus-project/pactus/types/tx/payload"
"github.com/pactus-project/pactus/util/linkedlist"
"github.com/pactus-project/pactus/util/linkedmap"
"github.com/pactus-project/pactus/util/logger"
)
Expand Down Expand Up @@ -52,7 +53,7 @@ func (p *txPool) SetNewSandboxAndRecheck(sb sandbox.Sandbox) {
p.sandbox = sb
p.logger.Debug("set new sandbox")

var next *linkedmap.LinkNode[linkedmap.Pair[tx.ID, *tx.Tx]]
var next *linkedlist.Element[linkedmap.Pair[tx.ID, *tx.Tx]]
for _, pool := range p.pools {
for e := pool.HeadNode(); e != nil; e = next {
next = e.Next
Expand Down
76 changes: 54 additions & 22 deletions util/linkedmap/doublylink.go → util/linkedlist/linkedlist.go
Original file line number Diff line number Diff line change
@@ -1,37 +1,37 @@
package linkedmap
package linkedlist

type LinkNode[T any] struct {
type Element[T any] struct {
Data T
Next *LinkNode[T]
Prev *LinkNode[T]
Next *Element[T]
Prev *Element[T]
}

func NewLinkNode[T any](data T) *LinkNode[T] {
return &LinkNode[T]{
func NewElement[T any](data T) *Element[T] {
return &Element[T]{
Data: data,
Next: nil,
Prev: nil,
}
}

// DoublyLinkedList represents a doubly linked list.
type DoublyLinkedList[T any] struct {
Head *LinkNode[T]
Tail *LinkNode[T]
// LinkedList represents a doubly linked list.
type LinkedList[T any] struct {
Head *Element[T]
Tail *Element[T]
length int
}

func NewDoublyLinkedList[T any]() *DoublyLinkedList[T] {
return &DoublyLinkedList[T]{
func New[T any]() *LinkedList[T] {
return &LinkedList[T]{
Head: nil,
Tail: nil,
length: 0,
}
}

// InsertAtHead inserts a new node at the head of the list.
func (l *DoublyLinkedList[T]) InsertAtHead(data T) *LinkNode[T] {
newNode := NewLinkNode(data)
func (l *LinkedList[T]) InsertAtHead(data T) *Element[T] {
newNode := NewElement(data)

if l.Head == nil {
// Empty list case
Expand All @@ -49,8 +49,8 @@ func (l *DoublyLinkedList[T]) InsertAtHead(data T) *LinkNode[T] {
}

// InsertAtTail appends a new node at the tail of the list.
func (l *DoublyLinkedList[T]) InsertAtTail(data T) *LinkNode[T] {
newNode := NewLinkNode(data)
func (l *LinkedList[T]) InsertAtTail(data T) *Element[T] {
newNode := NewElement(data)

if l.Head == nil {
// Empty list case
Expand All @@ -67,8 +67,40 @@ func (l *DoublyLinkedList[T]) InsertAtTail(data T) *LinkNode[T] {
return newNode
}

func (l *LinkedList[T]) InsertBefore(data T, at *Element[T]) *Element[T] {
e := NewElement[T](data)
kehiy marked this conversation as resolved.
Show resolved Hide resolved
if at == l.Head {
l.Head = e
e.Next = at
e.Next.Prev = e
} else {
e.Prev = at.Prev
e.Next = at
e.Next.Prev = e
e.Prev.Next = e
}
l.length++
return e
}

func (l *LinkedList[T]) InsertAfter(data T, at *Element[T]) *Element[T] {
e := NewElement[T](data)
if at == l.Tail {
l.Tail = e
e.Prev = at
e.Prev.Next = e
} else {
e.Prev = at
e.Next = at.Next
e.Prev.Next = e
e.Next.Prev = e
}
l.length++
return e
}

// DeleteAtHead deletes the node at the head of the list.
func (l *DoublyLinkedList[T]) DeleteAtHead() {
func (l *LinkedList[T]) DeleteAtHead() {
if l.Head == nil {
// Empty list case
return
Expand All @@ -85,7 +117,7 @@ func (l *DoublyLinkedList[T]) DeleteAtHead() {
}

// DeleteAtTail deletes the node at the tail of the list.
func (l *DoublyLinkedList[T]) DeleteAtTail() {
func (l *LinkedList[T]) DeleteAtTail() {
if l.Tail == nil {
// Empty list case
return
Expand All @@ -102,7 +134,7 @@ func (l *DoublyLinkedList[T]) DeleteAtTail() {
}

// Delete removes a specific node from the list.
func (l *DoublyLinkedList[T]) Delete(ln *LinkNode[T]) {
func (l *LinkedList[T]) Delete(ln *Element[T]) {
if ln.Prev != nil {
ln.Prev.Next = ln.Next
} else {
Expand All @@ -119,12 +151,12 @@ func (l *DoublyLinkedList[T]) Delete(ln *LinkNode[T]) {
}

// Length returns the number of nodes in the list.
func (l *DoublyLinkedList[T]) Length() int {
func (l *LinkedList[T]) Length() int {
return l.length
}

// Values returns a slice of values in the list.
func (l *DoublyLinkedList[T]) Values() []T {
func (l *LinkedList[T]) Values() []T {
values := []T{}
cur := l.Head
for cur != nil {
Expand All @@ -135,7 +167,7 @@ func (l *DoublyLinkedList[T]) Values() []T {
}

// Clear removes all nodes from the list, making it empty.
func (l *DoublyLinkedList[T]) Clear() {
func (l *LinkedList[T]) Clear() {
l.Head = nil
l.Tail = nil
l.length = 0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package linkedmap
package linkedlist_test

import (
"testing"

ll "github.com/pactus-project/pactus/util/linkedlist"
"github.com/stretchr/testify/assert"
)

func TestDoublyLink_InsertAtHead(t *testing.T) {
link := NewDoublyLinkedList[int]()
link := ll.New[int]()
link.InsertAtHead(1)
link.InsertAtHead(2)
link.InsertAtHead(3)
Expand All @@ -20,7 +21,7 @@ func TestDoublyLink_InsertAtHead(t *testing.T) {
}

func TestSinglyLink_InsertAtTail(t *testing.T) {
link := NewDoublyLinkedList[int]()
link := ll.New[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
Expand All @@ -33,7 +34,7 @@ func TestSinglyLink_InsertAtTail(t *testing.T) {
}

func TestDeleteAtHead(t *testing.T) {
link := NewDoublyLinkedList[int]()
link := ll.New[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
Expand All @@ -56,7 +57,7 @@ func TestDeleteAtHead(t *testing.T) {
}

func TestDeleteAtTail(t *testing.T) {
link := NewDoublyLinkedList[int]()
link := ll.New[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
Expand All @@ -79,7 +80,7 @@ func TestDeleteAtTail(t *testing.T) {
}

func TestDelete(t *testing.T) {
link := NewDoublyLinkedList[int]()
link := ll.New[int]()
n1 := link.InsertAtTail(1)
n2 := link.InsertAtTail(2)
n3 := link.InsertAtTail(3)
Expand All @@ -103,7 +104,7 @@ func TestDelete(t *testing.T) {
}

func TestClear(t *testing.T) {
link := NewDoublyLinkedList[int]()
link := ll.New[int]()
link.InsertAtTail(1)
link.InsertAtTail(2)
link.InsertAtTail(3)
Expand All @@ -112,3 +113,25 @@ func TestClear(t *testing.T) {
assert.Equal(t, link.Values(), []int{})
assert.Equal(t, link.Length(), 0)
}

func TestInsertAfter(t *testing.T) {
link := ll.New[int]()
e1 := link.InsertAtHead(1)
e2 := link.InsertAfter(2, e1)
link.InsertAfter(3, e2)
link.InsertAfter(4, link.Head)
link.InsertAfter(5, link.Tail)

assert.Equal(t, []int{1, 4, 2, 3, 5}, link.Values())
}

func TestInsertBefore(t *testing.T) {
link := ll.New[int]()
e1 := link.InsertAtHead(1)
e2 := link.InsertBefore(2, e1)
link.InsertBefore(3, e2)
link.InsertBefore(4, link.Head)
link.InsertBefore(5, link.Tail)

assert.Equal(t, []int{4, 3, 2, 5, 1}, link.Values())
}
Loading
Loading