From 3af58727f1cbb9488cc6e635d7c331004121c0bc Mon Sep 17 00:00:00 2001
From: capossele <angelocapossele@gmail.com>
Date: Thu, 5 Dec 2019 12:36:45 +0000
Subject: [PATCH] :construction: WIP

---
 go.mod                                        |   3 +-
 go.sum                                        |   6 +
 main.go                                       |   3 +-
 packages/gossip/events.go                     |  28 ++
 packages/gossip/manager.go                    | 204 +++++++++
 packages/gossip/manager_test.go               | 246 +++++++++++
 .../gossip/neighbor/neighbor.go               |  45 +-
 packages/gossip/proto/message.go              |  30 ++
 packages/gossip/proto/message.pb.go           | 121 ++++++
 packages/gossip/proto/message.proto           |  15 +
 packages/gossip/transport/connection.go       |  44 ++
 packages/gossip/transport/handshake.go        |  90 ++++
 .../gossip/transport/proto/handshake.pb.go    | 152 +++++++
 .../gossip/transport/proto/handshake.proto    |  21 +
 packages/gossip/transport/transport.go        | 386 ++++++++++++++++++
 packages/gossip/transport/transport_test.go   | 197 +++++++++
 plugins/autopeering/plugin.go                 |  38 +-
 plugins/gossip-on-solidification/plugin.go    |  15 -
 plugins/gossip/errors.go                      |  12 -
 plugins/gossip/events.go                      |  97 -----
 plugins/gossip/gossip.go                      |  86 ++++
 plugins/gossip/neighbors.go                   | 286 -------------
 plugins/gossip/plugin.go                      |  18 +-
 plugins/gossip/protocol.go                    | 199 ---------
 plugins/gossip/protocol_v1.go                 | 383 -----------------
 plugins/gossip/protocol_v1.png                | Bin 24951 -> 0 bytes
 plugins/gossip/send_queue.go                  | 156 -------
 plugins/gossip/server.go                      |  77 ----
 plugins/gossip/transaction_processor.go       |  26 --
 plugins/gossip/transaction_processor_test.go  |  49 ---
 plugins/tangle/events.go                      |   4 +-
 31 files changed, 1693 insertions(+), 1344 deletions(-)
 create mode 100644 packages/gossip/events.go
 create mode 100644 packages/gossip/manager.go
 create mode 100644 packages/gossip/manager_test.go
 rename plugins/gossip/neighborMap.go => packages/gossip/neighbor/neighbor.go (56%)
 create mode 100644 packages/gossip/proto/message.go
 create mode 100644 packages/gossip/proto/message.pb.go
 create mode 100644 packages/gossip/proto/message.proto
 create mode 100644 packages/gossip/transport/connection.go
 create mode 100644 packages/gossip/transport/handshake.go
 create mode 100644 packages/gossip/transport/proto/handshake.pb.go
 create mode 100644 packages/gossip/transport/proto/handshake.proto
 create mode 100644 packages/gossip/transport/transport.go
 create mode 100644 packages/gossip/transport/transport_test.go
 delete mode 100644 plugins/gossip-on-solidification/plugin.go
 delete mode 100644 plugins/gossip/errors.go
 delete mode 100644 plugins/gossip/events.go
 create mode 100644 plugins/gossip/gossip.go
 delete mode 100644 plugins/gossip/neighbors.go
 delete mode 100644 plugins/gossip/protocol.go
 delete mode 100644 plugins/gossip/protocol_v1.go
 delete mode 100644 plugins/gossip/protocol_v1.png
 delete mode 100644 plugins/gossip/send_queue.go
 delete mode 100644 plugins/gossip/server.go
 delete mode 100644 plugins/gossip/transaction_processor.go
 delete mode 100644 plugins/gossip/transaction_processor_test.go

