diff --git a/main.go b/main.go
index c58b8fc2e81a9caeb32febffd5d34af1a301a380..9d80fd890b2a9530e533e80e89e4947d404064be 100644
--- a/main.go
+++ b/main.go
@@ -5,6 +5,7 @@ import (
     "github.com/iotaledger/goshimmer/plugins/analysis"
     "github.com/iotaledger/goshimmer/plugins/autopeering"
     "github.com/iotaledger/goshimmer/plugins/cli"
+    "github.com/iotaledger/goshimmer/plugins/gossip"
     "github.com/iotaledger/goshimmer/plugins/gracefulshutdown"
     "github.com/iotaledger/goshimmer/plugins/statusscreen"
 )
@@ -13,6 +14,7 @@ func main() {
     node.Run(
         cli.PLUGIN,
         autopeering.PLUGIN,
+        gossip.PLUGIN,
         analysis.PLUGIN,
         statusscreen.PLUGIN,
         gracefulshutdown.PLUGIN,
diff --git a/packages/byteutils/byteutils.go b/packages/byteutils/byteutils.go
new file mode 100644
index 0000000000000000000000000000000000000000..ccf1b5f53ba055660e8143cd5264145f9dfb05dc
--- /dev/null
+++ b/packages/byteutils/byteutils.go
@@ -0,0 +1,17 @@
+package byteutils
+
+func ReadAvailableBytesToBuffer(target []byte, targetOffset int, source []byte, sourceOffset int, sourceLength int) int {
+    availableBytes := sourceLength - sourceOffset
+    requiredBytes := len(target) - targetOffset
+
+    var bytesToRead int
+    if availableBytes < requiredBytes {
+        bytesToRead = availableBytes
+    } else {
+        bytesToRead = requiredBytes
+    }
+
+    copy(target[targetOffset:], source[sourceOffset:sourceOffset + bytesToRead])
+
+    return bytesToRead
+}
diff --git a/packages/curl/batch_hasher.go b/packages/curl/batch_hasher.go
new file mode 100644
index 0000000000000000000000000000000000000000..f379583e00aa5eb3e826494890e2859c8da73449
--- /dev/null
+++ b/packages/curl/batch_hasher.go
@@ -0,0 +1,103 @@
+package curl
+
+import (
+    "fmt"
+    "github.com/iotaledger/goshimmer/packages/ternary"
+    "strconv"
+    "time"
+)
+
+type HashRequest struct {
+    input ternary.Trits
+    output chan ternary.Trits
+}
+
+type BatchHasher struct {
+    hashRequests chan HashRequest
+    hashLength int
+    rounds int
+}
+
+func NewBatchHasher(hashLength int, rounds int) *BatchHasher {
+    this := &BatchHasher{
+        hashLength: hashLength,
+        rounds: rounds,
+        hashRequests: make(chan HashRequest),
+    }
+
+    go this.startDispatcher()
+
+    return this
+}
+
+func (this *BatchHasher) startDispatcher() {
+    for {
+        collectedHashRequests := make([]HashRequest, 0)
+
+        // wait for first request to start processing at all
+        collectedHashRequests = append(collectedHashRequests, <- this.hashRequests)
+
+        // collect additional requests that arrive within the timeout
+    CollectAdditionalRequests:
+        for {
+            select {
+            case hashRequest := <- this.hashRequests:
+                collectedHashRequests = append(collectedHashRequests, hashRequest)
+
+                if len(collectedHashRequests) == strconv.IntSize {
+                    break CollectAdditionalRequests
+                }
+            case <- time.After(50 * time.Millisecond):
+                break CollectAdditionalRequests
+            }
+        }
+
+        go this.processHashes(collectedHashRequests)
+    }
+}
+
+func (this *BatchHasher) processHashes(collectedHashRequests []HashRequest) {
+    if len(collectedHashRequests) > 1 {
+        // multiplex the requests
+        multiplexer := ternary.NewBCTernaryMultiplexer()
+        for _, hashRequest := range collectedHashRequests {
+            multiplexer.Add(hashRequest.input)
+        }
+        bcTrinary, err := multiplexer.Extract()
+        if err != nil {
+            fmt.Println(err)
+        }
+
+        // calculate the hash
+        bctCurl := NewBCTCurl(this.hashLength, this.rounds)
+        bctCurl.Reset()
+        bctCurl.Absorb(bcTrinary)
+
+        // extract the results from the demultiplexer
+        demux := ternary.NewBCTernaryDemultiplexer(bctCurl.Squeeze(243))
+        for i, hashRequest := range collectedHashRequests {
+            hashRequest.output <- demux.Get(i)
+            close(hashRequest.output)
+        }
+    } else {
+        var resp = make(ternary.Trits, this.hashLength)
+
+        curl := NewCurl(this.hashLength, this.rounds)
+        curl.Absorb(collectedHashRequests[0].input, 0, len(collectedHashRequests[0].input))
+        curl.Squeeze(resp, 0, this.hashLength)
+
+        collectedHashRequests[0].output <- resp
+        close(collectedHashRequests[0].output)
+    }
+}
+
+func (this *BatchHasher) Hash(trinary ternary.Trits) chan ternary.Trits {
+    hashRequest := HashRequest{
+        input: trinary,
+        output: make(chan ternary.Trits, 1),
+    }
+
+    this.hashRequests <- hashRequest
+
+    return hashRequest.output
+}
\ No newline at end of file
diff --git a/packages/curl/bct_curl.go b/packages/curl/bct_curl.go
new file mode 100644
index 0000000000000000000000000000000000000000..5fef944527e9274512566cb6f425fb49b7953fe1
--- /dev/null
+++ b/packages/curl/bct_curl.go
@@ -0,0 +1,116 @@
+package curl
+
+import "github.com/iotaledger/goshimmer/packages/ternary"
+
+const (
+    HIGH_LONG_BITS = 0xFFFFFFFFFFFFFFFF
+)
+
+type BCTCurl struct {
+    hashLength int
+    numberOfRounds int
+    stateLength int
+    state ternary.BCTrinary
+    cTransform func()
+}
+
+func NewBCTCurl(hashLength int, numberOfRounds int) *BCTCurl {
+    this := &BCTCurl{
+        hashLength: hashLength,
+        numberOfRounds: numberOfRounds,
+        stateLength: ternary.NUMBER_OF_TRITS_IN_A_TRYTE * hashLength,
+        state: ternary.BCTrinary{
+            Lo: make([]uint, ternary.NUMBER_OF_TRITS_IN_A_TRYTE * hashLength),
+            Hi: make([]uint, ternary.NUMBER_OF_TRITS_IN_A_TRYTE * hashLength),
+        },
+        cTransform: nil,
+    }
+
+    this.Reset()
+
+    return this
+}
+
+func (this *BCTCurl) Reset() {
+    for i:= 0; i < this.stateLength; i++ {
+        this.state.Lo[i] = HIGH_LONG_BITS
+        this.state.Hi[i] = HIGH_LONG_BITS
+    }
+}
+
+func (this *BCTCurl) Transform() {
+    scratchPadLo := make([]uint, this.stateLength)
+    scratchPadHi := make([]uint, this.stateLength)
+    scratchPadIndex := 0
+
+    for round := this.numberOfRounds; round > 0; round-- {
+        copy(scratchPadLo, this.state.Lo)
+        copy(scratchPadHi, this.state.Hi)
+        for stateIndex := 0; stateIndex < this.stateLength; stateIndex++ {
+            alpha := scratchPadLo[scratchPadIndex]
+            beta := scratchPadHi[scratchPadIndex]
+
+            if scratchPadIndex < 365 {
+                scratchPadIndex += 364
+            } else {
+                scratchPadIndex -= 365
+            }
+
+            delta := beta ^ scratchPadLo[scratchPadIndex]
+
+            this.state.Lo[stateIndex] = ^(delta & alpha)
+            this.state.Hi[stateIndex] = delta | (alpha ^ scratchPadHi[scratchPadIndex])
+        }
+    }
+}
+
+func (this *BCTCurl) Absorb(bcTrits ternary.BCTrinary) {
+    length := len(bcTrits.Lo)
+    offset := 0
+
+    for {
+        var lengthToCopy int
+        if length < this.hashLength {
+            lengthToCopy = length
+        } else {
+            lengthToCopy = this.hashLength
+        }
+
+        copy(this.state.Lo[0:lengthToCopy], bcTrits.Lo[offset:offset + lengthToCopy])
+        copy(this.state.Hi[0:lengthToCopy], bcTrits.Hi[offset:offset + lengthToCopy])
+        this.Transform()
+
+        offset += lengthToCopy
+        length -= lengthToCopy
+
+        if length <= 0 {
+            break
+        }
+    }
+}
+
+func (this *BCTCurl) Squeeze(tritCount int) ternary.BCTrinary {
+    result := ternary.BCTrinary{
+        Lo: make([]uint, tritCount),
+        Hi: make([]uint, tritCount),
+    }
+    hashCount := tritCount / this.hashLength
+
+    for i := 0; i < hashCount; i++ {
+        copy(result.Lo[i*this.hashLength:(i+1)*this.hashLength], this.state.Lo[0:this.hashLength])
+        copy(result.Hi[i*this.hashLength:(i+1)*this.hashLength], this.state.Hi[0:this.hashLength])
+
+        this.Transform()
+    }
+
+    last := tritCount - hashCount*this.hashLength
+
+    copy(result.Lo[tritCount-last:], this.state.Lo[0:last])
+    copy(result.Hi[tritCount-last:], this.state.Hi[0:last])
+
+    if tritCount % this.hashLength != 0 {
+        this.Transform()
+    }
+
+    return result
+}
diff --git a/packages/curl/curl.go b/packages/curl/curl.go
new file mode 100644
index 0000000000000000000000000000000000000000..6ef01ac878881e288229403c46f87e1a98d283f1
--- /dev/null
+++ b/packages/curl/curl.go
@@ -0,0 +1,104 @@
+package curl
+
+import (
+    "github.com/iotaledger/goshimmer/packages/ternary"
+    "math"
+)
+
+const (
+    HASH_LENGTH         = 243
+    STATE_LENGTH        = ternary.NUMBER_OF_TRITS_IN_A_TRYTE * HASH_LENGTH
+)
+
+var (
+    TRUTH_TABLE = ternary.Trits{1, 0, -1, 2, 1, -1, 0, 2, -1, 1, 0}
+)
+
+
+type Hash interface {
+    Initialize()
+    InitializeCurl(trits *[]int8, length int, rounds int)
+    Reset()
+    Absorb(trits *[]int8, offset int, length int)
+    Squeeze(resp []int8, offset int, length int) []int
+}
+
+
+type Curl struct {
+    Hash
+    state  ternary.Trits
+    hashLength int
+    rounds int
+}
+
+func NewCurl(hashLength int, rounds int) *Curl {
+    this := &Curl{
+        hashLength: hashLength,
+        rounds: rounds,
+    }
+
+    this.Reset()
+
+    return this
+}
+
+func (curl *Curl) Initialize() {
+    curl.InitializeCurl(nil, 0, curl.rounds)
+}
+
+func (curl *Curl) InitializeCurl(trinary ternary.Trits, length int, rounds int) {
+    curl.rounds = rounds
+    if trinary != nil {
+        curl.state = trinary
+    } else {
+        curl.state = make(ternary.Trits, STATE_LENGTH)
+    }
+}
+
+func (curl *Curl) Reset() {
+    curl.InitializeCurl(nil, 0, curl.rounds)
+}
+
+func (curl *Curl) Absorb(trinary ternary.Trits, offset int, length int) {
+    for {
+        limit := int(math.Min(HASH_LENGTH, float64(length)))
+        copy(curl.state, trinary[offset:offset+limit])
+        curl.Transform()
+        offset += HASH_LENGTH
+        length -= HASH_LENGTH
+        if length <= 0 {
+            break
+        }
+    }
+}
+
+func (curl *Curl) Squeeze(resp ternary.Trits, offset int, length int) ternary.Trits {
+    for {
+        limit := int(math.Min(HASH_LENGTH, float64(length)))
+        copy(resp[offset:offset+limit], curl.state)
+        curl.Transform()
+        offset += HASH_LENGTH
+        length -= HASH_LENGTH
+        if length <= 0 {
+            break
+        }
+    }
+    return resp
+}
+
+func (curl *Curl) Transform() {
+    var index = 0
+    for round := 0; round < curl.rounds; round++ {
+        stateCopy := make(ternary.Trits, STATE_LENGTH)
+        copy(stateCopy, curl.state)
+        for i := 0; i < STATE_LENGTH; i++ {
+            incr := 364
+            if index >= 365 {
+                incr = -365
+            }
+            index2 := index + incr
+            curl.state[i] = TRUTH_TABLE[stateCopy[index]+(stateCopy[index2]<<2)+5]
+            index = index2
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/curl/curlp81.go b/packages/curl/curlp81.go
new file mode 100644
index 0000000000000000000000000000000000000000..7e6ed0b3434eaea6780e58a241dd776968a41b4a
--- /dev/null
+++ b/packages/curl/curlp81.go
@@ -0,0 +1,10 @@
+package curl
+
+const (
+    CURLP81_HASH_LENGTH = 243
+    CURLP81_ROUNDS = 81
+)
+
+var (
+    CURLP81 = NewBatchHasher(CURLP81_HASH_LENGTH, CURLP81_ROUNDS)
+)
diff --git a/packages/errors/errors.go b/packages/errors/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..6ca17ec9589582f6b52fbaa7b3c660a7444964b6
--- /dev/null
+++ b/packages/errors/errors.go
@@ -0,0 +1,365 @@
+// Package errors provides simple error handling primitives.
+//
+// The traditional error handling idiom in Go is roughly akin to
+//
+//     if err != nil {
+//             return err
+//     }
+//
+// which when applied recursively up the call stack results in error reports
+// without context or debugging information. The errors package allows
+// programmers to add context to the failure path in their code in a way
+// that does not destroy the original value of the error.
+//
+// Adding context to an error
+//
+// The errors.Wrap function returns a new error that adds context to the
+// original error by recording a stack trace at the point Wrap is called,
+// together with the supplied message. For example
+//
+//     _, err := ioutil.ReadAll(r)
+//     if err != nil {
+//             return errors.Wrap(err, "read failed")
+//     }
+//
+// If additional control is required, the errors.WithStack and
+// errors.WithMessage functions destructure errors.Wrap into its component
+// operations: annotating an error with a stack trace and with a message,
+// respectively.
+//
+// Retrieving the cause of an error
+//
+// Using errors.Wrap constructs a stack of errors, adding context to the
+// preceding error. Depending on the nature of the error it may be necessary
+// to reverse the operation of errors.Wrap to retrieve the original error
+// for inspection. Any error value which implements this interface
+//
+//     type causer interface {
+//             Cause() error
+//     }
+//
+// can be inspected by errors.Cause. errors.Cause will recursively retrieve
+// the topmost error that does not implement causer, which is assumed to be
+// the original cause. For example:
+//
+//     switch err := errors.Cause(err).(type) {
+//     case *MyError:
+//             // handle specifically
+//     default:
+//             // unknown error
+//     }
+//
+// Although the causer interface is not exported by this package, it is
+// considered a part of its stable public interface.
+//
+// Formatted printing of errors
+//
+// All error values returned from this package implement fmt.Formatter and can
+// be formatted by the fmt package. The following verbs are supported:
+//
+//     %s    print the error. If the error has a Cause it will be
+//           printed recursively.
+//     %v    see %s
+//     %+v   extended format. Each Frame of the error's StackTrace will
+//           be printed in detail.
+//
+// Retrieving the stack trace of an error or wrapper
+//
+// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
+// invoked. This information can be retrieved with the following interface:
+//
+//     type stackTracer interface {
+//             StackTrace() errors.StackTrace
+//     }
+//
+// The returned errors.StackTrace type is defined as
+//
+//     type StackTrace []Frame
+//
+// The Frame type represents a call site in the stack trace. Frame supports
+// the fmt.Formatter interface that can be used for printing information about
+// the stack trace of this error. For example:
+//
+//     if err, ok := err.(stackTracer); ok {
+//             for _, f := range err.StackTrace() {
+//                     fmt.Printf("%+s:%d\n", f, f)
+//             }
+//     }
+//
+// Although the stackTracer interface is not exported by this package, it is
+// considered a part of its stable public interface.
+//
+// See the documentation for Frame.Format for more details.
+package errors
+
+import (
+    "fmt"
+    "io"
+)
+
+var idCounter = 0
+
+// New returns an error with the supplied message.
+// New also records the stack trace at the point it was called.
+func New(message string) *fundamental {
+    idCounter++
+
+    return &fundamental{
+        id:    idCounter,
+        msg:   message,
+        stack: callers(),
+    }
+}
+
+// Errorf formats according to a format specifier and returns the string
+// as a value that satisfies error.
+// Errorf also records the stack trace at the point it was called.
+func Errorf(format string, args ...interface{}) IdentifiableError {
+    idCounter++
+
+    return &fundamental{
+        id:    idCounter,
+        msg:   fmt.Sprintf(format, args...),
+        stack: callers(),
+    }
+}
+
+// fundamental is an error that has a message and a stack, but no caller.
+type fundamental struct {
+    id int
+    msg string
+    *stack
+}
+
+func (f *fundamental) Derive(msg string) *fundamental {
+    return &fundamental{
+        id:    f.id,
+        msg:   msg,
+        stack: callers(),
+    }
+}
+
+func (f *fundamental) Error() string { return f.msg }
+
+func (f *fundamental) Equals(err IdentifiableError) bool {
+    return f.id == err.Id()
+}
+
+func (f *fundamental) Id() int {
+    return f.id
+}
+
+func (f *fundamental) Format(s fmt.State, verb rune) {
+    switch verb {
+    case 'v':
+        if s.Flag('+') {
+            io.WriteString(s, f.msg)
+            f.stack.Format(s, verb)
+            return
+        }
+        fallthrough
+    case 's':
+        io.WriteString(s, f.msg)
+    case 'q':
+        fmt.Fprintf(s, "%q", f.msg)
+    }
+}
+
+// WithStack annotates err with a stack trace at the point WithStack was called.
+// If err is nil, WithStack returns nil.
+func WithStack(err error) IdentifiableError {
+    if err == nil {
+        return nil
+    }
+
+    idCounter++
+
+    return &withStack{
+        idCounter,
+        err,
+        callers(),
+    }
+}
+
+type withStack struct {
+    int
+    error
+    *stack
+}
+
+func (w *withStack) Equals(err IdentifiableError) bool {
+    return w.int == err.Id()
+}
+
+func (w *withStack) Id() int {
+    return w.int
+}
+
+func (w *withStack) Derive(err error, message string) *withStack {
+    if err == nil {
+        return nil
+    }
+    return &withStack{
+        w.int,
+        &withMessage{
+            cause: err,
+            msg:   message,
+        },
+        callers(),
+    }
+}
+
+func (w *withStack) Cause() error { return w.error }
+
+func (w *withStack) Format(s fmt.State, verb rune) {
+    switch verb {
+    case 'v':
+        if s.Flag('+') {
+            fmt.Fprintf(s, "%+v", w.Cause())
+            w.stack.Format(s, verb)
+            return
+        }
+        fallthrough
+    case 's':
+        io.WriteString(s, w.Error())
+    case 'q':
+        fmt.Fprintf(s, "%q", w.Error())
+    }
+}
+
+// Wrap returns an error annotating err with a stack trace
+// at the point Wrap is called, and the supplied message.
+// If err is nil, Wrap returns nil.
+func Wrap(err error, message string) *withStack {
+    if err == nil {
+        return nil
+    }
+    err = &withMessage{
+        cause: err,
+        msg:   message,
+    }
+
+    idCounter++
+
+    return &withStack{
+        idCounter,
+        err,
+        callers(),
+    }
+}
+
+// Wrapf returns an error annotating err with a stack trace
+// at the point Wrapf is called, and the format specifier.
+// If err is nil, Wrapf returns nil.
+func Wrapf(err error, format string, args ...interface{}) IdentifiableError {
+    if err == nil {
+        return nil
+    }
+    err = &withMessage{
+        cause: err,
+        msg:   fmt.Sprintf(format, args...),
+    }
+
+    idCounter++
+
+    return &withStack{
+        idCounter,
+        err,
+        callers(),
+    }
+}
+
+// WithMessage annotates err with a new message.
+// If err is nil, WithMessage returns nil.
+func WithMessage(err error, message string) IdentifiableError {
+    if err == nil {
+        return nil
+    }
+
+    idCounter++
+
+    return &withMessage{
+        id: idCounter,
+        cause: err,
+        msg:   message,
+    }
+}
+
+// WithMessagef annotates err with the format specifier.
+// If err is nil, WithMessagef returns nil.
+func WithMessagef(err error, format string, args ...interface{}) IdentifiableError {
+    if err == nil {
+        return nil
+    }
+
+    idCounter++
+
+    return &withMessage{
+        id: idCounter,
+        cause: err,
+        msg:   fmt.Sprintf(format, args...),
+    }
+}
+
+type withMessage struct {
+    id    int
+    cause error
+    msg   string
+}
+
+func (w *withMessage) Equals(err IdentifiableError) bool {
+    return w.id == err.Id()
+}
+
+func (w *withMessage) Id() int {
+    return w.id
+}
+
+func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
+func (w *withMessage) Cause() error  { return w.cause }
+
+func (w *withMessage) Format(s fmt.State, verb rune) {
+    switch verb {
+    case 'v':
+        if s.Flag('+') {
+            fmt.Fprintf(s, "%+v\n", w.Cause())
+            io.WriteString(s, w.msg)
+            return
+        }
+        fallthrough
+    case 's', 'q':
+        io.WriteString(s, w.Error())
+    }
+}
+
+// Cause returns the underlying cause of the error, if possible.
+// An error value has a cause if it implements the following
+// interface:
+//
+//     type causer interface {
+//            Cause() error
+//     }
+//
+// If the error does not implement Cause, the original error will
+// be returned. If the error is nil, nil will be returned without further
+// investigation.
+func Cause(err error) error {
+    type causer interface {
+        Cause() error
+    }
+
+    for err != nil {
+        cause, ok := err.(causer)
+        if !ok {
+            break
+        }
+        err = cause.Cause()
+    }
+    return err
+}
+
+type IdentifiableError interface {
+    Error() string
+    Equals(identifiableError IdentifiableError) bool
+    Id() int
+}
\ No newline at end of file
diff --git a/packages/errors/stack.go b/packages/errors/stack.go
new file mode 100644
index 0000000000000000000000000000000000000000..54b202a1ff86c400582dce25df159af3f6bd58c1
--- /dev/null
+++ b/packages/errors/stack.go
@@ -0,0 +1,177 @@
+package errors
+
+import (
+    "fmt"
+    "io"
+    "path"
+    "runtime"
+    "strconv"
+    "strings"
+)
+
+// Frame represents a program counter inside a stack frame.
+// For historical reasons if Frame is interpreted as a uintptr
+// its value represents the program counter + 1.
+type Frame uintptr
+
+// pc returns the program counter for this frame;
+// multiple frames may have the same PC value.
+func (f Frame) pc() uintptr { return uintptr(f) - 1 }
+
+// file returns the full path to the file that contains the
+// function for this Frame's pc.
+func (f Frame) file() string {
+    fn := runtime.FuncForPC(f.pc())
+    if fn == nil {
+        return "unknown"
+    }
+    file, _ := fn.FileLine(f.pc())
+    return file
+}
+
+// line returns the line number of source code of the
+// function for this Frame's pc.
+func (f Frame) line() int {
+    fn := runtime.FuncForPC(f.pc())
+    if fn == nil {
+        return 0
+    }
+    _, line := fn.FileLine(f.pc())
+    return line
+}
+
+// name returns the name of this function, if known.
+func (f Frame) name() string {
+    fn := runtime.FuncForPC(f.pc())
+    if fn == nil {
+        return "unknown"
+    }
+    return fn.Name()
+}
+
+// Format formats the frame according to the fmt.Formatter interface.
+//
+//    %s    source file
+//    %d    source line
+//    %n    function name
+//    %v    equivalent to %s:%d
+//
+// Format accepts flags that alter the printing of some verbs, as follows:
+//
+//    %+s   function name and path of source file relative to the compile time
+//          GOPATH separated by \n\t (<funcname>\n\t<path>)
+//    %+v   equivalent to %+s:%d
+func (f Frame) Format(s fmt.State, verb rune) {
+    switch verb {
+    case 's':
+        switch {
+        case s.Flag('+'):
+            io.WriteString(s, f.name())
+            io.WriteString(s, "\n\t")
+            io.WriteString(s, f.file())
+        default:
+            io.WriteString(s, path.Base(f.file()))
+        }
+    case 'd':
+        io.WriteString(s, strconv.Itoa(f.line()))
+    case 'n':
+        io.WriteString(s, funcname(f.name()))
+    case 'v':
+        f.Format(s, 's')
+        io.WriteString(s, ":")
+        f.Format(s, 'd')
+    }
+}
+
+// MarshalText formats a stacktrace Frame as a text string. The output is the
+// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
+func (f Frame) MarshalText() ([]byte, error) {
+    name := f.name()
+    if name == "unknown" {
+        return []byte(name), nil
+    }
+    return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
+}
+
+// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
+type StackTrace []Frame
+
+// Format formats the stack of Frames according to the fmt.Formatter interface.
+//
+//    %s	lists source files for each Frame in the stack
+//    %v	lists the source file and line number for each Frame in the stack
+//
+// Format accepts flags that alter the printing of some verbs, as follows:
+//
+//    %+v   Prints filename, function, and line number for each Frame in the stack.
+func (st StackTrace) Format(s fmt.State, verb rune) {
+    switch verb {
+    case 'v':
+        switch {
+        case s.Flag('+'):
+            for _, f := range st {
+                io.WriteString(s, "\n")
+                f.Format(s, verb)
+            }
+        case s.Flag('#'):
+            fmt.Fprintf(s, "%#v", []Frame(st))
+        default:
+            st.formatSlice(s, verb)
+        }
+    case 's':
+        st.formatSlice(s, verb)
+    }
+}
+
+// formatSlice will format this StackTrace into the given buffer as a slice of
+// Frame, only valid when called with '%s' or '%v'.
+func (st StackTrace) formatSlice(s fmt.State, verb rune) {
+    io.WriteString(s, "[")
+    for i, f := range st {
+        if i > 0 {
+            io.WriteString(s, " ")
+        }
+        f.Format(s, verb)
+    }
+    io.WriteString(s, "]")
+}
+
+// stack represents a stack of program counters.
+type stack []uintptr
+
+func (s *stack) Format(st fmt.State, verb rune) {
+    switch verb {
+    case 'v':
+        switch {
+        case st.Flag('+'):
+            for _, pc := range *s {
+                f := Frame(pc)
+                fmt.Fprintf(st, "\n%+v", f)
+            }
+        }
+    }
+}
+
+func (s *stack) StackTrace() StackTrace {
+    f := make([]Frame, len(*s))
+    for i := 0; i < len(f); i++ {
+        f[i] = Frame((*s)[i])
+    }
+    return f
+}
+
+func callers() *stack {
+    const depth = 32
+    var pcs [depth]uintptr
+    n := runtime.Callers(3, pcs[:])
+    var st stack = pcs[0:n]
+    return &st
+}
+
+// funcname removes the path prefix component of a function's name reported by func.Name().
+func funcname(name string) string {
+    i := strings.LastIndex(name, "/")
+    name = name[i+1:]
+    i = strings.Index(name, ".")
+    return name[i+1:]
+}
\ No newline at end of file
diff --git a/packages/ternary/bc_ternary.go b/packages/ternary/bc_ternary.go
new file mode 100644
index 0000000000000000000000000000000000000000..75843830bdda5e6a4616357b3239d8b0b129fc21
--- /dev/null
+++ b/packages/ternary/bc_ternary.go
@@ -0,0 +1,13 @@
+package ternary
+
+// a Binary Coded Trit encodes a Trit in 2 bits with -1 => 00, 0 => 01 and 1 => 10
+type BCTrit struct {
+    Lo uint
+    Hi uint
+}
+
+// a Binary Coded Trinary consists out of many Binary Coded Trits
+type BCTrinary struct {
+    Lo []uint
+    Hi []uint
+}
diff --git a/packages/ternary/bc_ternary_demultiplexer.go b/packages/ternary/bc_ternary_demultiplexer.go
new file mode 100644
index 0000000000000000000000000000000000000000..02221f6eefa1575e87d03a2afb4d82b3dfc8cc23
--- /dev/null
+++ b/packages/ternary/bc_ternary_demultiplexer.go
@@ -0,0 +1,37 @@
+package ternary
+
+type BCTernaryDemultiplexer struct {
+    bcTrinary BCTrinary
+}
+
+func NewBCTernaryDemultiplexer(bcTrinary BCTrinary) *BCTernaryDemultiplexer {
+    this := &BCTernaryDemultiplexer{bcTrinary: bcTrinary}
+
+    return this
+}
+
+func (this *BCTernaryDemultiplexer) Get(index int) Trits {
+    length := len(this.bcTrinary.Lo)
+    result := make(Trits, length)
+
+    for i := 0; i < length; i++ {
+        low := (this.bcTrinary.Lo[i] >> uint(index)) & 1
+        hi := (this.bcTrinary.Hi[i] >> uint(index)) & 1
+
+        switch true {
+        case low == 1 && hi == 0:
+            result[i] = -1
+
+        case low == 0 && hi == 1:
+            result[i] = 1
+
+        case low == 1 && hi == 1:
+            result[i] = 0
+
+        default:
+            result[i] = 0
+        }
+    }
+
+    return result
+}
diff --git a/packages/ternary/bc_ternary_multiplexer.go b/packages/ternary/bc_ternary_multiplexer.go
new file mode 100644
index 0000000000000000000000000000000000000000..93a0eb57e5e83b644a3a4b740180ad59c704ae42
--- /dev/null
+++ b/packages/ternary/bc_ternary_multiplexer.go
@@ -0,0 +1,62 @@
+package ternary
+
+import (
+    "errors"
+    "strconv"
+)
+
+type BCTernaryMultiplexer struct {
+    trinaries []Trits
+}
+
+func NewBCTernaryMultiplexer() *BCTernaryMultiplexer {
+    this := &BCTernaryMultiplexer{make([]Trits, 0)}
+
+    return this
+}
+
+func (this *BCTernaryMultiplexer) Add(trinary Trits) int {
+    this.trinaries = append(this.trinaries, trinary)
+
+    return len(this.trinaries) - 1
+}
+
+func (this *BCTernaryMultiplexer) Get(index int) Trits {
+    return this.trinaries[index]
+}
+
+func (this *BCTernaryMultiplexer) Extract() (BCTrinary, error) {
+    trinariesCount := len(this.trinaries)
+    tritsCount := len(this.trinaries[0])
+
+    result := BCTrinary{
+        Lo: make([]uint, tritsCount),
+        Hi: make([]uint, tritsCount),
+    }
+
+    for i := 0; i < tritsCount; i++ {
+        bcTrit := &BCTrit{0, 0}
+
+        for j := 0; j < trinariesCount; j++ {
+            switch this.trinaries[j][i] {
+            case -1:
+                bcTrit.Lo |= 1 << uint(j)
+
+            case 1:
+                bcTrit.Hi |= 1 << uint(j)
+
+            case 0:
+                bcTrit.Lo |= 1 << uint(j)
+                bcTrit.Hi |= 1 << uint(j)
+
+            default:
+                return result, errors.New("Invalid trit #" + strconv.Itoa(i) + " in trinary #" + strconv.Itoa(j))
+            }
+        }
+
+        result.Lo[i] = bcTrit.Lo
+        result.Hi[i] = bcTrit.Hi
+    }
+
+    return result, nil
+}
diff --git a/packages/ternary/conversion.go b/packages/ternary/conversion.go
new file mode 100644
index 0000000000000000000000000000000000000000..ac65f1eacf1780d1bd06d9bb4672c0a0e2232a8e
--- /dev/null
+++ b/packages/ternary/conversion.go
@@ -0,0 +1,95 @@
+package ternary
+
+import "bytes"
+
+const (
+    NUMBER_OF_TRITS_IN_A_BYTE = 5
+    NUMBER_OF_TRITS_IN_A_TRYTE = 3
+)
+
+var (
+    TRYTE_ALPHABET = []string{
+        "9", "A", "B","C", "D", "E", "F", "G", "H",
+        "I", "J", "K", "L", "M", "N", "O", "P", "Q",
+        "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+    }
+
+    BYTES_TO_TRITS = []Trit {
+        0,  0,  0,  0,  0,  1,  0,  0,  0,  0, -1,  1,  0,  0,  0,  0,  1,  0,  0,  0,  1,  1,  0,  0,  0, -1, -1,
+        1,  0,  0,  0, -1,  1,  0,  0,  1, -1,  1,  0,  0, -1,  0,  1,  0,  0,  0,  0,  1,  0,  0,  1,  0,  1,  0,
+        0, -1,  1,  1,  0,  0,  0,  1,  1,  0,  0,  1,  1,  1,  0,  0, -1, -1, -1,  1,  0,  0, -1, -1,  1,  0,  1,
+        -1, -1,  1,  0, -1,  0, -1,  1,  0,  0,  0, -1,  1,  0,  1,  0, -1,  1,  0, -1,  1, -1,  1,  0,  0,  1, -1,
+        1,  0,  1,  1, -1,  1,  0, -1, -1,  0,  1,  0,  0, -1,  0,  1,  0,  1, -1,  0,  1,  0, -1,  0,  0,  1,  0,
+        0,  0,  0,  1,  0,  1,  0,  0,  1,  0, -1,  1,  0,  1,  0,  0,  1,  0,  1,  0,  1,  1,  0,  1,  0, -1, -1,
+        1,  1,  0,  0, -1,  1,  1,  0,  1, -1,  1,  1,  0, -1,  0,  1,  1,  0,  0,  0,  1,  1,  0,  1,  0,  1,  1,
+        0, -1,  1,  1,  1,  0,  0,  1,  1,  1,  0,  1,  1,  1,  1,  0, -1, -1, -1, -1,  1,  0, -1, -1, -1,  1,  1,
+        -1, -1, -1,  1, -1,  0, -1, -1,  1,  0,  0, -1, -1,  1,  1,  0, -1, -1,  1, -1,  1, -1, -1,  1,  0,  1, -1,
+        -1,  1,  1,  1, -1, -1,  1, -1, -1,  0, -1,  1,  0, -1,  0, -1,  1,  1, -1,  0, -1,  1, -1,  0,  0, -1,  1,
+        0,  0,  0, -1,  1,  1,  0,  0, -1,  1, -1,  1,  0, -1,  1,  0,  1,  0, -1,  1,  1,  1,  0, -1,  1, -1, -1,
+        1, -1,  1,  0, -1,  1, -1,  1,  1, -1,  1, -1,  1, -1,  0,  1, -1,  1,  0,  0,  1, -1,  1,  1,  0,  1, -1,
+        1, -1,  1,  1, -1,  1,  0,  1,  1, -1,  1,  1,  1,  1, -1,  1, -1, -1, -1,  0,  1,  0, -1, -1,  0,  1,  1,
+        -1, -1,  0,  1, -1,  0, -1,  0,  1,  0,  0, -1,  0,  1,  1,  0, -1,  0,  1, -1,  1, -1,  0,  1,  0,  1, -1,
+        0,  1,  1,  1, -1,  0,  1, -1, -1,  0,  0,  1,  0, -1,  0,  0,  1,  1, -1,  0,  0,  1, -1,  0,  0,  0,  1,
+        0,  0,  0,  0,  1,  1,  0,  0,  0,  1, -1,  1,  0,  0,  1,  0,  1,  0,  0,  1,  1,  1,  0,  0,  1, -1, -1,
+        1,  0,  1,  0, -1,  1,  0,  1,  1, -1,  1,  0,  1, -1,  0,  1,  0,  1,  0,  0,  1,  0,  1,  1,  0,  1,  0,
+        1, -1,  1,  1,  0,  1,  0,  1,  1,  0,  1,  1,  1,  1,  0,  1, -1, -1, -1,  1,  1,  0, -1, -1,  1,  1,  1,
+        -1, -1,  1,  1, -1,  0, -1,  1,  1,  0,  0, -1,  1,  1,  1,  0, -1,  1,  1, -1,  1, -1,  1,  1,  0,  1, -1,
+        1,  1,  1,  1, -1,  1,  1, -1, -1,  0,  1,  1,  0, -1,  0,  1,  1,  1, -1,  0,  1,  1, -1,  0,  0,  1,  1,
+        0,  0,  0,  1,  1,  1,  0,  0,  1,  1, -1,  1,  0,  1,  1,  0,  1,  0,  1,  1,  1,  1,  0,  1,  1, -1, -1,
+        1,  1,  1,  0, -1,  1,  1,  1,  1, -1,  1,  1,  1, -1,  0,  1,  1,  1,  0,  0,  1,  1,  1,  1,  0,  1,  1,
+        1, -1,  1,  1,  1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1, -1, -1, -1, -1, -1,  0, -1, -1, -1, -1,  1,
+        -1, -1, -1, -1, -1,  0, -1, -1, -1,  0,  0, -1, -1, -1,  1,  0, -1, -1, -1, -1,  1, -1, -1, -1,  0,  1, -1,
+        -1, -1,  1,  1, -1, -1, -1, -1, -1,  0, -1, -1,  0, -1,  0, -1, -1,  1, -1,  0, -1, -1, -1,  0,  0, -1, -1,
+        0,  0,  0, -1, -1,  1,  0,  0, -1, -1, -1,  1,  0, -1, -1,  0,  1,  0, -1, -1,  1,  1,  0, -1, -1, -1, -1,
+        1, -1, -1,  0, -1,  1, -1, -1,  1, -1,  1, -1, -1, -1,  0,  1, -1, -1,  0,  0,  1, -1, -1,  1,  0,  1, -1,
+        -1, -1,  1,  1, -1, -1,  0,  1,  1, -1, -1,  1,  1,  1, -1, -1, -1, -1, -1,  0, -1,  0, -1, -1,  0, -1,  1,
+        -1, -1,  0, -1, -1,  0, -1,  0, -1,  0,  0, -1,  0, -1,  1,  0, -1,  0, -1, -1,  1, -1,  0, -1,  0,  1, -1,
+        0, -1,  1,  1, -1,  0, -1, -1, -1,  0,  0, -1,  0, -1,  0,  0, -1,  1, -1,  0,  0, -1, -1,  0,  0,  0, -1,
+        0,  0,  0,  0, -1,  1,  0,  0,  0, -1, -1,  1,  0,  0, -1,  0,  1,  0,  0, -1,  1,  1,  0,  0, -1, -1, -1,
+        1,  0, -1,  0, -1,  1,  0, -1,  1, -1,  1,  0, -1, -1,  0,  1,  0, -1,  0,  0,  1,  0, -1,  1,  0,  1,  0,
+        -1, -1,  1,  1,  0, -1,  0,  1,  1,  0, -1,  1,  1,  1,  0, -1, -1, -1, -1,  1, -1,  0, -1, -1,  1, -1,  1,
+        -1, -1,  1, -1, -1,  0, -1,  1, -1,  0,  0, -1,  1, -1,  1,  0, -1,  1, -1, -1,  1, -1,  1, -1,  0,  1, -1,
+        1, -1,  1,  1, -1,  1, -1, -1, -1,  0,  1, -1,  0, -1,  0,  1, -1,  1, -1,  0,  1, -1, -1,  0,  0,  1, -1,
+        0,  0,  0,  1, -1,  1,  0,  0,  1, -1, -1,  1,  0,  1, -1,  0,  1,  0,  1, -1,  1,  1,  0,  1, -1, -1, -1,
+        1,  1, -1,  0, -1,  1,  1, -1,  1, -1,  1,  1, -1, -1,  0,  1,  1, -1,  0,  0,  1,  1, -1,  1,  0,  1,  1,
+        -1, -1,  1,  1,  1, -1,  0,  1,  1,  1, -1,  1,  1,  1,  1, -1, -1, -1, -1, -1,  0,  0, -1, -1, -1,  0,  1,
+        -1, -1, -1,  0, -1,  0, -1, -1,  0,  0,  0, -1, -1,  0,  1,  0, -1, -1,  0, -1,  1, -1, -1,  0,  0,  1, -1,
+        -1,  0,  1,  1, -1, -1,  0, -1, -1,  0, -1,  0,  0, -1,  0, -1,  0,  1, -1,  0, -1,  0, -1,  0,  0, -1,  0,
+        0,  0,  0, -1,  0,  1,  0,  0, -1,  0, -1,  1,  0, -1,  0,  0,  1,  0, -1,  0,  1,  1,  0, -1,  0, -1, -1,
+        1, -1,  0,  0, -1,  1, -1,  0,  1, -1,  1, -1,  0, -1,  0,  1, -1,  0,  0,  0,  1, -1,  0,  1,  0,  1, -1,
+        0, -1,  1,  1, -1,  0,  0,  1,  1, -1,  0,  1,  1,  1, -1,  0, -1, -1, -1,  0,  0,  0, -1, -1,  0,  0,  1,
+        -1, -1,  0,  0, -1,  0, -1,  0,  0,  0,  0, -1,  0,  0,  1,  0, -1,  0,  0, -1,  1, -1,  0,  0,  0,  1, -1,
+        0,  0,  1,  1, -1,  0,  0, -1, -1,  0,  0,  0,  0, -1,  0,  0,  0,  1, -1,  0,  0,  0, -1,  0,  0,  0,  0,
+    }
+)
+
+func BytesToTrits(bytes []byte) Trits {
+    size := len(bytes)
+    trits := make([]Trit, size*NUMBER_OF_TRITS_IN_A_BYTE)
+
+    for i := 0; i < size; i++ {
+        v := int(bytes[i])
+        if int8(bytes[i]) < 0 {
+            v -= 13
+        }
+
+        for j := 0; j < NUMBER_OF_TRITS_IN_A_BYTE; j++ {
+            trits[i*NUMBER_OF_TRITS_IN_A_BYTE+j] = BYTES_TO_TRITS[v*NUMBER_OF_TRITS_IN_A_BYTE+j]
+        }
+    }
+
+    return trits
+}
+
+func TritsToString(trits Trits, offset int, size int) string {
+    var buffer bytes.Buffer
+    for i := 0; i < (size + NUMBER_OF_TRITS_IN_A_TRYTE - 1) / NUMBER_OF_TRITS_IN_A_TRYTE; i++ {
+        j := int(trits[offset + i * NUMBER_OF_TRITS_IN_A_TRYTE]) + int(trits[offset + i * NUMBER_OF_TRITS_IN_A_TRYTE + 1]) * NUMBER_OF_TRITS_IN_A_TRYTE + int(trits[offset + i * NUMBER_OF_TRITS_IN_A_TRYTE + 2]) * NUMBER_OF_TRITS_IN_A_TRYTE * NUMBER_OF_TRITS_IN_A_TRYTE;
+        if j < 0 {
+            j += len(TRYTE_ALPHABET)
+        }
+        buffer.WriteString(TRYTE_ALPHABET[j]);
+    }
+
+    return buffer.String()
+}
\ No newline at end of file
diff --git a/packages/ternary/ternary.go b/packages/ternary/ternary.go
new file mode 100644
index 0000000000000000000000000000000000000000..3261e1bf212b66c7d0978627447f4e1a58c8cae5
--- /dev/null
+++ b/packages/ternary/ternary.go
@@ -0,0 +1,65 @@
+package ternary
+
+// a Trit can have the values 0, 1 and -1
+type Trit = int8
+
+// a Trinary consists out of many Trits
+type Trits []Trit
+
+func (this Trits) ToBytes() []byte {
+    tritsLength := len(this)
+    bytesLength := (tritsLength + NUMBER_OF_TRITS_IN_A_BYTE - 1) / NUMBER_OF_TRITS_IN_A_BYTE
+
+    bytes := make([]byte, bytesLength)
+    radix := int8(3)
+
+    tritIdx := bytesLength * NUMBER_OF_TRITS_IN_A_BYTE
+    for byteNum := bytesLength - 1; byteNum >= 0; byteNum-- {
+        var value int8 = 0
+
+        for i := 0; i < NUMBER_OF_TRITS_IN_A_BYTE; i++ {
+            tritIdx--
+
+            if tritIdx < tritsLength {
+                value = value * radix + this[tritIdx]
+            }
+        }
+        bytes[byteNum] = byte(value)
+    }
+
+    return bytes
+}
+
+func (this Trits) TrailingZeroes() int {
+    zeros := 0
+    index := len(this) - 1
+    for this[index] == 0 {
+        zeros++
+
+        index--
+    }
+
+    return zeros
+}
+
+func (this Trits) ToInt64() int64 {
+    var val int64
+    for i := len(this) - 1; i >= 0; i-- {
+        val = val * 3 + int64(this[i])
+    }
+
+    return val
+}
+
+func (this Trits) ToUint64() uint64 {
+    var val uint64
+    for i := len(this) - 1; i >= 0; i-- {
+        val = val * 3 + uint64(this[i])
+    }
+
+    return val
+}
+
+func (this Trits) ToString() string {
+    return TritsToString(this, 0, len(this))
+}
\ No newline at end of file
diff --git a/packages/transaction/constants.go b/packages/transaction/constants.go
index 2320114fab0ecd7dc8cbeca2d1c8c9575df6bfd1..6d632b6077adaa0fd064a8f7f77d7056068de635 100644
--- a/packages/transaction/constants.go
+++ b/packages/transaction/constants.go
@@ -44,5 +44,5 @@ const (
     NONCE_END                               = NONCE_OFFSET + NONCE_SIZE
 
     // the full size of a transaction
-    TRANSACTION_SIZE                        = NONCE_END
+    MARSHALLED_TOTAL_SIZE = NONCE_END
 )
diff --git a/packages/transaction/transaction.go b/packages/transaction/transaction.go
index cdc0c9ea806369943b1350e9679ba57b4f4713f1..2fc31aba22768cdf840ed2b5e2a1dc48c4fb1cdc 100644
--- a/packages/transaction/transaction.go
+++ b/packages/transaction/transaction.go
@@ -51,7 +51,7 @@ func FromTrits(trits ternary.Trits, optionalHash ...ternary.Trits) *Transaction
 }
 
 func FromBytes(bytes []byte) *Transaction {
-    transaction := FromTrits(ternary.BytesToTrits(bytes)[:TRANSACTION_SIZE])
+    transaction := FromTrits(ternary.BytesToTrits(bytes)[:MARSHALLED_TOTAL_SIZE])
     transaction.Bytes = bytes
 
     return transaction
diff --git a/plugins/gossip/errors.go b/plugins/gossip/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..d097f25725c3b583ebf020b223f43d5c46e3a293
--- /dev/null
+++ b/plugins/gossip/errors.go
@@ -0,0 +1,8 @@
+package gossip
+
+import "github.com/iotaledger/goshimmer/packages/errors"
+
+var (
+    ErrInvalidAuthenticationMessage = errors.Wrap(errors.New("protocol error"), "invalid authentication message")
+    ErrInvalidStateTransition = errors.New("protocol error: invalid state transition message")
+)
diff --git a/plugins/gossip/events.go b/plugins/gossip/events.go
new file mode 100644
index 0000000000000000000000000000000000000000..0a0a5a1a173d968624b85068f50b8ccc3e52f0ec
--- /dev/null
+++ b/plugins/gossip/events.go
@@ -0,0 +1,52 @@
+package gossip
+
+import (
+    "github.com/iotaledger/goshimmer/packages/events"
+    "github.com/iotaledger/goshimmer/packages/transaction"
+)
+
+var Events = pluginEvents{
+    AddNeighbor:        events.NewEvent(errorCaller),
+    RemoveNeighbor:     events.NewEvent(errorCaller),
+    DropNeighbor:       events.NewEvent(errorCaller),
+    IncomingConnection: events.NewEvent(errorCaller),
+    ReceiveTransaction: events.NewEvent(transactionCaller),
+    Error:              events.NewEvent(errorCaller),
+}
+
+type pluginEvents struct {
+    // neighbor events
+    AddNeighbor    *events.Event
+    RemoveNeighbor *events.Event
+    DropNeighbor   *events.Event
+
+    // low level network events
+    IncomingConnection *events.Event
+
+    // high level protocol events
+    SendTransaction           *events.Event
+    SendTransactionRequest    *events.Event
+    ReceiveTransaction        *events.Event
+    ReceiveTransactionRequest *events.Event
+    ProtocolError             *events.Event
+
+    // generic events
+    Error *events.Event
+}
+
+func intCaller(handler interface{}, params ...interface{}) { handler.(func(int))(params[0].(int)) }
+
+func errorCaller(handler interface{}, params ...interface{}) { handler.(func(error))(params[0].(error)) }
+
+func transactionCaller(handler interface{}, params ...interface{}) { handler.(func(*transaction.Transaction))(params[0].(*transaction.Transaction)) }
+
+type protocolEvents struct {
+    ReceiveVersion         *events.Event
+    ReceiveIdentification  *events.Event
+    AcceptConnection       *events.Event
+    RejectConnection       *events.Event
+    DropConnection         *events.Event
+    ReceiveTransactionData *events.Event
+    ReceiveRequestData     *events.Event
+    Error                  *events.Event
+}
diff --git a/plugins/gossip/neighbormanager.go b/plugins/gossip/neighbormanager.go
new file mode 100644
index 0000000000000000000000000000000000000000..60782adb30dec476b6b9b23845493ddb828ccfe8
--- /dev/null
+++ b/plugins/gossip/neighbormanager.go
@@ -0,0 +1,34 @@
+package gossip
+
+import (
+    "github.com/iotaledger/goshimmer/packages/identity"
+    "github.com/iotaledger/goshimmer/packages/network"
+    "net"
+)
+
+const (
+    MARSHALLED_NEIGHBOR_TOTAL_SIZE = 1
+)
+
+type Neighbor struct {
+    Conn     *network.ManagedConnection
+    Identity identity.Identity
+    Address  net.IP
+    Port     uint16
+}
+
+func UnmarshalNeighbor(data []byte) (*Neighbor, error) {
+    return &Neighbor{}, nil
+}
+
+func (neighbor *Neighbor) Marshal() []byte {
+    return nil
+}
+
+func AddNeighbor() {
+
+}
+
+func GetNeighbor() {
+
+}
diff --git a/plugins/gossip/parameters.go b/plugins/gossip/parameters.go
new file mode 100644
index 0000000000000000000000000000000000000000..6e962612fb448087e69d882f5b98ad3d7657535d
--- /dev/null
+++ b/plugins/gossip/parameters.go
@@ -0,0 +1,7 @@
+package gossip
+
+import "github.com/iotaledger/goshimmer/packages/parameter"
+
+var (
+    PORT = parameter.AddInt("GOSSIP/PORT", 14666, "tcp port for gossip connection")
+)
diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go
new file mode 100644
index 0000000000000000000000000000000000000000..560c86749ee14fc1dadd70349f5e5e188d0dac65
--- /dev/null
+++ b/plugins/gossip/plugin.go
@@ -0,0 +1,13 @@
+package gossip
+
+import "github.com/iotaledger/goshimmer/packages/node"
+
+var PLUGIN = node.NewPlugin("Gossip", configure, run)
+
+func configure(plugin *node.Plugin) {
+    configureServer(plugin)
+}
+
+func run(plugin *node.Plugin) {
+    runServer(plugin)
+}
diff --git a/plugins/gossip/protocol.go b/plugins/gossip/protocol.go
new file mode 100644
index 0000000000000000000000000000000000000000..1612eced68df85885633d0552e1b4d95cb2bf179
--- /dev/null
+++ b/plugins/gossip/protocol.go
@@ -0,0 +1,68 @@
+package gossip
+
+import (
+    "github.com/iotaledger/goshimmer/packages/errors"
+    "strconv"
+)
+
+//region interfaces ////////////////////////////////////////////////////////////////////////////////////////////////////
+
+type protocolState interface {
+    Consume(protocol *protocol, data []byte, offset int, length int) (int, errors.IdentifiableError)
+}
+
+//endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region protocol /////////////////////////////////////////////////////////////////////////////////////////////////////
+
+type protocol struct {
+    neighbor     *Neighbor
+    currentState protocolState
+}
+
+func newProtocol(neighbor *Neighbor) *protocol {
+    protocol := &protocol{
+        neighbor:     neighbor,
+        currentState: &versionState{},
+    }
+
+    return protocol
+}
+
+func (protocol *protocol) parseData(data []byte) {
+    offset := 0
+    length := len(data)
+    for offset < length && protocol.currentState != nil {
+        if readBytes, err := protocol.currentState.Consume(protocol, data, offset, length); err != nil {
+            Events.Error.Trigger(err)
+
+            protocol.neighbor.Conn.Close()
+
+            return
+        } else {
+            offset += readBytes
+        }
+    }
+}
+
+// endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// region versionState //////////////////////////////////////////////////////////////////////////////////////////////////
+
+type versionState struct{}
+
+func (state *versionState) Consume(protocol *protocol, data []byte, offset int, length int) (int, errors.IdentifiableError) {
+    switch data[offset] {
+    case 1:
+        Events.ReceiveVersion.Trigger(1)
+
+        protocol.currentState = newIndentificationStateV1()
+
+        return 1, nil
+
+    default:
+        return 1, ErrInvalidStateTransition.Derive("invalid version state transition (" + strconv.Itoa(int(data[offset])) + ")")
+    }
+}
+
+// endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plugins/gossip/protocol_v1.go b/plugins/gossip/protocol_v1.go
new file mode 100644
index 0000000000000000000000000000000000000000..008b024320d7d170ea5a8abc8014a561ff061197
--- /dev/null
+++ b/plugins/gossip/protocol_v1.go
@@ -0,0 +1,166 @@
+package gossip
+
+import (
+    "github.com/iotaledger/goshimmer/packages/byteutils"
+    "github.com/iotaledger/goshimmer/packages/errors"
+    "github.com/iotaledger/goshimmer/packages/transaction"
+    "strconv"
+)
+
+//region indentificationStateV1 ////////////////////////////////////////////////////////////////////////////////////////
+
+type indentificationStateV1 struct {
+    buffer []byte
+    offset int
+}
+
+func newIndentificationStateV1() *indentificationStateV1 {
+    return &indentificationStateV1{
+        buffer: make([]byte, MARSHALLED_NEIGHBOR_TOTAL_SIZE),
+        offset: 0,
+    }
+}
+
+func (state *indentificationStateV1) Consume(protocol *protocol, 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 == MARSHALLED_NEIGHBOR_TOTAL_SIZE {
+        if unmarshalledNeighbor, err := UnmarshalNeighbor(state.buffer); err != nil {
+            return bytesRead, ErrInvalidAuthenticationMessage.Derive(err, "invalid authentication message")
+        } else {
+            protocol.neighbor.Identity = unmarshalledNeighbor.Identity
+            protocol.neighbor.Port = unmarshalledNeighbor.Port
+
+            Events.ReceiveIdentification.Trigger(protocol.neighbor)
+
+            protocol.currentState = newacceptanceStateV1()
+            state.offset = 0
+        }
+    }
+
+    return bytesRead, nil
+}
+
+//endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//region acceptanceStateV1 /////////////////////////////////////////////////////////////////////////////////////////////
+
+type acceptanceStateV1 struct {}
+
+func newacceptanceStateV1() *acceptanceStateV1 {
+    return &acceptanceStateV1{}
+}
+
+func (state *acceptanceStateV1) Consume(protocol *protocol, data []byte, offset int, length int) (int, errors.IdentifiableError) {
+    switch data[offset] {
+        case 1:
+            Events.AcceptConnection.Trigger()
+
+            protocol.currentState = newDispatchStateV1()
+        break
+
+        case 2:
+            Events.RejectConnection.Trigger()
+
+            protocol.neighbor.Conn.Close()
+            protocol.currentState = nil
+        break
+
+        default:
+            return 1, ErrInvalidStateTransition.Derive("invalid acceptance state transition (" + strconv.Itoa(int(data[offset])) + ")")
+    }
+
+    return 1, nil
+}
+
+//endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//region dispatchStateV1 ///////////////////////////////////////////////////////////////////////////////////////////////
+
+type dispatchStateV1 struct {}
+
+func newDispatchStateV1() *dispatchStateV1 {
+    return &dispatchStateV1{}
+}
+
+func (state *dispatchStateV1) Consume(protocol *protocol, data []byte, offset int, length int) (int, errors.IdentifiableError) {
+    switch data[0] {
+        case 0:
+            Events.RejectConnection.Trigger()
+
+            protocol.neighbor.Conn.Close()
+            protocol.currentState = nil
+
+        case 1:
+            protocol.currentState = newTransactionStateV1()
+        break
+
+        case 2:
+            protocol.currentState = newRequestStateV1()
+        break
+
+        default:
+            return 1, ErrInvalidStateTransition.Derive("invalid dispatch state transition (" + strconv.Itoa(int(data[offset])) + ")")
+    }
+    return 1, nil
+}
+
+//endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//region transactionStateV1 ////////////////////////////////////////////////////////////////////////////////////////////
+
+type transactionStateV1 struct {
+    buffer []byte
+    offset int
+}
+
+func newTransactionStateV1() *transactionStateV1 {
+    return &transactionStateV1{
+        buffer: make([]byte, transaction.MARSHALLED_TOTAL_SIZE),
+        offset: 0,
+    }
+}
+
+func (state *transactionStateV1) Consume(protocol *protocol, 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 == transaction.MARSHALLED_TOTAL_SIZE {
+        transactionData := make([]byte, transaction.MARSHALLED_TOTAL_SIZE)
+        copy(transactionData, state.buffer)
+
+        Events.ReceiveTransactionData.Trigger(transactionData)
+
+        go func() {
+            Events.ReceiveTransaction.Trigger(transaction.FromBytes(transactionData))
+        }()
+
+        protocol.currentState = newDispatchStateV1()
+        state.offset = 0
+    }
+
+    return bytesRead, nil
+}
+
+//endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//region requestStateV1 ////////////////////////////////////////////////////////////////////////////////////////////////
+
+type requestStateV1 struct {
+    buffer []byte
+    offset int
+}
+
+func newRequestStateV1() *requestStateV1 {
+    return &requestStateV1{
+        buffer: make([]byte, 1),
+        offset: 0,
+    }
+}
+
+func (state *requestStateV1) Consume(protocol *protocol, data []byte, offset int, length int) (int, errors.IdentifiableError) {
+    return 0, nil
+}
+
+//endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/plugins/gossip/protocol_v1.png b/plugins/gossip/protocol_v1.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5c361b612b7bc7644c4e65829c63fa79547efb2
Binary files /dev/null and b/plugins/gossip/protocol_v1.png differ
diff --git a/plugins/gossip/server.go b/plugins/gossip/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..76bd245aa7b75353a50d1096d10abb3ded65fe46
--- /dev/null
+++ b/plugins/gossip/server.go
@@ -0,0 +1,56 @@
+package gossip
+
+import (
+    "github.com/iotaledger/goshimmer/packages/daemon"
+    "github.com/iotaledger/goshimmer/packages/events"
+    "github.com/iotaledger/goshimmer/packages/network"
+    "github.com/iotaledger/goshimmer/packages/network/tcp"
+    "github.com/iotaledger/goshimmer/packages/node"
+    "net"
+    "strconv"
+)
+
+var TCPServer = tcp.NewServer()
+
+func configureServer(plugin *node.Plugin) {
+    TCPServer.Events.Connect.Attach(events.NewClosure(func(conn *network.ManagedConnection) {
+        neighbor := &Neighbor{
+            Address: conn.RemoteAddr().(*net.TCPAddr).IP,
+        }
+
+        protocol := newProtocol(neighbor)
+
+        var onClose, onReceiveData *events.Closure
+
+        onReceiveData = events.NewClosure(func(data []byte) {
+            protocol.parseData(data)
+        })
+        onClose = events.NewClosure(func() {
+            conn.Events.ReceiveData.Detach(onReceiveData)
+            conn.Events.Close.Detach(onClose)
+        })
+
+        conn.Events.ReceiveData.Attach(onReceiveData)
+        conn.Events.Close.Attach(onClose)
+
+        go conn.Read(make([]byte, 1000))
+    }))
+
+    daemon.Events.Shutdown.Attach(events.NewClosure(func() {
+        plugin.LogInfo("Stopping TCP Server ...")
+
+        TCPServer.Shutdown()
+    }))
+}
+
+func runServer(plugin *node.Plugin) {
+    plugin.LogInfo("Starting TCP Server (port " + strconv.Itoa(*PORT.Value) + ") ...")
+
+    daemon.BackgroundWorker(func() {
+        plugin.LogSuccess("Starting TCP Server (port " + strconv.Itoa(*PORT.Value) + ") ... done")
+
+        TCPServer.Listen(*PORT.Value)
+
+        plugin.LogSuccess("Stopping TCP Server ... done")
+    })
+}