Skip to content

Commit

Permalink
fraud: add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vgonkivs committed Jul 29, 2022
1 parent 12bc2e7 commit dd7d034
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 3 deletions.
204 changes: 202 additions & 2 deletions fraud/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ipfs/go-datastore"
"github.com/ipfs/go-datastore/sync"
mdutils "github.com/ipfs/go-merkledag/test"
"github.com/libp2p/go-libp2p-core/event"
"github.com/libp2p/go-libp2p-core/host"
"github.com/libp2p/go-libp2p-core/peer"
pubsub "github.com/libp2p/go-libp2p-pubsub"
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"

Expand Down Expand Up @@ -56,18 +60,214 @@ func TestService_Broadcast(t *testing.T) {
require.NoError(t, p.Validate(faultHeader))
}

func TestService_BlackListPeer(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer t.Cleanup(cancel)
// create mock network
net, err := mocknet.FullMeshLinked(3)
require.NoError(t, err)
bServ := mdutils.Bserv()

// create first fraud service that will broadcast incorrect Fraud Proof
serviceA, store1 := createServiceWithHost(t, net.Hosts()[0])

h, err := store1.GetByHeight(context.TODO(), 1)
require.NoError(t, err)

// create and break byzantine error
_, err = generateByzantineError(ctx, t, h, bServ)
require.Error(t, err)
var errByz *ipld.ErrByzantine
require.True(t, errors.As(err, &errByz))
errByz.Index = 2

fserviceA := serviceA.(*service)
require.NotNil(t, fserviceA)

// create second service that will receive and validate Fraud Proof
serviceB, _ := createServiceWithHost(t, net.Hosts()[1])

fserviceB := serviceB.(*service)
require.NotNil(t, fserviceB)

bl := pubsub.NewMapBlacklist()
// create pub sub in order to listen for Fraud Proof
psC, err := pubsub.NewGossipSub(ctx, net.Hosts()[2], // -> C
pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign), pubsub.WithBlacklist(bl))
require.NoError(t, err)

addrB := host.InfoFromHost(net.Hosts()[1]) // -> B

serviceC := NewService(psC, store1.GetByHeight, sync.MutexWrap(datastore.NewMapDatastore()))

sub0, err := net.Hosts()[0].EventBus().Subscribe(&event.EvtPeerIdentificationCompleted{})
require.NoError(t, err)
sub2, err := net.Hosts()[2].EventBus().Subscribe(&event.EvtPeerIdentificationCompleted{})
require.NoError(t, err)

// connect peers: A -> B -> C, so A and C are not connected to each other
require.NoError(t, net.Hosts()[0].Connect(ctx, *addrB)) // host[0] is A
require.NoError(t, net.Hosts()[2].Connect(ctx, *addrB)) // host[2] is C

// wait on both peer identification events
for i := 0; i < 2; i++ {
select {
case <-sub0.Out():
case <-sub2.Out():
case <-ctx.Done():
assert.FailNow(t, "timeout waiting for peers to connect")
}
}

_, err = serviceA.Subscribe(BadEncoding)
require.NoError(t, err)

subsB, err := serviceB.Subscribe(BadEncoding)
require.NoError(t, err)
defer subsB.Cancel()

subsC, err := serviceC.Subscribe(BadEncoding)
require.NoError(t, err)
defer subsC.Cancel()

befp := CreateBadEncodingProof([]byte("hash"), uint64(h.Height), errByz)
// deregister validator in order to send Fraud Proof
fserviceA.pubsub.UnregisterTopicValidator(getSubTopic(BadEncoding)) //nolint:errcheck
// create a new validator for serviceB
fserviceB.pubsub.UnregisterTopicValidator(getSubTopic(BadEncoding)) //nolint:errcheck
fserviceB.pubsub.RegisterTopicValidator(getSubTopic(BadEncoding), //nolint:errcheck
func(ctx context.Context, from peer.ID, msg *pubsub.Message) pubsub.ValidationResult {
msg.ValidatorData = befp
return pubsub.ValidationAccept
})
err = fserviceA.Broadcast(ctx, befp, pubsub.WithReadiness(pubsub.MinTopicSize(1)))
require.NoError(t, err)

_, err = subsB.Proof(ctx)
require.NoError(t, err)

newCtx, cancel := context.WithTimeout(ctx, time.Millisecond*500)
t.Cleanup(cancel)
_, err = subsC.Proof(newCtx)
require.Error(t, err)
require.True(t, bl.Contains(net.Hosts()[1].ID()))
}

