Skip to content

Commit

Permalink
Routing: unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Gilthoniel committed May 20, 2020
1 parent 10f1779 commit 082efc1
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 25 deletions.
58 changes: 34 additions & 24 deletions mino/minogrpc/routing/routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package routing

import (
"bytes"
"crypto/sha256"
"encoding/binary"
"fmt"
"io"
Expand All @@ -12,8 +11,8 @@ import (
"sort"

"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/any"
"go.dedis.ch/fabric/crypto"
"go.dedis.ch/fabric/encoding"
"go.dedis.ch/fabric/mino"
"golang.org/x/xerrors"
Expand Down Expand Up @@ -52,38 +51,36 @@ type Routing interface {
GetDirectLinks(from mino.Address) []mino.Address
}

// TreeRouting holds the routing tree of a network. It allows each node of the
// tree to know which child it should contact in order to relay a message that
// is in it sub-tree.
//
// - implements Routing
type TreeRouting struct {
Root *treeNode
routingNodes map[mino.Address]*treeNode
}

// TreeRoutingFactory defines the factory for tree routing.
//
// - implements routing.Factory
type TreeRoutingFactory struct {
height int
encoder encoding.ProtoMarshaler
addrFactory mino.AddressFactory
hashFactory crypto.HashFactory
}

// NewTreeRoutingFactory returns a new treeRoutingFactory. The rootAddr should
// be comparable to the addresses that will be passed to build the tree.
func NewTreeRoutingFactory(height int, addrFactory mino.AddressFactory) *TreeRoutingFactory {
return &TreeRoutingFactory{
height: height,
encoder: encoding.NewProtoEncoder(),
addrFactory: addrFactory,
hashFactory: crypto.NewSha256Factory(),
}
}

// GetAddressFactory implements routing.Factory.
// GetAddressFactory implements routing.Factory. It returns the address factory
// for this routing.
func (t TreeRoutingFactory) GetAddressFactory() mino.AddressFactory {
return t.addrFactory
}

// FromIterator creates the network tree in a deterministic manner based on
// the addresses. The root address is automatically exluded if present.
// FromIterator implements routing.Factory. It creates the network tree in a
// deterministic manner based on the addresses. The root address is
// automatically exluded if present.
func (t TreeRoutingFactory) FromIterator(root mino.Address,
iterator mino.AddressIterator) (Routing, error) {

Expand All @@ -105,18 +102,19 @@ func (t TreeRoutingFactory) FromIterator(root mino.Address,

routing, err := t.fromAddrBuf(root, addrsBuf)
if err != nil {
return nil, xerrors.Errorf("failed to build from addrsBuf: %v", err)
return nil, xerrors.Errorf("failed to build routing: %v", err)
}

return routing, nil
}

// FromAny creates the network tree in a deterministic manner based on the proto
// message encoded as any. It must not contain the root address, which is the
// case if the Pack() method has been used.
// FromAny implements routing.Factory. It creates the network tree in a
// deterministic manner based on the proto message encoded as any. It must not
// contain the root address, which is the case if the Pack() method has been
// used.
func (t TreeRoutingFactory) FromAny(m *any.Any) (Routing, error) {
msg := &TreeRoutingProto{}
err := ptypes.UnmarshalAny(m, msg)
err := t.encoder.UnmarshalAny(m, msg)
if err != nil {
return nil, xerrors.Errorf("failed to unmarshal routing message: %v", err)
}
Expand All @@ -125,7 +123,7 @@ func (t TreeRoutingFactory) FromAny(m *any.Any) (Routing, error) {

routing, err := t.fromAddrBuf(root, msg.Addrs)
if err != nil {
return nil, xerrors.Errorf("failed to build from addrsBuf: %v", err)
return nil, xerrors.Errorf("failed to build routing: %v", err)
}

return routing, nil
Expand All @@ -136,7 +134,7 @@ func (t TreeRoutingFactory) fromAddrBuf(root mino.Address, addrsBuf addrsBuf) (R
sort.Stable(&addrsBuf)

// We will use the hash of the addresses to set the random seed.
hash := sha256.New()
hash := t.hashFactory.New()
for _, addr := range addrsBuf {
_, err := hash.Write(addr)
if err != nil {
Expand Down Expand Up @@ -189,6 +187,16 @@ func (t TreeRoutingFactory) fromAddrBuf(root mino.Address, addrsBuf addrsBuf) (R
}, nil
}

// TreeRouting holds the routing tree of a network. It allows each node of the
// tree to know which child it should contact in order to relay a message that
// is in it sub-tree.
//
// - implements routing.Routing
type TreeRouting struct {
Root *treeNode
routingNodes map[mino.Address]*treeNode
}

// GetRoute implements routing.Routing. It returns the node that is able to
// relay the message (or correspond to the address). We are able to easily know
// the route because each address has an index corresponding to its node index
Expand All @@ -204,7 +212,8 @@ func (t TreeRoutingFactory) fromAddrBuf(root mino.Address, addrsBuf addrsBuf) (R
// 3 and lastIndex = 5. Now if the root wants to know which of its children to
// contact in order to reach node 4, it then checks the "index" and "indexLast"
// for all its children, an see that for its child 3, 3 >= 4 <= 5, so the root
// will send its message to node 3.
// will send its message to node 3. If there is no route to the node, it will
// return nil.
func (t TreeRouting) GetRoute(from, to mino.Address) mino.Address {
fromNode, ok := t.routingNodes[from]
if !ok {
Expand Down Expand Up @@ -256,7 +265,8 @@ func (t TreeRouting) GetParent(addr mino.Address) mino.Address {
}
}

// GetDirectLinks implements routing.Routing.
// GetDirectLinks implements routing.Routing. It returns the addresses the node
// is responsible to route messages to.
func (t TreeRouting) GetDirectLinks(from mino.Address) []mino.Address {
fromNode, ok := t.routingNodes[from]
if !ok {
Expand Down
172 changes: 171 additions & 1 deletion mino/minogrpc/routing/routing_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
package routing

import (
"bytes"
"sort"
"testing"

proto "github.com/golang/protobuf/proto"
"github.com/stretchr/testify/require"
"go.dedis.ch/fabric/encoding"
internal "go.dedis.ch/fabric/internal/testing"
"go.dedis.ch/fabric/internal/testing/fake"
"go.dedis.ch/fabric/mino"
)

func TestMessages(t *testing.T) {
messages := []proto.Message{
&TreeRoutingProto{},
}

for _, m := range messages {
internal.CoverProtoMessage(t, m)
}
}

func TestTreeRoutingFactory_GetAddressFactory(t *testing.T) {
factory := NewTreeRoutingFactory(3, fake.AddressFactory{})
require.NotNil(t, factory.GetAddressFactory())
Expand All @@ -29,19 +42,36 @@ func TestTreeRoutingFactory_FromIterator(t *testing.T) {
iter := fake.NewAddressIterator([]mino.Address{fake.NewBadAddress()})
_, err = factory.FromIterator(fake.NewAddress(1), iter)
require.EqualError(t, err, "failed to marshal addr 'fake.Address[0]': fake error")

factory.hashFactory = fake.NewHashFactory(fake.NewBadHash())
_, err = factory.FromIterator(fake.NewAddress(0), authority.AddressIterator())
require.EqualError(t, err,
"failed to build routing: failed to write hash: fake error")
}

func TestTreeRoutingFactory_FromAny(t *testing.T) {
factory := NewTreeRoutingFactory(3, fake.AddressFactory{})

rting, err := factory.FromIterator(fake.NewAddress(0), fake.NewAddressIterator(nil))
iter := mino.NewAddresses(fake.NewAddress(1)).AddressIterator()
rting, err := factory.FromIterator(fake.NewAddress(0), iter)
require.NoError(t, err)
rtingAny, err := encoding.NewProtoEncoder().PackAny(rting)
require.NoError(t, err)

res, err := factory.FromAny(rtingAny)
require.NoError(t, err)
require.Equal(t, rting, res)

factory.encoder = fake.BadUnmarshalAnyEncoder{}
_, err = factory.FromAny(rtingAny)
require.EqualError(t, err,
"failed to unmarshal routing message: fake error")

factory.encoder = encoding.NewProtoEncoder()
factory.hashFactory = fake.NewHashFactory(fake.NewBadHash())
_, err = factory.FromAny(rtingAny)
require.EqualError(t, err,
"failed to build routing: failed to write hash: fake error")
}

func TestTreeRouting_GetRoute(t *testing.T) {
Expand Down Expand Up @@ -114,6 +144,146 @@ func TestTreeRouting_GetRoute(t *testing.T) {
require.Equal(t, addrs[6], res)
res = treeRouting.GetRoute(addrs[7], addrs[0])
require.Equal(t, addrs[6], res)

require.Nil(t, treeRouting.GetRoute(fake.NewAddress(999), addrs[5]))
require.Nil(t, treeRouting.GetRoute(addrs[0], fake.NewAddress(999)))
require.Nil(t, treeRouting.GetRoute(addrs[0], addrs[5]))
}

func TestTreeRouting_GetRoot(t *testing.T) {
treeRouting := &TreeRouting{
Root: &treeNode{Addr: fake.NewAddress(123)},
}

require.Equal(t, fake.NewAddress(123), treeRouting.GetRoot())
}

func TestTreeRouting_GetParent(t *testing.T) {
authority := fake.NewAuthority(10, fake.NewSigner)
factory := NewTreeRoutingFactory(3, fake.AddressFactory{})

treeRouting, err := factory.FromIterator(authority.GetAddress(0), authority.AddressIterator())
require.NoError(t, err)

// treeRouting.(*TreeRouting).Display(os.Stdout)

// Here is the deterministic tree that should be built:
//
// TreeRouting, Root: Node[fake.Address[0]-index[0]-lastIndex[9]](
// Node[fake.Address[4]-index[1]-lastIndex[4]](
// Node[fake.Address[2]-index[2]-lastIndex[4]](
// Node[fake.Address[6]-index[3]-lastIndex[3]](
// )
// Node[fake.Address[1]-index[4]-lastIndex[4]](
// )
// )
// )
// Node[fake.Address[7]-index[5]-lastIndex[9]](
// Node[fake.Address[5]-index[6]-lastIndex[7]](
// Node[fake.Address[3]-index[7]-lastIndex[7]](
// )
// )
// Node[fake.Address[8]-index[8]-lastIndex[9]](
// Node[fake.Address[9]-index[9]-lastIndex[9]](
// )
// )
// )
// )

parent := treeRouting.GetParent(authority.GetAddress(9))
require.Equal(t, authority.GetAddress(8), parent)

parent = treeRouting.GetParent(authority.GetAddress(8))
require.Equal(t, authority.GetAddress(7), parent)

parent = treeRouting.GetParent(authority.GetAddress(7))
require.Equal(t, authority.GetAddress(0), parent)

require.Nil(t, treeRouting.GetParent(authority.GetAddress(0)))
require.Nil(t, treeRouting.GetParent(fake.NewAddress(999)))
}

func TestTreeRouting_GetDirectLinks(t *testing.T) {
authority := fake.NewAuthority(10, fake.NewSigner)
factory := NewTreeRoutingFactory(3, fake.AddressFactory{})

treeRouting, err := factory.FromIterator(authority.GetAddress(0), authority.AddressIterator())
require.NoError(t, err)

// treeRouting.(*TreeRouting).Display(os.Stdout)

// Here is the deterministic tree that should be built:
//
// TreeRouting, Root: Node[fake.Address[0]-index[0]-lastIndex[9]](
// Node[fake.Address[4]-index[1]-lastIndex[4]](
// Node[fake.Address[2]-index[2]-lastIndex[4]](
// Node[fake.Address[6]-index[3]-lastIndex[3]](
// )
// Node[fake.Address[1]-index[4]-lastIndex[4]](
// )
// )
// )
// Node[fake.Address[7]-index[5]-lastIndex[9]](
// Node[fake.Address[5]-index[6]-lastIndex[7]](
// Node[fake.Address[3]-index[7]-lastIndex[7]](
// )
// )
// Node[fake.Address[8]-index[8]-lastIndex[9]](
// Node[fake.Address[9]-index[9]-lastIndex[9]](
// )
// )
// )
// )

children := treeRouting.GetDirectLinks(authority.GetAddress(7))
require.Len(t, children, 2)
require.Equal(t, authority.GetAddress(5), children[0])
require.Equal(t, authority.GetAddress(8), children[1])

require.Len(t, treeRouting.GetDirectLinks(authority.GetAddress(9)), 0)
require.Len(t, treeRouting.GetDirectLinks(fake.NewAddress(999)), 0)
}

func TestTreeRouting_Pack(t *testing.T) {
n := 10

authority := fake.NewAuthority(n, fake.NewSigner)
factory := NewTreeRoutingFactory(3, fake.AddressFactory{})

treeRouting, err := factory.FromIterator(authority.GetAddress(0), authority.AddressIterator())
require.NoError(t, err)

pb, err := treeRouting.Pack(encoding.NewProtoEncoder())
require.NoError(t, err)
require.NotNil(t, pb)
require.NotEmpty(t, pb.(*TreeRoutingProto).GetRoot())
require.Len(t, pb.(*TreeRoutingProto).GetAddrs(), n-1)

treeRouting = &TreeRouting{routingNodes: map[mino.Address]*treeNode{
fake.NewBadAddress(): {Addr: fake.NewBadAddress()},
}}
_, err = treeRouting.Pack(encoding.NewProtoEncoder())
require.EqualError(t, err, "failed to marshal address: fake error")
}

func TestTreeRouting_Display(t *testing.T) {
authority := fake.NewAuthority(3, fake.NewSigner)
factory := NewTreeRoutingFactory(2, fake.AddressFactory{})

treeRouting, err := factory.FromIterator(authority.GetAddress(0), authority.AddressIterator())
require.NoError(t, err)

buffer := new(bytes.Buffer)
treeRouting.(*TreeRouting).Display(buffer)

expected := `TreeRouting, Root: Node[fake.Address[0]-index[0]-lastIndex[2]](
Node[fake.Address[1]-index[1]-lastIndex[2]](
Node[fake.Address[2]-index[2]-lastIndex[2]](
)
)
)
`
require.Equal(t, expected, buffer.String())
}

func TestBuildNode(t *testing.T) {
Expand Down

0 comments on commit 082efc1

Please sign in to comment.