358 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			358 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package pss
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"math/rand"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/ethereum/go-ethereum/crypto"
 | 
						|
	"github.com/ethereum/go-ethereum/p2p"
 | 
						|
	"github.com/ethereum/go-ethereum/p2p/enode"
 | 
						|
	"github.com/ethereum/go-ethereum/p2p/protocols"
 | 
						|
	"github.com/ethereum/go-ethereum/swarm/network"
 | 
						|
	"github.com/ethereum/go-ethereum/swarm/pot"
 | 
						|
	whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
 | 
						|
)
 | 
						|
 | 
						|
type testCase struct {
 | 
						|
	name      string
 | 
						|
	recipient []byte
 | 
						|
	peers     []pot.Address
 | 
						|
	expected  []int
 | 
						|
	exclusive bool
 | 
						|
	nFails    int
 | 
						|
	success   bool
 | 
						|
	errors    string
 | 
						|
}
 | 
						|
 | 
						|
var testCases []testCase
 | 
						|
 | 
						|
// the purpose of this test is to see that pss.forward() function correctly
 | 
						|
// selects the peers for message forwarding, depending on the message address
 | 
						|
// and kademlia constellation.
 | 
						|
func TestForwardBasic(t *testing.T) {
 | 
						|
	baseAddrBytes := make([]byte, 32)
 | 
						|
	for i := 0; i < len(baseAddrBytes); i++ {
 | 
						|
		baseAddrBytes[i] = 0xFF
 | 
						|
	}
 | 
						|
	var c testCase
 | 
						|
	base := pot.NewAddressFromBytes(baseAddrBytes)
 | 
						|
	var peerAddresses []pot.Address
 | 
						|
	const depth = 10
 | 
						|
	for i := 0; i <= depth; i++ {
 | 
						|
		// add two peers for each proximity order
 | 
						|
		a := pot.RandomAddressAt(base, i)
 | 
						|
		peerAddresses = append(peerAddresses, a)
 | 
						|
		a = pot.RandomAddressAt(base, i)
 | 
						|
		peerAddresses = append(peerAddresses, a)
 | 
						|
	}
 | 
						|
 | 
						|
	// skip one level, add one peer at one level deeper.
 | 
						|
	// as a result, we will have an edge case of three peers in nearest neighbours' bin.
 | 
						|
	peerAddresses = append(peerAddresses, pot.RandomAddressAt(base, depth+2))
 | 
						|
 | 
						|
	kad := network.NewKademlia(base[:], network.NewKadParams())
 | 
						|
	ps := createPss(t, kad)
 | 
						|
	defer ps.Stop()
 | 
						|
	addPeers(kad, peerAddresses)
 | 
						|
 | 
						|
	const firstNearest = depth * 2 // shallowest peer in the nearest neighbours' bin
 | 
						|
	nearestNeighbours := []int{firstNearest, firstNearest + 1, firstNearest + 2}
 | 
						|
	var all []int // indices of all the peers
 | 
						|
	for i := 0; i < len(peerAddresses); i++ {
 | 
						|
		all = append(all, i)
 | 
						|
	}
 | 
						|
 | 
						|
	for i := 0; i < len(peerAddresses); i++ {
 | 
						|
		// send msg directly to the known peers (recipient address == peer address)
 | 
						|
		c = testCase{
 | 
						|
			name:      fmt.Sprintf("Send direct to known, id: [%d]", i),
 | 
						|
			recipient: peerAddresses[i][:],
 | 
						|
			peers:     peerAddresses,
 | 
						|
			expected:  []int{i},
 | 
						|
			exclusive: false,
 | 
						|
		}
 | 
						|
		testCases = append(testCases, c)
 | 
						|
	}
 | 
						|
 | 
						|
	for i := 0; i < firstNearest; i++ {
 | 
						|
		// send random messages with proximity orders, corresponding to PO of each bin,
 | 
						|
		// with one peer being closer to the recipient address
 | 
						|
		a := pot.RandomAddressAt(peerAddresses[i], 64)
 | 
						|
		c = testCase{
 | 
						|
			name:      fmt.Sprintf("Send random to each PO, id: [%d]", i),
 | 
						|
			recipient: a[:],
 | 
						|
			peers:     peerAddresses,
 | 
						|
			expected:  []int{i},
 | 
						|
			exclusive: false,
 | 
						|
		}
 | 
						|
		testCases = append(testCases, c)
 | 
						|
	}
 | 
						|
 | 
						|
	for i := 0; i < firstNearest; i++ {
 | 
						|
		// send random messages with proximity orders, corresponding to PO of each bin,
 | 
						|
		// with random proximity relative to the recipient address
 | 
						|
		po := i / 2
 | 
						|
		a := pot.RandomAddressAt(base, po)
 | 
						|
		c = testCase{
 | 
						|
			name:      fmt.Sprintf("Send direct to known, id: [%d]", i),
 | 
						|
			recipient: a[:],
 | 
						|
			peers:     peerAddresses,
 | 
						|
			expected:  []int{po * 2, po*2 + 1},
 | 
						|
			exclusive: true,
 | 
						|
		}
 | 
						|
		testCases = append(testCases, c)
 | 
						|
	}
 | 
						|
 | 
						|
	for i := firstNearest; i < len(peerAddresses); i++ {
 | 
						|
		// recipient address falls into the nearest neighbours' bin
 | 
						|
		a := pot.RandomAddressAt(base, i)
 | 
						|
		c = testCase{
 | 
						|
			name:      fmt.Sprintf("recipient address falls into the nearest neighbours' bin, id: [%d]", i),
 | 
						|
			recipient: a[:],
 | 
						|
			peers:     peerAddresses,
 | 
						|
			expected:  nearestNeighbours,
 | 
						|
			exclusive: false,
 | 
						|
		}
 | 
						|
		testCases = append(testCases, c)
 | 
						|
	}
 | 
						|
 | 
						|
	// send msg with proximity order much deeper than the deepest nearest neighbour
 | 
						|
	a2 := pot.RandomAddressAt(base, 77)
 | 
						|
	c = testCase{
 | 
						|
		name:      "proximity order much deeper than the deepest nearest neighbour",
 | 
						|
		recipient: a2[:],
 | 
						|
		peers:     peerAddresses,
 | 
						|
		expected:  nearestNeighbours,
 | 
						|
		exclusive: false,
 | 
						|
	}
 | 
						|
	testCases = append(testCases, c)
 | 
						|
 | 
						|
	// test with partial addresses
 | 
						|
	const part = 12
 | 
						|
 | 
						|
	for i := 0; i < firstNearest; i++ {
 | 
						|
		// send messages with partial address falling into different proximity orders
 | 
						|
		po := i / 2
 | 
						|
		if i%8 != 0 {
 | 
						|
			c = testCase{
 | 
						|
				name:      fmt.Sprintf("partial address falling into different proximity orders, id: [%d]", i),
 | 
						|
				recipient: peerAddresses[i][:i],
 | 
						|
				peers:     peerAddresses,
 | 
						|
				expected:  []int{po * 2, po*2 + 1},
 | 
						|
				exclusive: true,
 | 
						|
			}
 | 
						|
			testCases = append(testCases, c)
 | 
						|
		}
 | 
						|
		c = testCase{
 | 
						|
			name:      fmt.Sprintf("extended partial address falling into different proximity orders, id: [%d]", i),
 | 
						|
			recipient: peerAddresses[i][:part],
 | 
						|
			peers:     peerAddresses,
 | 
						|
			expected:  []int{po * 2, po*2 + 1},
 | 
						|
			exclusive: true,
 | 
						|
		}
 | 
						|
		testCases = append(testCases, c)
 | 
						|
	}
 | 
						|
 | 
						|
	for i := firstNearest; i < len(peerAddresses); i++ {
 | 
						|
		// partial address falls into the nearest neighbours' bin
 | 
						|
		c = testCase{
 | 
						|
			name:      fmt.Sprintf("partial address falls into the nearest neighbours' bin, id: [%d]", i),
 | 
						|
			recipient: peerAddresses[i][:part],
 | 
						|
			peers:     peerAddresses,
 | 
						|
			expected:  nearestNeighbours,
 | 
						|
			exclusive: false,
 | 
						|
		}
 | 
						|
		testCases = append(testCases, c)
 | 
						|
	}
 | 
						|
 | 
						|
	// partial address with proximity order deeper than any of the nearest neighbour
 | 
						|
	a3 := pot.RandomAddressAt(base, part)
 | 
						|
	c = testCase{
 | 
						|
		name:      "partial address with proximity order deeper than any of the nearest neighbour",
 | 
						|
		recipient: a3[:part],
 | 
						|
		peers:     peerAddresses,
 | 
						|
		expected:  nearestNeighbours,
 | 
						|
		exclusive: false,
 | 
						|
	}
 | 
						|
	testCases = append(testCases, c)
 | 
						|
 | 
						|
	// special cases where partial address matches a large group of peers
 | 
						|
 | 
						|
	// zero bytes of address is given, msg should be delivered to all the peers
 | 
						|
	c = testCase{
 | 
						|
		name:      "zero bytes of address is given",
 | 
						|
		recipient: []byte{},
 | 
						|
		peers:     peerAddresses,
 | 
						|
		expected:  all,
 | 
						|
		exclusive: false,
 | 
						|
	}
 | 
						|
	testCases = append(testCases, c)
 | 
						|
 | 
						|
	// luminous radius of 8 bits, proximity order 8
 | 
						|
	indexAtPo8 := 16
 | 
						|
	c = testCase{
 | 
						|
		name:      "luminous radius of 8 bits",
 | 
						|
		recipient: []byte{0xFF},
 | 
						|
		peers:     peerAddresses,
 | 
						|
		expected:  all[indexAtPo8:],
 | 
						|
		exclusive: false,
 | 
						|
	}
 | 
						|
	testCases = append(testCases, c)
 | 
						|
 | 
						|
	// luminous radius of 256 bits, proximity order 8
 | 
						|
	a4 := pot.Address{}
 | 
						|
	a4[0] = 0xFF
 | 
						|
	c = testCase{
 | 
						|
		name:      "luminous radius of 256 bits",
 | 
						|
		recipient: a4[:],
 | 
						|
		peers:     peerAddresses,
 | 
						|
		expected:  []int{indexAtPo8, indexAtPo8 + 1},
 | 
						|
		exclusive: true,
 | 
						|
	}
 | 
						|
	testCases = append(testCases, c)
 | 
						|
 | 
						|
	// check correct behaviour in case send fails
 | 
						|
	for i := 2; i < firstNearest-3; i += 2 {
 | 
						|
		po := i / 2
 | 
						|
		// send random messages with proximity orders, corresponding to PO of each bin,
 | 
						|
		// with different numbers of failed attempts.
 | 
						|
		// msg should be received by only one of the deeper peers.
 | 
						|
		a := pot.RandomAddressAt(base, po)
 | 
						|
		c = testCase{
 | 
						|
			name:      fmt.Sprintf("Send direct to known, id: [%d]", i),
 | 
						|
			recipient: a[:],
 | 
						|
			peers:     peerAddresses,
 | 
						|
			expected:  all[i+1:],
 | 
						|
			exclusive: true,
 | 
						|
			nFails:    rand.Int()%3 + 2,
 | 
						|
		}
 | 
						|
		testCases = append(testCases, c)
 | 
						|
	}
 | 
						|
 | 
						|
	for _, c := range testCases {
 | 
						|
		testForwardMsg(t, ps, &c)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// this function tests the forwarding of a single message. the recipient address is passed as param,
 | 
						|
// along with addresses of all peers, and indices of those peers which are expected to receive the message.
 | 
						|
func testForwardMsg(t *testing.T, ps *Pss, c *testCase) {
 | 
						|
	recipientAddr := c.recipient
 | 
						|
	peers := c.peers
 | 
						|
	expected := c.expected
 | 
						|
	exclusive := c.exclusive
 | 
						|
	nFails := c.nFails
 | 
						|
	tries := 0 // number of previous failed tries
 | 
						|
 | 
						|
	resultMap := make(map[pot.Address]int)
 | 
						|
 | 
						|
	defer func() { sendFunc = sendMsg }()
 | 
						|
	sendFunc = func(_ *Pss, sp *network.Peer, _ *PssMsg) bool {
 | 
						|
		if tries < nFails {
 | 
						|
			tries++
 | 
						|
			return false
 | 
						|
		}
 | 
						|
		a := pot.NewAddressFromBytes(sp.Address())
 | 
						|
		resultMap[a]++
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	msg := newTestMsg(recipientAddr)
 | 
						|
	ps.forward(msg)
 | 
						|
 | 
						|
	// check test results
 | 
						|
	var fail bool
 | 
						|
	precision := len(recipientAddr)
 | 
						|
	if precision > 4 {
 | 
						|
		precision = 4
 | 
						|
	}
 | 
						|
	s := fmt.Sprintf("test [%s]\nmsg address: %x..., radius: %d", c.name, recipientAddr[:precision], 8*len(recipientAddr))
 | 
						|
 | 
						|
	// false negatives (expected message didn't reach peer)
 | 
						|
	if exclusive {
 | 
						|
		var cnt int
 | 
						|
		for _, i := range expected {
 | 
						|
			a := peers[i]
 | 
						|
			cnt += resultMap[a]
 | 
						|
			resultMap[a] = 0
 | 
						|
		}
 | 
						|
		if cnt != 1 {
 | 
						|
			s += fmt.Sprintf("\n%d messages received by %d peers with indices: [%v]", cnt, len(expected), expected)
 | 
						|
			fail = true
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		for _, i := range expected {
 | 
						|
			a := peers[i]
 | 
						|
			received := resultMap[a]
 | 
						|
			if received != 1 {
 | 
						|
				s += fmt.Sprintf("\npeer number %d [%x...] received %d messages", i, a[:4], received)
 | 
						|
				fail = true
 | 
						|
			}
 | 
						|
			resultMap[a] = 0
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// false positives (unexpected message reached peer)
 | 
						|
	for k, v := range resultMap {
 | 
						|
		if v != 0 {
 | 
						|
			// find the index of the false positive peer
 | 
						|
			var j int
 | 
						|
			for j = 0; j < len(peers); j++ {
 | 
						|
				if peers[j] == k {
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
			s += fmt.Sprintf("\npeer number %d [%x...] received %d messages", j, k[:4], v)
 | 
						|
			fail = true
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if fail {
 | 
						|
		t.Fatal(s)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func addPeers(kad *network.Kademlia, addresses []pot.Address) {
 | 
						|
	for _, a := range addresses {
 | 
						|
		p := newTestDiscoveryPeer(a, kad)
 | 
						|
		kad.On(p)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func createPss(t *testing.T, kad *network.Kademlia) *Pss {
 | 
						|
	privKey, err := crypto.GenerateKey()
 | 
						|
	pssp := NewPssParams().WithPrivateKey(privKey)
 | 
						|
	ps, err := NewPss(kad, pssp)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatal(err.Error())
 | 
						|
	}
 | 
						|
	return ps
 | 
						|
}
 | 
						|
 | 
						|
func newTestDiscoveryPeer(addr pot.Address, kad *network.Kademlia) *network.Peer {
 | 
						|
	rw := &p2p.MsgPipeRW{}
 | 
						|
	p := p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{})
 | 
						|
	pp := protocols.NewPeer(p, rw, &protocols.Spec{})
 | 
						|
	bp := &network.BzzPeer{
 | 
						|
		Peer: pp,
 | 
						|
		BzzAddr: &network.BzzAddr{
 | 
						|
			OAddr: addr.Bytes(),
 | 
						|
			UAddr: []byte(fmt.Sprintf("%x", addr[:])),
 | 
						|
		},
 | 
						|
	}
 | 
						|
	return network.NewPeer(bp, kad)
 | 
						|
}
 | 
						|
 | 
						|
func newTestMsg(addr []byte) *PssMsg {
 | 
						|
	msg := newPssMsg(&msgParams{})
 | 
						|
	msg.To = addr[:]
 | 
						|
	msg.Expire = uint32(time.Now().Add(time.Second * 60).Unix())
 | 
						|
	msg.Payload = &whisper.Envelope{
 | 
						|
		Topic: [4]byte{},
 | 
						|
		Data:  []byte("i have nothing to hide"),
 | 
						|
	}
 | 
						|
	return msg
 | 
						|
}
 |