github.com/core-coin/go-core/v2@v2.1.9/p2p/discv5/database_test.go (about)

     1  // Copyright 2015 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 discv5
    18  
    19  import (
    20  	"bytes"
    21  	"io/ioutil"
    22  	"net"
    23  	"os"
    24  	"path/filepath"
    25  	"reflect"
    26  	"testing"
    27  	"time"
    28  )
    29  
    30  var nodeDBKeyTests = []struct {
    31  	id    NodeID
    32  	field string
    33  	key   []byte
    34  }{
    35  	{
    36  		id:    NodeID{},
    37  		field: "version",
    38  		key:   []byte{0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e}, // field
    39  	},
    40  	{
    41  		id:    MustHexID("1033b1bac4c731e800b6399a357e51cf1b20eec942aac608c90b89553003e2ed3f94bd80613ee9006b1e62b6bb45109d0db9a4833e78363992"),
    42  		field: ":discover",
    43  		key: []byte{0x6e, 0x3a, // prefix
    44  			0x10, 0x33, 0xb1, 0xba, 0xc4, 0xc7, 0x31, 0xe8, 0x00, // node id
    45  			0xb6, 0x39, 0x9a, 0x35, 0x7e, 0x51, 0xcf, 0x1b, 0x20,
    46  			0xee, 0xc9, 0x42, 0xaa, 0xc6, 0x08, 0xc9, 0x0b, 0x89,
    47  			0x55, 0x30, 0x03, 0xe2, 0xed, 0x3f, 0x94, 0xbd, 0x80,
    48  			0x61, 0x3e, 0xe9, 0x00, 0x6b, 0x1e, 0x62, 0xb6, 0xbb,
    49  			0x45, 0x10, 0x9d, 0x0d, 0xb9, 0xa4, 0x83, 0x3e, 0x78,
    50  			0x36, 0x39, 0x92,
    51  			0x3a, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, // field
    52  		},
    53  	},
    54  }
    55  
    56  func TestNodeDBKeys(t *testing.T) {
    57  	for i, tt := range nodeDBKeyTests {
    58  		if key := makeKey(tt.id, tt.field); !bytes.Equal(key, tt.key) {
    59  			t.Errorf("make test %d: key mismatch: have 0x%x, want 0x%x", i, key, tt.key)
    60  		}
    61  		id, field := splitKey(tt.key)
    62  		if !bytes.Equal(id[:], tt.id[:]) {
    63  			t.Errorf("split test %d: id mismatch: have 0x%x, want 0x%x", i, id, tt.id)
    64  		}
    65  		if field != tt.field {
    66  			t.Errorf("split test %d: field mismatch: have 0x%x, want 0x%x", i, field, tt.field)
    67  		}
    68  	}
    69  }
    70  
    71  var nodeDBInt64Tests = []struct {
    72  	key   []byte
    73  	value int64
    74  }{
    75  	{key: []byte{0x01}, value: 1},
    76  	{key: []byte{0x02}, value: 2},
    77  	{key: []byte{0x03}, value: 3},
    78  }
    79  
    80  func TestNodeDBInt64(t *testing.T) {
    81  	db, _ := newNodeDB("", Version, NodeID{})
    82  	defer db.close()
    83  
    84  	tests := nodeDBInt64Tests
    85  	for i := 0; i < len(tests); i++ {
    86  		// Insert the next value
    87  		if err := db.storeInt64(tests[i].key, tests[i].value); err != nil {
    88  			t.Errorf("test %d: failed to store value: %v", i, err)
    89  		}
    90  		// Check all existing and non existing values
    91  		for j := 0; j < len(tests); j++ {
    92  			num := db.fetchInt64(tests[j].key)
    93  			switch {
    94  			case j <= i && num != tests[j].value:
    95  				t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, tests[j].value)
    96  			case j > i && num != 0:
    97  				t.Errorf("test %d, item %d: value mismatch: have %v, want %v", i, j, num, 0)
    98  			}
    99  		}
   100  	}
   101  }
   102  
   103  func TestNodeDBFetchStore(t *testing.T) {
   104  	node := NewNode(
   105  		MustHexID("1033b1bac4c731e800b6399a357e51cf1b20eec942aac608c90b89553003e2ed3f94bd80613ee9006b1e62b6bb45109d0db9a4833e78363992"),
   106  		net.IP{192, 168, 0, 1},
   107  		30300,
   108  		30300,
   109  	)
   110  	inst := time.Now()
   111  	num := 314
   112  
   113  	db, _ := newNodeDB("", Version, NodeID{})
   114  	defer db.close()
   115  
   116  	// Check fetch/store operations on a node ping object
   117  	if stored := db.lastPing(node.ID); stored.Unix() != 0 {
   118  		t.Errorf("ping: non-existing object: %v", stored)
   119  	}
   120  	if err := db.updateLastPing(node.ID, inst); err != nil {
   121  		t.Errorf("ping: failed to update: %v", err)
   122  	}
   123  	if stored := db.lastPing(node.ID); stored.Unix() != inst.Unix() {
   124  		t.Errorf("ping: value mismatch: have %v, want %v", stored, inst)
   125  	}
   126  	// Check fetch/store operations on a node pong object
   127  	if stored := db.lastPong(node.ID); stored.Unix() != 0 {
   128  		t.Errorf("pong: non-existing object: %v", stored)
   129  	}
   130  	if err := db.updateLastPong(node.ID, inst); err != nil {
   131  		t.Errorf("pong: failed to update: %v", err)
   132  	}
   133  	if stored := db.lastPong(node.ID); stored.Unix() != inst.Unix() {
   134  		t.Errorf("pong: value mismatch: have %v, want %v", stored, inst)
   135  	}
   136  	// Check fetch/store operations on a node findnode-failure object
   137  	if stored := db.findFails(node.ID); stored != 0 {
   138  		t.Errorf("find-node fails: non-existing object: %v", stored)
   139  	}
   140  	if err := db.updateFindFails(node.ID, num); err != nil {
   141  		t.Errorf("find-node fails: failed to update: %v", err)
   142  	}
   143  	if stored := db.findFails(node.ID); stored != num {
   144  		t.Errorf("find-node fails: value mismatch: have %v, want %v", stored, num)
   145  	}
   146  	// Check fetch/store operations on an actual node object
   147  	if stored := db.node(node.ID); stored != nil {
   148  		t.Errorf("node: non-existing object: %v", stored)
   149  	}
   150  	if err := db.updateNode(node); err != nil {
   151  		t.Errorf("node: failed to update: %v", err)
   152  	}
   153  	if stored := db.node(node.ID); stored == nil {
   154  		t.Errorf("node: not found")
   155  	} else if !reflect.DeepEqual(stored, node) {
   156  		t.Errorf("node: data mismatch: have %v, want %v", stored, node)
   157  	}
   158  }
   159  
   160  var nodeDBSeedQueryNodes = []struct {
   161  	node *Node
   162  	pong time.Time
   163  }{
   164  	// This one should not be in the result set because its last
   165  	// pong time is too far in the past.
   166  	{
   167  		node: NewNode(
   168  			MustHexID("0x84d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"),
   169  			net.IP{127, 0, 0, 3},
   170  			30300,
   171  			30300,
   172  		),
   173  		pong: time.Now().Add(-3 * time.Hour),
   174  	},
   175  	// This one shouldn't be in the result set because its
   176  	// nodeID is the local node's ID.
   177  	{
   178  		node: NewNode(
   179  			MustHexID("0x57d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"),
   180  			net.IP{127, 0, 0, 3},
   181  			30300,
   182  			30300,
   183  		),
   184  		pong: time.Now().Add(-4 * time.Second),
   185  	},
   186  
   187  	// These should be in the result set.
   188  	{
   189  		node: NewNode(
   190  			MustHexID("0x22d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"),
   191  			net.IP{127, 0, 0, 1},
   192  			30300,
   193  			30300,
   194  		),
   195  		pong: time.Now().Add(-2 * time.Second),
   196  	},
   197  	{
   198  		node: NewNode(
   199  			MustHexID("0x44d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"),
   200  			net.IP{127, 0, 0, 2},
   201  			30300,
   202  			30300,
   203  		),
   204  		pong: time.Now().Add(-3 * time.Second),
   205  	},
   206  	{
   207  		node: NewNode(
   208  			MustHexID("0xe2d9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270"),
   209  			net.IP{127, 0, 0, 3},
   210  			30300,
   211  			30300,
   212  		),
   213  		pong: time.Now().Add(-1 * time.Second),
   214  	},
   215  }
   216  
   217  func TestNodeDBSeedQuery(t *testing.T) {
   218  	db, _ := newNodeDB("", Version, nodeDBSeedQueryNodes[1].node.ID)
   219  	defer db.close()
   220  
   221  	// Insert a batch of nodes for querying
   222  	for i, seed := range nodeDBSeedQueryNodes {
   223  		if err := db.updateNode(seed.node); err != nil {
   224  			t.Fatalf("node %d: failed to insert: %v", i, err)
   225  		}
   226  		if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil {
   227  			t.Fatalf("node %d: failed to insert lastPong: %v", i, err)
   228  		}
   229  	}
   230  
   231  	// Retrieve the entire batch and check for duplicates
   232  	seeds := db.querySeeds(len(nodeDBSeedQueryNodes)*2, time.Hour)
   233  	have := make(map[NodeID]struct{})
   234  	for _, seed := range seeds {
   235  		have[seed.ID] = struct{}{}
   236  	}
   237  	want := make(map[NodeID]struct{})
   238  	for _, seed := range nodeDBSeedQueryNodes[2:] {
   239  		want[seed.node.ID] = struct{}{}
   240  	}
   241  	if len(seeds) != len(want) {
   242  		t.Errorf("seed count mismatch: have %v, want %v", len(seeds), len(want))
   243  	}
   244  	for id := range have {
   245  		if _, ok := want[id]; !ok {
   246  			t.Errorf("extra seed: %v", id)
   247  		}
   248  	}
   249  	for id := range want {
   250  		if _, ok := have[id]; !ok {
   251  			t.Errorf("missing seed: %v", id)
   252  		}
   253  	}
   254  }
   255  
   256  func TestNodeDBPersistency(t *testing.T) {
   257  	root, err := ioutil.TempDir("", "nodedb-")
   258  	if err != nil {
   259  		t.Fatalf("failed to create temporary data folder: %v", err)
   260  	}
   261  	defer os.RemoveAll(root)
   262  
   263  	var (
   264  		testKey = []byte("somekey")
   265  		testInt = int64(314)
   266  	)
   267  
   268  	// Create a persistent database and store some values
   269  	db, err := newNodeDB(filepath.Join(root, "database"), Version, NodeID{})
   270  	if err != nil {
   271  		t.Fatalf("failed to create persistent database: %v", err)
   272  	}
   273  	if err := db.storeInt64(testKey, testInt); err != nil {
   274  		t.Fatalf("failed to store value: %v.", err)
   275  	}
   276  	db.close()
   277  
   278  	// Reopen the database and check the value
   279  	db, err = newNodeDB(filepath.Join(root, "database"), Version, NodeID{})
   280  	if err != nil {
   281  		t.Fatalf("failed to open persistent database: %v", err)
   282  	}
   283  	if val := db.fetchInt64(testKey); val != testInt {
   284  		t.Fatalf("value mismatch: have %v, want %v", val, testInt)
   285  	}
   286  	db.close()
   287  
   288  	// Change the database version and check flush
   289  	db, err = newNodeDB(filepath.Join(root, "database"), Version+1, NodeID{})
   290  	if err != nil {
   291  		t.Fatalf("failed to open persistent database: %v", err)
   292  	}
   293  	if val := db.fetchInt64(testKey); val != 0 {
   294  		t.Fatalf("value mismatch: have %v, want %v", val, 0)
   295  	}
   296  	db.close()
   297  }
   298  
   299  var nodeDBExpirationNodes = []struct {
   300  	node *Node
   301  	pong time.Time
   302  	exp  bool
   303  }{
   304  	{
   305  		node: NewNode(
   306  			MustHexID("1033b1bac4c731e800b6399a357e51cf1b20eec942aac608c90b89553003e2ed3f94bd80613ee9006b1e62b6bb45109d0db9a4833e78363997"),
   307  			net.IP{127, 0, 0, 1},
   308  			30300,
   309  			30300,
   310  		),
   311  		pong: time.Now().Add(-nodeDBNodeExpiration + time.Minute),
   312  		exp:  false,
   313  	}, {
   314  		node: NewNode(
   315  			MustHexID("1033b1bac4c731e800b6399a357e51cf1b20eec942aac608c90b89553003e2ed3f94bd80613ee9006b1e62b6bb45109d0db9a4833e78363998"),
   316  			net.IP{127, 0, 0, 2},
   317  			30300,
   318  			30300,
   319  		),
   320  		pong: time.Now().Add(-nodeDBNodeExpiration - time.Minute),
   321  		exp:  true,
   322  	},
   323  }
   324  
   325  func TestNodeDBExpiration(t *testing.T) {
   326  	db, _ := newNodeDB("", Version, NodeID{})
   327  	defer db.close()
   328  
   329  	// Add all the test nodes and set their last pong time
   330  	for i, seed := range nodeDBExpirationNodes {
   331  		if err := db.updateNode(seed.node); err != nil {
   332  			t.Fatalf("node %d: failed to insert: %v", i, err)
   333  		}
   334  		if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil {
   335  			t.Fatalf("node %d: failed to update pong: %v", i, err)
   336  		}
   337  	}
   338  	// Expire some of them, and check the rest
   339  	if err := db.expireNodes(); err != nil {
   340  		t.Fatalf("failed to expire nodes: %v", err)
   341  	}
   342  	for i, seed := range nodeDBExpirationNodes {
   343  		node := db.node(seed.node.ID)
   344  		if (node == nil && !seed.exp) || (node != nil && seed.exp) {
   345  			t.Errorf("node %d: expiration mismatch: have %v, want %v", i, node, seed.exp)
   346  		}
   347  	}
   348  }
   349  
   350  func TestNodeDBSelfExpiration(t *testing.T) {
   351  	// Find a node in the tests that shouldn't expire, and assign it as self
   352  	var self NodeID
   353  	for _, node := range nodeDBExpirationNodes {
   354  		if !node.exp {
   355  			self = node.node.ID
   356  			break
   357  		}
   358  	}
   359  	db, _ := newNodeDB("", Version, self)
   360  	defer db.close()
   361  
   362  	// Add all the test nodes and set their last pong time
   363  	for i, seed := range nodeDBExpirationNodes {
   364  		if err := db.updateNode(seed.node); err != nil {
   365  			t.Fatalf("node %d: failed to insert: %v", i, err)
   366  		}
   367  		if err := db.updateLastPong(seed.node.ID, seed.pong); err != nil {
   368  			t.Fatalf("node %d: failed to update pong: %v", i, err)
   369  		}
   370  	}
   371  	// Expire the nodes and make sure self has been evacuated too
   372  	if err := db.expireNodes(); err != nil {
   373  		t.Fatalf("failed to expire nodes: %v", err)
   374  	}
   375  	node := db.node(self)
   376  	if node != nil {
   377  		t.Errorf("self not evacuated")
   378  	}
   379  }