github.com/core-coin/go-core/v2@v2.1.9/p2p/nodestate/nodestate_test.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package nodestate
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"reflect"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/core-coin/go-core/v2/common/mclock"
    27  	"github.com/core-coin/go-core/v2/core/rawdb"
    28  	"github.com/core-coin/go-core/v2/p2p/enode"
    29  	"github.com/core-coin/go-core/v2/p2p/enr"
    30  	"github.com/core-coin/go-core/v2/rlp"
    31  )
    32  
    33  func testSetup(flagPersist []bool, fieldType []reflect.Type) (*Setup, []Flags, []Field) {
    34  	setup := &Setup{}
    35  	flags := make([]Flags, len(flagPersist))
    36  	for i, persist := range flagPersist {
    37  		if persist {
    38  			flags[i] = setup.NewPersistentFlag(fmt.Sprintf("flag-%d", i))
    39  		} else {
    40  			flags[i] = setup.NewFlag(fmt.Sprintf("flag-%d", i))
    41  		}
    42  	}
    43  	fields := make([]Field, len(fieldType))
    44  	for i, ftype := range fieldType {
    45  		switch ftype {
    46  		case reflect.TypeOf(uint64(0)):
    47  			fields[i] = setup.NewPersistentField(fmt.Sprintf("field-%d", i), ftype, uint64FieldEnc, uint64FieldDec)
    48  		case reflect.TypeOf(""):
    49  			fields[i] = setup.NewPersistentField(fmt.Sprintf("field-%d", i), ftype, stringFieldEnc, stringFieldDec)
    50  		default:
    51  			fields[i] = setup.NewField(fmt.Sprintf("field-%d", i), ftype)
    52  		}
    53  	}
    54  	return setup, flags, fields
    55  }
    56  
    57  func testNode(b byte) *enode.Node {
    58  	r := &enr.Record{}
    59  	r.SetSig(dummyIdentity{b}, []byte{42})
    60  	n, _ := enode.New(dummyIdentity{b}, r)
    61  	return n
    62  }
    63  
    64  func TestCallback(t *testing.T) {
    65  	mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
    66  
    67  	s, flags, _ := testSetup([]bool{false, false, false}, nil)
    68  	ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
    69  
    70  	set0 := make(chan struct{}, 1)
    71  	set1 := make(chan struct{}, 1)
    72  	set2 := make(chan struct{}, 1)
    73  	ns.SubscribeState(flags[0], func(n *enode.Node, oldState, newState Flags) { set0 <- struct{}{} })
    74  	ns.SubscribeState(flags[1], func(n *enode.Node, oldState, newState Flags) { set1 <- struct{}{} })
    75  	ns.SubscribeState(flags[2], func(n *enode.Node, oldState, newState Flags) { set2 <- struct{}{} })
    76  
    77  	ns.Start()
    78  
    79  	ns.SetState(testNode(1), flags[0], Flags{}, 0)
    80  	ns.SetState(testNode(1), flags[1], Flags{}, time.Second)
    81  	ns.SetState(testNode(1), flags[2], Flags{}, 2*time.Second)
    82  
    83  	for i := 0; i < 3; i++ {
    84  		select {
    85  		case <-set0:
    86  		case <-set1:
    87  		case <-set2:
    88  		case <-time.After(time.Second):
    89  			t.Fatalf("failed to invoke callback")
    90  		}
    91  	}
    92  }
    93  
    94  func TestPersistentFlags(t *testing.T) {
    95  	mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
    96  
    97  	s, flags, _ := testSetup([]bool{true, true, true, false}, nil)
    98  	ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
    99  
   100  	saveNode := make(chan *nodeInfo, 5)
   101  	ns.saveNodeHook = func(node *nodeInfo) {
   102  		saveNode <- node
   103  	}
   104  
   105  	ns.Start()
   106  
   107  	ns.SetState(testNode(1), flags[0], Flags{}, time.Second) // state with timeout should not be saved
   108  	ns.SetState(testNode(2), flags[1], Flags{}, 0)
   109  	ns.SetState(testNode(3), flags[2], Flags{}, 0)
   110  	ns.SetState(testNode(4), flags[3], Flags{}, 0)
   111  	ns.SetState(testNode(5), flags[0], Flags{}, 0)
   112  	ns.Persist(testNode(5))
   113  	select {
   114  	case <-saveNode:
   115  	case <-time.After(time.Second):
   116  		t.Fatalf("Timeout")
   117  	}
   118  	ns.Stop()
   119  
   120  	for i := 0; i < 2; i++ {
   121  		select {
   122  		case <-saveNode:
   123  		case <-time.After(time.Second):
   124  			t.Fatalf("Timeout")
   125  		}
   126  	}
   127  	select {
   128  	case <-saveNode:
   129  		t.Fatalf("Unexpected saveNode")
   130  	case <-time.After(time.Millisecond * 100):
   131  	}
   132  }
   133  
   134  func TestSetField(t *testing.T) {
   135  	mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
   136  
   137  	s, flags, fields := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf("")})
   138  	ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
   139  
   140  	saveNode := make(chan *nodeInfo, 1)
   141  	ns.saveNodeHook = func(node *nodeInfo) {
   142  		saveNode <- node
   143  	}
   144  
   145  	ns.Start()
   146  
   147  	// Set field before setting state
   148  	ns.SetField(testNode(1), fields[0], "hello world")
   149  	field := ns.GetField(testNode(1), fields[0])
   150  	if field == nil {
   151  		t.Fatalf("Field should be set before setting states")
   152  	}
   153  	ns.SetField(testNode(1), fields[0], nil)
   154  	field = ns.GetField(testNode(1), fields[0])
   155  	if field != nil {
   156  		t.Fatalf("Field should be unset")
   157  	}
   158  	// Set field after setting state
   159  	ns.SetState(testNode(1), flags[0], Flags{}, 0)
   160  	ns.SetField(testNode(1), fields[0], "hello world")
   161  	field = ns.GetField(testNode(1), fields[0])
   162  	if field == nil {
   163  		t.Fatalf("Field should be set after setting states")
   164  	}
   165  	if err := ns.SetField(testNode(1), fields[0], 123); err == nil {
   166  		t.Fatalf("Invalid field should be rejected")
   167  	}
   168  	// Dirty node should be written back
   169  	ns.Stop()
   170  	select {
   171  	case <-saveNode:
   172  	case <-time.After(time.Second):
   173  		t.Fatalf("Timeout")
   174  	}
   175  }
   176  
   177  func TestSetState(t *testing.T) {
   178  	mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
   179  
   180  	s, flags, _ := testSetup([]bool{false, false, false}, nil)
   181  	ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
   182  
   183  	type change struct{ old, new Flags }
   184  	set := make(chan change, 1)
   185  	ns.SubscribeState(flags[0].Or(flags[1]), func(n *enode.Node, oldState, newState Flags) {
   186  		set <- change{
   187  			old: oldState,
   188  			new: newState,
   189  		}
   190  	})
   191  
   192  	ns.Start()
   193  
   194  	check := func(expectOld, expectNew Flags, expectChange bool) {
   195  		if expectChange {
   196  			select {
   197  			case c := <-set:
   198  				if !c.old.Equals(expectOld) {
   199  					t.Fatalf("Old state mismatch")
   200  				}
   201  				if !c.new.Equals(expectNew) {
   202  					t.Fatalf("New state mismatch")
   203  				}
   204  			case <-time.After(time.Second):
   205  			}
   206  			return
   207  		}
   208  		select {
   209  		case <-set:
   210  			t.Fatalf("Unexpected change")
   211  		case <-time.After(time.Millisecond * 100):
   212  			return
   213  		}
   214  	}
   215  	ns.SetState(testNode(1), flags[0], Flags{}, 0)
   216  	check(Flags{}, flags[0], true)
   217  
   218  	ns.SetState(testNode(1), flags[1], Flags{}, 0)
   219  	check(flags[0], flags[0].Or(flags[1]), true)
   220  
   221  	ns.SetState(testNode(1), flags[2], Flags{}, 0)
   222  	check(Flags{}, Flags{}, false)
   223  
   224  	ns.SetState(testNode(1), Flags{}, flags[0], 0)
   225  	check(flags[0].Or(flags[1]), flags[1], true)
   226  
   227  	ns.SetState(testNode(1), Flags{}, flags[1], 0)
   228  	check(flags[1], Flags{}, true)
   229  
   230  	ns.SetState(testNode(1), Flags{}, flags[2], 0)
   231  	check(Flags{}, Flags{}, false)
   232  
   233  	ns.SetState(testNode(1), flags[0].Or(flags[1]), Flags{}, time.Second)
   234  	check(Flags{}, flags[0].Or(flags[1]), true)
   235  	clock.Run(time.Second)
   236  	check(flags[0].Or(flags[1]), Flags{}, true)
   237  }
   238  
   239  func uint64FieldEnc(field interface{}) ([]byte, error) {
   240  	if u, ok := field.(uint64); ok {
   241  		enc, err := rlp.EncodeToBytes(&u)
   242  		return enc, err
   243  	}
   244  	return nil, errors.New("invalid field type")
   245  }
   246  
   247  func uint64FieldDec(enc []byte) (interface{}, error) {
   248  	var u uint64
   249  	err := rlp.DecodeBytes(enc, &u)
   250  	return u, err
   251  }
   252  
   253  func stringFieldEnc(field interface{}) ([]byte, error) {
   254  	if s, ok := field.(string); ok {
   255  		return []byte(s), nil
   256  	}
   257  	return nil, errors.New("invalid field type")
   258  }
   259  
   260  func stringFieldDec(enc []byte) (interface{}, error) {
   261  	return string(enc), nil
   262  }
   263  
   264  func TestPersistentFields(t *testing.T) {
   265  	mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
   266  
   267  	s, flags, fields := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf(uint64(0)), reflect.TypeOf("")})
   268  	ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
   269  
   270  	ns.Start()
   271  	ns.SetState(testNode(1), flags[0], Flags{}, 0)
   272  	ns.SetField(testNode(1), fields[0], uint64(100))
   273  	ns.SetField(testNode(1), fields[1], "hello world")
   274  	ns.Stop()
   275  
   276  	ns2 := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
   277  
   278  	ns2.Start()
   279  	field0 := ns2.GetField(testNode(1), fields[0])
   280  	if !reflect.DeepEqual(field0, uint64(100)) {
   281  		t.Fatalf("Field changed")
   282  	}
   283  	field1 := ns2.GetField(testNode(1), fields[1])
   284  	if !reflect.DeepEqual(field1, "hello world") {
   285  		t.Fatalf("Field changed")
   286  	}
   287  
   288  	s.Version++
   289  	ns3 := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
   290  	ns3.Start()
   291  	if ns3.GetField(testNode(1), fields[0]) != nil {
   292  		t.Fatalf("Old field version should have been discarded")
   293  	}
   294  }
   295  
   296  func TestFieldSub(t *testing.T) {
   297  	mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
   298  
   299  	s, flags, fields := testSetup([]bool{true}, []reflect.Type{reflect.TypeOf(uint64(0))})
   300  	ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
   301  
   302  	var (
   303  		lastState                  Flags
   304  		lastOldValue, lastNewValue interface{}
   305  	)
   306  	ns.SubscribeField(fields[0], func(n *enode.Node, state Flags, oldValue, newValue interface{}) {
   307  		lastState, lastOldValue, lastNewValue = state, oldValue, newValue
   308  	})
   309  	check := func(state Flags, oldValue, newValue interface{}) {
   310  		if !lastState.Equals(state) || lastOldValue != oldValue || lastNewValue != newValue {
   311  			t.Fatalf("Incorrect field sub callback (expected [%v %v %v], got [%v %v %v])", state, oldValue, newValue, lastState, lastOldValue, lastNewValue)
   312  		}
   313  	}
   314  	ns.Start()
   315  	ns.SetState(testNode(1), flags[0], Flags{}, 0)
   316  	ns.SetField(testNode(1), fields[0], uint64(100))
   317  	check(flags[0], nil, uint64(100))
   318  	ns.Stop()
   319  	check(s.OfflineFlag(), uint64(100), nil)
   320  
   321  	ns2 := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
   322  	ns2.SubscribeField(fields[0], func(n *enode.Node, state Flags, oldValue, newValue interface{}) {
   323  		lastState, lastOldValue, lastNewValue = state, oldValue, newValue
   324  	})
   325  	ns2.Start()
   326  	check(s.OfflineFlag(), nil, uint64(100))
   327  	ns2.SetState(testNode(1), Flags{}, flags[0], 0)
   328  	ns2.SetField(testNode(1), fields[0], nil)
   329  	check(Flags{}, uint64(100), nil)
   330  	ns2.Stop()
   331  }
   332  
   333  func TestDuplicatedFlags(t *testing.T) {
   334  	mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
   335  
   336  	s, flags, _ := testSetup([]bool{true}, nil)
   337  	ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
   338  
   339  	type change struct{ old, new Flags }
   340  	set := make(chan change, 1)
   341  	ns.SubscribeState(flags[0], func(n *enode.Node, oldState, newState Flags) {
   342  		set <- change{oldState, newState}
   343  	})
   344  
   345  	ns.Start()
   346  	defer ns.Stop()
   347  
   348  	check := func(expectOld, expectNew Flags, expectChange bool) {
   349  		if expectChange {
   350  			select {
   351  			case c := <-set:
   352  				if !c.old.Equals(expectOld) {
   353  					t.Fatalf("Old state mismatch")
   354  				}
   355  				if !c.new.Equals(expectNew) {
   356  					t.Fatalf("New state mismatch")
   357  				}
   358  			case <-time.After(time.Second):
   359  			}
   360  			return
   361  		}
   362  		select {
   363  		case <-set:
   364  			t.Fatalf("Unexpected change")
   365  		case <-time.After(time.Millisecond * 100):
   366  			return
   367  		}
   368  	}
   369  	ns.SetState(testNode(1), flags[0], Flags{}, time.Second)
   370  	check(Flags{}, flags[0], true)
   371  	ns.SetState(testNode(1), flags[0], Flags{}, 2*time.Second) // extend the timeout to 2s
   372  	check(Flags{}, flags[0], false)
   373  
   374  	clock.Run(2 * time.Second)
   375  	check(flags[0], Flags{}, true)
   376  }
   377  
   378  func TestCallbackOrder(t *testing.T) {
   379  	mdb, clock := rawdb.NewMemoryDatabase(), &mclock.Simulated{}
   380  
   381  	s, flags, _ := testSetup([]bool{false, false, false, false}, nil)
   382  	ns := NewNodeStateMachine(mdb, []byte("-ns"), clock, s)
   383  
   384  	ns.SubscribeState(flags[0], func(n *enode.Node, oldState, newState Flags) {
   385  		if newState.Equals(flags[0]) {
   386  			ns.SetStateSub(n, flags[1], Flags{}, 0)
   387  			ns.SetStateSub(n, flags[2], Flags{}, 0)
   388  		}
   389  	})
   390  	ns.SubscribeState(flags[1], func(n *enode.Node, oldState, newState Flags) {
   391  		if newState.Equals(flags[1]) {
   392  			ns.SetStateSub(n, flags[3], Flags{}, 0)
   393  		}
   394  	})
   395  	lastState := Flags{}
   396  	ns.SubscribeState(MergeFlags(flags[1], flags[2], flags[3]), func(n *enode.Node, oldState, newState Flags) {
   397  		if !oldState.Equals(lastState) {
   398  			t.Fatalf("Wrong callback order")
   399  		}
   400  		lastState = newState
   401  	})
   402  
   403  	ns.Start()
   404  	defer ns.Stop()
   405  
   406  	ns.SetState(testNode(1), flags[0], Flags{}, 0)
   407  }