mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-26 00:25:17 +00:00
1065 lines
24 KiB
Go
1065 lines
24 KiB
Go
|
package blossomsub
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"crypto/sha1"
|
||
|
"crypto/sha256"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"math/rand"
|
||
|
"sync"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
tnet "github.com/libp2p/go-libp2p-testing/net"
|
||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||
|
pb "source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
||
|
)
|
||
|
|
||
|
func getBitmasks(psubs []*PubSub, bitmask []byte, opts ...BitmaskOpt) []*Bitmask {
|
||
|
bitmasks := make([]*Bitmask, len(psubs))
|
||
|
|
||
|
for i, ps := range psubs {
|
||
|
t, err := ps.Join(bitmask, opts...)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
bitmasks[i] = t
|
||
|
}
|
||
|
|
||
|
return bitmasks
|
||
|
}
|
||
|
|
||
|
func getBitmaskEvts(bitmasks []*Bitmask, opts ...BitmaskEventHandlerOpt) []*BitmaskEventHandler {
|
||
|
handlers := make([]*BitmaskEventHandler, len(bitmasks))
|
||
|
|
||
|
for i, t := range bitmasks {
|
||
|
h, err := t.EventHandler(opts...)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
handlers[i] = h
|
||
|
}
|
||
|
|
||
|
return handlers
|
||
|
}
|
||
|
|
||
|
func TestBitmaskCloseWithOpenSubscription(t *testing.T) {
|
||
|
var sub *Subscription
|
||
|
var err error
|
||
|
testBitmaskCloseWithOpenResource(t,
|
||
|
func(bitmask *Bitmask) {
|
||
|
sub, err = bitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
},
|
||
|
func() {
|
||
|
sub.Cancel()
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func TestBitmaskCloseWithOpenEventHandler(t *testing.T) {
|
||
|
var evts *BitmaskEventHandler
|
||
|
var err error
|
||
|
testBitmaskCloseWithOpenResource(t,
|
||
|
func(bitmask *Bitmask) {
|
||
|
evts, err = bitmask.EventHandler()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
},
|
||
|
func() {
|
||
|
evts.Cancel()
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func TestBitmaskCloseWithOpenRelay(t *testing.T) {
|
||
|
var relayCancel RelayCancelFunc
|
||
|
var err error
|
||
|
testBitmaskCloseWithOpenResource(t,
|
||
|
func(bitmask *Bitmask) {
|
||
|
relayCancel, err = bitmask.Relay()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
},
|
||
|
func() {
|
||
|
relayCancel()
|
||
|
},
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func testBitmaskCloseWithOpenResource(t *testing.T, openResource func(bitmask *Bitmask), closeResource func()) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
const numHosts = 1
|
||
|
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
ps := getPubsub(ctx, hosts[0])
|
||
|
|
||
|
// Try create and cancel bitmask
|
||
|
bitmask, err := ps.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if err := bitmask.Close(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Try create and cancel bitmask while there's an outstanding subscription/event handler
|
||
|
bitmask, err = ps.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
openResource(bitmask)
|
||
|
|
||
|
if err := bitmask.Close(); err == nil {
|
||
|
t.Fatal("expected an error closing a bitmask with an open resource")
|
||
|
}
|
||
|
|
||
|
// Check if the bitmask closes properly after closing the resource
|
||
|
closeResource()
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
if err := bitmask.Close(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBitmaskReuse(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
const numHosts = 2
|
||
|
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
|
||
|
sender := getPubsub(ctx, hosts[0], WithDiscovery(&dummyDiscovery{}))
|
||
|
receiver := getPubsub(ctx, hosts[1])
|
||
|
|
||
|
connectAll(t, hosts)
|
||
|
|
||
|
// Sender creates bitmask
|
||
|
sendBitmask, err := sender.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Receiver creates and subscribes to the bitmask
|
||
|
receiveBitmask, err := receiver.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
sub, err := receiveBitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
firstMsg := []byte("1")
|
||
|
if err := sendBitmask.Publish(ctx, firstMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
msg, err := sub.Next(ctx)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if !bytes.Equal(msg.GetData(), firstMsg) {
|
||
|
t.Fatal("received incorrect message")
|
||
|
}
|
||
|
|
||
|
if err := sendBitmask.Close(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Recreate the same bitmask
|
||
|
newSendBitmask, err := sender.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Try sending data with original bitmask
|
||
|
illegalSend := []byte("illegal")
|
||
|
if err := sendBitmask.Publish(ctx, illegalSend); err != ErrBitmaskClosed {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
timeoutCtx, timeoutCancel := context.WithTimeout(ctx, time.Second*2)
|
||
|
defer timeoutCancel()
|
||
|
msg, err = sub.Next(timeoutCtx)
|
||
|
if err != context.DeadlineExceeded {
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if !bytes.Equal(msg.GetData(), illegalSend) {
|
||
|
t.Fatal("received incorrect message from illegal bitmask")
|
||
|
}
|
||
|
t.Fatal("received message sent by illegal bitmask")
|
||
|
}
|
||
|
timeoutCancel()
|
||
|
|
||
|
// Try cancelling the new bitmask by using the original bitmask
|
||
|
if err := sendBitmask.Close(); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
secondMsg := []byte("2")
|
||
|
if err := newSendBitmask.Publish(ctx, secondMsg); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
timeoutCtx, timeoutCancel = context.WithTimeout(ctx, time.Second*2)
|
||
|
defer timeoutCancel()
|
||
|
msg, err = sub.Next(timeoutCtx)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if !bytes.Equal(msg.GetData(), secondMsg) {
|
||
|
t.Fatal("received incorrect message")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBitmaskEventHandlerCancel(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
const numHosts = 5
|
||
|
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
ps := getPubsub(ctx, hosts[0])
|
||
|
|
||
|
// Try create and cancel bitmask
|
||
|
bitmask, err := ps.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
evts, err := bitmask.EventHandler()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
evts.Cancel()
|
||
|
timeoutCtx, timeoutCancel := context.WithTimeout(ctx, time.Second*2)
|
||
|
defer timeoutCancel()
|
||
|
connectAll(t, hosts)
|
||
|
_, err = evts.NextPeerEvent(timeoutCtx)
|
||
|
if err != context.DeadlineExceeded {
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
t.Fatal("received event after cancel")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSubscriptionJoinNotification(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
const numLateSubscribers = 10
|
||
|
const numHosts = 20
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
bitmasks := getBitmasks(getPubsubs(ctx, hosts), []byte{0xf0, 0x0b, 0xa1, 0x20})
|
||
|
evts := getBitmaskEvts(bitmasks)
|
||
|
|
||
|
subs := make([]*Subscription, numHosts)
|
||
|
bitmaskPeersFound := make([]map[peer.ID]struct{}, numHosts)
|
||
|
|
||
|
// Have some peers subscribe earlier than other peers.
|
||
|
// This exercises whether we get subscription notifications from
|
||
|
// existing peers.
|
||
|
for i, bitmask := range bitmasks[numLateSubscribers:] {
|
||
|
subch, err := bitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
subs[i] = subch
|
||
|
}
|
||
|
|
||
|
connectAll(t, hosts)
|
||
|
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
// Have the rest subscribe
|
||
|
for i, bitmask := range bitmasks[:numLateSubscribers] {
|
||
|
subch, err := bitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
subs[i+numLateSubscribers] = subch
|
||
|
}
|
||
|
|
||
|
wg := sync.WaitGroup{}
|
||
|
for i := 0; i < numHosts; i++ {
|
||
|
peersFound := make(map[peer.ID]struct{})
|
||
|
bitmaskPeersFound[i] = peersFound
|
||
|
evt := evts[i]
|
||
|
wg.Add(1)
|
||
|
go func(peersFound map[peer.ID]struct{}) {
|
||
|
defer wg.Done()
|
||
|
for len(peersFound) < numHosts-1 {
|
||
|
event, err := evt.NextPeerEvent(ctx)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
if event.Type == PeerJoin {
|
||
|
peersFound[event.Peer] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
}(peersFound)
|
||
|
}
|
||
|
|
||
|
wg.Wait()
|
||
|
for _, peersFound := range bitmaskPeersFound {
|
||
|
if len(peersFound) != numHosts-1 {
|
||
|
t.Fatal("incorrect number of peers found")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSubscriptionLeaveNotification(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
const numHosts = 20
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
psubs := getPubsubs(ctx, hosts)
|
||
|
bitmasks := getBitmasks(psubs, []byte{0xf0, 0x0b, 0xa1, 0x20})
|
||
|
evts := getBitmaskEvts(bitmasks)
|
||
|
|
||
|
subs := make([]*Subscription, numHosts)
|
||
|
bitmaskPeersFound := make([]map[peer.ID]struct{}, numHosts)
|
||
|
|
||
|
// Subscribe all peers and wait until they've all been found
|
||
|
for i, bitmask := range bitmasks {
|
||
|
subch, err := bitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
subs[i] = subch
|
||
|
}
|
||
|
|
||
|
connectAll(t, hosts)
|
||
|
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
wg := sync.WaitGroup{}
|
||
|
for i := 0; i < numHosts; i++ {
|
||
|
peersFound := make(map[peer.ID]struct{})
|
||
|
bitmaskPeersFound[i] = peersFound
|
||
|
evt := evts[i]
|
||
|
wg.Add(1)
|
||
|
go func(peersFound map[peer.ID]struct{}) {
|
||
|
defer wg.Done()
|
||
|
for len(peersFound) < numHosts-1 {
|
||
|
event, err := evt.NextPeerEvent(ctx)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
if event.Type == PeerJoin {
|
||
|
peersFound[event.Peer] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
}(peersFound)
|
||
|
}
|
||
|
|
||
|
wg.Wait()
|
||
|
for _, peersFound := range bitmaskPeersFound {
|
||
|
if len(peersFound) != numHosts-1 {
|
||
|
t.Fatal("incorrect number of peers found")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Test removing peers and verifying that they cause events
|
||
|
subs[1].Cancel()
|
||
|
_ = hosts[2].Close()
|
||
|
psubs[0].BlacklistPeer(hosts[3].ID())
|
||
|
|
||
|
leavingPeers := make(map[peer.ID]struct{})
|
||
|
for len(leavingPeers) < 3 {
|
||
|
event, err := evts[0].NextPeerEvent(ctx)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if event.Type == PeerLeave {
|
||
|
leavingPeers[event.Peer] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if _, ok := leavingPeers[hosts[1].ID()]; !ok {
|
||
|
t.Fatal(fmt.Errorf("canceling subscription did not cause a leave event"))
|
||
|
}
|
||
|
if _, ok := leavingPeers[hosts[2].ID()]; !ok {
|
||
|
t.Fatal(fmt.Errorf("closing host did not cause a leave event"))
|
||
|
}
|
||
|
if _, ok := leavingPeers[hosts[3].ID()]; !ok {
|
||
|
t.Fatal(fmt.Errorf("blacklisting peer did not cause a leave event"))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSubscriptionManyNotifications(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
|
||
|
const numHosts = 33
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||
|
evts := getBitmaskEvts(bitmasks)
|
||
|
|
||
|
subs := make([]*Subscription, numHosts)
|
||
|
bitmaskPeersFound := make([]map[peer.ID]struct{}, numHosts)
|
||
|
|
||
|
// Subscribe all peers except one and wait until they've all been found
|
||
|
for i := 1; i < numHosts; i++ {
|
||
|
subch, err := bitmasks[i].Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
subs[i] = subch
|
||
|
}
|
||
|
|
||
|
connectAll(t, hosts)
|
||
|
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
wg := sync.WaitGroup{}
|
||
|
for i := 1; i < numHosts; i++ {
|
||
|
peersFound := make(map[peer.ID]struct{})
|
||
|
bitmaskPeersFound[i] = peersFound
|
||
|
evt := evts[i]
|
||
|
wg.Add(1)
|
||
|
go func(peersFound map[peer.ID]struct{}) {
|
||
|
defer wg.Done()
|
||
|
for len(peersFound) < numHosts-2 {
|
||
|
event, err := evt.NextPeerEvent(ctx)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
if event.Type == PeerJoin {
|
||
|
peersFound[event.Peer] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
}(peersFound)
|
||
|
}
|
||
|
|
||
|
wg.Wait()
|
||
|
for _, peersFound := range bitmaskPeersFound[1:] {
|
||
|
if len(peersFound) != numHosts-2 {
|
||
|
t.Fatalf("found %d peers, expected %d", len(peersFound), numHosts-2)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Wait for remaining peer to find other peers
|
||
|
remPeerBitmask, remPeerEvts := bitmasks[0], evts[0]
|
||
|
for len(remPeerBitmask.ListPeers()) < numHosts-1 {
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
}
|
||
|
|
||
|
// Subscribe the remaining peer and check that all the events came through
|
||
|
sub, err := remPeerBitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
subs[0] = sub
|
||
|
|
||
|
peerState := readAllQueuedEvents(ctx, t, remPeerEvts)
|
||
|
|
||
|
if len(peerState) != numHosts-1 {
|
||
|
t.Fatal("incorrect number of peers found")
|
||
|
}
|
||
|
|
||
|
for _, e := range peerState {
|
||
|
if e != PeerJoin {
|
||
|
t.Fatal("non Join event occurred")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Unsubscribe all peers except one and check that all the events came through
|
||
|
for i := 1; i < numHosts; i++ {
|
||
|
subs[i].Cancel()
|
||
|
}
|
||
|
|
||
|
// Wait for remaining peer to disconnect from the other peers
|
||
|
for len(bitmasks[0].ListPeers()) != 0 {
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
}
|
||
|
|
||
|
peerState = readAllQueuedEvents(ctx, t, remPeerEvts)
|
||
|
|
||
|
if len(peerState) != numHosts-1 {
|
||
|
t.Fatal("incorrect number of peers found")
|
||
|
}
|
||
|
|
||
|
for _, e := range peerState {
|
||
|
if e != PeerLeave {
|
||
|
t.Fatal("non Leave event occurred")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSubscriptionNotificationSubUnSub(t *testing.T) {
|
||
|
// Resubscribe and Unsubscribe a peers and check the state for consistency
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
|
||
|
const numHosts = 35
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||
|
|
||
|
for i := 1; i < numHosts; i++ {
|
||
|
connect(t, hosts[0], hosts[i])
|
||
|
}
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
notifSubThenUnSub(ctx, t, bitmasks)
|
||
|
}
|
||
|
|
||
|
func TestBitmaskRelay(t *testing.T) {
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||
|
defer cancel()
|
||
|
|
||
|
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
const numHosts = 5
|
||
|
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||
|
|
||
|
// [0.Rel] - [1.Rel] - [2.Sub]
|
||
|
// |
|
||
|
// [3.Rel] - [4.Sub]
|
||
|
|
||
|
connect(t, hosts[0], hosts[1])
|
||
|
connect(t, hosts[1], hosts[2])
|
||
|
connect(t, hosts[1], hosts[3])
|
||
|
connect(t, hosts[3], hosts[4])
|
||
|
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
var subs []*Subscription
|
||
|
|
||
|
for i, bitmask := range bitmasks {
|
||
|
if i == 2 || i == 4 {
|
||
|
sub, err := bitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
subs = append(subs, sub)
|
||
|
} else {
|
||
|
_, err := bitmask.Relay()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
for i := 0; i < 100; i++ {
|
||
|
msg := []byte("message")
|
||
|
|
||
|
owner := rand.Intn(len(bitmasks))
|
||
|
|
||
|
err := bitmasks[owner].Publish(ctx, msg)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
for _, sub := range subs {
|
||
|
received, err := sub.Next(ctx)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if !bytes.Equal(msg, received.Data) {
|
||
|
t.Fatal("received message is other than expected")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBitmaskRelayReuse(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
const numHosts = 1
|
||
|
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
pubsubs := getPubsubs(ctx, hosts)
|
||
|
bitmasks := getBitmasks(pubsubs, bitmask)
|
||
|
|
||
|
relay1Cancel, err := bitmasks[0].Relay()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
relay2Cancel, err := bitmasks[0].Relay()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
relay3Cancel, err := bitmasks[0].Relay()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
res := make(chan bool, 1)
|
||
|
pubsubs[0].eval <- func() {
|
||
|
res <- pubsubs[0].myRelays[string(bitmask)] == 3
|
||
|
}
|
||
|
|
||
|
isCorrectNumber := <-res
|
||
|
if !isCorrectNumber {
|
||
|
t.Fatal("incorrect number of relays")
|
||
|
}
|
||
|
|
||
|
// only the first invocation should take effect
|
||
|
relay1Cancel()
|
||
|
relay1Cancel()
|
||
|
relay1Cancel()
|
||
|
|
||
|
pubsubs[0].eval <- func() {
|
||
|
res <- pubsubs[0].myRelays[string(bitmask)] == 2
|
||
|
}
|
||
|
|
||
|
isCorrectNumber = <-res
|
||
|
if !isCorrectNumber {
|
||
|
t.Fatal("incorrect number of relays")
|
||
|
}
|
||
|
|
||
|
relay2Cancel()
|
||
|
relay3Cancel()
|
||
|
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
pubsubs[0].eval <- func() {
|
||
|
res <- pubsubs[0].myRelays[string(bitmask)] == 0
|
||
|
}
|
||
|
|
||
|
isCorrectNumber = <-res
|
||
|
if !isCorrectNumber {
|
||
|
t.Fatal("incorrect number of relays")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBitmaskRelayOnClosedBitmask(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
const numHosts = 1
|
||
|
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||
|
|
||
|
err := bitmasks[0].Close()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
_, err = bitmasks[0].Relay()
|
||
|
if err == nil {
|
||
|
t.Fatalf("error should be returned")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestProducePanic(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
const numHosts = 5
|
||
|
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
ps := getPubsub(ctx, hosts[0])
|
||
|
|
||
|
// Create bitmask
|
||
|
bitmask, err := ps.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Create subscription we're going to cancel
|
||
|
s, err := bitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
// Create second subscription to keep us alive on the subscription map
|
||
|
// after the first one is canceled
|
||
|
s2, err := bitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
_ = s2
|
||
|
|
||
|
s.Cancel()
|
||
|
time.Sleep(time.Second)
|
||
|
s.Cancel()
|
||
|
time.Sleep(time.Second)
|
||
|
}
|
||
|
|
||
|
func notifSubThenUnSub(ctx context.Context, t *testing.T, bitmasks []*Bitmask) {
|
||
|
primaryBitmask := bitmasks[0]
|
||
|
msgs := make([]*Subscription, len(bitmasks))
|
||
|
checkSize := len(bitmasks) - 1
|
||
|
|
||
|
// Subscribe all peers to the bitmask
|
||
|
var err error
|
||
|
for i, bitmask := range bitmasks {
|
||
|
msgs[i], err = bitmask.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Wait for the primary peer to be connected to the other peers
|
||
|
for len(primaryBitmask.ListPeers()) < checkSize {
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
}
|
||
|
|
||
|
// Unsubscribe all peers except the primary
|
||
|
for i := 1; i < checkSize+1; i++ {
|
||
|
msgs[i].Cancel()
|
||
|
}
|
||
|
|
||
|
// Wait for the unsubscribe messages to reach the primary peer
|
||
|
for len(primaryBitmask.ListPeers()) < 0 {
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
}
|
||
|
|
||
|
// read all available events and verify that there are no events to process
|
||
|
// this is because every peer that joined also left
|
||
|
primaryEvts, err := primaryBitmask.EventHandler()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
peerState := readAllQueuedEvents(ctx, t, primaryEvts)
|
||
|
|
||
|
if len(peerState) != 0 {
|
||
|
for p, s := range peerState {
|
||
|
fmt.Println(p, s)
|
||
|
}
|
||
|
t.Fatalf("Received incorrect events. %d extra events", len(peerState))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func readAllQueuedEvents(ctx context.Context, t *testing.T, evt *BitmaskEventHandler) map[peer.ID]EventType {
|
||
|
peerState := make(map[peer.ID]EventType)
|
||
|
for {
|
||
|
ctx, cancel := context.WithTimeout(ctx, time.Millisecond*100)
|
||
|
event, err := evt.NextPeerEvent(ctx)
|
||
|
cancel()
|
||
|
|
||
|
if err == context.DeadlineExceeded {
|
||
|
break
|
||
|
} else if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
e, ok := peerState[event.Peer]
|
||
|
if !ok {
|
||
|
peerState[event.Peer] = event.Type
|
||
|
} else if e != event.Type {
|
||
|
delete(peerState, event.Peer)
|
||
|
}
|
||
|
}
|
||
|
return peerState
|
||
|
}
|
||
|
|
||
|
func TestMinBitmaskSizeNoDiscovery(t *testing.T) {
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||
|
defer cancel()
|
||
|
|
||
|
const numHosts = 3
|
||
|
bitmaskID := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
|
||
|
sender := getPubsub(ctx, hosts[0])
|
||
|
receiver1 := getPubsub(ctx, hosts[1])
|
||
|
receiver2 := getPubsub(ctx, hosts[2])
|
||
|
|
||
|
connectAll(t, hosts)
|
||
|
|
||
|
// Sender creates bitmask
|
||
|
sendBitmask, err := sender.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
// Receiver creates and subscribes to the bitmask
|
||
|
receiveBitmask1, err := receiver1.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
sub1, err := receiveBitmask1.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
oneMsg := []byte("minimum one")
|
||
|
if err := sendBitmask.Publish(ctx, oneMsg, WithReadiness(MinBitmaskSize(1))); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if msg, err := sub1.Next(ctx); err != nil {
|
||
|
t.Fatal(err)
|
||
|
} else if !bytes.Equal(msg.GetData(), oneMsg) {
|
||
|
t.Fatal("received incorrect message")
|
||
|
}
|
||
|
|
||
|
twoMsg := []byte("minimum two")
|
||
|
|
||
|
// Attempting to publish with a minimum bitmask size of two should fail.
|
||
|
{
|
||
|
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||
|
defer cancel()
|
||
|
if err := sendBitmask.Publish(ctx, twoMsg, WithReadiness(MinBitmaskSize(2))); !errors.Is(err, context.DeadlineExceeded) {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Subscribe the second receiver; the publish should now work.
|
||
|
receiveBitmask2, err := receiver2.Join(bitmaskID)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
sub2, err := receiveBitmask2.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
{
|
||
|
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||
|
defer cancel()
|
||
|
if err := sendBitmask.Publish(ctx, twoMsg, WithReadiness(MinBitmaskSize(2))); err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if msg, err := sub2.Next(ctx); err != nil {
|
||
|
t.Fatal(err)
|
||
|
} else if !bytes.Equal(msg.GetData(), twoMsg) {
|
||
|
t.Fatal("received incorrect message")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestWithBitmaskMsgIdFunction(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
bitmaskA, bitmaskB := []byte{0xf0, 0x0b, 0xa1, 0x2a}, []byte{0xf0, 0x0b, 0xa1, 0x2b}
|
||
|
const numHosts = 2
|
||
|
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
pubsubs := getPubsubs(ctx, hosts, WithMessageIdFn(func(pmsg *pb.Message) string {
|
||
|
hash := sha256.Sum256(pmsg.Data)
|
||
|
return string(hash[:])
|
||
|
}))
|
||
|
connectAll(t, hosts)
|
||
|
|
||
|
bitmasksA := getBitmasks(pubsubs, bitmaskA) // uses global msgIdFn
|
||
|
bitmasksB := getBitmasks(pubsubs, bitmaskB, WithBitmaskMessageIdFn(func(pmsg *pb.Message) string { // uses custom
|
||
|
hash := sha1.Sum(pmsg.Data)
|
||
|
return string(hash[:])
|
||
|
}))
|
||
|
|
||
|
payload := []byte("pubsub rocks")
|
||
|
|
||
|
subA, err := bitmasksA[0].Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
err = bitmasksA[1].Publish(ctx, payload, WithReadiness(MinBitmaskSize(1)))
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
msgA, err := subA.Next(ctx)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
subB, err := bitmasksB[0].Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
err = bitmasksB[1].Publish(ctx, payload, WithReadiness(MinBitmaskSize(1)))
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
msgB, err := subB.Next(ctx)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if msgA.ID == msgB.ID {
|
||
|
t.Fatal("msg ids are equal")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBitmaskPublishWithKeyInvalidParameters(t *testing.T) {
|
||
|
t.Parallel()
|
||
|
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||
|
defer cancel()
|
||
|
|
||
|
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
const numHosts = 5
|
||
|
|
||
|
virtualPeer := tnet.RandPeerNetParamsOrFatal(t)
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||
|
|
||
|
t.Run("nil sign private key should error", func(t *testing.T) {
|
||
|
withVirtualKey := WithSecretKeyAndPeerId(nil, virtualPeer.ID)
|
||
|
err := bitmasks[0].Publish(ctx, []byte("buff"), withVirtualKey)
|
||
|
if err != ErrNilSignKey {
|
||
|
t.Fatal("error should have been of type errNilSignKey")
|
||
|
}
|
||
|
})
|
||
|
t.Run("empty peer ID should error", func(t *testing.T) {
|
||
|
withVirtualKey := WithSecretKeyAndPeerId(virtualPeer.PrivKey, "")
|
||
|
err := bitmasks[0].Publish(ctx, []byte("buff"), withVirtualKey)
|
||
|
if err != ErrEmptyPeerID {
|
||
|
t.Fatal("error should have been of type errEmptyPeerID")
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestBitmaskRelayPublishWithKey(t *testing.T) {
|
||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||
|
defer cancel()
|
||
|
|
||
|
bitmask := []byte{0xf0, 0x0b, 0xa1, 0x20}
|
||
|
const numHosts = 5
|
||
|
|
||
|
virtualPeer := tnet.RandPeerNetParamsOrFatal(t)
|
||
|
hosts := getNetHosts(t, ctx, numHosts)
|
||
|
bitmasks := getBitmasks(getPubsubs(ctx, hosts), bitmask)
|
||
|
|
||
|
// [0.Rel] - [1.Rel] - [2.Sub]
|
||
|
// |
|
||
|
// [3.Rel] - [4.Sub]
|
||
|
|
||
|
connect(t, hosts[0], hosts[1])
|
||
|
connect(t, hosts[1], hosts[2])
|
||
|
connect(t, hosts[1], hosts[3])
|
||
|
connect(t, hosts[3], hosts[4])
|
||
|
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
var subs []*Subscription
|
||
|
|
||
|
for i, bitmaskValue := range bitmasks {
|
||
|
if i == 2 || i == 4 {
|
||
|
sub, err := bitmaskValue.Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
subs = append(subs, sub)
|
||
|
} else {
|
||
|
_, err := bitmaskValue.Relay()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
time.Sleep(time.Millisecond * 100)
|
||
|
|
||
|
for i := 0; i < 100; i++ {
|
||
|
msg := []byte("message")
|
||
|
|
||
|
owner := rand.Intn(len(bitmasks))
|
||
|
|
||
|
withVirtualKey := WithSecretKeyAndPeerId(virtualPeer.PrivKey, virtualPeer.ID)
|
||
|
err := bitmasks[owner].Publish(ctx, msg, withVirtualKey)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
for _, sub := range subs {
|
||
|
received, errSub := sub.Next(ctx)
|
||
|
if errSub != nil {
|
||
|
t.Fatal(errSub)
|
||
|
}
|
||
|
|
||
|
if !bytes.Equal(msg, received.Data) {
|
||
|
t.Fatal("received message is other than expected")
|
||
|
}
|
||
|
if string(received.From) != string(virtualPeer.ID) {
|
||
|
t.Fatal("received message is not from the virtual peer")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestWithLocalPublication(t *testing.T) {
|
||
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
defer cancel()
|
||
|
|
||
|
bitmask := []byte{0x7e, 57}
|
||
|
|
||
|
hosts := getNetHosts(t, ctx, 2)
|
||
|
pubsubs := getPubsubs(ctx, hosts)
|
||
|
bitmasks := getBitmasks(pubsubs, bitmask)
|
||
|
connectAll(t, hosts)
|
||
|
|
||
|
payload := []byte("pubsub smashes")
|
||
|
|
||
|
local, err := bitmasks[0].Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
remote, err := bitmasks[1].Subscribe()
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
err = bitmasks[0].Publish(ctx, payload, WithLocalPublication(true))
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
|
||
|
remoteCtx, cancel := context.WithTimeout(ctx, time.Millisecond*100)
|
||
|
defer cancel()
|
||
|
|
||
|
msg, err := remote.Next(remoteCtx)
|
||
|
if msg != nil || err == nil {
|
||
|
t.Fatal("unexpected msg")
|
||
|
}
|
||
|
|
||
|
msg, err = local.Next(ctx)
|
||
|
if err != nil {
|
||
|
t.Fatal(err)
|
||
|
}
|
||
|
if !msg.Local || !bytes.Equal(msg.Data, payload) {
|
||
|
t.Fatal("wrong message")
|
||
|
}
|
||
|
}
|