diff --git a/packages/ca/constants.go b/packages/ca/constants.go new file mode 100644 index 0000000000000000000000000000000000000000..c770ef641cec595eae18347ca3a82ff04c46583a --- /dev/null +++ b/packages/ca/constants.go @@ -0,0 +1,5 @@ +package ca + +const ( + MAX_STATEMENT_TIMEOUT = uint64(5000) +) diff --git a/packages/ca/errors.go b/packages/ca/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..2fbec995bde3bf9e63639a6ad28a7029b9b30d55 --- /dev/null +++ b/packages/ca/errors.go @@ -0,0 +1,11 @@ +package ca + +import ( + "github.com/iotaledger/goshimmer/packages/errors" +) + +var ( + ErrMalformedHeartbeat = errors.New("malformed heartbeat") + ErrUnknownNeighbor = errors.New("unknown neighbor") + ErrTooManyNeighbors = errors.New("too many neighbors") +) diff --git a/packages/ca/heartbeat/heartbeat.go b/packages/ca/heartbeat/heartbeat.go new file mode 100644 index 0000000000000000000000000000000000000000..6087afd891c0c00ad61cf71c9d03668ec2e68e6e --- /dev/null +++ b/packages/ca/heartbeat/heartbeat.go @@ -0,0 +1,128 @@ +package heartbeat + +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/errors" + "github.com/iotaledger/goshimmer/packages/marshaling" + + "github.com/golang/protobuf/proto" + heartbeatProto "github.com/iotaledger/goshimmer/packages/ca/heartbeat/proto" +) + +type Heartbeat struct { + nodeId string + mainStatement *OpinionStatement + neighborStatements map[string]*OpinionStatement + signature []byte + + nodeIdMutex sync.RWMutex + mainStatementMutex sync.RWMutex + neighborStatementsMutex sync.RWMutex + signatureMutex sync.RWMutex +} + +func NewHeartbeat() *Heartbeat { + return &Heartbeat{} +} + +func (heartbeat *Heartbeat) GetNodeId() string { + heartbeat.nodeIdMutex.RLock() + defer heartbeat.nodeIdMutex.RLock() + + return heartbeat.nodeId +} + +func (heartbeat *Heartbeat) SetNodeId(nodeId string) { + heartbeat.nodeIdMutex.Lock() + defer heartbeat.nodeIdMutex.Unlock() + + heartbeat.nodeId = nodeId +} + +func (heartbeat *Heartbeat) GetMainStatement() *OpinionStatement { + heartbeat.mainStatementMutex.RLock() + defer heartbeat.mainStatementMutex.RUnlock() + + return heartbeat.mainStatement +} + +func (heartbeat *Heartbeat) SetMainStatement(mainStatement *OpinionStatement) { + heartbeat.mainStatementMutex.Lock() + defer heartbeat.mainStatementMutex.Unlock() + + heartbeat.mainStatement = mainStatement +} + +func (heartbeat *Heartbeat) GetNeighborStatements() map[string]*OpinionStatement { + heartbeat.neighborStatementsMutex.RLock() + defer heartbeat.neighborStatementsMutex.RUnlock() + + return heartbeat.neighborStatements +} + +func (heartbeat *Heartbeat) SetNeighborStatements(neighborStatements map[string]*OpinionStatement) { + heartbeat.neighborStatementsMutex.Lock() + defer heartbeat.neighborStatementsMutex.Unlock() + + heartbeat.neighborStatements = neighborStatements +} + +func (heartbeat *Heartbeat) GetSignature() []byte { + heartbeat.signatureMutex.RLock() + defer heartbeat.signatureMutex.RUnlock() + + return heartbeat.signature +} + +func (heartbeat *Heartbeat) SetSignature(signature []byte) { + heartbeat.signatureMutex.Lock() + defer heartbeat.signatureMutex.Unlock() + + heartbeat.signature = signature +} + +func (heartbeat *Heartbeat) FromProto(proto proto.Message) { + protoHeartbeat := proto.(*heartbeatProto.HeartBeat) + + var mainStatement OpinionStatement + mainStatement.FromProto(protoHeartbeat.MainStatement) + + neighborStatements := make(map[string]*OpinionStatement, len(protoHeartbeat.NeighborStatements)) + for _, neighborStatement := range protoHeartbeat.NeighborStatements { + var newNeighborStatement OpinionStatement + newNeighborStatement.FromProto(neighborStatement) + + neighborStatements[neighborStatement.NodeId] = &newNeighborStatement + } + + heartbeat.nodeId = protoHeartbeat.NodeId + heartbeat.mainStatement = &mainStatement + heartbeat.neighborStatements = neighborStatements + heartbeat.signature = protoHeartbeat.Signature +} + +func (heartbeat *Heartbeat) ToProto() proto.Message { + neighborStatements := make([]*heartbeatProto.OpinionStatement, len(heartbeat.neighborStatements)) + i := 0 + for _, neighborStatement := range heartbeat.neighborStatements { + neighborStatements[i] = neighborStatement.ToProto().(*heartbeatProto.OpinionStatement) + + i++ + } + + return &heartbeatProto.HeartBeat{ + NodeId: heartbeat.nodeId, + MainStatement: heartbeat.mainStatement.ToProto().(*heartbeatProto.OpinionStatement), + NeighborStatements: neighborStatements, + Signature: heartbeat.signature, + } +} + +func (heartbeat *Heartbeat) MarshalBinary() ([]byte, errors.IdentifiableError) { + return marshaling.Marshal(heartbeat) +} + +func (heartbeat *Heartbeat) UnmarshalBinary(data []byte) (err errors.IdentifiableError) { + return marshaling.Unmarshal(heartbeat, data, &heartbeatProto.HeartBeat{}) +} diff --git a/packages/ca/heartbeat/opinion_statement.go b/packages/ca/heartbeat/opinion_statement.go new file mode 100644 index 0000000000000000000000000000000000000000..8071114d54ffc3b40ffcaf9f031afed4addd433d --- /dev/null +++ b/packages/ca/heartbeat/opinion_statement.go @@ -0,0 +1,167 @@ +package heartbeat + +import ( + "encoding/hex" + "strconv" + "sync" + + "github.com/iotaledger/goshimmer/packages/errors" + "github.com/iotaledger/goshimmer/packages/marshaling" + + "github.com/golang/protobuf/proto" + heartbeatProto "github.com/iotaledger/goshimmer/packages/ca/heartbeat/proto" +) + +type OpinionStatement struct { + previousStatementHash []byte + nodeId string + time uint64 + toggledTransactions []*ToggledTransaction + signature []byte + hash []byte + + previousStatementHashMutex sync.RWMutex + nodeIdMutex sync.RWMutex + timeMutex sync.RWMutex + toggledTransactionsMutex sync.RWMutex + signatureMutex sync.RWMutex + hashMutex sync.RWMutex +} + +func NewOpinionStatement() *OpinionStatement { + return &OpinionStatement{} +} + +func (opinionStatement *OpinionStatement) GetPreviousStatementHash() []byte { + opinionStatement.previousStatementHashMutex.RLock() + defer opinionStatement.previousStatementHashMutex.RUnlock() + + return opinionStatement.previousStatementHash +} + +func (opinionStatement *OpinionStatement) SetPreviousStatementHash(previousStatementHash []byte) { + opinionStatement.previousStatementHashMutex.Lock() + defer opinionStatement.previousStatementHashMutex.Unlock() + + opinionStatement.previousStatementHash = previousStatementHash +} + +func (opinionStatement *OpinionStatement) GetNodeId() string { + opinionStatement.nodeIdMutex.RLock() + defer opinionStatement.nodeIdMutex.RUnlock() + + return opinionStatement.nodeId +} + +func (opinionStatement *OpinionStatement) SetNodeId(nodeId string) { + opinionStatement.nodeIdMutex.Lock() + defer opinionStatement.nodeIdMutex.Unlock() + + opinionStatement.nodeId = nodeId +} + +func (opinionStatement *OpinionStatement) GetTime() uint64 { + opinionStatement.timeMutex.RLock() + defer opinionStatement.timeMutex.RUnlock() + + return opinionStatement.time +} + +func (opinionStatement *OpinionStatement) SetTime(time uint64) { + opinionStatement.timeMutex.Lock() + defer opinionStatement.timeMutex.Unlock() + + opinionStatement.time = time +} + +func (opinionStatement *OpinionStatement) GetToggledTransactions() []*ToggledTransaction { + opinionStatement.toggledTransactionsMutex.RLock() + defer opinionStatement.toggledTransactionsMutex.RUnlock() + + return opinionStatement.toggledTransactions +} + +func (opinionStatement *OpinionStatement) SetToggledTransactions(toggledTransactions []*ToggledTransaction) { + opinionStatement.toggledTransactionsMutex.Lock() + defer opinionStatement.toggledTransactionsMutex.Unlock() + + opinionStatement.toggledTransactions = toggledTransactions +} + +func (opinionStatement *OpinionStatement) GetSignature() []byte { + opinionStatement.signatureMutex.RLock() + defer opinionStatement.signatureMutex.RUnlock() + + return opinionStatement.signature +} + +func (opinionStatement *OpinionStatement) SetSignature(signature []byte) { + opinionStatement.signatureMutex.Lock() + defer opinionStatement.signatureMutex.Unlock() + + opinionStatement.signature = signature +} + +func (opinionStatement *OpinionStatement) GetHash() []byte { + opinionStatement.hashMutex.RLock() + defer opinionStatement.hashMutex.RUnlock() + + return opinionStatement.hash +} + +func (opinionStatement *OpinionStatement) SetHash(hash []byte) { + opinionStatement.hashMutex.Lock() + defer opinionStatement.hashMutex.Unlock() + + opinionStatement.hash = hash +} + +func (opinionStatement *OpinionStatement) FromProto(proto proto.Message) { + protoOpinionStatement := proto.(*heartbeatProto.OpinionStatement) + + opinionStatement.previousStatementHash = protoOpinionStatement.PreviousStatementHash + opinionStatement.nodeId = protoOpinionStatement.NodeId + opinionStatement.time = protoOpinionStatement.Time + opinionStatement.signature = protoOpinionStatement.Signature + + opinionStatement.toggledTransactions = make([]*ToggledTransaction, len(protoOpinionStatement.ToggledTransactions)) + for i, toggledTransaction := range protoOpinionStatement.ToggledTransactions { + var newToggledTransaction ToggledTransaction + newToggledTransaction.FromProto(toggledTransaction) + + opinionStatement.toggledTransactions[i] = &newToggledTransaction + } +} + +func (opinionStatement *OpinionStatement) ToProto() proto.Message { + toggledTransactions := make([]*heartbeatProto.ToggledTransaction, len(opinionStatement.toggledTransactions)) + for i, toggledTransaction := range opinionStatement.toggledTransactions { + toggledTransactions[i] = toggledTransaction.ToProto().(*heartbeatProto.ToggledTransaction) + } + + return &heartbeatProto.OpinionStatement{ + PreviousStatementHash: opinionStatement.previousStatementHash, + NodeId: opinionStatement.nodeId, + Time: opinionStatement.time, + ToggledTransactions: toggledTransactions, + Signature: opinionStatement.signature, + } +} + +func (opinionStatement *OpinionStatement) MarshalBinary() ([]byte, errors.IdentifiableError) { + return marshaling.Marshal(opinionStatement) +} + +func (opinionStatement *OpinionStatement) UnmarshalBinary(data []byte) (err errors.IdentifiableError) { + return marshaling.Unmarshal(opinionStatement, data, &heartbeatProto.OpinionStatement{}) +} + +func (opinionStatement *OpinionStatement) String() string { + return "OpinionStatement {\n" + + " previousStatementHash: 0x" + hex.EncodeToString(opinionStatement.previousStatementHash) + "\n" + + " nodeId: " + opinionStatement.nodeId + "\n" + + " time: " + strconv.Itoa(int(opinionStatement.time)) + "\n" + + " toggledTransactions: [" + "" + "]\n" + + " signature: 0x" + hex.EncodeToString(opinionStatement.signature) + "\n" + + "}" +} diff --git a/packages/ca/heartbeat/proto/heartbeat.pb.go b/packages/ca/heartbeat/proto/heartbeat.pb.go index c18bcd3efed1de5edfddbe1af3a0fdee83f45e33..ee8c13d28c54d29dd3590b7b9f489c0d527deda6 100644 --- a/packages/ca/heartbeat/proto/heartbeat.pb.go +++ b/packages/ca/heartbeat/proto/heartbeat.pb.go @@ -21,8 +21,8 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type HeartBeat struct { - NodeId []byte `protobuf:"bytes,1,opt,name=nodeId,proto3" json:"nodeId,omitempty"` - OwnStatement *OpinionStatement `protobuf:"bytes,2,opt,name=ownStatement,proto3" json:"ownStatement,omitempty"` + NodeId string `protobuf:"bytes,1,opt,name=nodeId,proto3" json:"nodeId,omitempty"` + MainStatement *OpinionStatement `protobuf:"bytes,2,opt,name=mainStatement,proto3" json:"mainStatement,omitempty"` NeighborStatements []*OpinionStatement `protobuf:"bytes,3,rep,name=neighborStatements,proto3" json:"neighborStatements,omitempty"` Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -55,16 +55,16 @@ func (m *HeartBeat) XXX_DiscardUnknown() { var xxx_messageInfo_HeartBeat proto.InternalMessageInfo -func (m *HeartBeat) GetNodeId() []byte { +func (m *HeartBeat) GetNodeId() string { if m != nil { return m.NodeId } - return nil + return "" } -func (m *HeartBeat) GetOwnStatement() *OpinionStatement { +func (m *HeartBeat) GetMainStatement() *OpinionStatement { if m != nil { - return m.OwnStatement + return m.MainStatement } return nil } @@ -90,16 +90,16 @@ func init() { func init() { proto.RegisterFile("heartbeat.proto", fileDescriptor_3c667767fb9826a9) } var fileDescriptor_3c667767fb9826a9 = []byte{ - // 172 bytes of a gzipped FileDescriptorProto + // 176 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcf, 0x48, 0x4d, 0x2c, 0x2a, 0x49, 0x4a, 0x4d, 0x2c, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x52, 0xe2, 0xf9, 0x05, 0x99, 0x79, 0x99, 0xf9, 0x79, 0xf1, 0xc5, 0x25, 0x89, 0x25, 0xa9, 0xb9, 0xa9, - 0x79, 0x50, 0x79, 0xa5, 0xe3, 0x8c, 0x5c, 0x9c, 0x1e, 0x20, 0x3d, 0x4e, 0xa9, 0x89, 0x25, 0x42, - 0x62, 0x5c, 0x6c, 0x79, 0xf9, 0x29, 0xa9, 0x9e, 0x29, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, - 0x50, 0x9e, 0x90, 0x35, 0x17, 0x4f, 0x7e, 0x79, 0x5e, 0x30, 0x4c, 0xaf, 0x04, 0x93, 0x02, 0xa3, - 0x06, 0xb7, 0x91, 0x38, 0xc4, 0x0c, 0x3d, 0x7f, 0x88, 0xd9, 0x70, 0xe9, 0x20, 0x14, 0xc5, 0x42, - 0xee, 0x5c, 0x42, 0x79, 0xa9, 0x99, 0xe9, 0x19, 0x49, 0xf9, 0x45, 0x70, 0xc1, 0x62, 0x09, 0x66, - 0x05, 0x66, 0x7c, 0x46, 0x60, 0xd1, 0x22, 0x24, 0xc3, 0xc5, 0x59, 0x9c, 0x99, 0x9e, 0x97, 0x58, - 0x52, 0x5a, 0x94, 0x2a, 0xc1, 0x02, 0x76, 0x20, 0x42, 0x20, 0x89, 0x0d, 0x6c, 0x92, 0x31, 0x20, - 0x00, 0x00, 0xff, 0xff, 0x53, 0x6b, 0x3e, 0x03, 0x03, 0x01, 0x00, 0x00, + 0x79, 0x50, 0x79, 0xa5, 0x93, 0x8c, 0x5c, 0x9c, 0x1e, 0x20, 0x3d, 0x4e, 0xa9, 0x89, 0x25, 0x42, + 0x62, 0x5c, 0x6c, 0x79, 0xf9, 0x29, 0xa9, 0x9e, 0x29, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, + 0x50, 0x9e, 0x90, 0x2d, 0x17, 0x6f, 0x6e, 0x62, 0x66, 0x5e, 0x30, 0x4c, 0xb3, 0x04, 0x93, 0x02, + 0xa3, 0x06, 0xb7, 0x91, 0x38, 0xc4, 0x10, 0x3d, 0x7f, 0x88, 0xe1, 0x70, 0xe9, 0x20, 0x54, 0xd5, + 0x42, 0xee, 0x5c, 0x42, 0x79, 0xa9, 0x99, 0xe9, 0x19, 0x49, 0xf9, 0x45, 0x70, 0xc1, 0x62, 0x09, + 0x66, 0x05, 0x66, 0x7c, 0x66, 0x60, 0xd1, 0x22, 0x24, 0xc3, 0xc5, 0x59, 0x9c, 0x99, 0x9e, 0x97, + 0x58, 0x52, 0x5a, 0x94, 0x2a, 0xc1, 0xa2, 0xc0, 0xa8, 0xc1, 0x13, 0x84, 0x10, 0x48, 0x62, 0x03, + 0x9b, 0x64, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xde, 0x82, 0x59, 0xb6, 0x05, 0x01, 0x00, 0x00, } diff --git a/packages/ca/heartbeat/proto/heartbeat.proto b/packages/ca/heartbeat/proto/heartbeat.proto index 74c49f3362c4521ab3cc00d7ece9977dfb7ae136..f5a63eb2e9d68094c3c73f70181ef08acc8f4e8e 100644 --- a/packages/ca/heartbeat/proto/heartbeat.proto +++ b/packages/ca/heartbeat/proto/heartbeat.proto @@ -5,8 +5,8 @@ import "opinion_statement.proto"; package proto; message HeartBeat { - bytes nodeId = 1; - OpinionStatement ownStatement = 2; + string nodeId = 1; + OpinionStatement mainStatement = 2; repeated OpinionStatement neighborStatements = 3; bytes signature = 4; } diff --git a/packages/ca/heartbeat/proto/opinion_statement.pb.go b/packages/ca/heartbeat/proto/opinion_statement.pb.go index 1b71fec0ab3cbf2ef1304504599995fa4ec91076..539aaa09bd39ff03e5eba161f14cfabab7f24c22 100644 --- a/packages/ca/heartbeat/proto/opinion_statement.pb.go +++ b/packages/ca/heartbeat/proto/opinion_statement.pb.go @@ -21,13 +21,14 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type OpinionStatement struct { - NodeId []byte `protobuf:"bytes,1,opt,name=nodeId,proto3" json:"nodeId,omitempty"` - Time uint64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"` - ToggledTransactions []*ToggledTransaction `protobuf:"bytes,3,rep,name=toggledTransactions,proto3" json:"toggledTransactions,omitempty"` - Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + PreviousStatementHash []byte `protobuf:"bytes,1,opt,name=previousStatementHash,proto3" json:"previousStatementHash,omitempty"` + NodeId string `protobuf:"bytes,2,opt,name=nodeId,proto3" json:"nodeId,omitempty"` + Time uint64 `protobuf:"varint,3,opt,name=time,proto3" json:"time,omitempty"` + ToggledTransactions []*ToggledTransaction `protobuf:"bytes,4,rep,name=toggledTransactions,proto3" json:"toggledTransactions,omitempty"` + Signature []byte `protobuf:"bytes,5,opt,name=signature,proto3" json:"signature,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *OpinionStatement) Reset() { *m = OpinionStatement{} } @@ -55,13 +56,20 @@ func (m *OpinionStatement) XXX_DiscardUnknown() { var xxx_messageInfo_OpinionStatement proto.InternalMessageInfo -func (m *OpinionStatement) GetNodeId() []byte { +func (m *OpinionStatement) GetPreviousStatementHash() []byte { if m != nil { - return m.NodeId + return m.PreviousStatementHash } return nil } +func (m *OpinionStatement) GetNodeId() string { + if m != nil { + return m.NodeId + } + return "" +} + func (m *OpinionStatement) GetTime() uint64 { if m != nil { return m.Time @@ -90,16 +98,18 @@ func init() { func init() { proto.RegisterFile("opinion_statement.proto", fileDescriptor_a5a0e519e658baf5) } var fileDescriptor_a5a0e519e658baf5 = []byte{ - // 174 bytes of a gzipped FileDescriptorProto + // 202 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcf, 0x2f, 0xc8, 0xcc, 0xcb, 0xcc, 0xcf, 0x8b, 0x2f, 0x2e, 0x49, 0x2c, 0x49, 0xcd, 0x4d, 0xcd, 0x2b, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x52, 0x92, 0x25, 0xf9, 0xe9, 0xe9, 0x39, 0xa9, 0x29, - 0xf1, 0x25, 0x45, 0x89, 0x79, 0xc5, 0x89, 0xc9, 0x25, 0x99, 0xf9, 0x79, 0x10, 0x15, 0x4a, 0x2b, - 0x19, 0xb9, 0x04, 0xfc, 0x21, 0xba, 0x83, 0x61, 0x9a, 0x85, 0xc4, 0xb8, 0xd8, 0xf2, 0xf2, 0x53, - 0x52, 0x3d, 0x53, 0x24, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0xa0, 0x3c, 0x21, 0x21, 0x2e, 0x96, - 0x92, 0xcc, 0xdc, 0x54, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x96, 0x20, 0x30, 0x5b, 0xc8, 0x9b, 0x4b, - 0x18, 0x6a, 0x7a, 0x08, 0xc2, 0xf0, 0x62, 0x09, 0x66, 0x05, 0x66, 0x0d, 0x6e, 0x23, 0x49, 0x88, - 0x2d, 0x7a, 0x21, 0x18, 0x2a, 0x82, 0xb0, 0xe9, 0x12, 0x92, 0xe1, 0xe2, 0x2c, 0xce, 0x4c, 0xcf, - 0x4b, 0x2c, 0x29, 0x2d, 0x4a, 0x95, 0x60, 0x01, 0xdb, 0x8d, 0x10, 0x48, 0x62, 0x03, 0x1b, 0x66, - 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x22, 0xe9, 0x1c, 0x03, 0xef, 0x00, 0x00, 0x00, + 0xf1, 0x25, 0x45, 0x89, 0x79, 0xc5, 0x89, 0xc9, 0x25, 0x99, 0xf9, 0x79, 0x10, 0x15, 0x4a, 0xf7, + 0x19, 0xb9, 0x04, 0xfc, 0x21, 0xba, 0x83, 0x61, 0x9a, 0x85, 0x4c, 0xb8, 0x44, 0x0b, 0x8a, 0x52, + 0xcb, 0x32, 0xf3, 0x4b, 0x8b, 0xe1, 0x82, 0x1e, 0x89, 0xc5, 0x19, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, + 0x3c, 0x41, 0xd8, 0x25, 0x85, 0xc4, 0xb8, 0xd8, 0xf2, 0xf2, 0x53, 0x52, 0x3d, 0x53, 0x24, 0x98, + 0x14, 0x18, 0x35, 0x38, 0x83, 0xa0, 0x3c, 0x21, 0x21, 0x2e, 0x96, 0x92, 0xcc, 0xdc, 0x54, 0x09, + 0x66, 0x05, 0x46, 0x0d, 0x96, 0x20, 0x30, 0x5b, 0xc8, 0x9b, 0x4b, 0x18, 0xea, 0xa6, 0x10, 0x84, + 0x93, 0x8a, 0x25, 0x58, 0x14, 0x98, 0x35, 0xb8, 0x8d, 0x24, 0x21, 0x6e, 0xd3, 0x0b, 0xc1, 0x50, + 0x11, 0x84, 0x4d, 0x97, 0x90, 0x0c, 0x17, 0x67, 0x71, 0x66, 0x7a, 0x5e, 0x62, 0x49, 0x69, 0x51, + 0xaa, 0x04, 0x2b, 0xd8, 0x89, 0x08, 0x81, 0x24, 0x36, 0xb0, 0x61, 0xc6, 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xc0, 0x51, 0x41, 0x73, 0x25, 0x01, 0x00, 0x00, } diff --git a/packages/ca/heartbeat/proto/opinion_statement.proto b/packages/ca/heartbeat/proto/opinion_statement.proto index c3d24b70ba6c0047064f549b22e3c46d3f23211a..8f00edcb5559932c959f430a21af91a48d8f90a2 100644 --- a/packages/ca/heartbeat/proto/opinion_statement.proto +++ b/packages/ca/heartbeat/proto/opinion_statement.proto @@ -5,8 +5,9 @@ import "toggled_transaction.proto"; package proto; message OpinionStatement { - bytes nodeId = 1; - uint64 time = 2; - repeated ToggledTransaction toggledTransactions = 3; - bytes signature = 4; -}; \ No newline at end of file + bytes previousStatementHash = 1; + string nodeId = 2; + uint64 time = 3; + repeated ToggledTransaction toggledTransactions = 4; + bytes signature = 5; +} \ No newline at end of file diff --git a/packages/ca/heartbeat/proto/toggle_type.pb.go b/packages/ca/heartbeat/proto/toggle_type.pb.go deleted file mode 100644 index bb9e8534b0705caeebc1d5514b2e71d1605ba1c0..0000000000000000000000000000000000000000 --- a/packages/ca/heartbeat/proto/toggle_type.pb.go +++ /dev/null @@ -1,62 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: toggle_type.proto - -package proto - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type ToggleType int32 - -const ( - ToggleType_Like ToggleType = 0 - ToggleType_Dislike ToggleType = 1 -) - -var ToggleType_name = map[int32]string{ - 0: "Like", - 1: "Dislike", -} - -var ToggleType_value = map[string]int32{ - "Like": 0, - "Dislike": 1, -} - -func (x ToggleType) String() string { - return proto.EnumName(ToggleType_name, int32(x)) -} - -func (ToggleType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_f8d4c81885a98bf0, []int{0} -} - -func init() { - proto.RegisterEnum("proto.ToggleType", ToggleType_name, ToggleType_value) -} - -func init() { proto.RegisterFile("toggle_type.proto", fileDescriptor_f8d4c81885a98bf0) } - -var fileDescriptor_f8d4c81885a98bf0 = []byte{ - // 83 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2c, 0xc9, 0x4f, 0x4f, - 0xcf, 0x49, 0x8d, 0x2f, 0xa9, 0x2c, 0x48, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, - 0x53, 0x5a, 0xca, 0x5c, 0x5c, 0x21, 0x60, 0xb9, 0x90, 0xca, 0x82, 0x54, 0x21, 0x0e, 0x2e, 0x16, - 0x9f, 0xcc, 0xec, 0x54, 0x01, 0x06, 0x21, 0x6e, 0x2e, 0x76, 0x97, 0xcc, 0xe2, 0x1c, 0x10, 0x87, - 0x31, 0x89, 0x0d, 0xac, 0xd6, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xad, 0x5e, 0x8a, 0xc7, 0x47, - 0x00, 0x00, 0x00, -} diff --git a/packages/ca/heartbeat/proto/toggle_type.proto b/packages/ca/heartbeat/proto/toggle_type.proto deleted file mode 100644 index 51435c992de7a03703ea9354f78ae006e76017ba..0000000000000000000000000000000000000000 --- a/packages/ca/heartbeat/proto/toggle_type.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto3"; - -package proto; - -enum ToggleType { - Like = 0; - Dislike = 1; -} \ No newline at end of file diff --git a/packages/ca/heartbeat/proto/toggled_transaction.pb.go b/packages/ca/heartbeat/proto/toggled_transaction.pb.go index 593cbeaed69ca5c6e61c24fdb3034d517990b611..2261bd8a44c6780fb3da0592faef8b77a83081da 100644 --- a/packages/ca/heartbeat/proto/toggled_transaction.pb.go +++ b/packages/ca/heartbeat/proto/toggled_transaction.pb.go @@ -21,11 +21,12 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type ToggledTransaction struct { - TransactionId []byte `protobuf:"bytes,1,opt,name=transactionId,proto3" json:"transactionId,omitempty"` - ToggleReason ToggleType `protobuf:"varint,2,opt,name=toggleReason,proto3,enum=proto.ToggleType" json:"toggleReason,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + TransactionId []byte `protobuf:"bytes,1,opt,name=transactionId,proto3" json:"transactionId,omitempty"` + InitialStatement bool `protobuf:"varint,2,opt,name=initialStatement,proto3" json:"initialStatement,omitempty"` + FinalStatement bool `protobuf:"varint,3,opt,name=finalStatement,proto3" json:"finalStatement,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ToggledTransaction) Reset() { *m = ToggledTransaction{} } @@ -60,11 +61,18 @@ func (m *ToggledTransaction) GetTransactionId() []byte { return nil } -func (m *ToggledTransaction) GetToggleReason() ToggleType { +func (m *ToggledTransaction) GetInitialStatement() bool { if m != nil { - return m.ToggleReason + return m.InitialStatement } - return ToggleType_Like + return false +} + +func (m *ToggledTransaction) GetFinalStatement() bool { + if m != nil { + return m.FinalStatement + } + return false } func init() { @@ -74,14 +82,14 @@ func init() { func init() { proto.RegisterFile("toggled_transaction.proto", fileDescriptor_ddb0b50608ac9f9d) } var fileDescriptor_ddb0b50608ac9f9d = []byte{ - // 130 bytes of a gzipped FileDescriptorProto + // 142 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2c, 0xc9, 0x4f, 0x4f, 0xcf, 0x49, 0x4d, 0x89, 0x2f, 0x29, 0x4a, 0xcc, 0x2b, 0x4e, 0x4c, 0x2e, 0xc9, 0xcc, 0xcf, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x52, 0x82, 0x10, 0x15, 0xf1, 0x25, 0x95, - 0x05, 0xa9, 0x10, 0x19, 0xa5, 0x42, 0x2e, 0xa1, 0x10, 0x88, 0xb6, 0x10, 0x84, 0x2e, 0x21, 0x15, - 0x2e, 0x5e, 0x24, 0x43, 0x3c, 0x53, 0x24, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x50, 0x05, 0x85, - 0x4c, 0xb9, 0x78, 0x20, 0x06, 0x06, 0xa5, 0x26, 0x16, 0xe7, 0xe7, 0x49, 0x30, 0x29, 0x30, 0x6a, - 0xf0, 0x19, 0x09, 0x42, 0x4c, 0xd6, 0x83, 0x18, 0x1b, 0x52, 0x59, 0x90, 0x1a, 0x84, 0xa2, 0x2c, - 0x89, 0x0d, 0x2c, 0x6f, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xed, 0xb6, 0x2b, 0x6a, 0xb0, 0x00, - 0x00, 0x00, + 0x05, 0xa9, 0x10, 0x19, 0xa5, 0x3e, 0x46, 0x2e, 0xa1, 0x10, 0x88, 0xbe, 0x10, 0x84, 0x36, 0x21, + 0x15, 0x2e, 0x5e, 0x24, 0x53, 0x3c, 0x53, 0x24, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x50, 0x05, + 0x85, 0xb4, 0xb8, 0x04, 0x32, 0xf3, 0x32, 0x4b, 0x32, 0x13, 0x73, 0x82, 0x4b, 0x12, 0x4b, 0x52, + 0x73, 0x53, 0xf3, 0x4a, 0x24, 0x98, 0x14, 0x18, 0x35, 0x38, 0x82, 0x30, 0xc4, 0x85, 0xd4, 0xb8, + 0xf8, 0xd2, 0x32, 0xf3, 0x90, 0x55, 0x32, 0x83, 0x55, 0xa2, 0x89, 0x26, 0xb1, 0x81, 0xdd, 0x65, + 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x84, 0x60, 0x33, 0xd0, 0xce, 0x00, 0x00, 0x00, } diff --git a/packages/ca/heartbeat/proto/toggled_transaction.proto b/packages/ca/heartbeat/proto/toggled_transaction.proto index 99558098336b91ad50660874fc97f41e1a1e832f..4439d27461b4d22b097483dda2485b0ac26a3cbd 100644 --- a/packages/ca/heartbeat/proto/toggled_transaction.proto +++ b/packages/ca/heartbeat/proto/toggled_transaction.proto @@ -1,10 +1,9 @@ syntax = "proto3"; -import "toggle_type.proto"; - package proto; message ToggledTransaction { bytes transactionId = 1; - ToggleType toggleReason = 2; -}; \ No newline at end of file + bool initialStatement = 2; + bool finalStatement = 3; +} \ No newline at end of file diff --git a/packages/ca/heartbeat/serialization_test.go b/packages/ca/heartbeat/serialization_test.go index c98c532bbbffc27525eb8fc9d5825a420c4cd6d1..89098aa7edb4ecfe112da77d1769eb87faa8d0e0 100644 --- a/packages/ca/heartbeat/serialization_test.go +++ b/packages/ca/heartbeat/serialization_test.go @@ -13,14 +13,13 @@ import ( ) func TestMarshal(t *testing.T) { - ownNodeId := identity.GenerateRandomIdentity().Identifier + ownNodeId := identity.GenerateRandomIdentity().StringIdentifier toggledTransactions := make([]*heartbeatproto.ToggledTransaction, 10000) for i := 0; i < len(toggledTransactions); i++ { toggledTransactions[i] = &heartbeatproto.ToggledTransaction{ TransactionId: make([]byte, 3), - ToggleReason: 0, } } @@ -43,7 +42,7 @@ func TestMarshal(t *testing.T) { heartbeat := &heartbeatproto.HeartBeat{ NodeId: ownNodeId, - OwnStatement: ownStatement, + MainStatement: ownStatement, NeighborStatements: neighborStatements, Signature: make([]byte, 32), } diff --git a/packages/ca/heartbeat/toggled_transaction.go b/packages/ca/heartbeat/toggled_transaction.go new file mode 100644 index 0000000000000000000000000000000000000000..a6fb4bb97035c2aaf1e5675f1bce55a0f681a05e --- /dev/null +++ b/packages/ca/heartbeat/toggled_transaction.go @@ -0,0 +1,91 @@ +package heartbeat + +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/errors" + "github.com/iotaledger/goshimmer/packages/marshaling" + + "github.com/golang/protobuf/proto" + heartbeatProto "github.com/iotaledger/goshimmer/packages/ca/heartbeat/proto" +) + +type ToggledTransaction struct { + transactionId []byte + initialStatement bool + finalStatement bool + + transactionIdMutex sync.RWMutex + initialStatementMutex sync.RWMutex + finalStatementMutex sync.RWMutex +} + +func NewToggledTransaction() *ToggledTransaction { + return &ToggledTransaction{} +} + +func (toggledTransaction *ToggledTransaction) GetTransactionId() []byte { + toggledTransaction.transactionIdMutex.RLock() + defer toggledTransaction.transactionIdMutex.RUnlock() + + return toggledTransaction.transactionId +} + +func (toggledTransaction *ToggledTransaction) SetTransactionId(transactionId []byte) { + toggledTransaction.transactionIdMutex.Lock() + defer toggledTransaction.transactionIdMutex.Unlock() + + toggledTransaction.transactionId = transactionId +} + +func (toggledTransaction *ToggledTransaction) IsInitialStatement() bool { + toggledTransaction.initialStatementMutex.RLock() + defer toggledTransaction.initialStatementMutex.RUnlock() + + return toggledTransaction.initialStatement +} + +func (toggledTransaction *ToggledTransaction) SetInitialStatement(initialStatement bool) { + toggledTransaction.initialStatementMutex.Lock() + defer toggledTransaction.initialStatementMutex.Unlock() + + toggledTransaction.initialStatement = initialStatement +} + +func (toggledTransaction *ToggledTransaction) IsFinalStatement() bool { + toggledTransaction.finalStatementMutex.RLock() + defer toggledTransaction.finalStatementMutex.RUnlock() + + return toggledTransaction.finalStatement +} + +func (toggledTransaction *ToggledTransaction) SetFinalStatement(finalStatement bool) { + toggledTransaction.finalStatementMutex.Lock() + defer toggledTransaction.finalStatementMutex.Unlock() + + toggledTransaction.finalStatement = finalStatement +} + +func (toggledTransaction *ToggledTransaction) FromProto(proto proto.Message) { + protoToggledTransaction := proto.(*heartbeatProto.ToggledTransaction) + + toggledTransaction.transactionId = protoToggledTransaction.TransactionId + toggledTransaction.initialStatement = protoToggledTransaction.InitialStatement + toggledTransaction.finalStatement = protoToggledTransaction.FinalStatement +} + +func (toggledTransaction *ToggledTransaction) ToProto() proto.Message { + return &heartbeatProto.ToggledTransaction{ + TransactionId: toggledTransaction.transactionId, + InitialStatement: toggledTransaction.initialStatement, + FinalStatement: toggledTransaction.finalStatement, + } +} + +func (toggledTransaction *ToggledTransaction) MarshalBinary() ([]byte, errors.IdentifiableError) { + return marshaling.Marshal(toggledTransaction) +} + +func (toggledTransaction *ToggledTransaction) UnmarshalBinary(data []byte) (err errors.IdentifiableError) { + return marshaling.Unmarshal(toggledTransaction, data, &heartbeatProto.ToggledTransaction{}) +} diff --git a/packages/ca/heartbeat_manager.go b/packages/ca/heartbeat_manager.go new file mode 100644 index 0000000000000000000000000000000000000000..a1d8d64e3499e6bac7b6ec330501dcffec479e63 --- /dev/null +++ b/packages/ca/heartbeat_manager.go @@ -0,0 +1,111 @@ +package ca + +import ( + "sync" + "time" + + "github.com/iotaledger/goshimmer/packages/identity" + + "github.com/iotaledger/goshimmer/packages/ca/heartbeat" + + "github.com/iotaledger/goshimmer/packages/errors" +) + +type HeartbeatManager struct { + identity *identity.Identity + options *HeartbeatManagerOptions + statementChain *StatementChain + neighborManagers map[string]*NeighborManager + initialOpinions map[string][]byte + + neighborManagersMutex sync.RWMutex +} + +func NewHeartbeatManager(identity *identity.Identity, options ...HeartbeatManagerOption) *HeartbeatManager { + return &HeartbeatManager{ + identity: identity, + options: DEFAULT_OPTIONS.Override(options...), + + statementChain: NewStatementChain(), + neighborManagers: make(map[string]*NeighborManager), + initialOpinions: make(map[string][]byte), + } +} + +func (heartbeatManager *HeartbeatManager) SetInitialOpinion(transactionId []byte) { + heartbeatManager.initialOpinions[string(transactionId)] = transactionId +} + +func (heartbeatManager *HeartbeatManager) GenerateMainStatement() (result *heartbeat.OpinionStatement, err errors.IdentifiableError) { + toggledTransactions := make([]*heartbeat.ToggledTransaction, 0) + for _, transactionId := range heartbeatManager.initialOpinions { + newToggledTransaction := heartbeat.NewToggledTransaction() + newToggledTransaction.SetInitialStatement(true) + newToggledTransaction.SetFinalStatement(false) + newToggledTransaction.SetTransactionId(transactionId) + + toggledTransactions = append(toggledTransactions, newToggledTransaction) + } + + mainStatement := heartbeat.NewOpinionStatement() + mainStatement.SetNodeId(heartbeatManager.identity.StringIdentifier) + mainStatement.SetTime(uint64(time.Now().Unix())) + mainStatement.SetToggledTransactions(toggledTransactions) + + if lastAppliedStatement := heartbeatManager.statementChain.lastAppliedStatement; lastAppliedStatement != nil { + mainStatement.SetPreviousStatementHash(lastAppliedStatement.GetHash()) + } + + marshaledStatement, marshalErr := mainStatement.MarshalBinary() + if marshalErr != nil { + err = marshalErr + + return + } + + signature, signingErr := heartbeatManager.identity.Sign(marshaledStatement) + if signingErr != nil { + err = ErrMalformedHeartbeat.Derive(signingErr.Error()) + } + + mainStatement.SetSignature(signature) + + result = mainStatement + + return +} + +func (heartbeatManager *HeartbeatManager) GenerateHeartbeat() (result *heartbeat.Heartbeat, err errors.IdentifiableError) { + mainStatement, mainStatementErr := heartbeatManager.GenerateMainStatement() + if mainStatementErr != nil { + err = mainStatementErr + + return + } + + generatedHeartbeat := heartbeat.NewHeartbeat() + generatedHeartbeat.SetNodeId(heartbeatManager.identity.StringIdentifier) + generatedHeartbeat.SetMainStatement(mainStatement) + generatedHeartbeat.SetNeighborStatements(nil) + generatedHeartbeat.SetSignature(nil) + + result = generatedHeartbeat + + return +} + +func (heartbeatManager *HeartbeatManager) ApplyHeartbeat(heartbeat *heartbeat.Heartbeat) (err errors.IdentifiableError) { + heartbeatManager.neighborManagersMutex.RLock() + defer heartbeatManager.neighborManagersMutex.RUnlock() + + issuerId := heartbeat.GetNodeId() + + neighborManager, neighborExists := heartbeatManager.neighborManagers[issuerId] + if !neighborExists { + err = ErrUnknownNeighbor.Derive("unknown neighbor: " + issuerId) + } else { + err = neighborManager.ApplyHeartbeat(heartbeat) + } + + return +} diff --git a/packages/ca/heartbeat_manager_options.go b/packages/ca/heartbeat_manager_options.go new file mode 100644 index 0000000000000000000000000000000000000000..af507e472ae89a66f593ff1a85edfbb189020c3d --- /dev/null +++ b/packages/ca/heartbeat_manager_options.go @@ -0,0 +1,26 @@ +package ca + +type HeartbeatManagerOptions struct { + maxNeighborChains int +} + +func (options HeartbeatManagerOptions) Override(optionalOptions ...HeartbeatManagerOption) *HeartbeatManagerOptions { + result := &options + for _, option := range optionalOptions { + option(result) + } + + return result +} + +type HeartbeatManagerOption func(*HeartbeatManagerOptions) + +func MaxNeighborChains(maxNeighborChains int) HeartbeatManagerOption { + return func(args *HeartbeatManagerOptions) { + args.maxNeighborChains = maxNeighborChains + } +} + +var DEFAULT_OPTIONS = &HeartbeatManagerOptions{ + maxNeighborChains: 8, +} diff --git a/packages/ca/heartbeat_manager_test.go b/packages/ca/heartbeat_manager_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9c5bc1a59387e8feb15ab1aeaee7b061b891cc7d --- /dev/null +++ b/packages/ca/heartbeat_manager_test.go @@ -0,0 +1,26 @@ +package ca + +import ( + "crypto/rand" + "fmt" + "testing" + + "github.com/iotaledger/goshimmer/packages/identity" +) + +func TestHeartbeatManager_GenerateHeartbeat(t *testing.T) { + transactionId1 := make([]byte, 50) + rand.Read(transactionId1) + + heartbeatManager := NewHeartbeatManager(identity.GenerateRandomIdentity()) + heartbeatManager.SetInitialOpinion(transactionId1) + + result, err := heartbeatManager.GenerateHeartbeat() + if err != nil { + t.Error(err) + + return + } + + fmt.Println(result.GetMainStatement()) +} diff --git a/packages/ca/neighbor_manager.go b/packages/ca/neighbor_manager.go new file mode 100644 index 0000000000000000000000000000000000000000..b9a03aa6c608f44f5538eaa674dd4ab794e0f075 --- /dev/null +++ b/packages/ca/neighbor_manager.go @@ -0,0 +1,97 @@ +package ca + +import ( + "bytes" + "strconv" + + "github.com/iotaledger/goshimmer/packages/ca/heartbeat" + + "github.com/iotaledger/goshimmer/packages/errors" +) + +type NeighborManager struct { + options *NeighborManagerOptions + mainChain *StatementChain + neighborChains map[string]*StatementChain +} + +func NewNeighborManager(options ...NeighborManagerOption) *NeighborManager { + return &NeighborManager{ + options: (&NeighborManagerOptions{}).Override(options...), + mainChain: NewStatementChain(), + neighborChains: make(map[string]*StatementChain), + } +} + +func (neighborManager *NeighborManager) Reset() { + neighborManager.mainChain.Reset() + neighborManager.neighborChains = make(map[string]*StatementChain) +} + +func (neighborManager *NeighborManager) ApplyHeartbeat(heartbeat *heartbeat.Heartbeat) (err errors.IdentifiableError) { + // region check if heartbeat is "syntactically correct" //////////////////////////////////////////////////////////// + + mainStatement := heartbeat.GetMainStatement() + if mainStatement == nil { + return ErrMalformedHeartbeat.Derive("missing main statement in heartbeat") + } + + neighborStatements := heartbeat.GetNeighborStatements() + if len(neighborStatements) > neighborManager.options.maxNeighborChains { + return ErrTooManyNeighbors.Derive("too many neighbors in statement of " + heartbeat.GetNodeId() + ": " + strconv.Itoa(len(neighborStatements))) + } + + // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// + + // region check if heartbeat is semantically correct /////////////////////////////////////////////////////////////// + + // check if referenced main statement is missing + if previousStatementHash := mainStatement.GetPreviousStatementHash(); len(previousStatementHash) != 0 { + lastAppliedMainStatement := neighborManager.mainChain.GetLastAppliedStatement() + if lastAppliedMainStatement != nil && !bytes.Equal(lastAppliedMainStatement.GetHash(), previousStatementHash) { + if (mainStatement.GetTime() - lastAppliedMainStatement.GetTime()) < MAX_STATEMENT_TIMEOUT { + // request missing heartbeat + neighborManager.mainChain.AddStatement(mainStatement) + + return + } else { + neighborManager.Reset() + } + } + } else { + neighborManager.mainChain.AddStatement(mainStatement) + } + + // check if referenced neighbor statements are missing + for neighborId, neighborStatement := range neighborStatements { + neighborChain, exists := neighborManager.neighborChains[neighborId] + if exists { + lastAppliedNeighborStatement := neighborChain.GetLastAppliedStatement() + if lastAppliedNeighborStatement != nil && !bytes.Equal(lastAppliedNeighborStatement.GetHash(), neighborStatement.GetPreviousStatementHash()) { + return ErrMalformedHeartbeat.Derive("missing neighbor statement") + } + } + } + + // check if neighbor statements are a valid justification for main statement + // sthsth + + // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// + + return +} + +type NeighborManagerOptions struct { + maxNeighborChains int +} + +func (options NeighborManagerOptions) Override(optionalOptions ...NeighborManagerOption) *NeighborManagerOptions { + result := &options + for _, option := range optionalOptions { + option(result) + } + + return result +} + +type NeighborManagerOption func(*NeighborManagerOptions) diff --git a/packages/ca/statement_chain.go b/packages/ca/statement_chain.go new file mode 100644 index 0000000000000000000000000000000000000000..7333bc40980863d7da9441b90f1cd279555c8af5 --- /dev/null +++ b/packages/ca/statement_chain.go @@ -0,0 +1,39 @@ +package ca + +import ( + "github.com/iotaledger/goshimmer/packages/ca/heartbeat" + "github.com/iotaledger/goshimmer/packages/errors" +) + +type StatementChain struct { + lastAppliedStatement *heartbeat.OpinionStatement + lastReceivedStatement *heartbeat.OpinionStatement +} + +func NewStatementChain() *StatementChain { + return &StatementChain{} +} + +func (statementChain *StatementChain) AddStatement(statement *heartbeat.OpinionStatement) { + +} + +func (statementChain *StatementChain) Reset() { + +} + +func (statementChain *StatementChain) Apply(statement *heartbeat.OpinionStatement) (err errors.IdentifiableError) { + return +} + +func (statementChain *StatementChain) SetLastReceivedStatement(statement *heartbeat.OpinionStatement) { + statementChain.lastReceivedStatement = statement +} + +func (statementChain *StatementChain) GetLastAppliedStatement() *heartbeat.OpinionStatement { + return statementChain.lastAppliedStatement +} + +func (statementChain *StatementChain) StatementExists(statementHash []byte) bool { + return true +} diff --git a/packages/marshaling/errors.go b/packages/marshaling/errors.go new file mode 100644 index 0000000000000000000000000000000000000000..73b27016e283cb154bfd11cdab942e75243dd241 --- /dev/null +++ b/packages/marshaling/errors.go @@ -0,0 +1,8 @@ +package marshaling + +import "github.com/iotaledger/goshimmer/packages/errors" + +var ( + ErrUnmarshalFailed = errors.Wrap(errors.New("unmarshal failed"), "unmarshal failed") + ErrMarshalFailed = errors.Wrap(errors.New("marshal failed"), "marshal failed") +) diff --git a/packages/marshaling/marshal.go b/packages/marshaling/marshal.go new file mode 100644 index 0000000000000000000000000000000000000000..04cdf7f2591b066df0e521c034d6bf8e673bf631 --- /dev/null +++ b/packages/marshaling/marshal.go @@ -0,0 +1,16 @@ +package marshaling + +import ( + "github.com/golang/protobuf/proto" + "github.com/iotaledger/goshimmer/packages/errors" +) + +func Marshal(source ProtocolBufferTarget) (result []byte, err errors.IdentifiableError) { + if marshaledData, marshalErr := proto.Marshal(source.ToProto()); marshalErr != nil { + err = ErrMarshalFailed.Derive(marshalErr, "marshal failed") + } else { + result = marshaledData + } + + return +} diff --git a/packages/marshaling/types.go b/packages/marshaling/types.go new file mode 100644 index 0000000000000000000000000000000000000000..0d33468134914cf5b866a1a5fb80b6a8b8f7ee43 --- /dev/null +++ b/packages/marshaling/types.go @@ -0,0 +1,8 @@ +package marshaling + +import "github.com/golang/protobuf/proto" + +type ProtocolBufferTarget interface { + ToProto() (result proto.Message) + FromProto(proto proto.Message) +} diff --git a/packages/marshaling/unmarshal.go b/packages/marshaling/unmarshal.go new file mode 100644 index 0000000000000000000000000000000000000000..fd2d69aaef2b5b83dcc29ea22cf47aa61353a273 --- /dev/null +++ b/packages/marshaling/unmarshal.go @@ -0,0 +1,17 @@ +package marshaling + +import ( + "github.com/golang/protobuf/proto" + "github.com/iotaledger/goshimmer/packages/errors" +) + +// Unmarshals the given data into the target using the given protobuf type. +func Unmarshal(target ProtocolBufferTarget, data []byte, messageType proto.Message) (err errors.IdentifiableError) { + if unmarshalError := proto.Unmarshal(data, messageType); unmarshalError != nil { + err = ErrUnmarshalFailed.Derive(unmarshalError, "unmarshal failed") + } else { + target.FromProto(messageType) + } + + return +}