les, p2p/nodestate: fixed tests

This commit is contained in:
Zsolt Felfoldi 2020-04-28 22:38:46 +02:00
parent 1fa4919c12
commit b032eab80f
5 changed files with 109 additions and 133 deletions

View file

@ -21,17 +21,17 @@ import (
"time"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/les/utils"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/p2p/nodestate"
)
var (
sfTest1 = utils.NewFlag("test1")
sfTest2 = utils.NewFlag("test2")
sfTest3 = utils.NewFlag("test3")
sfTest4 = utils.NewFlag("test4")
testSetup = utils.NodeStateSetup{Flags: []nodestate.Flags{sfTest1, sfTest2, sfTest3, sfTest4}}
testSetup = &nodestate.Setup{}
sfTest1 = testSetup.NewFlag("test1")
sfTest2 = testSetup.NewFlag("test2")
sfTest3 = testSetup.NewFlag("test3")
sfTest4 = testSetup.NewFlag("test4")
)
const iterTestNodeCount = 6
@ -61,15 +61,11 @@ func testNode(i int) *enode.Node {
}
func TestQueueIterator(t *testing.T) {
ns := utils.NewNodeStateMachine(nil, nil, &mclock.Simulated{}, testSetup)
st1 := ns.StateMask(sfTest1)
st2 := ns.StateMask(sfTest2)
st3 := ns.StateMask(sfTest3)
st4 := ns.StateMask(sfTest4)
qi := NewQueueIterator(ns, st2, st3, sfTest4)
ns := nodestate.NewNodeStateMachine(nil, nil, &mclock.Simulated{}, testSetup)
qi := NewQueueIterator(ns, sfTest2, sfTest3, sfTest4)
ns.Start()
for i := 1; i <= iterTestNodeCount; i++ {
ns.SetState(testNode(i), st1, 0, 0)
ns.SetState(testNode(i), sfTest1, nodestate.Flags{}, 0)
}
ch := make(chan *enode.Node)
go func() {
@ -93,25 +89,25 @@ func TestQueueIterator(t *testing.T) {
}
}
exp(0)
ns.SetState(testNode(1), st2, 0, 0)
ns.SetState(testNode(2), st2, 0, 0)
ns.SetState(testNode(3), st2, 0, 0)
ns.SetState(testNode(1), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(2), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(3), sfTest2, nodestate.Flags{}, 0)
exp(1)
exp(2)
exp(3)
exp(0)
ns.SetState(testNode(4), st2, 0, 0)
ns.SetState(testNode(5), st2, 0, 0)
ns.SetState(testNode(6), st2, 0, 0)
ns.SetState(testNode(5), st3, 0, 0)
ns.SetState(testNode(4), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(5), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(6), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(5), sfTest3, nodestate.Flags{}, 0)
exp(4)
exp(6)
exp(0)
ns.SetState(testNode(1), 0, st4, 0)
ns.SetState(testNode(2), 0, st4, 0)
ns.SetState(testNode(3), 0, st4, 0)
ns.SetState(testNode(2), st3, 0, 0)
ns.SetState(testNode(2), 0, st3, 0)
ns.SetState(testNode(1), nodestate.Flags{}, sfTest4, 0)
ns.SetState(testNode(2), nodestate.Flags{}, sfTest4, 0)
ns.SetState(testNode(3), nodestate.Flags{}, sfTest4, 0)
ns.SetState(testNode(2), sfTest3, nodestate.Flags{}, 0)
ns.SetState(testNode(2), nodestate.Flags{}, sfTest3, 0)
exp(1)
exp(3)
exp(2)

View file

@ -21,16 +21,12 @@ import (
"time"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/les/utils"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nodestate"
)
func TestWrsIterator(t *testing.T) {
ns := utils.NewNodeStateMachine(nil, nil, &mclock.Simulated{}, testSetup)
st1 := ns.StateMask(sfTest1)
st2 := ns.StateMask(sfTest2)
st3 := ns.StateMask(sfTest3)
st4 := ns.StateMask(sfTest4)
ns := nodestate.NewNodeStateMachine(nil, nil, &mclock.Simulated{}, testSetup)
weights := make([]uint64, iterTestNodeCount+1)
wfn := func(i interface{}) uint64 {
id := i.(enode.ID)
@ -44,11 +40,11 @@ func TestWrsIterator(t *testing.T) {
}
return weights[idx]
}
w := NewWrsIterator(ns, st2, st3, sfTest4, wfn)
w := NewWrsIterator(ns, sfTest2, sfTest3, sfTest4, wfn)
ns.Start()
for i := 1; i <= iterTestNodeCount; i++ {
weights[i] = 1
ns.SetState(testNode(i), st1, 0, 0)
ns.SetState(testNode(i), sfTest1, nodestate.Flags{}, 0)
}
ch := make(chan *enode.Node)
go func() {
@ -84,32 +80,32 @@ func TestWrsIterator(t *testing.T) {
}
exp(0)
ns.SetState(testNode(1), st2, 0, 0)
ns.SetState(testNode(2), st2, 0, 0)
ns.SetState(testNode(3), st2, 0, 0)
ns.SetState(testNode(1), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(2), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(3), sfTest2, nodestate.Flags{}, 0)
set[1] = true
set[2] = true
set[3] = true
expset()
ns.SetState(testNode(4), st2, 0, 0)
ns.SetState(testNode(5), st2, 0, 0)
ns.SetState(testNode(6), st2, 0, 0)
ns.SetState(testNode(5), st3, 0, 0)
ns.SetState(testNode(4), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(5), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(6), sfTest2, nodestate.Flags{}, 0)
ns.SetState(testNode(5), sfTest3, nodestate.Flags{}, 0)
set[4] = true
set[6] = true
expset()
ns.SetState(testNode(1), 0, st4, 0)
ns.SetState(testNode(2), 0, st4, 0)
ns.SetState(testNode(3), 0, st4, 0)
ns.SetState(testNode(1), nodestate.Flags{}, sfTest4, 0)
ns.SetState(testNode(2), nodestate.Flags{}, sfTest4, 0)
ns.SetState(testNode(3), nodestate.Flags{}, sfTest4, 0)
weights[2] = 0
set[1] = true
set[3] = true
expset()
weights[2] = 1
ns.SetState(testNode(2), 0, st2, 0)
ns.SetState(testNode(1), 0, st4, 0)
ns.SetState(testNode(2), st2, st4, 0)
ns.SetState(testNode(3), 0, st4, 0)
ns.SetState(testNode(2), nodestate.Flags{}, sfTest2, 0)
ns.SetState(testNode(1), nodestate.Flags{}, sfTest4, 0)
ns.SetState(testNode(2), sfTest2, sfTest4, 0)
ns.SetState(testNode(3), nodestate.Flags{}, sfTest4, 0)
set[1] = true
set[2] = true
set[3] = true

View file

@ -25,10 +25,10 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
lpc "github.com/ethereum/go-ethereum/les/lespay/client"
"github.com/ethereum/go-ethereum/les/utils"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/p2p/nodestate"
)
const (
@ -87,7 +87,7 @@ func (s *serverPoolTest) addTrusted(i int) {
}
func (s *serverPoolTest) start() {
s.ns = utils.NewNodeStateMachine(s.db, []byte("nodestate:"), s.clock, serverPoolSetup)
s.ns = nodestate.NewNodeStateMachine(s.db, []byte("nodestate:"), s.clock, serverPoolSetup)
s.vt = lpc.NewValueTracker(s.db, s.clock, requestList, time.Minute, 1/float64(time.Hour), 1/float64(time.Hour*100), 1/float64(time.Hour*1000))
s.sp = newServerPool(s.db, []byte("serverpool:"), s.ns, s.vt, s.input, s.clock, s.trusted, true)
s.disconnect = make(map[int][]int)

View file

@ -282,6 +282,9 @@ func MergeFlags(list ...Flags) Flags {
// String returns a list of the names of the flags specified in the bit mask
func (f Flags) String() string {
if f.mask == 0 {
return "[]"
}
s := "["
comma := false
for index, flag := range f.setup.flags {

View file

@ -29,39 +29,28 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
func testSetup(flagPersist []bool, fieldType []reflect.Type) Setup {
flags := make([]*flagDefinition, len(flagPersist))
func testSetup(flagPersist []bool, fieldType []reflect.Type) (*Setup, []Flags, []Field) {
setup := &Setup{}
flags := make([]Flags, len(flagPersist))
for i, persist := range flagPersist {
if persist {
flags[i] = NewPersistentFlag(fmt.Sprintf("flag-%d", i))
flags[i] = setup.NewPersistentFlag(fmt.Sprintf("flag-%d", i))
} else {
flags[i] = NewFlag(fmt.Sprintf("flag-%d", i))
flags[i] = setup.NewFlag(fmt.Sprintf("flag-%d", i))
}
}
fields := make([]*fieldDefinition, len(fieldType))
fields := make([]Field, len(fieldType))
for i, ftype := range fieldType {
switch ftype {
case reflect.TypeOf(uint64(0)):
fields[i] = NewPersistentField(fmt.Sprintf("field-%d", i), ftype, uint64FieldEnc, uint64FieldDec)
fields[i] = setup.NewPersistentField(fmt.Sprintf("field-%d", i), ftype, uint64FieldEnc, uint64FieldDec)
case reflect.TypeOf(""):
fields[i] = NewPersistentField(fmt.Sprintf("field-%d", i), ftype, stringFieldEnc, stringFieldDec)
fields[i] = setup.NewPersistentField(fmt.Sprintf("field-%d", i), ftype, stringFieldEnc, stringFieldDec)
default:
fields[i] = NewField(fmt.Sprintf("field-%d", i), ftype)
fields[i] = setup.NewField(fmt.Sprintf("field-%d", i), ftype)
}
}
return Setup{flags, fields}
}
func regSetup(ns *NodeStateMachine, setup Setup) ([]bitMask, []int) {
masks := make([]bitMask, len(setup.Flags))
for i, flag := range setup.Flags {
masks[i] = ns.StateMask(flag)
}
fieldIndex := make([]int, len(setup.Fields))
for i, field := range setup.Fields {
fieldIndex[i] = ns.FieldIndex(field)
}
return masks, fieldIndex
return setup, flags, fields
}
func testNode(b byte) *enode.Node {
@ -74,22 +63,21 @@ func testNode(b byte) *enode.Node {
func TestCallback(t *testing.T) {
mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
s := testSetup([]bool{false, false, false}, nil)
s, flags, _ := testSetup([]bool{false, false, false}, nil)
ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
flags, _ := regSetup(ns, s)
set0 := make(chan struct{}, 1)
set1 := make(chan struct{}, 1)
set2 := make(chan struct{}, 1)
ns.SubscribeState(flags[0], func(n *enode.Node, oldState, newState bitMask) { set0 <- struct{}{} })
ns.SubscribeState(flags[1], func(n *enode.Node, oldState, newState bitMask) { set1 <- struct{}{} })
ns.SubscribeState(flags[2], func(n *enode.Node, oldState, newState bitMask) { set2 <- struct{}{} })
ns.SubscribeState(flags[0], func(n *enode.Node, oldState, newState Flags) { set0 <- struct{}{} })
ns.SubscribeState(flags[1], func(n *enode.Node, oldState, newState Flags) { set1 <- struct{}{} })
ns.SubscribeState(flags[2], func(n *enode.Node, oldState, newState Flags) { set2 <- struct{}{} })
ns.Start()
ns.SetState(testNode(1), flags[0], 0, 0)
ns.SetState(testNode(1), flags[1], 0, time.Second)
ns.SetState(testNode(1), flags[2], 0, 2*time.Second)
ns.SetState(testNode(1), flags[0], Flags{}, 0)
ns.SetState(testNode(1), flags[1], Flags{}, time.Second)
ns.SetState(testNode(1), flags[2], Flags{}, 2*time.Second)
for i := 0; i < 3; i++ {
select {
@ -105,9 +93,8 @@ func TestCallback(t *testing.T) {
func TestPersistentFlags(t *testing.T) {
mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
s := testSetup([]bool{true, true, true, false}, nil)
s, flags, _ := testSetup([]bool{true, true, true, false}, nil)
ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
flags, _ := regSetup(ns, s)
saveNode := make(chan *nodeInfo, 5)
ns.saveNodeHook = func(node *nodeInfo) {
@ -116,11 +103,11 @@ func TestPersistentFlags(t *testing.T) {
ns.Start()
ns.SetState(testNode(1), flags[0], 0, time.Second) // state with timeout should not be saved
ns.SetState(testNode(2), flags[1], 0, 0)
ns.SetState(testNode(3), flags[2], 0, 0)
ns.SetState(testNode(4), flags[3], 0, 0)
ns.SetState(testNode(5), flags[0], 0, 0)
ns.SetState(testNode(1), flags[0], Flags{}, time.Second) // state with timeout should not be saved
ns.SetState(testNode(2), flags[1], Flags{}, 0)
ns.SetState(testNode(3), flags[2], Flags{}, 0)
ns.SetState(testNode(4), flags[3], Flags{}, 0)
ns.SetState(testNode(5), flags[0], Flags{}, 0)
ns.Persist(testNode(5))
select {
case <-saveNode:
@ -146,9 +133,8 @@ func TestPersistentFlags(t *testing.T) {
func TestSetField(t *testing.T) {
mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
s := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf("")})
s, flags, fields := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf("")})
ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
flags, fields := regSetup(ns, s)
saveNode := make(chan *nodeInfo, 1)
ns.saveNodeHook = func(node *nodeInfo) {
@ -164,7 +150,7 @@ func TestSetField(t *testing.T) {
t.Fatalf("Field shouldn't be set before setting states")
}
// Set field after setting state
ns.SetState(testNode(1), flags[0], 0, 0)
ns.SetState(testNode(1), flags[0], Flags{}, 0)
ns.SetField(testNode(1), fields[0], "hello world")
field = ns.GetField(testNode(1), fields[0])
if field == nil {
@ -185,16 +171,15 @@ func TestSetField(t *testing.T) {
func TestUnsetField(t *testing.T) {
mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
s := testSetup([]bool{false}, []reflect.Type{reflect.TypeOf("")})
s, flags, fields := testSetup([]bool{false}, []reflect.Type{reflect.TypeOf("")})
ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
flags, fields := regSetup(ns, s)
ns.Start()
ns.SetState(testNode(1), flags[0], 0, time.Second)
ns.SetState(testNode(1), flags[0], Flags{}, time.Second)
ns.SetField(testNode(1), fields[0], "hello world")
ns.SetState(testNode(1), 0, flags[0], 0)
ns.SetState(testNode(1), Flags{}, flags[0], 0)
if field := ns.GetField(testNode(1), fields[0]); field != nil {
t.Fatalf("Field should be unset")
}
@ -203,13 +188,12 @@ func TestUnsetField(t *testing.T) {
func TestSetState(t *testing.T) {
mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
s := testSetup([]bool{false, false, false}, nil)
s, flags, _ := testSetup([]bool{false, false, false}, nil)
ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
flags, _ := regSetup(ns, s)
type change struct{ old, new bitMask }
type change struct{ old, new Flags }
set := make(chan change, 1)
ns.SubscribeState(flags[0]|flags[1], func(n *enode.Node, oldState, newState bitMask) {
ns.SubscribeState(flags[0].Or(flags[1]), func(n *enode.Node, oldState, newState Flags) {
set <- change{
old: oldState,
new: newState,
@ -218,14 +202,14 @@ func TestSetState(t *testing.T) {
ns.Start()
check := func(expectOld, expectNew bitMask, expectChange bool) {
check := func(expectOld, expectNew Flags, expectChange bool) {
if expectChange {
select {
case c := <-set:
if c.old != expectOld {
if !c.old.Equals(expectOld) {
t.Fatalf("Old state mismatch")
}
if c.new != expectNew {
if !c.new.Equals(expectNew) {
t.Fatalf("New state mismatch")
}
case <-time.After(time.Second):
@ -239,28 +223,28 @@ func TestSetState(t *testing.T) {
return
}
}
ns.SetState(testNode(1), flags[0], 0, 0)
check(0, flags[0], true)
ns.SetState(testNode(1), flags[0], Flags{}, 0)
check(Flags{}, flags[0], true)
ns.SetState(testNode(1), flags[1], 0, 0)
check(flags[0], flags[0]|flags[1], true)
ns.SetState(testNode(1), flags[1], Flags{}, 0)
check(flags[0], flags[0].Or(flags[1]), true)
ns.SetState(testNode(1), flags[2], 0, 0)
check(0, 0, false)
ns.SetState(testNode(1), flags[2], Flags{}, 0)
check(Flags{}, Flags{}, false)
ns.SetState(testNode(1), 0, flags[0], 0)
check(flags[0]|flags[1], flags[1], true)
ns.SetState(testNode(1), Flags{}, flags[0], 0)
check(flags[0].Or(flags[1]), flags[1], true)
ns.SetState(testNode(1), 0, flags[1], 0)
check(flags[1], 0, true)
ns.SetState(testNode(1), Flags{}, flags[1], 0)
check(flags[1], Flags{}, true)
ns.SetState(testNode(1), 0, flags[2], 0)
check(0, 0, false)
ns.SetState(testNode(1), Flags{}, flags[2], 0)
check(Flags{}, Flags{}, false)
ns.SetState(testNode(1), flags[0]|flags[1], 0, time.Second)
check(0, flags[0]|flags[1], true)
ns.SetState(testNode(1), flags[0].Or(flags[1]), Flags{}, time.Second)
check(Flags{}, flags[0].Or(flags[1]), true)
clock.Run(time.Second)
check(flags[0]|flags[1], 0, true)
check(flags[0].Or(flags[1]), Flags{}, true)
}
func uint64FieldEnc(field interface{}) ([]byte, error) {
@ -293,12 +277,11 @@ func stringFieldDec(enc []byte) (interface{}, error) {
func TestPersistentFields(t *testing.T) {
mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
s := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf(uint64(0)), reflect.TypeOf("")})
s, flags, fields := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf(uint64(0)), reflect.TypeOf("")})
ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
flags, fields := regSetup(ns, s)
ns.Start()
ns.SetState(testNode(1), flags[0], 0, 0)
ns.SetState(testNode(1), flags[0], Flags{}, 0)
ns.SetField(testNode(1), fields[0], uint64(100))
ns.SetField(testNode(1), fields[1], "hello world")
ns.Stop()
@ -316,12 +299,11 @@ func TestPersistentFields(t *testing.T) {
}
// additional registration
s = testSetup([]bool{true, true}, []reflect.Type{reflect.TypeOf(uint64(0)), reflect.TypeOf(""), reflect.TypeOf(uint32(0))})
s, _, fields = testSetup([]bool{true, true}, []reflect.Type{reflect.TypeOf(uint64(0)), reflect.TypeOf(""), reflect.TypeOf(uint32(0))})
// Different order
s.Flags[0], s.Flags[1] = s.Flags[1], s.Flags[0]
s.Fields[0], s.Fields[1] = s.Fields[1], s.Fields[0]
s.flags[0], s.flags[1] = s.flags[1], s.flags[0]
s.fields[0], s.fields[1] = s.fields[1], s.fields[0]
ns3 := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
_, fields = regSetup(ns3, s)
ns3.Start()
field0 = ns3.GetField(testNode(1), fields[1])
@ -337,36 +319,35 @@ func TestPersistentFields(t *testing.T) {
func TestFieldSub(t *testing.T) {
mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
s := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf(uint64(0))})
s, flags, fields := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf(uint64(0))})
ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
flags, fields := regSetup(ns, s)
var (
lastState bitMask
lastState Flags
lastOldValue, lastNewValue interface{}
)
ns.SubscribeField(fields[0], func(n *enode.Node, state bitMask, oldValue, newValue interface{}) {
ns.SubscribeField(fields[0], func(n *enode.Node, state Flags, oldValue, newValue interface{}) {
lastState, lastOldValue, lastNewValue = state, oldValue, newValue
})
check := func(state bitMask, oldValue, newValue interface{}) {
if lastState != state || lastOldValue != oldValue || lastNewValue != newValue {
check := func(state Flags, oldValue, newValue interface{}) {
if !lastState.Equals(state) || lastOldValue != oldValue || lastNewValue != newValue {
t.Fatalf("Incorrect field sub callback (expected [%v %v %v], got [%v %v %v])", state, oldValue, newValue, lastState, lastOldValue, lastNewValue)
}
}
ns.Start()
ns.SetState(testNode(1), flags[0], 0, 0)
ns.SetState(testNode(1), flags[0], Flags{}, 0)
ns.SetField(testNode(1), fields[0], uint64(100))
check(flags[0], nil, uint64(100))
ns.Stop()
check(offlineState, uint64(100), nil)
check(s.OfflineFlag(), uint64(100), nil)
ns2 := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
ns2.SubscribeField(fields[0], func(n *enode.Node, state bitMask, oldValue, newValue interface{}) {
ns2.SubscribeField(fields[0], func(n *enode.Node, state Flags, oldValue, newValue interface{}) {
lastState, lastOldValue, lastNewValue = state, oldValue, newValue
})
ns2.Start()
check(offlineState, nil, uint64(100))
ns2.SetState(testNode(1), 0, flags[0], 0)
check(0, uint64(100), nil)
check(s.OfflineFlag(), nil, uint64(100))
ns2.SetState(testNode(1), Flags{}, flags[0], 0)
check(Flags{}, uint64(100), nil)
ns2.Stop()
}