From 9258f54adceb28664a6a47ec573b4a60b76b65b0 Mon Sep 17 00:00:00 2001
From: Hans Moog <hm@mkjc.net>
Date: Thu, 9 May 2019 02:59:18 +0200
Subject: [PATCH] Feat: derivable errors + beginning of gossip

---
 main.go                                      |   2 +
 packages/byteutils/byteutils.go              |  17 +
 packages/curl/batch_hasher.go                | 103 ++++++
 packages/curl/bct_curl.go                    | 116 ++++++
 packages/curl/curl.go                        | 104 ++++++
 packages/curl/curlp81.go                     |  10 +
 packages/errors/errors.go                    | 365 +++++++++++++++++++
 packages/errors/stack.go                     | 177 +++++++++
 packages/ternary/bc_ternary.go               |  13 +
 packages/ternary/bc_ternary_demultiplexer.go |  37 ++
 packages/ternary/bc_ternary_multiplexer.go   |  62 ++++
 packages/ternary/conversion.go               |  95 +++++
 packages/ternary/ternary.go                  |  65 ++++
 packages/transaction/constants.go            |   2 +-
 packages/transaction/transaction.go          |   2 +-
 plugins/gossip/errors.go                     |   8 +
 plugins/gossip/events.go                     |  52 +++
 plugins/gossip/neighbormanager.go            |  34 ++
 plugins/gossip/parameters.go                 |   7 +
 plugins/gossip/plugin.go                     |  13 +
 plugins/gossip/protocol.go                   |  68 ++++
 plugins/gossip/protocol_v1.go                | 166 +++++++++
 plugins/gossip/protocol_v1.png               | Bin 0 -> 22672 bytes
 plugins/gossip/server.go                     |  56 +++
 24 files changed, 1572 insertions(+), 2 deletions(-)
 create mode 100644 packages/byteutils/byteutils.go
 create mode 100644 packages/curl/batch_hasher.go
 create mode 100644 packages/curl/bct_curl.go
 create mode 100644 packages/curl/curl.go
 create mode 100644 packages/curl/curlp81.go
 create mode 100644 packages/errors/errors.go
 create mode 100644 packages/errors/stack.go
 create mode 100644 packages/ternary/bc_ternary.go
 create mode 100644 packages/ternary/bc_ternary_demultiplexer.go
 create mode 100644 packages/ternary/bc_ternary_multiplexer.go
 create mode 100644 packages/ternary/conversion.go
 create mode 100644 packages/ternary/ternary.go
 create mode 100644 plugins/gossip/errors.go
 create mode 100644 plugins/gossip/events.go
 create mode 100644 plugins/gossip/neighbormanager.go
 create mode 100644 plugins/gossip/parameters.go
 create mode 100644 plugins/gossip/plugin.go
 create mode 100644 plugins/gossip/protocol.go
 create mode 100644 plugins/gossip/protocol_v1.go
 create mode 100644 plugins/gossip/protocol_v1.png
 create mode 100644 plugins/gossip/server.go

diff --git a/main.go b/main.go
index c58b8fc2..9d80fd89 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 00000000..ccf1b5f5
--- /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 00000000..f379583e
--- /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 00000000..5fef9445
--- /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 00000000..6ef01ac8
--- /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 00000000..7e6ed0b3
--- /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 00000000..6ca17ec9
--- /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 00000000..54b202a1
--- /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 00000000..75843830
--- /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 00000000..02221f6e
--- /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 00000000..93a0eb57
--- /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 00000000..ac65f1ea
--- /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 00000000..3261e1bf
--- /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 2320114f..6d632b60 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 cdc0c9ea..2fc31aba 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 00000000..d097f257
--- /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 00000000..0a0a5a1a
--- /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 00000000..60782adb
--- /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 00000000..6e962612
--- /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 00000000..560c8674
--- /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 00000000..1612eced
--- /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 00000000..008b0243
--- /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
GIT binary patch
literal 22672
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
Oz~JfX=d#Wzp$PzX`iDdS

literal 0
HcmV?d00001

diff --git a/plugins/gossip/server.go b/plugins/gossip/server.go
new file mode 100644
index 00000000..76bd245a
--- /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")
+    })
+}
-- 
GitLab