func TestService_GossipingOfFaultBEFP(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer t.Cleanup(cancel)
// create mock network
net, err := mocknet.FullMeshLinked(3)
require.NoError(t, err)
bServ := mdutils.Bserv()

// create first fraud service that will broadcast incorrect Fraud Proof
serviceA, store1 := createServiceWithHost(t, net.Hosts()[0])

h, err := store1.GetByHeight(context.TODO(), 1)
require.NoError(t, err)

// create and break byzantine error
_, err = generateByzantineError(ctx, t, h, bServ)
require.Error(t, err)
var errByz *ipld.ErrByzantine
require.True(t, errors.As(err, &errByz))
errByz.Index = 2

fserviceA := serviceA.(*service)
require.NotNil(t, fserviceA)

bl := pubsub.NewMapBlacklist()
// create pub sub in order to listen for Fraud Proof
psB, err := pubsub.NewGossipSub(ctx, net.Hosts()[1], // -> B
pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign), pubsub.WithBlacklist(bl))
require.NoError(t, err)
// create second service that will receive and validate Fraud Proof
serviceB := NewService(psB, store1.GetByHeight, sync.MutexWrap(datastore.NewMapDatastore()))
fserviceB := serviceB.(*service)
require.NotNil(t, fserviceB)
addrB := host.InfoFromHost(net.Hosts()[1]) // -> B

// create pub sub in order to listen for Fraud Proof
psC, err := pubsub.NewGossipSub(ctx, net.Hosts()[2], // -> C
pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign))
require.NoError(t, err)
serviceC := NewService(psC, store1.GetByHeight, sync.MutexWrap(datastore.NewMapDatastore()))

// perform subscriptions
sub0, err := net.Hosts()[0].EventBus().Subscribe(&event.EvtPeerIdentificationCompleted{})
require.NoError(t, err)
sub2, err := net.Hosts()[2].EventBus().Subscribe(&event.EvtPeerIdentificationCompleted{})
require.NoError(t, err)

// establish connections
// connect peers: A -> B -> C, so A and C are not connected to each other
require.NoError(t, net.Hosts()[0].Connect(ctx, *addrB)) // host[0] is A
require.NoError(t, net.Hosts()[2].Connect(ctx, *addrB)) // host[2] is C

// wait on both peer identification events
for i := 0; i < 2; i++ {
select {
case <-sub0.Out():
case <-sub2.Out():
case <-ctx.Done():
assert.FailNow(t, "timeout waiting for peers to connect")
}
}

// subscribe to BEFP
_, err = serviceA.Subscribe(BadEncoding)
require.NoError(t, err)

subsB, err := serviceB.Subscribe(BadEncoding)
require.NoError(t, err)
defer subsB.Cancel()

subsC, err := serviceC.Subscribe(BadEncoding)
require.NoError(t, err)
defer subsC.Cancel()

// deregister validator in order to send Fraud Proof
fserviceA.pubsub.UnregisterTopicValidator(getSubTopic(BadEncoding)) //nolint:errcheck
// Broadcast BEFP
err = fserviceA.Broadcast(ctx, CreateBadEncodingProof([]byte("hash"), uint64(h.Height), errByz),
pubsub.WithReadiness(pubsub.MinTopicSize(1)))
require.NoError(t, err)

newCtx, cancel := context.WithTimeout(ctx, time.Millisecond*100)
t.Cleanup(cancel)
_, err = subsB.Proof(newCtx)
require.Error(t, err)
require.True(t, bl.Contains(net.Hosts()[0].ID()))

proofs, err := serviceC.Get(ctx, BadEncoding)
require.Error(t, err)
require.Nil(t, proofs)
}

func createService(t *testing.T) (Service, *mockStore) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
t.Cleanup(cancel)

// create mock network
net, err := mocknet.FullMeshLinked(2)
net, err := mocknet.FullMeshLinked(1)
require.NoError(t, err)

// create pubsub for host
ps, err := pubsub.NewGossipSub(ctx, net.Hosts()[0],
pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign))
require.NoError(t, err)
store := createStore(t, 10)
return NewService(ps, store.GetByHeight, sync.MutexWrap(datastore.NewMapDatastore())), store
}

func createServiceWithHost(t *testing.T, host host.Host) (Service, *mockStore) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
t.Cleanup(cancel)

// create pubsub for host
ps, err := pubsub.NewGossipSub(ctx, host,
pubsub.WithMessageSignaturePolicy(pubsub.StrictNoSign))
require.NoError(t, err)
store := createStore(t, 10)
return NewService(ps, store.GetByHeight, sync.MutexWrap(datastore.NewMapDatastore())), store
}
3 changes: 2 additions & 1 deletion fraud/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/ipfs/go-blockservice"
pubsub "github.com/libp2p/go-libp2p-pubsub"

"github.com/celestiaorg/celestia-node/header"
"github.com/celestiaorg/celestia-node/ipld"
Expand All @@ -13,7 +14,7 @@ import (
type DummyService struct {
}

func (d *DummyService) Broadcast(context.Context, Proof) error {
func (d *DummyService) Broadcast(context.Context, Proof, ...pubsub.PubOpt) error {
return nil
}

Expand Down

0 comments on commit dd7d034

Please sign in to comment.