github.com/rayrapetyan/go-ethereum@v1.8.21/swarm/pss/forwarding_test.go (about)

     1  package pss
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/ethereum/go-ethereum/crypto"
    10  	"github.com/ethereum/go-ethereum/p2p"
    11  	"github.com/ethereum/go-ethereum/p2p/enode"
    12  	"github.com/ethereum/go-ethereum/p2p/protocols"
    13  	"github.com/ethereum/go-ethereum/swarm/network"
    14  	"github.com/ethereum/go-ethereum/swarm/pot"
    15  	whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
    16  )
    17  
    18  type testCase struct {
    19  	name      string
    20  	recipient []byte
    21  	peers     []pot.Address
    22  	expected  []int
    23  	exclusive bool
    24  	nFails    int
    25  	success   bool
    26  	errors    string
    27  }
    28  
    29  var testCases []testCase
    30  
    31  // the purpose of this test is to see that pss.forward() function correctly
    32  // selects the peers for message forwarding, depending on the message address
    33  // and kademlia constellation.
    34  func TestForwardBasic(t *testing.T) {
    35  	baseAddrBytes := make([]byte, 32)
    36  	for i := 0; i < len(baseAddrBytes); i++ {
    37  		baseAddrBytes[i] = 0xFF
    38  	}
    39  	var c testCase
    40  	base := pot.NewAddressFromBytes(baseAddrBytes)
    41  	var peerAddresses []pot.Address
    42  	const depth = 10
    43  	for i := 0; i <= depth; i++ {
    44  		// add two peers for each proximity order
    45  		a := pot.RandomAddressAt(base, i)
    46  		peerAddresses = append(peerAddresses, a)
    47  		a = pot.RandomAddressAt(base, i)
    48  		peerAddresses = append(peerAddresses, a)
    49  	}
    50  
    51  	// skip one level, add one peer at one level deeper.
    52  	// as a result, we will have an edge case of three peers in nearest neighbours' bin.
    53  	peerAddresses = append(peerAddresses, pot.RandomAddressAt(base, depth+2))
    54  
    55  	kad := network.NewKademlia(base[:], network.NewKadParams())
    56  	ps := createPss(t, kad)
    57  	addPeers(kad, peerAddresses)
    58  
    59  	const firstNearest = depth * 2 // shallowest peer in the nearest neighbours' bin
    60  	nearestNeighbours := []int{firstNearest, firstNearest + 1, firstNearest + 2}
    61  	var all []int // indices of all the peers
    62  	for i := 0; i < len(peerAddresses); i++ {
    63  		all = append(all, i)
    64  	}
    65  
    66  	for i := 0; i < len(peerAddresses); i++ {
    67  		// send msg directly to the known peers (recipient address == peer address)
    68  		c = testCase{
    69  			name:      fmt.Sprintf("Send direct to known, id: [%d]", i),
    70  			recipient: peerAddresses[i][:],
    71  			peers:     peerAddresses,
    72  			expected:  []int{i},
    73  			exclusive: false,
    74  		}
    75  		testCases = append(testCases, c)
    76  	}
    77  
    78  	for i := 0; i < firstNearest; i++ {
    79  		// send random messages with proximity orders, corresponding to PO of each bin,
    80  		// with one peer being closer to the recipient address
    81  		a := pot.RandomAddressAt(peerAddresses[i], 64)
    82  		c = testCase{
    83  			name:      fmt.Sprintf("Send random to each PO, id: [%d]", i),
    84  			recipient: a[:],
    85  			peers:     peerAddresses,
    86  			expected:  []int{i},
    87  			exclusive: false,
    88  		}
    89  		testCases = append(testCases, c)
    90  	}
    91  
    92  	for i := 0; i < firstNearest; i++ {
    93  		// send random messages with proximity orders, corresponding to PO of each bin,
    94  		// with random proximity relative to the recipient address
    95  		po := i / 2
    96  		a := pot.RandomAddressAt(base, po)
    97  		c = testCase{
    98  			name:      fmt.Sprintf("Send direct to known, id: [%d]", i),
    99  			recipient: a[:],
   100  			peers:     peerAddresses,
   101  			expected:  []int{po * 2, po*2 + 1},
   102  			exclusive: true,
   103  		}
   104  		testCases = append(testCases, c)
   105  	}
   106  
   107  	for i := firstNearest; i < len(peerAddresses); i++ {
   108  		// recipient address falls into the nearest neighbours' bin
   109  		a := pot.RandomAddressAt(base, i)
   110  		c = testCase{
   111  			name:      fmt.Sprintf("recipient address falls into the nearest neighbours' bin, id: [%d]", i),
   112  			recipient: a[:],
   113  			peers:     peerAddresses,
   114  			expected:  nearestNeighbours,
   115  			exclusive: false,
   116  		}
   117  		testCases = append(testCases, c)
   118  	}
   119  
   120  	// send msg with proximity order much deeper than the deepest nearest neighbour
   121  	a2 := pot.RandomAddressAt(base, 77)
   122  	c = testCase{
   123  		name:      "proximity order much deeper than the deepest nearest neighbour",
   124  		recipient: a2[:],
   125  		peers:     peerAddresses,
   126  		expected:  nearestNeighbours,
   127  		exclusive: false,
   128  	}
   129  	testCases = append(testCases, c)
   130  
   131  	// test with partial addresses
   132  	const part = 12
   133  
   134  	for i := 0; i < firstNearest; i++ {
   135  		// send messages with partial address falling into different proximity orders
   136  		po := i / 2
   137  		if i%8 != 0 {
   138  			c = testCase{
   139  				name:      fmt.Sprintf("partial address falling into different proximity orders, id: [%d]", i),
   140  				recipient: peerAddresses[i][:i],
   141  				peers:     peerAddresses,
   142  				expected:  []int{po * 2, po*2 + 1},
   143  				exclusive: true,
   144  			}
   145  			testCases = append(testCases, c)
   146  		}
   147  		c = testCase{
   148  			name:      fmt.Sprintf("extended partial address falling into different proximity orders, id: [%d]", i),
   149  			recipient: peerAddresses[i][:part],
   150  			peers:     peerAddresses,
   151  			expected:  []int{po * 2, po*2 + 1},
   152  			exclusive: true,
   153  		}
   154  		testCases = append(testCases, c)
   155  	}
   156  
   157  	for i := firstNearest; i < len(peerAddresses); i++ {
   158  		// partial address falls into the nearest neighbours' bin
   159  		c = testCase{
   160  			name:      fmt.Sprintf("partial address falls into the nearest neighbours' bin, id: [%d]", i),
   161  			recipient: peerAddresses[i][:part],
   162  			peers:     peerAddresses,
   163  			expected:  nearestNeighbours,
   164  			exclusive: false,
   165  		}
   166  		testCases = append(testCases, c)
   167  	}
   168  
   169  	// partial address with proximity order deeper than any of the nearest neighbour
   170  	a3 := pot.RandomAddressAt(base, part)
   171  	c = testCase{
   172  		name:      "partial address with proximity order deeper than any of the nearest neighbour",
   173  		recipient: a3[:part],
   174  		peers:     peerAddresses,
   175  		expected:  nearestNeighbours,
   176  		exclusive: false,
   177  	}
   178  	testCases = append(testCases, c)
   179  
   180  	// special cases where partial address matches a large group of peers
   181  
   182  	// zero bytes of address is given, msg should be delivered to all the peers
   183  	c = testCase{
   184  		name:      "zero bytes of address is given",
   185  		recipient: []byte{},
   186  		peers:     peerAddresses,
   187  		expected:  all,
   188  		exclusive: false,
   189  	}
   190  	testCases = append(testCases, c)
   191  
   192  	// luminous radius of 8 bits, proximity order 8
   193  	indexAtPo8 := 16
   194  	c = testCase{
   195  		name:      "luminous radius of 8 bits",
   196  		recipient: []byte{0xFF},
   197  		peers:     peerAddresses,
   198  		expected:  all[indexAtPo8:],
   199  		exclusive: false,
   200  	}
   201  	testCases = append(testCases, c)
   202  
   203  	// luminous radius of 256 bits, proximity order 8
   204  	a4 := pot.Address{}
   205  	a4[0] = 0xFF
   206  	c = testCase{
   207  		name:      "luminous radius of 256 bits",
   208  		recipient: a4[:],
   209  		peers:     peerAddresses,
   210  		expected:  []int{indexAtPo8, indexAtPo8 + 1},
   211  		exclusive: true,
   212  	}
   213  	testCases = append(testCases, c)
   214  
   215  	// check correct behaviour in case send fails
   216  	for i := 2; i < firstNearest-3; i += 2 {
   217  		po := i / 2
   218  		// send random messages with proximity orders, corresponding to PO of each bin,
   219  		// with different numbers of failed attempts.
   220  		// msg should be received by only one of the deeper peers.
   221  		a := pot.RandomAddressAt(base, po)
   222  		c = testCase{
   223  			name:      fmt.Sprintf("Send direct to known, id: [%d]", i),
   224  			recipient: a[:],
   225  			peers:     peerAddresses,
   226  			expected:  all[i+1:],
   227  			exclusive: true,
   228  			nFails:    rand.Int()%3 + 2,
   229  		}
   230  		testCases = append(testCases, c)
   231  	}
   232  
   233  	for _, c := range testCases {
   234  		testForwardMsg(t, ps, &c)
   235  	}
   236  }
   237  
   238  // this function tests the forwarding of a single message. the recipient address is passed as param,
   239  // along with addresses of all peers, and indices of those peers which are expected to receive the message.
   240  func testForwardMsg(t *testing.T, ps *Pss, c *testCase) {
   241  	recipientAddr := c.recipient
   242  	peers := c.peers
   243  	expected := c.expected
   244  	exclusive := c.exclusive
   245  	nFails := c.nFails
   246  	tries := 0 // number of previous failed tries
   247  
   248  	resultMap := make(map[pot.Address]int)
   249  
   250  	defer func() { sendFunc = sendMsg }()
   251  	sendFunc = func(_ *Pss, sp *network.Peer, _ *PssMsg) bool {
   252  		if tries < nFails {
   253  			tries++
   254  			return false
   255  		}
   256  		a := pot.NewAddressFromBytes(sp.Address())
   257  		resultMap[a]++
   258  		return true
   259  	}
   260  
   261  	msg := newTestMsg(recipientAddr)
   262  	ps.forward(msg)
   263  
   264  	// check test results
   265  	var fail bool
   266  	precision := len(recipientAddr)
   267  	if precision > 4 {
   268  		precision = 4
   269  	}
   270  	s := fmt.Sprintf("test [%s]\nmsg address: %x..., radius: %d", c.name, recipientAddr[:precision], 8*len(recipientAddr))
   271  
   272  	// false negatives (expected message didn't reach peer)
   273  	if exclusive {
   274  		var cnt int
   275  		for _, i := range expected {
   276  			a := peers[i]
   277  			cnt += resultMap[a]
   278  			resultMap[a] = 0
   279  		}
   280  		if cnt != 1 {
   281  			s += fmt.Sprintf("\n%d messages received by %d peers with indices: [%v]", cnt, len(expected), expected)
   282  			fail = true
   283  		}
   284  	} else {
   285  		for _, i := range expected {
   286  			a := peers[i]
   287  			received := resultMap[a]
   288  			if received != 1 {
   289  				s += fmt.Sprintf("\npeer number %d [%x...] received %d messages", i, a[:4], received)
   290  				fail = true
   291  			}
   292  			resultMap[a] = 0
   293  		}
   294  	}
   295  
   296  	// false positives (unexpected message reached peer)
   297  	for k, v := range resultMap {
   298  		if v != 0 {
   299  			// find the index of the false positive peer
   300  			var j int
   301  			for j = 0; j < len(peers); j++ {
   302  				if peers[j] == k {
   303  					break
   304  				}
   305  			}
   306  			s += fmt.Sprintf("\npeer number %d [%x...] received %d messages", j, k[:4], v)
   307  			fail = true
   308  		}
   309  	}
   310  
   311  	if fail {
   312  		t.Fatal(s)
   313  	}
   314  }
   315  
   316  func addPeers(kad *network.Kademlia, addresses []pot.Address) {
   317  	for _, a := range addresses {
   318  		p := newTestDiscoveryPeer(a, kad)
   319  		kad.On(p)
   320  	}
   321  }
   322  
   323  func createPss(t *testing.T, kad *network.Kademlia) *Pss {
   324  	privKey, err := crypto.GenerateKey()
   325  	pssp := NewPssParams().WithPrivateKey(privKey)
   326  	ps, err := NewPss(kad, pssp)
   327  	if err != nil {
   328  		t.Fatal(err.Error())
   329  	}
   330  	return ps
   331  }
   332  
   333  func newTestDiscoveryPeer(addr pot.Address, kad *network.Kademlia) *network.Peer {
   334  	rw := &p2p.MsgPipeRW{}
   335  	p := p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{})
   336  	pp := protocols.NewPeer(p, rw, &protocols.Spec{})
   337  	bp := &network.BzzPeer{
   338  		Peer: pp,
   339  		BzzAddr: &network.BzzAddr{
   340  			OAddr: addr.Bytes(),
   341  			UAddr: []byte(fmt.Sprintf("%x", addr[:])),
   342  		},
   343  	}
   344  	return network.NewPeer(bp, kad)
   345  }
   346  
   347  func newTestMsg(addr []byte) *PssMsg {
   348  	msg := newPssMsg(&msgParams{})
   349  	msg.To = addr[:]
   350  	msg.Expire = uint32(time.Now().Add(time.Second * 60).Unix())
   351  	msg.Payload = &whisper.Envelope{
   352  		Topic: [4]byte{},
   353  		Data:  []byte("i have nothing to hide"),
   354  	}
   355  	return msg
   356  }