diff --git a/go.mod b/go.mod
index 41d77852..1a51bdbc 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.13
 
 require (
 	github.com/StabbyCutyou/buffstreams v2.0.0+incompatible
+	github.com/capossele/gossip v0.0.0-20191205112840-0e578079b414
 	github.com/dgraph-io/badger v1.6.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/gdamore/tcell v1.3.0
@@ -11,7 +12,7 @@ require (
 	github.com/golang/protobuf v1.3.2
 	github.com/google/open-location-code/go v0.0.0-20190903173953-119bc96a3a51
 	github.com/gorilla/websocket v1.4.1
-	github.com/iotaledger/autopeering-sim v0.0.0-20191201144404-58a6f3b1a56d
+	github.com/iotaledger/autopeering-sim v0.0.0-20191202192349-f8e7a238c2bb
 	github.com/iotaledger/hive.go v0.0.0-20191125115115-f88d4ecab6dd
 	github.com/iotaledger/iota.go v1.0.0-beta.10
 	github.com/labstack/echo v3.3.10+incompatible
diff --git a/go.sum b/go.sum
index 756f7266..55861d76 100644
--- a/go.sum
+++ b/go.sum
@@ -18,6 +18,10 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
 github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/capossele/gossip v0.0.0-20191204191545-36eddf08c1aa h1:46C1Ce+93zGZ+JJ4JUw3EuXHQXqKYzIX7+CRG0fweNk=
+github.com/capossele/gossip v0.0.0-20191204191545-36eddf08c1aa/go.mod h1:P8rJEmorO5QpCL236F8vNhUFwFFjezt2d/+/LeRlELA=
+github.com/capossele/gossip v0.0.0-20191205112840-0e578079b414 h1:C9Q279xU15Qt5WVZNH6t/1L7exbTVYp1uMRS9NSmL/U=
+github.com/capossele/gossip v0.0.0-20191205112840-0e578079b414/go.mod h1:DnYLNZclq7cY6s2oA6wwhQ4tDB0j38enJHPrhpzOpJc=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -113,6 +117,8 @@ github.com/iotaledger/autopeering-sim v0.0.0-20191127100001-7ff75c77f051 h1:6JWv
 github.com/iotaledger/autopeering-sim v0.0.0-20191127100001-7ff75c77f051/go.mod h1:JiaqaxLkQVnd8e/sya9y/LlRW56WlRKRl2TQXQCVssI=
 github.com/iotaledger/autopeering-sim v0.0.0-20191201144404-58a6f3b1a56d h1:wZoNQRwLj4PqhKfruVQ1Yeci6kaSXnE9nSNCFtJWZ9s=
 github.com/iotaledger/autopeering-sim v0.0.0-20191201144404-58a6f3b1a56d/go.mod h1:JiaqaxLkQVnd8e/sya9y/LlRW56WlRKRl2TQXQCVssI=
+github.com/iotaledger/autopeering-sim v0.0.0-20191202192349-f8e7a238c2bb h1:nfe6HpDhLjvnliVwaz8sO2E/fpD4BEnI/FwfrU+iDRU=
+github.com/iotaledger/autopeering-sim v0.0.0-20191202192349-f8e7a238c2bb/go.mod h1:JiaqaxLkQVnd8e/sya9y/LlRW56WlRKRl2TQXQCVssI=
 github.com/iotaledger/goshimmer v0.0.0-20191113134331-c2d1b2f9d533/go.mod h1:7vYiofXphp9+PkgVAEM0pvw3aoi4ksrZ7lrEgX50XHs=
 github.com/iotaledger/hive.go v0.0.0-20191118130432-89eebe8fe8eb h1:nuS/LETRJ8obUyBIZeyxeei0ZPlyOMj8YPziOgSM4Og=
 github.com/iotaledger/hive.go v0.0.0-20191118130432-89eebe8fe8eb/go.mod h1:1Thhlil4lHzuy53EVvmEbEvWBFY0Tasp4kCBfxBCPIk=
diff --git a/main.go b/main.go
index b43e2240..2d313d68 100644
--- a/main.go
+++ b/main.go
@@ -7,7 +7,6 @@ import (
 	"github.com/iotaledger/goshimmer/plugins/cli"
 	"github.com/iotaledger/goshimmer/plugins/dashboard"
 	"github.com/iotaledger/goshimmer/plugins/gossip"
-	gossip_on_solidification "github.com/iotaledger/goshimmer/plugins/gossip-on-solidification"
 	"github.com/iotaledger/goshimmer/plugins/gracefulshutdown"
 	"github.com/iotaledger/goshimmer/plugins/metrics"
 	"github.com/iotaledger/goshimmer/plugins/statusscreen"
@@ -28,7 +27,7 @@ func main() {
 		cli.PLUGIN,
 		autopeering.PLUGIN,
 		gossip.PLUGIN,
-		gossip_on_solidification.PLUGIN,
+		//gossip_on_solidification.PLUGIN,
 		tangle.PLUGIN,
 		bundleprocessor.PLUGIN,
 		analysis.PLUGIN,
diff --git a/packages/gossip/events.go b/packages/gossip/events.go
new file mode 100644
index 00000000..4c4e34a0
--- /dev/null
+++ b/packages/gossip/events.go
@@ -0,0 +1,28 @@
+package gossip
+
+import (
+	"github.com/iotaledger/autopeering-sim/peer"
+	"github.com/iotaledger/hive.go/events"
+)
+
+// Events contains all the events that are triggered during the gossip protocol.
+type Events struct {
+	NewTransaction *events.Event
+	DropNeighbor   *events.Event
+}
+
+type NewTransactionEvent struct {
+	Body []byte
+	Peer *peer.Peer
+}
+type DropNeighborEvent struct {
+	Peer *peer.Peer
+}
+
+func newTransaction(handler interface{}, params ...interface{}) {
+	handler.(func(*NewTransactionEvent))(params[0].(*NewTransactionEvent))
+}
+
+func dropNeighbor(handler interface{}, params ...interface{}) {
+	handler.(func(*DropNeighborEvent))(params[0].(*DropNeighborEvent))
+}
diff --git a/packages/gossip/manager.go b/packages/gossip/manager.go
new file mode 100644
index 00000000..6c889e36
--- /dev/null
+++ b/packages/gossip/manager.go
@@ -0,0 +1,204 @@
+package gossip
+
+import (
+	"net"
+
+	"github.com/capossele/gossip/neighbor"
+	pb "github.com/capossele/gossip/proto"
+	"github.com/capossele/gossip/transport"
+	"github.com/golang/protobuf/proto"
+	"github.com/iotaledger/autopeering-sim/peer"
+	"github.com/iotaledger/hive.go/events"
+	"github.com/pkg/errors"
+	"go.uber.org/zap"
+)
+
+const (
+	maxAttempts = 3
+)
+
+var (
+	Event Events
+)
+
+type GetTransaction func(txHash []byte) ([]byte, error)
+
+type Manager struct {
+	neighborhood   *neighbor.NeighborMap
+	trans          *transport.TransportTCP
+	log            *zap.SugaredLogger
+	getTransaction GetTransaction
+	Events         Events
+}
+
+func NewManager(t *transport.TransportTCP, log *zap.SugaredLogger, f GetTransaction) *Manager {
+	mgr := &Manager{
+		neighborhood:   neighbor.NewMap(),
+		trans:          t,
+		log:            log,
+		getTransaction: f,
+		Events: Events{
+			NewTransaction: events.NewEvent(newTransaction),
+			DropNeighbor:   events.NewEvent(dropNeighbor)},
+	}
+	Event = mgr.Events
+	return mgr
+}
+
+func (m *Manager) AddOutbound(p *peer.Peer) error {
+	return m.addNeighbor(p, m.trans.DialPeer)
+}
+
+func (m *Manager) AddInbound(p *peer.Peer) error {
+	return m.addNeighbor(p, m.trans.AcceptPeer)
+}
+
+func (m *Manager) DropNeighbor(id peer.ID) {
+	m.deleteNeighbor(id)
+}
+
+func (m *Manager) RequestTransaction(data []byte, to ...*neighbor.Neighbor) {
+	req := &pb.TransactionRequest{}
+	err := proto.Unmarshal(data, req)
+	if err != nil {
+		m.log.Warnw("Data to send is not a Transaction Request", "err", err)
+	}
+	msg := marshal(req)
+
+	m.send(msg, to...)
+}
+
+func (m *Manager) Send(data []byte, to ...*neighbor.Neighbor) {
+	tx := &pb.Transaction{}
+	err := proto.Unmarshal(data, tx)
+	if err != nil {
+		m.log.Warnw("Data to send is not a Transaction", "err", err)
+	}
+	msg := marshal(tx)
+
+	m.send(msg, to...)
+}
+
+func (m *Manager) send(msg []byte, to ...*neighbor.Neighbor) {
+	neighbors := m.neighborhood.GetSlice()
+	if to != nil {
+		neighbors = to
+	}
+
+	for _, neighbor := range neighbors {
+		m.log.Debugw("Sending", "to", neighbor.Peer.ID().String(), "msg", msg)
+		err := neighbor.Conn.Write(msg)
+		if err != nil {
+			m.log.Debugw("send error", "err", err)
+		}
+	}
+}
+
+func (m *Manager) addNeighbor(peer *peer.Peer, handshake func(*peer.Peer) (*transport.Connection, error)) error {
+	if _, ok := m.neighborhood.Load(peer.ID().String()); ok {
+		return errors.New("Neighbor already added")
+	}
+
+	var err error
+	var conn *transport.Connection
+	i := 0
+	for i = 0; i < maxAttempts; i++ {
+		conn, err = handshake(peer)
+		if err != nil {
+			m.log.Warnw("Connection attempt failed", "attempt", i+1)
+		} else {
+			break
+		}
+	}
+	if i == maxAttempts {
+		m.log.Warnw("Connection failed to", "peer", peer.ID().String())
+		m.Events.DropNeighbor.Trigger(&DropNeighborEvent{Peer: peer})
+		return err
+	}
+
+	// add the new neighbor
+	neighbor := neighbor.New(peer, conn)
+	m.neighborhood.Store(peer.ID().String(), neighbor)
+
+	// start listener for the new neighbor
+	go m.readLoop(neighbor)
+
+	return nil
+}
+
+func (m *Manager) deleteNeighbor(id peer.ID) {
+	m.log.Debugw("Deleting neighbor", "neighbor", id.String())
+
+	p, ok := m.neighborhood.Delete(id.String())
+	if ok {
+		m.Events.DropNeighbor.Trigger(&DropNeighborEvent{Peer: p.Peer})
+	}
+}
+
+func (m *Manager) readLoop(neighbor *neighbor.Neighbor) {
+	for {
+		data, err := neighbor.Conn.Read()
+		if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
+			// ignore temporary read errors.
+			//m.log.Debugw("temporary read error", "err", err)
+			continue
+		} else if err != nil {
+			// return from the loop on all other errors
+			m.log.Debugw("reading stopped")
+			m.deleteNeighbor(neighbor.Peer.ID())
+
+			return
+		}
+		if err := m.handlePacket(data, neighbor); err != nil {
+			m.log.Warnw("failed to handle packet", "from", neighbor.Peer.ID().String(), "err", err)
+		}
+	}
+}
+
+func (m *Manager) handlePacket(data []byte, neighbor *neighbor.Neighbor) error {
+	switch pb.MType(data[0]) {
+
+	// Incoming Transaction
+	case pb.MTransaction:
+		msg := new(pb.Transaction)
+		if err := proto.Unmarshal(data[1:], msg); err != nil {
+			return errors.Wrap(err, "invalid message")
+		}
+		m.log.Debugw("Received Transaction", "data", msg.GetBody())
+		m.Events.NewTransaction.Trigger(&NewTransactionEvent{Body: msg.GetBody(), Peer: neighbor.Peer})
+
+	// Incoming Transaction request
+	case pb.MTransactionRequest:
+		msg := new(pb.TransactionRequest)
+		if err := proto.Unmarshal(data[1:], msg); err != nil {
+			return errors.Wrap(err, "invalid message")
+		}
+		m.log.Debugw("Received Tx Req", "data", msg.GetHash())
+		// do something
+		tx, err := m.getTransaction(msg.GetHash())
+		if err != nil {
+			m.log.Debugw("Tx not available", "tx", msg.GetHash())
+		} else {
+			m.log.Debugw("Tx found", "tx", tx)
+			m.Send(tx, neighbor)
+		}
+
+	default:
+		return nil
+	}
+
+	return nil
+}
+
+func marshal(msg pb.Message) []byte {
+	mType := msg.Type()
+	if mType > 0xFF {
+		panic("invalid message")
+	}
+
+	data, err := proto.Marshal(msg)
+	if err != nil {
+		panic("invalid message")
+	}
+	return append([]byte{byte(mType)}, data...)
+}
diff --git a/packages/gossip/manager_test.go b/packages/gossip/manager_test.go
new file mode 100644
index 00000000..e037c22d
--- /dev/null
+++ b/packages/gossip/manager_test.go
@@ -0,0 +1,246 @@
+package gossip
+
+import (
+	"log"
+	"sync"
+	"testing"
+	"time"
+
+	pb "github.com/capossele/gossip/proto"
+	"github.com/capossele/gossip/transport"
+	"github.com/golang/protobuf/proto"
+	"github.com/iotaledger/autopeering-sim/peer"
+	"github.com/iotaledger/autopeering-sim/peer/service"
+	"github.com/iotaledger/hive.go/events"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.uber.org/zap"
+)
+
+const graceTime = 5 * time.Millisecond
+
+var logger *zap.SugaredLogger
+
+func init() {
+	l, err := zap.NewDevelopment()
+	if err != nil {
+		log.Fatalf("cannot initialize logger: %v", err)
+	}
+	logger = l.Sugar()
+}
+func testGetTransaction([]byte) ([]byte, error) {
+	tx := &pb.TransactionRequest{
+		Hash: []byte("testTx"),
+	}
+	b, _ := proto.Marshal(tx)
+	return b, nil
+}
+
+func newTest(t require.TestingT, name string) (*Manager, func(), *peer.Peer) {
+	log := logger.Named(name)
+	db := peer.NewMemoryDB(log.Named("db"))
+	local, err := peer.NewLocal("peering", name, db)
+	require.NoError(t, err)
+	require.NoError(t, local.UpdateService(service.GossipKey, "tcp", "localhost:0"))
+
+	trans, err := transport.Listen(local, log)
+	require.NoError(t, err)
+
+	mgr := NewManager(trans, log, testGetTransaction)
+
+	// update the service with the actual address
+	require.NoError(t, local.UpdateService(service.GossipKey, trans.LocalAddr().Network(), trans.LocalAddr().String()))
+
+	teardown := func() {
+		trans.Close()
+		db.Close()
+	}
+	return mgr, teardown, &local.Peer
+}
+
+func TestClose(t *testing.T) {
+	_, teardown, _ := newTest(t, "A")
+	teardown()
+}
+
+func TestUnicast(t *testing.T) {
+	mgrA, closeA, peerA := newTest(t, "A")
+	defer closeA()
+	mgrB, closeB, peerB := newTest(t, "B")
+	defer closeB()
+
+	var wg sync.WaitGroup
+	wg.Add(2)
+
+	go func() {
+		defer wg.Done()
+		err := mgrA.addNeighbor(peerB, mgrA.trans.AcceptPeer)
+		assert.NoError(t, err)
+	}()
+	time.Sleep(graceTime)
+	go func() {
+		defer wg.Done()
+		err := mgrB.addNeighbor(peerA, mgrB.trans.DialPeer)
+		assert.NoError(t, err)
+	}()
+
+	// wait for the connections to establish
+	wg.Wait()
+
+	tx := &pb.Transaction{Body: []byte("Hello!")}
+
+	triggered := make(chan struct{}, 1)
+	mgrB.Events.NewTransaction.Attach(events.NewClosure(func(ev *NewTransactionEvent) {
+		require.Empty(t, triggered) // only once
+		assert.Equal(t, tx.GetBody(), ev.Body)
+		assert.Equal(t, peerA, ev.Peer)
+		triggered <- struct{}{}
+	}))
+
+	b, err := proto.Marshal(tx)
+	require.NoError(t, err)
+	mgrA.Send(b)
+
+	// eventually the event should be triggered
+	assert.Eventually(t, func() bool { return len(triggered) >= 1 }, time.Second, 10*time.Millisecond)
+}
+
+func TestBroadcast(t *testing.T) {
+	mgrA, closeA, peerA := newTest(t, "A")
+	defer closeA()
+	mgrB, closeB, peerB := newTest(t, "B")
+	defer closeB()
+	mgrC, closeC, peerC := newTest(t, "C")
+	defer closeC()
+
+	var wg sync.WaitGroup
+	wg.Add(4)
+
+	go func() {
+		defer wg.Done()
+		err := mgrA.addNeighbor(peerB, mgrA.trans.AcceptPeer)
+		assert.NoError(t, err)
+	}()
+	go func() {
+		defer wg.Done()
+		err := mgrA.addNeighbor(peerC, mgrA.trans.AcceptPeer)
+		assert.NoError(t, err)
+	}()
+	time.Sleep(graceTime)
+	go func() {
+		defer wg.Done()
+		err := mgrB.addNeighbor(peerA, mgrB.trans.DialPeer)
+		assert.NoError(t, err)
+	}()
+	go func() {
+		defer wg.Done()
+		err := mgrC.addNeighbor(peerA, mgrC.trans.DialPeer)
+		assert.NoError(t, err)
+	}()
+
+	// wait for the connections to establish
+	wg.Wait()
+
+	tx := &pb.Transaction{Body: []byte("Hello!")}
+
+	triggeredB := make(chan struct{}, 1)
+	mgrB.Events.NewTransaction.Attach(events.NewClosure(func(ev *NewTransactionEvent) {
+		require.Empty(t, triggeredB) // only once
+		assert.Equal(t, tx.GetBody(), ev.Body)
+		assert.Equal(t, peerA, ev.Peer)
+		triggeredB <- struct{}{}
+	}))
+
+	triggeredC := make(chan struct{}, 1)
+	mgrC.Events.NewTransaction.Attach(events.NewClosure(func(ev *NewTransactionEvent) {
+		require.Empty(t, triggeredC) // only once
+		assert.Equal(t, tx.GetBody(), ev.Body)
+		assert.Equal(t, peerA, ev.Peer)
+		triggeredC <- struct{}{}
+	}))
+
+	b, err := proto.Marshal(tx)
+	assert.NoError(t, err)
+	mgrA.Send(b)
+
+	// eventually the events should be triggered
+	success := func() bool {
+		return len(triggeredB) >= 1 && len(triggeredC) >= 1
+	}
+	assert.Eventually(t, success, time.Second, 10*time.Millisecond)
+}
+
+func TestDropUnsuccessfulAccept(t *testing.T) {
+	mgrA, closeA, _ := newTest(t, "A")
+	defer closeA()
+	_, closeB, peerB := newTest(t, "B")
+	defer closeB()
+
+	triggered := make(chan struct{}, 1)
+	mgrA.Events.DropNeighbor.Attach(events.NewClosure(func(ev *DropNeighborEvent) {
+		require.Empty(t, triggered) // only once
+		assert.Equal(t, peerB, ev.Peer)
+		triggered <- struct{}{}
+	}))
+
+	err := mgrA.addNeighbor(peerB, mgrA.trans.AcceptPeer)
+	assert.Error(t, err)
+
+	// eventually the event should be triggered
+	assert.Eventually(t, func() bool { return len(triggered) >= 1 }, time.Second, 10*time.Millisecond)
+}
+
+func TestTxRequest(t *testing.T) {
+	mgrA, closeA, peerA := newTest(t, "A")
+	defer closeA()
+	mgrB, closeB, peerB := newTest(t, "B")
+	defer closeB()
+
+	var wg sync.WaitGroup
+	wg.Add(2)
+
+	go func() {
+		defer wg.Done()
+		err := mgrA.addNeighbor(peerB, mgrA.trans.AcceptPeer)
+		assert.NoError(t, err)
+		logger.Debugw("Len", "len", mgrA.neighborhood.Len())
+	}()
+	go func() {
+		defer wg.Done()
+		err := mgrB.addNeighbor(peerA, mgrB.trans.DialPeer)
+		assert.NoError(t, err)
+		logger.Debugw("Len", "len", mgrB.neighborhood.Len())
+	}()
+
+	wg.Wait()
+
+	tx := &pb.TransactionRequest{
+		Hash: []byte("Hello!"),
+	}
+	b, err := proto.Marshal(tx)
+	assert.NoError(t, err)
+
+	sendChan := make(chan struct{})
+	sendSuccess := false
+
+	mgrA.Events.NewTransaction.Attach(events.NewClosure(func(ev *NewTransactionEvent) {
+		logger.Debugw("New TX Event triggered", "data", ev.Body, "from", ev.Peer.ID().String())
+		assert.Equal(t, []byte("testTx"), ev.Body)
+		assert.Equal(t, peerB, ev.Peer)
+		sendChan <- struct{}{}
+	}))
+
+	mgrA.RequestTransaction(b)
+
+	timer := time.NewTimer(5 * time.Second)
+	defer timer.Stop()
+
+	select {
+	case <-sendChan:
+		sendSuccess = true
+	case <-timer.C:
+		sendSuccess = false
+	}
+
+	assert.True(t, sendSuccess)
+}
diff --git a/plugins/gossip/neighborMap.go b/packages/gossip/neighbor/neighbor.go
similarity index 56%
rename from plugins/gossip/neighborMap.go
rename to packages/gossip/neighbor/neighbor.go
index 9823c355..41d2d25f 100644
--- a/plugins/gossip/neighborMap.go
+++ b/packages/gossip/neighbor/neighbor.go
@@ -1,24 +1,40 @@
-package gossip
+package neighbor
 
 import (
 	"sync"
+
+	"github.com/capossele/gossip/transport"
+	"github.com/iotaledger/autopeering-sim/peer"
 )
 
-// NeighborMap is the mapping of neighbor identifier and their neighbor struct
-// It uses a mutex to handle concurrent access to its internal map
+// Neighbor defines a neighbor
+type Neighbor struct {
+	Peer *peer.Peer
+	Conn *transport.Connection
+}
+
+// NeighborMap implements a map of neighbors thread safe
 type NeighborMap struct {
 	sync.RWMutex
 	internal map[string]*Neighbor
 }
 
-// NewPeerMap returns a new PeerMap
-func NewNeighborMap() *NeighborMap {
+// NewMap returns a new NeighborMap
+func NewMap() *NeighborMap {
 	return &NeighborMap{
 		internal: make(map[string]*Neighbor),
 	}
 }
 
-// Len returns the number of peers stored in a PeerMap
+// New returns a new Neighbor
+func New(peer *peer.Peer, conn *transport.Connection) *Neighbor {
+	return &Neighbor{
+		Peer: peer,
+		Conn: conn,
+	}
+}
+
+// Len returns the number of neighbors stored in a NeighborMap
 func (nm *NeighborMap) Len() int {
 	nm.RLock()
 	defer nm.RUnlock()
@@ -36,7 +52,7 @@ func (nm *NeighborMap) GetMap() map[string]*Neighbor {
 	return newMap
 }
 
-// GetMap returns the content of the entire internal map
+// GetSlice returns a slice of the content of the entire internal map
 func (nm *NeighborMap) GetSlice() []*Neighbor {
 	newSlice := make([]*Neighbor, nm.Len())
 	nm.RLock()
@@ -49,9 +65,9 @@ func (nm *NeighborMap) GetSlice() []*Neighbor {
 	return newSlice
 }
 
-// Load returns the peer for a given key.
+// Load returns the neighbor for a given key.
 // It also return a bool to communicate the presence of the given
-// peer into the internal map
+// neighbor into the internal map
 func (nm *NeighborMap) Load(key string) (value *Neighbor, ok bool) {
 	nm.RLock()
 	defer nm.RUnlock()
@@ -60,18 +76,21 @@ func (nm *NeighborMap) Load(key string) (value *Neighbor, ok bool) {
 }
 
 // Delete removes the entire entry for a given key and return true if successful
-func (nm *NeighborMap) Delete(key string) (deletedPeer *Neighbor, ok bool) {
-	deletedPeer, ok = nm.Load(key)
+func (nm *NeighborMap) Delete(key string) (deletedNeighbor *Neighbor, ok bool) {
+	deletedNeighbor, ok = nm.Load(key)
 	if !ok {
 		return nil, false
 	}
 	nm.Lock()
 	defer nm.Unlock()
+	if deletedNeighbor.Conn != nil {
+		deletedNeighbor.Conn.Close()
+	}
 	delete(nm.internal, key)
-	return deletedPeer, true
+	return deletedNeighbor, true
 }
 
-// Store adds a new peer to the PeerMap
+// Store adds a new neighbor to the NeighborMap
 func (nm *NeighborMap) Store(key string, value *Neighbor) {
 	nm.Lock()
 	defer nm.Unlock()
diff --git a/packages/gossip/proto/message.go b/packages/gossip/proto/message.go
new file mode 100644
index 00000000..f9404c1e
--- /dev/null
+++ b/packages/gossip/proto/message.go
@@ -0,0 +1,30 @@
+package proto
+
+import (
+	"github.com/golang/protobuf/proto"
+)
+
+// MType is the type of message type enum.
+type MType uint
+
+// An enum for the different message types.
+const (
+	MTransaction MType = 20 + iota
+	MTransactionRequest
+)
+
+// Message extends the proto.Message interface with additional util functions.
+type Message interface {
+	proto.Message
+
+	// Name returns the name of the corresponding message type for debugging.
+	Name() string
+	// Type returns the type of the corresponding message as an enum.
+	Type() MType
+}
+
+func (m *Transaction) Name() string { return "TRANSACTION" }
+func (m *Transaction) Type() MType  { return MTransaction }
+
+func (m *TransactionRequest) Name() string { return "TRANSACTION_REQUEST" }
+func (m *TransactionRequest) Type() MType  { return MTransactionRequest }
diff --git a/packages/gossip/proto/message.pb.go b/packages/gossip/proto/message.pb.go
new file mode 100644
index 00000000..1e67ece1
--- /dev/null
+++ b/packages/gossip/proto/message.pb.go
@@ -0,0 +1,121 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: proto/message.proto
+
+package proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type Transaction struct {
+	// body of the tx
+	Body                 []byte   `protobuf:"bytes,1,opt,name=body,proto3" json:"body,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Transaction) Reset()         { *m = Transaction{} }
+func (m *Transaction) String() string { return proto.CompactTextString(m) }
+func (*Transaction) ProtoMessage()    {}
+func (*Transaction) Descriptor() ([]byte, []int) {
+	return fileDescriptor_33f3a5e1293a7bcd, []int{0}
+}
+
+func (m *Transaction) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Transaction.Unmarshal(m, b)
+}
+func (m *Transaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Transaction.Marshal(b, m, deterministic)
+}
+func (m *Transaction) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Transaction.Merge(m, src)
+}
+func (m *Transaction) XXX_Size() int {
+	return xxx_messageInfo_Transaction.Size(m)
+}
+func (m *Transaction) XXX_DiscardUnknown() {
+	xxx_messageInfo_Transaction.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Transaction proto.InternalMessageInfo
+
+func (m *Transaction) GetBody() []byte {
+	if m != nil {
+		return m.Body
+	}
+	return nil
+}
+
+type TransactionRequest struct {
+	// transaction hash
+	Hash                 []byte   `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *TransactionRequest) Reset()         { *m = TransactionRequest{} }
+func (m *TransactionRequest) String() string { return proto.CompactTextString(m) }
+func (*TransactionRequest) ProtoMessage()    {}
+func (*TransactionRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_33f3a5e1293a7bcd, []int{1}
+}
+
+func (m *TransactionRequest) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_TransactionRequest.Unmarshal(m, b)
+}
+func (m *TransactionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_TransactionRequest.Marshal(b, m, deterministic)
+}
+func (m *TransactionRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_TransactionRequest.Merge(m, src)
+}
+func (m *TransactionRequest) XXX_Size() int {
+	return xxx_messageInfo_TransactionRequest.Size(m)
+}
+func (m *TransactionRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_TransactionRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TransactionRequest proto.InternalMessageInfo
+
+func (m *TransactionRequest) GetHash() []byte {
+	if m != nil {
+		return m.Hash
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Transaction)(nil), "proto.Transaction")
+	proto.RegisterType((*TransactionRequest)(nil), "proto.TransactionRequest")
+}
+
+func init() { proto.RegisterFile("proto/message.proto", fileDescriptor_33f3a5e1293a7bcd) }
+
+var fileDescriptor_33f3a5e1293a7bcd = []byte{
+	// 139 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2e, 0x28, 0xca, 0x2f,
+	0xc9, 0xd7, 0xcf, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c, 0x4f, 0xd5, 0x03, 0xf3, 0x84, 0x58, 0xc1, 0x94,
+	0x92, 0x22, 0x17, 0x77, 0x48, 0x51, 0x62, 0x5e, 0x71, 0x62, 0x72, 0x49, 0x66, 0x7e, 0x9e, 0x90,
+	0x10, 0x17, 0x4b, 0x52, 0x7e, 0x4a, 0xa5, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x4f, 0x10, 0x98, 0xad,
+	0xa4, 0xc1, 0x25, 0x84, 0xa4, 0x24, 0x28, 0xb5, 0xb0, 0x34, 0xb5, 0xb8, 0x04, 0xa4, 0x32, 0x23,
+	0xb1, 0x38, 0x03, 0xa6, 0x12, 0xc4, 0x76, 0x52, 0x8e, 0x52, 0x4c, 0xcf, 0x2c, 0xc9, 0x28, 0x4d,
+	0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0x4e, 0x2c, 0xc8, 0x2f, 0x2e, 0x4e, 0xcd, 0x49, 0xd5, 0x4f,
+	0xcf, 0x2f, 0x2e, 0xce, 0x2c, 0xd0, 0x07, 0xdb, 0x98, 0xc4, 0x06, 0xa6, 0x8c, 0x01, 0x01, 0x00,
+	0x00, 0xff, 0xff, 0x34, 0x46, 0xa5, 0x0f, 0x96, 0x00, 0x00, 0x00,
+}
diff --git a/packages/gossip/proto/message.proto b/packages/gossip/proto/message.proto
new file mode 100644
index 00000000..b01b9368
--- /dev/null
+++ b/packages/gossip/proto/message.proto
@@ -0,0 +1,15 @@
+syntax = "proto3";
+
+option go_package = "github.com/capossele/gossip/proto";
+
+package proto;
+
+message Transaction {
+  // body of the tx
+  bytes body = 1;
+}
+
+message TransactionRequest {
+  // transaction hash
+  bytes hash = 1;
+}
\ No newline at end of file
diff --git a/packages/gossip/transport/connection.go b/packages/gossip/transport/connection.go
new file mode 100644
index 00000000..f6886129
--- /dev/null
+++ b/packages/gossip/transport/connection.go
@@ -0,0 +1,44 @@
+package transport
+
+import (
+	"net"
+
+	"github.com/iotaledger/autopeering-sim/peer"
+)
+
+const (
+	// MaxPacketSize specifies the maximum allowed size of packets.
+	// Packets larger than this will be cut and thus treated as invalid.
+	MaxPacketSize = 1280
+)
+
+type Connection struct {
+	peer *peer.Peer
+	conn net.Conn
+}
+
+func newConnection(p *peer.Peer, c net.Conn) *Connection {
+	return &Connection{
+		peer: p,
+		conn: c,
+	}
+}
+
+func (c *Connection) Close() {
+	c.conn.Close()
+}
+
+func (c *Connection) Read() ([]byte, error) {
+	b := make([]byte, MaxPacketSize)
+	n, err := c.conn.Read(b)
+	if err != nil {
+		return nil, err
+	}
+
+	return b[:n], nil
+}
+
+func (c *Connection) Write(b []byte) error {
+	_, err := c.conn.Write(b)
+	return err
+}
diff --git a/packages/gossip/transport/handshake.go b/packages/gossip/transport/handshake.go
new file mode 100644
index 00000000..c6e253ff
--- /dev/null
+++ b/packages/gossip/transport/handshake.go
@@ -0,0 +1,90 @@
+package transport
+
+import (
+	"bytes"
+	"time"
+
+	pb "github.com/capossele/gossip/transport/proto"
+	"github.com/golang/protobuf/proto"
+	"github.com/iotaledger/autopeering-sim/server"
+)
+
+const (
+	HandshakeExpiration = 20 * time.Second
+	VersionNum          = 0
+)
+
+// isExpired checks whether the given UNIX time stamp is too far in the past.
+func isExpired(ts int64) bool {
+	return time.Since(time.Unix(ts, 0)) >= HandshakeExpiration
+}
+
+func newHandshakeRequest(fromAddr string, toAddr string) ([]byte, error) {
+	m := &pb.HandshakeRequest{
+		Version:   VersionNum,
+		From:      fromAddr,
+		To:        toAddr,
+		Timestamp: time.Now().Unix(),
+	}
+	return proto.Marshal(m)
+}
+
+func newHandshakeResponse(reqData []byte) ([]byte, error) {
+	m := &pb.HandshakeResponse{
+		ReqHash: server.PacketHash(reqData),
+	}
+	return proto.Marshal(m)
+}
+
+func (t *TransportTCP) validateHandshakeRequest(reqData []byte, fromAddr string) bool {
+	m := new(pb.HandshakeRequest)
+	if err := proto.Unmarshal(reqData, m); err != nil {
+		t.log.Debugw("invalid handshake",
+			"err", err,
+		)
+		return false
+	}
+	if m.GetVersion() != VersionNum {
+		t.log.Debugw("invalid handshake",
+			"version", m.GetVersion(),
+		)
+		return false
+	}
+	if m.GetFrom() != fromAddr {
+		t.log.Debugw("invalid handshake",
+			"from", m.GetFrom(),
+		)
+		return false
+	}
+	if m.GetTo() != t.LocalAddr().String() {
+		t.log.Debugw("invalid handshake",
+			"to", m.GetTo(),
+		)
+		return false
+	}
+	if isExpired(m.GetTimestamp()) {
+		t.log.Debugw("invalid handshake",
+			"timestamp", time.Unix(m.GetTimestamp(), 0),
+		)
+	}
+
+	return true
+}
+
+func (t *TransportTCP) validateHandshakeResponse(resData []byte, reqData []byte) bool {
+	m := new(pb.HandshakeResponse)
+	if err := proto.Unmarshal(resData, m); err != nil {
+		t.log.Debugw("invalid handshake",
+			"err", err,
+		)
+		return false
+	}
+	if !bytes.Equal(m.GetReqHash(), server.PacketHash(reqData)) {
+		t.log.Debugw("invalid handshake",
+			"hash", m.GetReqHash(),
+		)
+		return false
+	}
+
+	return true
+}
diff --git a/packages/gossip/transport/proto/handshake.pb.go b/packages/gossip/transport/proto/handshake.pb.go
new file mode 100644
index 00000000..c7f36887
--- /dev/null
+++ b/packages/gossip/transport/proto/handshake.pb.go
@@ -0,0 +1,152 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: transport/proto/handshake.proto
+
+package proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type HandshakeRequest struct {
+	// protocol version number
+	Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
+	// string form of the sender address (e.g. "192.0.2.1:25", "[2001:db8::1]:80")
+	From string `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"`
+	// string form of the recipient address
+	To string `protobuf:"bytes,3,opt,name=to,proto3" json:"to,omitempty"`
+	// unix time
+	Timestamp            int64    `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *HandshakeRequest) Reset()         { *m = HandshakeRequest{} }
+func (m *HandshakeRequest) String() string { return proto.CompactTextString(m) }
+func (*HandshakeRequest) ProtoMessage()    {}
+func (*HandshakeRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_d7101ffe19b05443, []int{0}
+}
+
+func (m *HandshakeRequest) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_HandshakeRequest.Unmarshal(m, b)
+}
+func (m *HandshakeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_HandshakeRequest.Marshal(b, m, deterministic)
+}
+func (m *HandshakeRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_HandshakeRequest.Merge(m, src)
+}
+func (m *HandshakeRequest) XXX_Size() int {
+	return xxx_messageInfo_HandshakeRequest.Size(m)
+}
+func (m *HandshakeRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_HandshakeRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_HandshakeRequest proto.InternalMessageInfo
+
+func (m *HandshakeRequest) GetVersion() uint32 {
+	if m != nil {
+		return m.Version
+	}
+	return 0
+}
+
+func (m *HandshakeRequest) GetFrom() string {
+	if m != nil {
+		return m.From
+	}
+	return ""
+}
+
+func (m *HandshakeRequest) GetTo() string {
+	if m != nil {
+		return m.To
+	}
+	return ""
+}
+
+func (m *HandshakeRequest) GetTimestamp() int64 {
+	if m != nil {
+		return m.Timestamp
+	}
+	return 0
+}
+
+type HandshakeResponse struct {
+	// hash of the ping packet
+	ReqHash              []byte   `protobuf:"bytes,1,opt,name=req_hash,json=reqHash,proto3" json:"req_hash,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *HandshakeResponse) Reset()         { *m = HandshakeResponse{} }
+func (m *HandshakeResponse) String() string { return proto.CompactTextString(m) }
+func (*HandshakeResponse) ProtoMessage()    {}
+func (*HandshakeResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_d7101ffe19b05443, []int{1}
+}
+
+func (m *HandshakeResponse) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_HandshakeResponse.Unmarshal(m, b)
+}
+func (m *HandshakeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_HandshakeResponse.Marshal(b, m, deterministic)
+}
+func (m *HandshakeResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_HandshakeResponse.Merge(m, src)
+}
+func (m *HandshakeResponse) XXX_Size() int {
+	return xxx_messageInfo_HandshakeResponse.Size(m)
+}
+func (m *HandshakeResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_HandshakeResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_HandshakeResponse proto.InternalMessageInfo
+
+func (m *HandshakeResponse) GetReqHash() []byte {
+	if m != nil {
+		return m.ReqHash
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*HandshakeRequest)(nil), "proto.HandshakeRequest")
+	proto.RegisterType((*HandshakeResponse)(nil), "proto.HandshakeResponse")
+}
+
+func init() { proto.RegisterFile("transport/proto/handshake.proto", fileDescriptor_d7101ffe19b05443) }
+
+var fileDescriptor_d7101ffe19b05443 = []byte{
+	// 203 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x5c, 0x8f, 0x3f, 0x4b, 0x04, 0x31,
+	0x10, 0xc5, 0xb9, 0x3f, 0x7a, 0xde, 0xa0, 0xa2, 0xa9, 0x22, 0x08, 0xca, 0x55, 0x82, 0xb8, 0x29,
+	0xfc, 0x06, 0x56, 0x57, 0xa7, 0xb4, 0x91, 0xec, 0x3a, 0x6e, 0x82, 0x26, 0x93, 0xcd, 0x64, 0xfd,
+	0xfc, 0x0e, 0x81, 0x45, 0xb1, 0x7a, 0xef, 0xf7, 0x0b, 0xe4, 0x25, 0x70, 0x57, 0x8b, 0x4b, 0x9c,
+	0xa9, 0x54, 0x93, 0x0b, 0x55, 0x32, 0xde, 0xa5, 0x77, 0xf6, 0xee, 0x13, 0xbb, 0xc6, 0xea, 0xa4,
+	0xc5, 0x21, 0xc1, 0xd5, 0x71, 0x39, 0xb1, 0x38, 0xcd, 0xc8, 0x55, 0x69, 0xd8, 0x7d, 0x63, 0xe1,
+	0x40, 0x49, 0xaf, 0xee, 0x57, 0x0f, 0x17, 0x76, 0x41, 0xa5, 0x60, 0xfb, 0x51, 0x28, 0xea, 0xb5,
+	0xe8, 0xbd, 0x6d, 0x5d, 0x5d, 0xc2, 0xba, 0x92, 0xde, 0x34, 0x23, 0x4d, 0xdd, 0xc2, 0xbe, 0x86,
+	0x28, 0xf7, 0xb8, 0x98, 0xf5, 0x56, 0xf4, 0xc6, 0xfe, 0x8a, 0x43, 0x07, 0xd7, 0x7f, 0xf6, 0xe4,
+	0x81, 0x89, 0x51, 0xdd, 0xc0, 0x59, 0xc1, 0xe9, 0xcd, 0x3b, 0xf6, 0x6d, 0xf1, 0xdc, 0xee, 0x84,
+	0x8f, 0x82, 0x2f, 0x4f, 0xaf, 0x8f, 0x63, 0xa8, 0x7e, 0xee, 0xbb, 0x81, 0xa2, 0x19, 0x5c, 0x26,
+	0x66, 0xfc, 0x42, 0x33, 0x4a, 0x86, 0x6c, 0xfe, 0xfd, 0xb2, 0x3f, 0x6d, 0xf1, 0xfc, 0x13, 0x00,
+	0x00, 0xff, 0xff, 0x51, 0xe0, 0x08, 0xd0, 0xff, 0x00, 0x00, 0x00,
+}
diff --git a/packages/gossip/transport/proto/handshake.proto b/packages/gossip/transport/proto/handshake.proto
new file mode 100644
index 00000000..2f9c6281
--- /dev/null
+++ b/packages/gossip/transport/proto/handshake.proto
@@ -0,0 +1,21 @@
+syntax = "proto3";
+
+option go_package = "github.com/capossele/gossip/transport/proto";
+
+package proto;
+
+message HandshakeRequest {
+  // protocol version number
+  uint32 version = 1;
+  // string form of the sender address (e.g. "192.0.2.1:25", "[2001:db8::1]:80")
+  string from = 2;
+  // string form of the recipient address
+  string to = 3;
+  // unix time
+  int64 timestamp = 4;
+}
+
+message HandshakeResponse {
+  // hash of the ping packet
+  bytes req_hash = 1;
+}
\ No newline at end of file
diff --git a/packages/gossip/transport/transport.go b/packages/gossip/transport/transport.go
new file mode 100644
index 00000000..f9ae8101
--- /dev/null
+++ b/packages/gossip/transport/transport.go
@@ -0,0 +1,386 @@
+package transport
+
+import (
+	"bytes"
+	"container/list"
+	"errors"
+	"net"
+	"sync"
+	"time"
+
+	"github.com/golang/protobuf/proto"
+	"github.com/iotaledger/autopeering-sim/peer"
+	"github.com/iotaledger/autopeering-sim/peer/service"
+	pb "github.com/iotaledger/autopeering-sim/server/proto"
+	"go.uber.org/zap"
+)
+
+var (
+	ErrTimeout          = errors.New("accept timeout")
+	ErrClosed           = errors.New("listener closed")
+	ErrInvalidHandshake = errors.New("invalid handshake")
+	ErrNoGossip         = errors.New("peer does not have a gossip service")
+)
+
+// connection timeouts
+const (
+	acceptTimeout     = 500 * time.Millisecond
+	handshakeTimeout  = 100 * time.Millisecond
+	connectionTimeout = acceptTimeout + handshakeTimeout
+)
+
+type TransportTCP struct {
+	local    *peer.Local
+	listener *net.TCPListener
+	log      *zap.SugaredLogger
+
+	addAcceptMatcher chan *acceptMatcher
+	acceptReceived   chan accept
+
+	closeOnce sync.Once
+	wg        sync.WaitGroup
+	closing   chan struct{} // if this channel gets closed all pending waits should terminate
+}
+
+// connect contains the result of an incoming connection.
+type connect struct {
+	c   *Connection
+	err error
+}
+
+type acceptMatcher struct {
+	peer      *peer.Peer   // connecting peer
+	deadline  time.Time    // deadline for the incoming call
+	connected chan connect // result of the connection is signaled here
+}
+
+type accept struct {
+	fromID peer.ID  // ID of the connecting peer
+	req    []byte   // raw data of the handshake request
+	conn   net.Conn // the actual network connection
+}
+
+func Listen(local *peer.Local, log *zap.SugaredLogger) (*TransportTCP, error) {
+	t := &TransportTCP{
+		local:            local,
+		log:              log,
+		addAcceptMatcher: make(chan *acceptMatcher),
+		acceptReceived:   make(chan accept),
+		closing:          make(chan struct{}),
+	}
+
+	gossipAddr := local.Services().Get(service.GossipKey)
+	if gossipAddr == nil {
+		return nil, ErrNoGossip
+	}
+	tcpAddr, err := net.ResolveTCPAddr(gossipAddr.Network(), gossipAddr.String())
+	if err != nil {
+		return nil, err
+	}
+	listener, err := net.ListenTCP(gossipAddr.Network(), tcpAddr)
+	if err != nil {
+		return nil, err
+	}
+	t.listener = listener
+
+	t.wg.Add(2)
+	go t.run()
+	go t.listenLoop()
+
+	return t, nil
+}
+
+// Close stops listening on the gossip address.
+func (t *TransportTCP) Close() {
+	t.closeOnce.Do(func() {
+		close(t.closing)
+		if err := t.listener.Close(); err != nil {
+			t.log.Warnw("close error", "err", err)
+		}
+		t.wg.Wait()
+	})
+}
+
+// LocalAddr returns the listener's network address,
+func (t *TransportTCP) LocalAddr() net.Addr {
+	return t.listener.Addr()
+}
+
+// DialPeer establishes a gossip connection to the given peer.
+// If the peer does not accept the connection or the handshake fails, an error is returned.
+func (t *TransportTCP) DialPeer(p *peer.Peer) (*Connection, error) {
+	gossipAddr := p.Services().Get(service.GossipKey)
+	if gossipAddr == nil {
+		return nil, ErrNoGossip
+	}
+
+	conn, err := net.DialTimeout(gossipAddr.Network(), gossipAddr.String(), acceptTimeout)
+	if err != nil {
+		return nil, err
+	}
+
+	err = t.doHandshake(p.PublicKey(), gossipAddr.String(), conn)
+	if err != nil {
+		return nil, err
+	}
+
+	t.log.Debugw("connected", "id", p.ID(), "addr", conn.RemoteAddr(), "direction", "out")
+	return newConnection(p, conn), nil
+}
+
+// AcceptPeer awaits an incoming connection from the given peer.
+// If the peer does not establish the connection or the handshake fails, an error is returned.
+func (t *TransportTCP) AcceptPeer(p *peer.Peer) (*Connection, error) {
+	if p.Services().Get(service.GossipKey) == nil {
+		return nil, ErrNoGossip
+	}
+	// wait for the connection
+	connected := <-t.acceptPeer(p)
+	if connected.err != nil {
+		return nil, connected.err
+	}
+	t.log.Debugw("connected", "id", p.ID(), "addr", connected.c.conn.RemoteAddr(), "direction", "in")
+	return connected.c, nil
+}
+
+func (t *TransportTCP) acceptPeer(p *peer.Peer) <-chan connect {
+	connected := make(chan connect, 1)
+	// add the matcher
+	select {
+	case t.addAcceptMatcher <- &acceptMatcher{peer: p, connected: connected}:
+	case <-t.closing:
+		connected <- connect{nil, ErrClosed}
+	}
+	return connected
+}
+
+func (t *TransportTCP) closeConnection(c net.Conn) {
+	if err := c.Close(); err != nil {
+		t.log.Warnw("close error", "err", err)
+	}
+}
+
+func (t *TransportTCP) run() {
+	defer t.wg.Done()
+
+	var (
+		mlist   = list.New()
+		timeout = time.NewTimer(0)
+	)
+	defer timeout.Stop()
+
+	<-timeout.C // ignore first timeout
+
+	for {
+
+		// Set the timer so that it fires when the next accept expires
+		if el := mlist.Front(); el != nil {
+			// the first element always has the closest deadline
+			m := el.Value.(*acceptMatcher)
+			timeout.Reset(time.Until(m.deadline))
+		} else {
+			timeout.Stop()
+		}
+
+		select {
+
+		// add a new matcher to the list
+		case m := <-t.addAcceptMatcher:
+			m.deadline = time.Now().Add(connectionTimeout)
+			mlist.PushBack(m)
+
+		// on accept received, check all matchers for a fit
+		case a := <-t.acceptReceived:
+			matched := false
+			for el := mlist.Front(); el != nil; el = el.Next() {
+				m := el.Value.(*acceptMatcher)
+				if m.peer.ID() == a.fromID {
+					matched = true
+					mlist.Remove(el)
+					// finish the handshake
+					go t.matchAccept(m, a.req, a.conn)
+				}
+			}
+			// close the connection if not matched
+			if !matched {
+				t.log.Debugw("unexpected connection", "id", a.fromID, "addr", a.conn.RemoteAddr())
+				t.closeConnection(a.conn)
+			}
+
+		// on timeout, check for expired matchers
+		case <-timeout.C:
+			now := time.Now()
+
+			// notify and remove any expired matchers
+			for el := mlist.Front(); el != nil; el = el.Next() {
+				m := el.Value.(*acceptMatcher)
+				if now.After(m.deadline) || now.Equal(m.deadline) {
+					m.connected <- connect{nil, ErrTimeout}
+					mlist.Remove(el)
+				}
+			}
+
+		// on close, notify all the matchers
+		case <-t.closing:
+			for el := mlist.Front(); el != nil; el = el.Next() {
+				el.Value.(*acceptMatcher).connected <- connect{nil, ErrClosed}
+			}
+			return
+
+		}
+	}
+}
+
+func (t *TransportTCP) matchAccept(m *acceptMatcher, req []byte, conn net.Conn) {
+	t.wg.Add(1)
+	defer t.wg.Done()
+
+	if err := t.writeHandshakeResponse(req, conn); err != nil {
+		t.log.Warnw("failed handshake", "addr", conn.RemoteAddr(), "err", err)
+		m.connected <- connect{nil, err}
+		t.closeConnection(conn)
+		return
+	}
+	m.connected <- connect{newConnection(m.peer, conn), nil}
+}
+
+func (t *TransportTCP) listenLoop() {
+	defer t.wg.Done()
+
+	for {
+		conn, err := t.listener.AcceptTCP()
+		if err, ok := err.(net.Error); ok && err.Temporary() {
+			t.log.Debugw("temporary read error", "err", err)
+			continue
+		} else if err != nil {
+			// return from the loop on all other errors
+			t.log.Warnw("read error", "err", err)
+			return
+		}
+
+		key, req, err := t.readHandshakeRequest(conn)
+		if err != nil {
+			t.log.Warnw("failed handshake", "addr", conn.RemoteAddr(), "err", err)
+			t.closeConnection(conn)
+			continue
+		}
+
+		select {
+		case t.acceptReceived <- accept{
+			fromID: key.ID(),
+			req:    req,
+			conn:   conn,
+		}:
+		case <-t.closing:
+			t.closeConnection(conn)
+			return
+		}
+	}
+}
+
+func (t *TransportTCP) doHandshake(key peer.PublicKey, remoteAddr string, conn net.Conn) error {
+	reqData, err := newHandshakeRequest(conn.LocalAddr().String(), remoteAddr)
+	if err != nil {
+		return err
+	}
+
+	pkt := &pb.Packet{
+		PublicKey: t.local.PublicKey(),
+		Signature: t.local.Sign(reqData),
+		Data:      reqData,
+	}
+	b, err := proto.Marshal(pkt)
+	if err != nil {
+		return err
+	}
+
+	if err := conn.SetWriteDeadline(time.Now().Add(handshakeTimeout)); err != nil {
+		return err
+	}
+	_, err = conn.Write(b)
+	if err != nil {
+		return err
+	}
+
+	if err := conn.SetReadDeadline(time.Now().Add(handshakeTimeout)); err != nil {
+		return err
+	}
+	b = make([]byte, MaxPacketSize)
+	n, err := conn.Read(b)
+	if err != nil {
+		return err
+	}
+
+	pkt = new(pb.Packet)
+	if err := proto.Unmarshal(b[:n], pkt); err != nil {
+		return err
+	}
+
+	signer, err := peer.RecoverKeyFromSignedData(pkt)
+	if err != nil {
+		return err
+	}
+	if !bytes.Equal(key, signer) {
+		return errors.New("invalid key")
+	}
+
+	if !t.validateHandshakeResponse(pkt.GetData(), reqData) {
+		return ErrInvalidHandshake
+	}
+
+	return nil
+}
+
+func (t *TransportTCP) readHandshakeRequest(conn net.Conn) (peer.PublicKey, []byte, error) {
+	if err := conn.SetReadDeadline(time.Now().Add(handshakeTimeout)); err != nil {
+		return nil, nil, err
+	}
+	b := make([]byte, MaxPacketSize)
+	n, err := conn.Read(b)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pkt := new(pb.Packet)
+	if err := proto.Unmarshal(b[:n], pkt); err != nil {
+		return nil, nil, err
+	}
+
+	key, err := peer.RecoverKeyFromSignedData(pkt)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if !t.validateHandshakeRequest(pkt.GetData(), conn.RemoteAddr().String()) {
+		return nil, nil, ErrInvalidHandshake
+	}
+
+	return key, pkt.GetData(), nil
+}
+
+func (t *TransportTCP) writeHandshakeResponse(reqData []byte, conn net.Conn) error {
+	data, err := newHandshakeResponse(reqData)
+	if err != nil {
+		return err
+	}
+
+	pkt := &pb.Packet{
+		PublicKey: t.local.PublicKey(),
+		Signature: t.local.Sign(data),
+		Data:      data,
+	}
+	b, err := proto.Marshal(pkt)
+	if err != nil {
+		return err
+	}
+
+	if err := conn.SetWriteDeadline(time.Now().Add(handshakeTimeout)); err != nil {
+		return err
+	}
+	_, err = conn.Write(b)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/packages/gossip/transport/transport_test.go b/packages/gossip/transport/transport_test.go
new file mode 100644
index 00000000..c0b67047
--- /dev/null
+++ b/packages/gossip/transport/transport_test.go
@@ -0,0 +1,197 @@
+package transport
+
+import (
+	"log"
+	"net"
+	"sync"
+	"testing"
+	"time"
+
+	"github.com/iotaledger/autopeering-sim/peer"
+	"github.com/iotaledger/autopeering-sim/peer/service"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.uber.org/zap"
+)
+
+const graceTime = 5 * time.Millisecond
+
+var logger *zap.SugaredLogger
+
+func init() {
+	l, err := zap.NewDevelopment()
+	if err != nil {
+		log.Fatalf("cannot initialize logger: %v", err)
+	}
+	logger = l.Sugar()
+}
+
+func newTest(t require.TestingT, name string) (*TransportTCP, func()) {
+	l := logger.Named(name)
+	db := peer.NewMemoryDB(l.Named("db"))
+	local, err := peer.NewLocal("peering", name, db)
+	require.NoError(t, err)
+
+	// enable TCP gossipping
+	require.NoError(t, local.UpdateService(service.GossipKey, "tcp", ":0"))
+
+	trans, err := Listen(local, l)
+	require.NoError(t, err)
+
+	// update the service with the actual address
+	require.NoError(t, local.UpdateService(service.GossipKey, trans.LocalAddr().Network(), trans.LocalAddr().String()))
+
+	teardown := func() {
+		trans.Close()
+		db.Close()
+	}
+	return trans, teardown
+}
+
+func getPeer(t *TransportTCP) *peer.Peer {
+	return &t.local.Peer
+}
+
+func TestClose(t *testing.T) {
+	_, teardown := newTest(t, "A")
+	teardown()
+}
+
+func TestUnansweredAccept(t *testing.T) {
+	transA, closeA := newTest(t, "A")
+	defer closeA()
+
+	_, err := transA.AcceptPeer(getPeer(transA))
+	assert.Error(t, err)
+}
+
+func TestCloseWhileAccepting(t *testing.T) {
+	transA, closeA := newTest(t, "A")
+
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		defer wg.Done()
+		_, err := transA.AcceptPeer(getPeer(transA))
+		assert.Error(t, err)
+	}()
+	time.Sleep(graceTime)
+
+	closeA()
+	wg.Wait()
+}
+
+func TestUnansweredDial(t *testing.T) {
+	transA, closeA := newTest(t, "A")
+	defer closeA()
+
+	// create peer with invalid gossip address
+	services := getPeer(transA).Services().CreateRecord()
+	services.Update(service.GossipKey, "tcp", ":0")
+	unreachablePeer := peer.NewPeer(getPeer(transA).PublicKey(), services)
+
+	_, err := transA.DialPeer(unreachablePeer)
+	assert.Error(t, err)
+}
+
+func TestNoHandshakeResponse(t *testing.T) {
+	transA, closeA := newTest(t, "A")
+	defer closeA()
+
+	// accept and read incoming connections
+	lis, err := net.Listen("tcp", ":0")
+	require.NoError(t, err)
+	go func() {
+		conn, err := lis.Accept()
+		require.NoError(t, err)
+		n, _ := conn.Read(make([]byte, MaxPacketSize))
+		assert.NotZero(t, n)
+		_ = conn.Close()
+		_ = lis.Close()
+	}()
+
+	// create peer for the listener
+	services := getPeer(transA).Services().CreateRecord()
+	services.Update(service.GossipKey, lis.Addr().Network(), lis.Addr().String())
+	p := peer.NewPeer(getPeer(transA).PublicKey(), services)
+
+	_, err = transA.DialPeer(p)
+	assert.Error(t, err)
+}
+
+func TestNoHandshakeRequest(t *testing.T) {
+	transA, closeA := newTest(t, "A")
+	defer closeA()
+
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+		defer wg.Done()
+		_, err := transA.AcceptPeer(getPeer(transA))
+		assert.Error(t, err)
+	}()
+	time.Sleep(graceTime)
+
+	conn, err := net.Dial(transA.LocalAddr().Network(), transA.LocalAddr().String())
+	require.NoError(t, err)
+	time.Sleep(handshakeTimeout)
+	_ = conn.Close()
+
+	wg.Wait()
+}
+
+func TestConnect(t *testing.T) {
+	transA, closeA := newTest(t, "A")
+	defer closeA()
+	transB, closeB := newTest(t, "B")
+	defer closeB()
+
+	var wg sync.WaitGroup
+	wg.Add(2)
+
+	go func() {
+		defer wg.Done()
+		c, err := transA.AcceptPeer(getPeer(transB))
+		assert.NoError(t, err)
+		if assert.NotNil(t, c) {
+			c.Close()
+		}
+	}()
+	time.Sleep(graceTime)
+	go func() {
+		defer wg.Done()
+		c, err := transB.DialPeer(getPeer(transA))
+		assert.NoError(t, err)
+		if assert.NotNil(t, c) {
+			c.Close()
+		}
+	}()
+
+	wg.Wait()
+}
+
+func TestWrongConnect(t *testing.T) {
+	transA, closeA := newTest(t, "A")
+	defer closeA()
+	transB, closeB := newTest(t, "B")
+	defer closeB()
+	transC, closeC := newTest(t, "C")
+	defer closeC()
+
+	var wg sync.WaitGroup
+	wg.Add(2)
+
+	// a expects connection from B, but C is connecting
+	go func() {
+		defer wg.Done()
+		_, err := transA.AcceptPeer(getPeer(transB))
+		assert.Error(t, err)
+	}()
+	go func() {
+		defer wg.Done()
+		_, err := transC.DialPeer(getPeer(transA))
+		assert.Error(t, err)
+	}()
+
+	wg.Wait()
+}
diff --git a/plugins/autopeering/plugin.go b/plugins/autopeering/plugin.go
index 3ebd4a00..f406622f 100644
--- a/plugins/autopeering/plugin.go
+++ b/plugins/autopeering/plugin.go
@@ -1,10 +1,8 @@
 package autopeering
 
 import (
-	"net"
-
 	"github.com/iotaledger/autopeering-sim/discover"
-	"github.com/iotaledger/autopeering-sim/selection"
+	"github.com/iotaledger/goshimmer/packages/gossip/neighbor"
 	"github.com/iotaledger/goshimmer/plugins/gossip"
 	"github.com/iotaledger/hive.go/daemon"
 	"github.com/iotaledger/hive.go/events"
@@ -28,26 +26,26 @@ func run(plugin *node.Plugin) {
 }
 
 func configureLogging(plugin *node.Plugin) {
-	gossip.Events.RemoveNeighbor.Attach(events.NewClosure(func(peer *gossip.Neighbor) {
+	gossip.Events.DropNeighbor.Attach(events.NewClosure(func(peer *neighbor.Neighbor) {
 		Selection.DropPeer(peer.Peer)
 	}))
 
-	selection.Events.Dropped.Attach(events.NewClosure(func(ev *selection.DroppedEvent) {
-		log.Debug("neighbor removed: " + ev.DroppedID.String())
-		gossip.RemoveNeighbor(ev.DroppedID.String())
-	}))
-
-	selection.Events.IncomingPeering.Attach(events.NewClosure(func(ev *selection.PeeringEvent) {
-		log.Debug("accepted neighbor added: " + ev.Peer.Address() + " / " + ev.Peer.String())
-		address, port, _ := net.SplitHostPort(ev.Services["gossip"].Address)
-		gossip.AddNeighbor(gossip.NewNeighbor(ev.Peer, address, port))
-	}))
-
-	selection.Events.OutgoingPeering.Attach(events.NewClosure(func(ev *selection.PeeringEvent) {
-		log.Debug("chosen neighbor added: " + ev.Peer.Address() + " / " + ev.Peer.String())
-		address, port, _ := net.SplitHostPort(ev.Services["gossip"].Address)
-		gossip.AddNeighbor(gossip.NewNeighbor(ev.Peer, address, port))
-	}))
+	// selection.Events.Dropped.Attach(events.NewClosure(func(ev *selection.DroppedEvent) {
+	// 	log.Debug("neighbor removed: " + ev.DroppedID.String())
+	// 	gossip.RemoveNeighbor(ev.DroppedID.String())
+	// }))
+
+	// selection.Events.IncomingPeering.Attach(events.NewClosure(func(ev *selection.PeeringEvent) {
+	// 	log.Debug("accepted neighbor added: " + ev.Peer.Address() + " / " + ev.Peer.String())
+	// 	address, port, _ := net.SplitHostPort(ev.Services["gossip"].Address)
+	// 	gossip.AddNeighbor(gossip.NewNeighbor(ev.Peer, address, port))
+	// }))
+
+	// selection.Events.OutgoingPeering.Attach(events.NewClosure(func(ev *selection.PeeringEvent) {
+	// 	log.Debug("chosen neighbor added: " + ev.Peer.Address() + " / " + ev.Peer.String())
+	// 	address, port, _ := net.SplitHostPort(ev.Services["gossip"].Address)
+	// 	gossip.AddNeighbor(gossip.NewNeighbor(ev.Peer, address, port))
+	// }))
 
 	discover.Events.PeerDiscovered.Attach(events.NewClosure(func(ev *discover.DiscoveredEvent) {
 		log.Info("new peer discovered: " + ev.Peer.Address() + " / " + ev.Peer.ID().String())
diff --git a/plugins/gossip-on-solidification/plugin.go b/plugins/gossip-on-solidification/plugin.go
deleted file mode 100644
index 42b09256..00000000
--- a/plugins/gossip-on-solidification/plugin.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package gossip_on_solidification
-
-import (
-	"github.com/iotaledger/goshimmer/packages/model/value_transaction"
-	"github.com/iotaledger/goshimmer/plugins/gossip"
-	"github.com/iotaledger/goshimmer/plugins/tangle"
-	"github.com/iotaledger/hive.go/events"
-	"github.com/iotaledger/hive.go/node"
-)
-
-var PLUGIN = node.NewPlugin("Gossip On Solidification", node.Enabled, func(plugin *node.Plugin) {
-	tangle.Events.TransactionSolid.Attach(events.NewClosure(func(tx *value_transaction.ValueTransaction) {
-		gossip.SendTransaction(tx.MetaTransaction)
-	}))
-})
diff --git a/plugins/gossip/errors.go b/plugins/gossip/errors.go
deleted file mode 100644
index 8da04cfa..00000000
--- a/plugins/gossip/errors.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package gossip
-
-import "github.com/iotaledger/goshimmer/packages/errors"
-
-var (
-	ErrConnectionFailed             = errors.Wrap(errors.New("connection error"), "could not connect to neighbor")
-	ErrInvalidAuthenticationMessage = errors.Wrap(errors.New("protocol error"), "invalid authentication message")
-	ErrInvalidIdentity              = errors.Wrap(errors.New("protocol error"), "invalid identity message")
-	ErrInvalidStateTransition       = errors.New("protocol error: invalid state transition message")
-	ErrSendFailed                   = errors.Wrap(errors.New("protocol error"), "failed to send message")
-	ErrInvalidSendParam             = errors.New("invalid parameter passed to send")
-)
diff --git a/plugins/gossip/events.go b/plugins/gossip/events.go
deleted file mode 100644
index 4bcb484a..00000000
--- a/plugins/gossip/events.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package gossip
-
-import (
-	"github.com/iotaledger/goshimmer/packages/errors"
-	"github.com/iotaledger/goshimmer/packages/identity"
-	"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
-	"github.com/iotaledger/goshimmer/packages/network"
-	"github.com/iotaledger/hive.go/events"
-)
-
-var Events = pluginEvents{
-	// neighbor events
-	AddNeighbor:    events.NewEvent(neighborCaller),
-	UpdateNeighbor: events.NewEvent(neighborCaller),
-	RemoveNeighbor: events.NewEvent(neighborCaller),
-
-	// low level network events
-	IncomingConnection: events.NewEvent(connectionCaller),
-
-	// high level protocol events
-	DropNeighbor:              events.NewEvent(neighborCaller),
-	SendTransaction:           events.NewEvent(transactionCaller),
-	SendTransactionRequest:    events.NewEvent(transactionCaller), // TODO
-	ReceiveTransaction:        events.NewEvent(transactionCaller),
-	ReceiveTransactionRequest: events.NewEvent(transactionCaller), // TODO
-	ProtocolError:             events.NewEvent(transactionCaller), // TODO
-
-	// generic events
-	Error: events.NewEvent(errorCaller),
-}
-
-type pluginEvents struct {
-	// neighbor events
-	AddNeighbor    *events.Event
-	UpdateNeighbor *events.Event
-	RemoveNeighbor *events.Event
-
-	// low level network events
-	IncomingConnection *events.Event
-
-	// high level protocol events
-	DropNeighbor              *events.Event
-	SendTransaction           *events.Event
-	SendTransactionRequest    *events.Event
-	ReceiveTransaction        *events.Event
-	ReceiveTransactionRequest *events.Event
-	ProtocolError             *events.Event
-
-	// generic events
-	Error *events.Event
-}
-
-type protocolEvents struct {
-	ReceiveVersion            *events.Event
-	ReceiveIdentification     *events.Event
-	ReceiveConnectionAccepted *events.Event
-	ReceiveConnectionRejected *events.Event
-	ReceiveDropConnection     *events.Event
-	ReceiveTransactionData    *events.Event
-	ReceiveRequestData        *events.Event
-	HandshakeCompleted        *events.Event
-	Error                     *events.Event
-}
-
-type neighborEvents struct {
-	ProtocolConnectionEstablished *events.Event
-}
-
-func intCaller(handler interface{}, params ...interface{}) { handler.(func(int))(params[0].(int)) }
-
-func identityCaller(handler interface{}, params ...interface{}) {
-	handler.(func(*identity.Identity))(params[0].(*identity.Identity))
-}
-
-func connectionCaller(handler interface{}, params ...interface{}) {
-	handler.(func(*network.ManagedConnection))(params[0].(*network.ManagedConnection))
-}
-
-func protocolCaller(handler interface{}, params ...interface{}) {
-	handler.(func(*protocol))(params[0].(*protocol))
-}
-
-func neighborCaller(handler interface{}, params ...interface{}) {
-	handler.(func(*Neighbor))(params[0].(*Neighbor))
-}
-
-func errorCaller(handler interface{}, params ...interface{}) {
-	handler.(func(errors.IdentifiableError))(params[0].(errors.IdentifiableError))
-}
-
-func dataCaller(handler interface{}, params ...interface{}) {
-	handler.(func([]byte))(params[0].([]byte))
-}
-
-func transactionCaller(handler interface{}, params ...interface{}) {
-	handler.(func(*meta_transaction.MetaTransaction))(params[0].(*meta_transaction.MetaTransaction))
-}
diff --git a/plugins/gossip/gossip.go b/plugins/gossip/gossip.go
new file mode 100644
index 00000000..0ca491a8
--- /dev/null
+++ b/plugins/gossip/gossip.go
@@ -0,0 +1,86 @@
+package gossip
+
+import (
+	"github.com/iotaledger/goshimmer/packages/gossip/transport"
+	"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
+	gp "github.com/iotaledger/goshimmer/packages/gossip"
+	pb "github.com/iotaledger/goshimmer/packages/gossip/proto"
+	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
+	"github.com/iotaledger/autopeering-sim/selection"
+	"github.com/iotaledger/goshimmer/plugins/tangle"
+	"go.uber.org/zap"
+	"github.com/golang/protobuf/proto"
+	"github.com/iotaledger/hive.go/events"
+)
+
+var (
+	zLogger *zap.SugaredLogger
+	mgr        *gp.Manager
+	SendTransaction = mgr.Send
+	RequestTransaction = mgr.RequestTransaction
+	AddInbound = mgr.AddInbound
+	AddOutbound = mgr.AddOutbound
+	DropNeighbor = mgr.DropNeighbor
+)
+
+func init() {
+	l, err := zap.NewDevelopment()
+	if err != nil {
+		log.Fatalf("cannot initialize logger: %v", err)
+	}
+	zLogger = l.Sugar()
+}
+
+func getTransaction(h []byte) ([]byte, error) {
+	tx := &pb.TransactionRequest{
+		Hash: []byte("testTx"),
+	}
+	b, _ := proto.Marshal(tx)
+	return b, nil
+}
+
+func configureGossip() {
+	defer func() { _ = zLogger.Sync() }() // ignore the returned error
+
+	trans, err := transport.Listen(local.INSTANCE, zLogger)
+	if err != nil {
+		// TODO: handle error
+	}
+
+	mgr = gp.NewManager(trans, zLogger, getTransaction)
+}
+
+func configureEvents() {
+	
+	selection.Events.Dropped.Attach(events.NewClosure(func(ev *selection.DroppedEvent) {
+		log.Debug("neighbor removed: " + ev.DroppedID.String())
+		DropNeighbor(ev.DroppedID)
+	}))
+
+	selection.Events.IncomingPeering.Attach(events.NewClosure(func(ev *selection.PeeringEvent) {
+		log.Debug("accepted neighbor added: " + ev.Peer.Address() + " / " + ev.Peer.String())
+		AddInbound(ev.Peer)
+	}))
+
+	selection.Events.OutgoingPeering.Attach(events.NewClosure(func(ev *selection.PeeringEvent) {
+		log.Debug("chosen neighbor added: " + ev.Peer.Address() + " / " + ev.Peer.String())
+		AddOutbound(ev.Peer)
+	}))
+
+	// mgr.Events.NewTransaction.Attach(events.NewClosure(func(ev *gp.NewTransactionEvent) {
+	// 	tx := ev.Body
+	// 	metaTx := meta_transaction.FromBytes(tx)
+	// 	Events.NewTransaction.Trigger(metaTx)
+	// }))
+
+	tangle.Events.TransactionSolid.Attach(events.NewClosure(func(tx *meta_transaction.MetaTransaction) {
+		t := &pb.Transaction{
+			Body: tx.GetBytes(),
+		}
+		b, err := proto.Marshal(t)
+		if err != nil {
+			return
+		}
+		SendTransaction(b)
+	}))
+}
diff --git a/plugins/gossip/neighbors.go b/plugins/gossip/neighbors.go
deleted file mode 100644
index 2acecf29..00000000
--- a/plugins/gossip/neighbors.go
+++ /dev/null
@@ -1,286 +0,0 @@
-package gossip
-
-import (
-	"math"
-	"net"
-	"sync"
-	"time"
-
-	"github.com/iotaledger/autopeering-sim/peer"
-	"github.com/iotaledger/goshimmer/packages/errors"
-	"github.com/iotaledger/goshimmer/packages/identity"
-	"github.com/iotaledger/goshimmer/packages/network"
-	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
-	"github.com/iotaledger/hive.go/daemon"
-	"github.com/iotaledger/hive.go/events"
-	"github.com/iotaledger/hive.go/node"
-)
-
-func configureNeighbors(plugin *node.Plugin) {
-	Events.AddNeighbor.Attach(events.NewClosure(func(neighbor *Neighbor) {
-		log.Info("new neighbor added " + neighbor.GetIdentity().StringIdentifier + "@" + neighbor.GetAddress().String() + ":" + neighbor.GetPort())
-		//plugin.LogSuccess("new neighbor added " + hex.EncodeToString(neighbor.Peer.ID().Bytes()) + "@" + neighbor.GetAddress().String() + ":" + neighbor.GetPort())
-	}))
-
-	Events.UpdateNeighbor.Attach(events.NewClosure(func(neighbor *Neighbor) {
-		log.Info("existing neighbor updated " + neighbor.GetIdentity().StringIdentifier + "@" + neighbor.GetAddress().String() + ":" + neighbor.GetPort())
-	}))
-
-	Events.RemoveNeighbor.Attach(events.NewClosure(func(neighbor *Neighbor) {
-		log.Info("existing neighbor removed " + neighbor.GetIdentity().StringIdentifier + "@" + neighbor.GetAddress().String() + ":" + neighbor.GetPort())
-	}))
-}
-
-func runNeighbors(plugin *node.Plugin) {
-	log.Info("Starting Neighbor Connection Manager ...")
-
-	neighborLock.RLock()
-	for _, neighbor := range neighbors.GetMap() {
-		manageConnection(plugin, neighbor)
-	}
-	neighborLock.RUnlock()
-
-	Events.AddNeighbor.Attach(events.NewClosure(func(neighbor *Neighbor) {
-		manageConnection(plugin, neighbor)
-	}))
-
-	log.Info("Starting Neighbor Connection Manager ... done")
-}
-
-func manageConnection(plugin *node.Plugin, neighbor *Neighbor) {
-	daemon.BackgroundWorker("Connection Manager ("+neighbor.GetIdentity().StringIdentifier+")", func() {
-		failedConnectionAttempts := 0
-
-		for _, exists := neighbors.Load(neighbor.GetIdentity().StringIdentifier); exists && failedConnectionAttempts < CONNECTION_MAX_ATTEMPTS; {
-			protocol, dialed, err := neighbor.Connect()
-			if err != nil {
-				failedConnectionAttempts++
-
-				log.Errorf("connection attempt [%d / %d] %s", failedConnectionAttempts, CONNECTION_MAX_ATTEMPTS, err.Error())
-
-				if failedConnectionAttempts <= CONNECTION_MAX_ATTEMPTS {
-					select {
-					case <-daemon.ShutdownSignal:
-						return
-
-					case <-time.After(time.Duration(int(math.Pow(2, float64(failedConnectionAttempts-1)))) * CONNECTION_BASE_TIMEOUT):
-						continue
-					}
-				}
-			}
-
-			failedConnectionAttempts = 0
-
-			disconnectSignal := make(chan int, 1)
-			protocol.Conn.Events.Close.Attach(events.NewClosure(func() {
-				close(disconnectSignal)
-			}))
-
-			if dialed {
-				go protocol.Init()
-			}
-
-			// wait for shutdown or
-			select {
-			case <-daemon.ShutdownSignal:
-				return
-
-			case <-disconnectSignal:
-				continue
-			}
-		}
-
-		RemoveNeighbor(neighbor.GetIdentity().StringIdentifier)
-	})
-}
-
-type Neighbor struct {
-	identity               *identity.Identity
-	identityMutex          sync.RWMutex
-	address                net.IP
-	addressMutex           sync.RWMutex
-	port                   string
-	portMutex              sync.RWMutex
-	initiatedProtocol      *protocol
-	initiatedProtocolMutex sync.RWMutex
-	acceptedProtocol       *protocol
-	Events                 neighborEvents
-	acceptedProtocolMutex  sync.RWMutex
-	Peer                   *peer.Peer
-}
-
-func NewNeighbor(peer *peer.Peer, address, port string) *Neighbor {
-	return &Neighbor{
-		identity: identity.NewPublicIdentity(peer.ToProto().GetPublicKey()),
-		address:  net.ParseIP(address),
-		port:     port,
-		Peer:     peer,
-		Events: neighborEvents{
-			ProtocolConnectionEstablished: events.NewEvent(protocolCaller),
-		},
-	}
-}
-
-func (neighbor *Neighbor) GetIdentity() (result *identity.Identity) {
-	neighbor.identityMutex.RLock()
-	result = neighbor.identity
-	neighbor.identityMutex.RUnlock()
-
-	return result
-}
-
-func (neighbor *Neighbor) SetIdentity(identity *identity.Identity) {
-	neighbor.identityMutex.Lock()
-	neighbor.identity = identity
-	neighbor.identityMutex.Unlock()
-}
-
-func (neighbor *Neighbor) GetAddress() (result net.IP) {
-	neighbor.addressMutex.RLock()
-	result = neighbor.address
-	neighbor.addressMutex.RUnlock()
-
-	return result
-}
-
-func (neighbor *Neighbor) SetAddress(address net.IP) {
-	neighbor.addressMutex.Lock()
-	neighbor.address = address
-	neighbor.addressMutex.Unlock()
-}
-
-func (neighbor *Neighbor) GetPort() (result string) {
-	neighbor.portMutex.RLock()
-	result = neighbor.port
-	neighbor.portMutex.RUnlock()
-
-	return result
-}
-
-func (neighbor *Neighbor) SetPort(port string) {
-	neighbor.portMutex.Lock()
-	neighbor.port = port
-	neighbor.portMutex.Unlock()
-}
-
-func (neighbor *Neighbor) GetInitiatedProtocol() (result *protocol) {
-	neighbor.initiatedProtocolMutex.RLock()
-	result = neighbor.initiatedProtocol
-	neighbor.initiatedProtocolMutex.RUnlock()
-
-	return result
-}
-
-func (neighbor *Neighbor) SetInitiatedProtocol(p *protocol) {
-	neighbor.initiatedProtocolMutex.Lock()
-	neighbor.initiatedProtocol = p
-	neighbor.initiatedProtocolMutex.Unlock()
-}
-
-func (neighbor *Neighbor) GetAcceptedProtocol() (result *protocol) {
-	neighbor.acceptedProtocolMutex.RLock()
-	result = neighbor.acceptedProtocol
-	neighbor.acceptedProtocolMutex.RUnlock()
-
-	return result
-}
-
-func (neighbor *Neighbor) SetAcceptedProtocol(p *protocol) {
-	neighbor.acceptedProtocolMutex.Lock()
-	neighbor.acceptedProtocol = p
-	neighbor.acceptedProtocolMutex.Unlock()
-}
-
-func UnmarshalPeer(data []byte) (*Neighbor, error) {
-	return &Neighbor{}, nil
-}
-
-func (neighbor *Neighbor) Connect() (*protocol, bool, errors.IdentifiableError) {
-	// return existing connections first
-	if neighbor.GetInitiatedProtocol() != nil {
-		return neighbor.GetInitiatedProtocol(), false, nil
-	}
-
-	// if we already have an accepted connection -> use it instead
-	if neighbor.GetAcceptedProtocol() != nil {
-		return neighbor.GetAcceptedProtocol(), false, nil
-	}
-
-	// otherwise try to dial
-	conn, err := net.Dial("tcp", neighbor.GetAddress().String()+":"+neighbor.GetPort())
-	if err != nil {
-		return nil, false, ErrConnectionFailed.Derive(err, "error when connecting to neighbor "+
-			neighbor.GetIdentity().StringIdentifier+"@"+neighbor.GetAddress().String()+":"+neighbor.GetPort())
-	}
-
-	neighbor.SetInitiatedProtocol(newProtocol(network.NewManagedConnection(conn)))
-
-	neighbor.GetInitiatedProtocol().Conn.Events.Close.Attach(events.NewClosure(func() {
-		neighbor.SetInitiatedProtocol(nil)
-	}))
-
-	// drop the "secondary" connection upon successful handshake
-	neighbor.GetInitiatedProtocol().Events.HandshakeCompleted.Attach(events.NewClosure(func() {
-		if local.INSTANCE.ID().String() <= neighbor.Peer.ID().String() {
-			var acceptedProtocolConn *network.ManagedConnection
-			if neighbor.GetAcceptedProtocol() != nil {
-				acceptedProtocolConn = neighbor.GetAcceptedProtocol().Conn
-			}
-
-			if acceptedProtocolConn != nil {
-				_ = acceptedProtocolConn.Close()
-			}
-		}
-
-		neighbor.Events.ProtocolConnectionEstablished.Trigger(neighbor.GetInitiatedProtocol())
-	}))
-
-	return neighbor.GetInitiatedProtocol(), true, nil
-}
-
-func (neighbor *Neighbor) Marshal() []byte {
-	return nil
-}
-
-func (neighbor *Neighbor) Equals(other *Neighbor) bool {
-	return neighbor.GetIdentity().StringIdentifier == other.GetIdentity().StringIdentifier &&
-		neighbor.GetPort() == other.GetPort() && neighbor.GetAddress().String() == other.GetAddress().String()
-}
-
-func AddNeighbor(newNeighbor *Neighbor) {
-	if neighbor, exists := neighbors.Load(newNeighbor.GetIdentity().StringIdentifier); !exists {
-		neighbors.Store(newNeighbor.GetIdentity().StringIdentifier, newNeighbor)
-		Events.AddNeighbor.Trigger(newNeighbor)
-	} else {
-		if !neighbor.Equals(newNeighbor) {
-			neighbor.SetIdentity(newNeighbor.GetIdentity())
-			neighbor.SetPort(newNeighbor.GetPort())
-			neighbor.SetAddress(newNeighbor.GetAddress())
-
-			Events.UpdateNeighbor.Trigger(neighbor)
-		}
-	}
-}
-
-func RemoveNeighbor(identifier string) {
-	if neighbor, exists := neighbors.Delete(identifier); exists {
-		Events.RemoveNeighbor.Trigger(neighbor)
-	}
-}
-
-func GetNeighbor(identifier string) (*Neighbor, bool) {
-	return neighbors.Load(identifier)
-}
-
-func GetNeighbors() map[string]*Neighbor {
-	return neighbors.GetMap()
-}
-
-const (
-	CONNECTION_MAX_ATTEMPTS = 5
-	CONNECTION_BASE_TIMEOUT = 10 * time.Second
-)
-
-var neighbors = NewNeighborMap()
-
-var neighborLock sync.RWMutex
diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go
index c3d7ffef..2e6a902f 100644
--- a/plugins/gossip/plugin.go
+++ b/plugins/gossip/plugin.go
@@ -3,19 +3,25 @@ package gossip
 import (
 	"github.com/iotaledger/hive.go/logger"
 	"github.com/iotaledger/hive.go/node"
+	"github.com/iotaledger/hive.go/daemon"
+	"github.com/iotaledger/hive.go/events"
 )
 
 var PLUGIN = node.NewPlugin("Gossip", node.Enabled, configure, run)
 var log = logger.NewLogger("Gossip")
 
+var (
+	debugLevel = "debug"
+	close      = make(chan struct{}, 1)
+)
+
 func configure(plugin *node.Plugin) {
-	configureNeighbors(plugin)
-	configureServer(plugin)
-	configureSendQueue(plugin)
+	daemon.Events.Shutdown.Attach(events.NewClosure(func() {
+		close <- struct{}{}
+	}))
+	configureGossip()
+	configureEvents()
 }
 
 func run(plugin *node.Plugin) {
-	runNeighbors(plugin)
-	runServer(plugin)
-	runSendQueue(plugin)
 }
diff --git a/plugins/gossip/protocol.go b/plugins/gossip/protocol.go
deleted file mode 100644
index 54c89171..00000000
--- a/plugins/gossip/protocol.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package gossip
-
-import (
-	"strconv"
-	"sync"
-
-	"github.com/iotaledger/goshimmer/packages/errors"
-	"github.com/iotaledger/goshimmer/packages/network"
-	"github.com/iotaledger/hive.go/events"
-)
-
-// region constants and variables //////////////////////////////////////////////////////////////////////////////////////
-
-var DEFAULT_PROTOCOL = protocolDefinition{
-	version:     VERSION_1,
-	initializer: protocolV1,
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region protocol /////////////////////////////////////////////////////////////////////////////////////////////////////
-
-type protocol struct {
-	Conn                      *network.ManagedConnection
-	Neighbor                  *Neighbor
-	Version                   byte
-	sendHandshakeCompleted    bool
-	receiveHandshakeCompleted bool
-	SendState                 protocolState
-	ReceivingState            protocolState
-	Events                    protocolEvents
-	sendMutex                 sync.Mutex
-	handshakeMutex            sync.Mutex
-}
-
-func newProtocol(conn *network.ManagedConnection) *protocol {
-	protocol := &protocol{
-		Conn: conn,
-		Events: protocolEvents{
-			ReceiveVersion:            events.NewEvent(intCaller),
-			ReceiveIdentification:     events.NewEvent(identityCaller),
-			ReceiveConnectionAccepted: events.NewEvent(events.CallbackCaller),
-			ReceiveConnectionRejected: events.NewEvent(events.CallbackCaller),
-			ReceiveTransactionData:    events.NewEvent(dataCaller),
-			HandshakeCompleted:        events.NewEvent(events.CallbackCaller),
-			Error:                     events.NewEvent(errorCaller),
-		},
-		sendHandshakeCompleted:    false,
-		receiveHandshakeCompleted: false,
-	}
-
-	protocol.SendState = &versionState{protocol: protocol}
-	protocol.ReceivingState = &versionState{protocol: protocol}
-
-	return protocol
-}
-
-func (protocol *protocol) Init() {
-	// setup event handlers
-	onReceiveData := events.NewClosure(protocol.Receive)
-	onConnectionAccepted := events.NewClosure(func() {
-		protocol.handshakeMutex.Lock()
-		defer protocol.handshakeMutex.Unlock()
-
-		protocol.receiveHandshakeCompleted = true
-		if protocol.sendHandshakeCompleted {
-			protocol.Events.HandshakeCompleted.Trigger()
-		}
-	})
-	var onClose *events.Closure
-	onClose = events.NewClosure(func() {
-		protocol.Conn.Events.ReceiveData.Detach(onReceiveData)
-		protocol.Conn.Events.Close.Detach(onClose)
-		protocol.Events.ReceiveConnectionAccepted.Detach(onConnectionAccepted)
-	})
-
-	// region register event handlers
-	protocol.Conn.Events.ReceiveData.Attach(onReceiveData)
-	protocol.Conn.Events.Close.Attach(onClose)
-	protocol.Events.ReceiveConnectionAccepted.Attach(onConnectionAccepted)
-
-	// send protocol version
-	if err := protocol.Send(DEFAULT_PROTOCOL.version); err != nil {
-		return
-	}
-
-	// initialize default protocol
-	if err := DEFAULT_PROTOCOL.initializer(protocol); err != nil {
-		protocol.SendState = nil
-
-		_ = protocol.Conn.Close()
-
-		protocol.Events.Error.Trigger(err)
-
-		return
-	}
-
-	// start reading from the connection
-	_, _ = protocol.Conn.Read(make([]byte, 1000))
-}
-
-func (protocol *protocol) Receive(data []byte) {
-	offset := 0
-	length := len(data)
-	for offset < length && protocol.ReceivingState != nil {
-		if readBytes, err := protocol.ReceivingState.Receive(data, offset, length); err != nil {
-			Events.Error.Trigger(err)
-
-			_ = protocol.Conn.Close()
-
-			return
-		} else {
-			offset += readBytes
-		}
-	}
-}
-
-func (protocol *protocol) Send(data interface{}) errors.IdentifiableError {
-	protocol.sendMutex.Lock()
-	defer protocol.sendMutex.Unlock()
-
-	return protocol.send(data)
-}
-
-func (protocol *protocol) send(data interface{}) errors.IdentifiableError {
-	if protocol.SendState != nil {
-		if err := protocol.SendState.Send(data); err != nil {
-			protocol.SendState = nil
-
-			_ = protocol.Conn.Close()
-
-			protocol.Events.Error.Trigger(err)
-
-			return err
-		}
-	}
-
-	return nil
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region versionState /////////////////////////////////////////////////////////////////////////////////////////////////
-
-type versionState struct {
-	protocol *protocol
-}
-
-func (state *versionState) Receive(data []byte, offset int, length int) (int, errors.IdentifiableError) {
-	switch data[offset] {
-	case 1:
-		protocol := state.protocol
-
-		protocol.Version = 1
-		protocol.Events.ReceiveVersion.Trigger(1)
-
-		protocol.ReceivingState = newIndentificationStateV1(protocol)
-
-		return 1, nil
-
-	default:
-		return 1, ErrInvalidStateTransition.Derive("invalid version state transition (" + strconv.Itoa(int(data[offset])) + ")")
-	}
-}
-
-func (state *versionState) Send(param interface{}) errors.IdentifiableError {
-	if version, ok := param.(byte); ok {
-		switch version {
-		case VERSION_1:
-			protocol := state.protocol
-
-			if _, err := protocol.Conn.Write([]byte{version}); err != nil {
-				return ErrSendFailed.Derive(err, "failed to send version byte")
-			}
-
-			protocol.SendState = newIndentificationStateV1(protocol)
-
-			return nil
-		}
-	}
-
-	return ErrInvalidSendParam.Derive("passed in parameter is not a valid version byte")
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region types and interfaces /////////////////////////////////////////////////////////////////////////////////////////
-
-type protocolState interface {
-	Send(param interface{}) errors.IdentifiableError
-	Receive(data []byte, offset int, length int) (int, errors.IdentifiableError)
-}
-
-type protocolDefinition struct {
-	version     byte
-	initializer func(*protocol) errors.IdentifiableError
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plugins/gossip/protocol_v1.go b/plugins/gossip/protocol_v1.go
deleted file mode 100644
index 268ec8dc..00000000
--- a/plugins/gossip/protocol_v1.go
+++ /dev/null
@@ -1,383 +0,0 @@
-package gossip
-
-import (
-	"strconv"
-
-	"github.com/iotaledger/goshimmer/packages/byteutils"
-	"github.com/iotaledger/goshimmer/packages/errors"
-	"github.com/iotaledger/goshimmer/packages/identity"
-	"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
-	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
-	"github.com/iotaledger/hive.go/events"
-	"github.com/iotaledger/iota.go/consts"
-)
-
-// region protocolV1 ///////////////////////////////////////////////////////////////////////////////////////////////////
-
-func protocolV1(protocol *protocol) errors.IdentifiableError {
-	if err := protocol.Send(local.INSTANCE.ID().Bytes()); err != nil {
-		return err
-	}
-
-	onReceiveIdentification := events.NewClosure(func(identity *identity.Identity) {
-		if protocol.Neighbor == nil {
-			if err := protocol.Send(CONNECTION_REJECT); err != nil {
-				return
-			}
-		} else {
-			if err := protocol.Send(CONNECTION_ACCEPT); err != nil {
-				return
-			}
-
-			protocol.handshakeMutex.Lock()
-			defer protocol.handshakeMutex.Unlock()
-
-			protocol.sendHandshakeCompleted = true
-			if protocol.receiveHandshakeCompleted {
-				protocol.Events.HandshakeCompleted.Trigger()
-			}
-		}
-	})
-
-	protocol.Events.ReceiveIdentification.Attach(onReceiveIdentification)
-
-	return nil
-}
-
-func sendTransactionV1(protocol *protocol, tx *meta_transaction.MetaTransaction) {
-	if _, ok := protocol.SendState.(*dispatchStateV1); ok {
-		protocol.sendMutex.Lock()
-		defer protocol.sendMutex.Unlock()
-
-		if err := protocol.send(DISPATCH_TRANSACTION); err != nil {
-			return
-		}
-		if err := protocol.send(tx); err != nil {
-			return
-		}
-	}
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region indentificationStateV1 ///////////////////////////////////////////////////////////////////////////////////////
-
-type indentificationStateV1 struct {
-	protocol *protocol
-	buffer   []byte
-	offset   int
-}
-
-func newIndentificationStateV1(protocol *protocol) *indentificationStateV1 {
-	return &indentificationStateV1{
-		protocol: protocol,
-		buffer:   make([]byte, MARSHALED_IDENTITY_TOTAL_SIZE),
-		offset:   0,
-	}
-}
-
-func (state *indentificationStateV1) Receive(data []byte, offset int, length int) (int, errors.IdentifiableError) {
-	bytesRead := byteutils.ReadAvailableBytesToBuffer(state.buffer, state.offset, data, offset, length)
-
-	state.offset += bytesRead
-	if state.offset == MARSHALED_IDENTITY_TOTAL_SIZE {
-		receivedIdentity, err := identity.FromSignedData(state.buffer)
-		if err != nil {
-			return bytesRead, ErrInvalidAuthenticationMessage.Derive(err, "invalid authentication message")
-		}
-		protocol := state.protocol
-
-		if neighbor, exists := GetNeighbor(receivedIdentity.StringIdentifier); exists {
-			protocol.Neighbor = neighbor
-		} else {
-			protocol.Neighbor = nil
-		}
-
-		protocol.Events.ReceiveIdentification.Trigger(receivedIdentity)
-
-		// switch to new state
-		protocol.ReceivingState = newacceptanceStateV1(protocol)
-		state.offset = 0
-	}
-
-	return bytesRead, nil
-}
-
-func (state *indentificationStateV1) Send(param interface{}) errors.IdentifiableError {
-	id, ok := param.(*identity.Identity)
-	if !ok {
-		return ErrInvalidSendParam.Derive("parameter is not a valid identity")
-	}
-
-	msg := id.Identifier.Bytes()
-	data := id.AddSignature(msg)
-
-	protocol := state.protocol
-	if _, err := protocol.Conn.Write(data); err != nil {
-		return ErrSendFailed.Derive(err, "failed to send identification")
-	}
-
-	// switch to new state
-	protocol.SendState = newacceptanceStateV1(protocol)
-
-	return nil
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region acceptanceStateV1 ////////////////////////////////////////////////////////////////////////////////////////////
-
-type acceptanceStateV1 struct {
-	protocol *protocol
-}
-
-func newacceptanceStateV1(protocol *protocol) *acceptanceStateV1 {
-	return &acceptanceStateV1{protocol: protocol}
-}
-
-func (state *acceptanceStateV1) Receive(data []byte, offset int, length int) (int, errors.IdentifiableError) {
-	protocol := state.protocol
-
-	switch data[offset] {
-	case 0:
-		protocol.Events.ReceiveConnectionRejected.Trigger()
-
-		_ = protocol.Conn.Close()
-
-		protocol.ReceivingState = nil
-
-	case 1:
-		protocol.Events.ReceiveConnectionAccepted.Trigger()
-
-		protocol.ReceivingState = newDispatchStateV1(protocol)
-
-	default:
-		return 1, ErrInvalidStateTransition.Derive("invalid acceptance state transition (" + strconv.Itoa(int(data[offset])) + ")")
-	}
-
-	return 1, nil
-}
-
-func (state *acceptanceStateV1) Send(param interface{}) errors.IdentifiableError {
-	if responseType, ok := param.(byte); ok {
-		switch responseType {
-		case CONNECTION_REJECT:
-			protocol := state.protocol
-
-			if _, err := protocol.Conn.Write([]byte{CONNECTION_REJECT}); err != nil {
-				return ErrSendFailed.Derive(err, "failed to send reject message")
-			}
-
-			_ = protocol.Conn.Close()
-
-			protocol.SendState = nil
-
-			return nil
-
-		case CONNECTION_ACCEPT:
-			protocol := state.protocol
-
-			if _, err := protocol.Conn.Write([]byte{CONNECTION_ACCEPT}); err != nil {
-				return ErrSendFailed.Derive(err, "failed to send accept message")
-			}
-
-			protocol.SendState = newDispatchStateV1(protocol)
-
-			return nil
-		}
-	}
-
-	return ErrInvalidSendParam.Derive("passed in parameter is not a valid acceptance byte")
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region dispatchStateV1 //////////////////////////////////////////////////////////////////////////////////////////////
-
-type dispatchStateV1 struct {
-	protocol *protocol
-}
-
-func newDispatchStateV1(protocol *protocol) *dispatchStateV1 {
-	return &dispatchStateV1{
-		protocol: protocol,
-	}
-}
-
-func (state *dispatchStateV1) Receive(data []byte, offset int, length int) (int, errors.IdentifiableError) {
-	switch data[0] {
-	case DISPATCH_DROP:
-		protocol := state.protocol
-
-		protocol.Events.ReceiveConnectionRejected.Trigger()
-
-		_ = protocol.Conn.Close()
-
-		protocol.ReceivingState = nil
-
-	case DISPATCH_TRANSACTION:
-		protocol := state.protocol
-
-		protocol.ReceivingState = newTransactionStateV1(protocol)
-
-	case DISPATCH_REQUEST:
-		protocol := state.protocol
-
-		protocol.ReceivingState = newRequestStateV1(protocol)
-
-	default:
-		return 1, ErrInvalidStateTransition.Derive("invalid dispatch state transition (" + strconv.Itoa(int(data[offset])) + ")")
-	}
-
-	return 1, nil
-}
-
-func (state *dispatchStateV1) Send(param interface{}) errors.IdentifiableError {
-	if dispatchByte, ok := param.(byte); ok {
-		switch dispatchByte {
-		case DISPATCH_DROP:
-			protocol := state.protocol
-
-			if _, err := protocol.Conn.Write([]byte{DISPATCH_DROP}); err != nil {
-				return ErrSendFailed.Derive(err, "failed to send drop message")
-			}
-
-			_ = protocol.Conn.Close()
-
-			protocol.SendState = nil
-
-			return nil
-
-		case DISPATCH_TRANSACTION:
-			protocol := state.protocol
-
-			if _, err := protocol.Conn.Write([]byte{DISPATCH_TRANSACTION}); err != nil {
-				return ErrSendFailed.Derive(err, "failed to send transaction dispatch byte")
-			}
-
-			protocol.SendState = newTransactionStateV1(protocol)
-
-			return nil
-
-		case DISPATCH_REQUEST:
-			protocol := state.protocol
-
-			if _, err := protocol.Conn.Write([]byte{DISPATCH_REQUEST}); err != nil {
-				return ErrSendFailed.Derive(err, "failed to send request dispatch byte")
-			}
-
-			protocol.SendState = newTransactionStateV1(protocol)
-
-			return nil
-		}
-	}
-
-	return ErrInvalidSendParam.Derive("passed in parameter is not a valid dispatch byte")
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region transactionStateV1 ///////////////////////////////////////////////////////////////////////////////////////////
-
-type transactionStateV1 struct {
-	protocol *protocol
-	buffer   []byte
-	offset   int
-}
-
-func newTransactionStateV1(protocol *protocol) *transactionStateV1 {
-	return &transactionStateV1{
-		protocol: protocol,
-		buffer:   make([]byte, meta_transaction.MARSHALED_TOTAL_SIZE/consts.NumberOfTritsInAByte),
-		offset:   0,
-	}
-}
-
-func (state *transactionStateV1) Receive(data []byte, offset int, length int) (int, errors.IdentifiableError) {
-	bytesRead := byteutils.ReadAvailableBytesToBuffer(state.buffer, state.offset, data, offset, length)
-
-	state.offset += bytesRead
-	if state.offset == meta_transaction.MARSHALED_TOTAL_SIZE/consts.NumberOfTritsInAByte {
-		protocol := state.protocol
-
-		transactionData := make([]byte, meta_transaction.MARSHALED_TOTAL_SIZE/consts.NumberOfTritsInAByte)
-		copy(transactionData, state.buffer)
-
-		protocol.Events.ReceiveTransactionData.Trigger(transactionData)
-
-		go ProcessReceivedTransactionData(transactionData)
-
-		protocol.ReceivingState = newDispatchStateV1(protocol)
-		state.offset = 0
-	}
-
-	return bytesRead, nil
-}
-
-func (state *transactionStateV1) Send(param interface{}) errors.IdentifiableError {
-	if tx, ok := param.(*meta_transaction.MetaTransaction); ok {
-		protocol := state.protocol
-
-		if _, err := protocol.Conn.Write(tx.GetBytes()); err != nil {
-			return ErrSendFailed.Derive(err, "failed to send transaction")
-		}
-
-		protocol.SendState = newDispatchStateV1(protocol)
-
-		return nil
-	}
-
-	return ErrInvalidSendParam.Derive("passed in parameter is not a valid transaction")
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region requestStateV1 ///////////////////////////////////////////////////////////////////////////////////////////////
-
-type requestStateV1 struct {
-	buffer []byte
-	offset int
-}
-
-func newRequestStateV1(protocol *protocol) *requestStateV1 {
-	return &requestStateV1{
-		buffer: make([]byte, 1),
-		offset: 0,
-	}
-}
-
-func (state *requestStateV1) Receive(data []byte, offset int, length int) (int, errors.IdentifiableError) {
-	return 0, nil
-}
-
-func (state *requestStateV1) Send(param interface{}) errors.IdentifiableError {
-	return nil
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region constants and variables //////////////////////////////////////////////////////////////////////////////////////
-
-const (
-	VERSION_1 = byte(1)
-
-	CONNECTION_REJECT = byte(0)
-	CONNECTION_ACCEPT = byte(1)
-
-	DISPATCH_DROP        = byte(0)
-	DISPATCH_TRANSACTION = byte(1)
-	DISPATCH_REQUEST     = byte(2)
-
-	MARSHALED_IDENTITY_IDENTIFIER_START = 0
-	MARSHALED_IDENTITY_SIGNATURE_START  = MARSHALED_IDENTITY_IDENTIFIER_END
-
-	MARSHALED_IDENTITY_IDENTIFIER_SIZE = identity.IDENTIFIER_BYTE_LENGTH
-	MARSHALED_IDENTITY_SIGNATURE_SIZE  = identity.SIGNATURE_BYTE_LENGTH
-
-	MARSHALED_IDENTITY_IDENTIFIER_END = MARSHALED_IDENTITY_IDENTIFIER_START + MARSHALED_IDENTITY_IDENTIFIER_SIZE
-	MARSHALED_IDENTITY_SIGNATURE_END  = MARSHALED_IDENTITY_SIGNATURE_START + MARSHALED_IDENTITY_SIGNATURE_SIZE
-
-	MARSHALED_IDENTITY_TOTAL_SIZE = MARSHALED_IDENTITY_SIGNATURE_END
-)
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plugins/gossip/protocol_v1.png b/plugins/gossip/protocol_v1.png
deleted file mode 100644
index ce667756467d35bf33185cf5c3727894ea7c4e57..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 24951
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

diff --git a/plugins/gossip/send_queue.go b/plugins/gossip/send_queue.go
deleted file mode 100644
index c8550289..00000000
--- a/plugins/gossip/send_queue.go
+++ /dev/null
@@ -1,156 +0,0 @@
-package gossip
-
-import (
-	"sync"
-
-	"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
-	"github.com/iotaledger/hive.go/daemon"
-	"github.com/iotaledger/hive.go/events"
-	"github.com/iotaledger/hive.go/node"
-)
-
-// region plugin module setup //////////////////////////////////////////////////////////////////////////////////////////
-
-func configureSendQueue(plugin *node.Plugin) {
-	for _, neighbor := range neighbors.GetMap() {
-		setupEventHandlers(neighbor)
-	}
-
-	Events.AddNeighbor.Attach(events.NewClosure(setupEventHandlers))
-
-	daemon.Events.Shutdown.Attach(events.NewClosure(func() {
-		log.Info("Stopping Send Queue Dispatcher ...")
-	}))
-}
-
-func runSendQueue(plugin *node.Plugin) {
-	log.Info("Starting Send Queue Dispatcher ...")
-
-	daemon.BackgroundWorker("Gossip Send Queue Dispatcher", func() {
-		log.Info("Starting Send Queue Dispatcher ... done")
-
-		for {
-			select {
-			case <-daemon.ShutdownSignal:
-				log.Info("Stopping Send Queue Dispatcher ... done")
-
-				return
-
-			case tx := <-sendQueue:
-				connectedNeighborsMutex.RLock()
-				for _, neighborQueue := range neighborQueues {
-					select {
-					case neighborQueue.queue <- tx:
-						// log sth
-
-					default:
-						// log sth
-					}
-				}
-				connectedNeighborsMutex.RUnlock()
-			}
-		}
-	})
-
-	connectedNeighborsMutex.Lock()
-	for _, neighborQueue := range neighborQueues {
-		startNeighborSendQueue(neighborQueue.protocol.Neighbor, neighborQueue)
-	}
-	connectedNeighborsMutex.Unlock()
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region public api ///////////////////////////////////////////////////////////////////////////////////////////////////
-
-func SendTransaction(transaction *meta_transaction.MetaTransaction) {
-	sendQueue <- transaction
-}
-
-func (neighbor *Neighbor) SendTransaction(transaction *meta_transaction.MetaTransaction) {
-	if queue, exists := neighborQueues[neighbor.GetIdentity().StringIdentifier]; exists {
-		select {
-		case queue.queue <- transaction:
-			return
-
-		default:
-			return
-		}
-	}
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region utility methods //////////////////////////////////////////////////////////////////////////////////////////////
-
-func setupEventHandlers(neighbor *Neighbor) {
-	neighbor.Events.ProtocolConnectionEstablished.Attach(events.NewClosure(func(protocol *protocol) {
-		queue := &neighborQueue{
-			protocol:       protocol,
-			queue:          make(chan *meta_transaction.MetaTransaction, SEND_QUEUE_SIZE),
-			disconnectChan: make(chan int, 1),
-		}
-
-		connectedNeighborsMutex.Lock()
-		neighborQueues[neighbor.GetIdentity().StringIdentifier] = queue
-		connectedNeighborsMutex.Unlock()
-
-		protocol.Conn.Events.Close.Attach(events.NewClosure(func() {
-			close(queue.disconnectChan)
-
-			connectedNeighborsMutex.Lock()
-			delete(neighborQueues, neighbor.GetIdentity().StringIdentifier)
-			connectedNeighborsMutex.Unlock()
-		}))
-
-		if daemon.IsRunning() {
-			startNeighborSendQueue(neighbor, queue)
-		}
-	}))
-}
-
-func startNeighborSendQueue(neighbor *Neighbor, neighborQueue *neighborQueue) {
-	daemon.BackgroundWorker("Gossip Send Queue ("+neighbor.GetIdentity().StringIdentifier+")", func() {
-		for {
-			select {
-			case <-daemon.ShutdownSignal:
-				return
-
-			case <-neighborQueue.disconnectChan:
-				return
-
-			case tx := <-neighborQueue.queue:
-				switch neighborQueue.protocol.Version {
-				case VERSION_1:
-					sendTransactionV1(neighborQueue.protocol, tx)
-				}
-			}
-		}
-	})
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region types and interfaces /////////////////////////////////////////////////////////////////////////////////////////
-
-type neighborQueue struct {
-	protocol       *protocol
-	queue          chan *meta_transaction.MetaTransaction
-	disconnectChan chan int
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region constants and variables //////////////////////////////////////////////////////////////////////////////////////
-
-var neighborQueues = make(map[string]*neighborQueue)
-
-var connectedNeighborsMutex sync.RWMutex
-
-var sendQueue = make(chan *meta_transaction.MetaTransaction, SEND_QUEUE_SIZE)
-
-const (
-	SEND_QUEUE_SIZE = 500
-)
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plugins/gossip/server.go b/plugins/gossip/server.go
deleted file mode 100644
index 3a344240..00000000
--- a/plugins/gossip/server.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package gossip
-
-import (
-	"github.com/iotaledger/goshimmer/packages/errors"
-	"github.com/iotaledger/goshimmer/packages/identity"
-	"github.com/iotaledger/goshimmer/packages/network"
-	"github.com/iotaledger/goshimmer/packages/network/tcp"
-	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
-	"github.com/iotaledger/hive.go/daemon"
-	"github.com/iotaledger/hive.go/events"
-	"github.com/iotaledger/hive.go/node"
-	"github.com/iotaledger/hive.go/parameter"
-)
-
-var TCPServer = tcp.NewServer()
-
-func configureServer(plugin *node.Plugin) {
-	TCPServer.Events.Connect.Attach(events.NewClosure(func(conn *network.ManagedConnection) {
-		protocol := newProtocol(conn)
-
-		// print protocol errors
-		protocol.Events.Error.Attach(events.NewClosure(func(err errors.IdentifiableError) {
-			log.Error(err.Error())
-		}))
-
-		// store protocol in neighbor if its a neighbor calling
-		protocol.Events.ReceiveIdentification.Attach(events.NewClosure(func(identity *identity.Identity) {
-			if protocol.Neighbor != nil {
-
-				if protocol.Neighbor.GetAcceptedProtocol() == nil {
-					protocol.Neighbor.SetAcceptedProtocol(protocol)
-
-					protocol.Conn.Events.Close.Attach(events.NewClosure(func() {
-						protocol.Neighbor.SetAcceptedProtocol(nil)
-					}))
-				}
-			}
-		}))
-
-		// drop the "secondary" connection upon successful handshake
-		protocol.Events.HandshakeCompleted.Attach(events.NewClosure(func() {
-			if protocol.Neighbor.Peer.ID().String() <= local.INSTANCE.ID().String() {
-				var initiatedProtocolConn *network.ManagedConnection
-				if protocol.Neighbor.GetInitiatedProtocol() != nil {
-					initiatedProtocolConn = protocol.Neighbor.GetInitiatedProtocol().Conn
-				}
-
-				if initiatedProtocolConn != nil {
-					_ = initiatedProtocolConn.Close()
-				}
-			}
-
-			protocol.Neighbor.Events.ProtocolConnectionEstablished.Trigger(protocol)
-		}))
-
-		go protocol.Init()
-	}))
-
-	daemon.Events.Shutdown.Attach(events.NewClosure(func() {
-		log.Info("Stopping TCP Server ...")
-
-		TCPServer.Shutdown()
-	}))
-}
-
-func runServer(plugin *node.Plugin) {
-	gossipPort := parameter.NodeConfig.GetInt(GOSSIP_PORT)
-	log.Infof("Starting TCP Server (port %d) ...", gossipPort)
-
-	daemon.BackgroundWorker("Gossip TCP Server", func() {
-		log.Infof("Starting TCP Server (port %d) ... done", gossipPort)
-
-		TCPServer.Listen(gossipPort)
-
-		log.Info("Stopping TCP Server ... done")
-	})
-}
diff --git a/plugins/gossip/transaction_processor.go b/plugins/gossip/transaction_processor.go
deleted file mode 100644
index 742ac369..00000000
--- a/plugins/gossip/transaction_processor.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package gossip
-
-import (
-	"github.com/iotaledger/goshimmer/packages/filter"
-	"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
-)
-
-// region public api ///////////////////////////////////////////////////////////////////////////////////////////////////
-
-func ProcessReceivedTransactionData(transactionData []byte) {
-	if transactionFilter.Add(transactionData) {
-		Events.ReceiveTransaction.Trigger(meta_transaction.FromBytes(transactionData))
-	}
-}
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// region constants and variables //////////////////////////////////////////////////////////////////////////////////////
-
-var transactionFilter = filter.NewByteArrayFilter(TRANSACTION_FILTER_SIZE)
-
-const (
-	TRANSACTION_FILTER_SIZE = 500
-)
-
-// endregion ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plugins/gossip/transaction_processor_test.go b/plugins/gossip/transaction_processor_test.go
deleted file mode 100644
index 082eba9d..00000000
--- a/plugins/gossip/transaction_processor_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package gossip
-
-import (
-	"sync"
-	"testing"
-
-	"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
-	"github.com/iotaledger/iota.go/consts"
-)
-
-func BenchmarkProcessSimilarTransactionsFiltered(b *testing.B) {
-	byteArray := setupTransaction(meta_transaction.MARSHALED_TOTAL_SIZE / consts.NumberOfTritsInAByte)
-
-	b.ResetTimer()
-
-	for i := 0; i < b.N; i++ {
-		ProcessReceivedTransactionData(byteArray)
-	}
-}
-
-func BenchmarkProcessSimilarTransactionsUnfiltered(b *testing.B) {
-	byteArray := setupTransaction(meta_transaction.MARSHALED_TOTAL_SIZE / consts.NumberOfTritsInAByte)
-
-	b.ResetTimer()
-
-	var wg sync.WaitGroup
-
-	for i := 0; i < b.N; i++ {
-		wg.Add(1)
-
-		go func() {
-			Events.ReceiveTransaction.Trigger(meta_transaction.FromBytes(byteArray))
-
-			wg.Done()
-		}()
-	}
-
-	wg.Wait()
-}
-
-func setupTransaction(byteArraySize int) []byte {
-	byteArray := make([]byte, byteArraySize)
-
-	for i := 0; i < len(byteArray); i++ {
-		byteArray[i] = byte(i % 128)
-	}
-
-	return byteArray
-}
diff --git a/plugins/tangle/events.go b/plugins/tangle/events.go
index 3cc90ba4..b9c0acf5 100644
--- a/plugins/tangle/events.go
+++ b/plugins/tangle/events.go
@@ -1,7 +1,7 @@
 package tangle
 
 import (
-	"github.com/iotaledger/goshimmer/packages/model/value_transaction"
+	"github.com/iotaledger/goshimmer/packages/model/meta_transaction"
 	"github.com/iotaledger/hive.go/events"
 )
 
@@ -16,5 +16,5 @@ type pluginEvents struct {
 }
 
 func transactionCaller(handler interface{}, params ...interface{}) {
-	handler.(func(*value_transaction.ValueTransaction))(params[0].(*value_transaction.ValueTransaction))
+	handler.(func(*meta_transaction.MetaTransaction))(params[0].(*meta_transaction.MetaTransaction))
 }
-- 
GitLab