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") + }) +}