diff --git a/packages/ca/heartbeat/errors.go b/packages/ca/heartbeat/errors.go index a472a252c023cff0df75efa012b7e0cf35810e17..e93c8d37ffccf4ab229c3fcaa90f43c1f430d1e6 100644 --- a/packages/ca/heartbeat/errors.go +++ b/packages/ca/heartbeat/errors.go @@ -5,5 +5,6 @@ import ( ) var ( - ErrSigningFailed = errors.Wrap(errors.New("failed to sign"), "failed to sign") + ErrSigningFailed = errors.Wrap(errors.New("failed to sign"), "failed to sign") + ErrSignatureCorrupt = errors.Wrap(errors.New("failed to sign"), "failed to sign") ) diff --git a/packages/ca/heartbeat/heartbeat.go b/packages/ca/heartbeat/heartbeat.go index 0b95910096868c4a9c5dfc860d8accd3a32da308..0873ff994f7176bd383ed29e53e0c6bde60fb04c 100644 --- a/packages/ca/heartbeat/heartbeat.go +++ b/packages/ca/heartbeat/heartbeat.go @@ -17,7 +17,7 @@ import ( type Heartbeat struct { nodeId string mainStatement *OpinionStatement - neighborStatements map[string]*OpinionStatement + neighborStatements map[string][]*OpinionStatement signature []byte nodeIdMutex sync.RWMutex @@ -58,14 +58,14 @@ func (heartbeat *Heartbeat) SetMainStatement(mainStatement *OpinionStatement) { heartbeat.mainStatement = mainStatement } -func (heartbeat *Heartbeat) GetNeighborStatements() map[string]*OpinionStatement { +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) { +func (heartbeat *Heartbeat) SetNeighborStatements(neighborStatements map[string][]*OpinionStatement) { heartbeat.neighborStatementsMutex.Lock() defer heartbeat.neighborStatementsMutex.Unlock() @@ -79,6 +79,13 @@ func (heartbeat *Heartbeat) GetSignature() []byte { return heartbeat.signature } +func (heartbeat *Heartbeat) SetSignature(signature []byte) { + heartbeat.signatureMutex.Lock() + defer heartbeat.signatureMutex.Unlock() + + heartbeat.signature = signature +} + func (heartbeat *Heartbeat) Sign(identity *identity.Identity) (err errors.IdentifiableError) { if marshaledHeartbeat, marshalErr := heartbeat.MarshalBinary(); marshalErr == nil { if signature, signingErr := identity.Sign(marshaledHeartbeat); signingErr == nil { @@ -93,11 +100,25 @@ func (heartbeat *Heartbeat) Sign(identity *identity.Identity) (err errors.Identi return } -func (heartbeat *Heartbeat) SetSignature(signature []byte) { - heartbeat.signatureMutex.Lock() - defer heartbeat.signatureMutex.Unlock() +func (heartbeat *Heartbeat) VerifySignature() (result bool, err errors.IdentifiableError) { + signature := heartbeat.GetSignature() + heartbeat.SetSignature(nil) - heartbeat.signature = signature + if marshaledHeartbeat, marshalErr := heartbeat.MarshalBinary(); marshalErr != nil { + heartbeat.SetSignature(signature) + + err = marshalErr + } else { + heartbeat.SetSignature(signature) + + if identity, identityErr := identity.FromSignedData(marshaledHeartbeat, signature); identityErr != nil { + err = ErrSignatureCorrupt.Derive(identityErr, "failed to retrieve identity from signature of heartbeat") + } else { + result = identity.StringIdentifier == heartbeat.GetNodeId() + } + } + + return } func (heartbeat *Heartbeat) FromProto(proto proto.Message) { @@ -106,12 +127,16 @@ func (heartbeat *Heartbeat) FromProto(proto proto.Message) { var mainStatement OpinionStatement mainStatement.FromProto(protoHeartbeat.MainStatement) - neighborStatements := make(map[string]*OpinionStatement, len(protoHeartbeat.NeighborStatements)) + neighborStatements := make(map[string][]*OpinionStatement, len(protoHeartbeat.NeighborStatements)) for _, neighborStatement := range protoHeartbeat.NeighborStatements { var newNeighborStatement OpinionStatement newNeighborStatement.FromProto(neighborStatement) - neighborStatements[neighborStatement.NodeId] = &newNeighborStatement + if _, exists := neighborStatements[neighborStatement.NodeId]; !exists { + neighborStatements[neighborStatement.NodeId] = make([]*OpinionStatement, 0) + } + + neighborStatements[neighborStatement.NodeId] = append(neighborStatements[neighborStatement.NodeId], &newNeighborStatement) } heartbeat.nodeId = protoHeartbeat.NodeId @@ -123,10 +148,12 @@ func (heartbeat *Heartbeat) FromProto(proto proto.Message) { 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) + for _, statementsOfNeighbor := range heartbeat.neighborStatements { + for _, neighborStatement := range statementsOfNeighbor { + neighborStatements[i] = neighborStatement.ToProto().(*heartbeatProto.OpinionStatement) - i++ + i++ + } } return &heartbeatProto.HeartBeat{ diff --git a/packages/ca/heartbeat_manager.go b/packages/ca/heartbeat_manager.go index bda2408cc96ae6dba52fa9ad1ec18b3c124f61ba..c00fd126708a47b7fda5082d4fd133a00bdd0a3c 100644 --- a/packages/ca/heartbeat_manager.go +++ b/packages/ca/heartbeat_manager.go @@ -32,6 +32,12 @@ func NewHeartbeatManager(identity *identity.Identity, options ...HeartbeatManage } } +func (heartbeatManager *HeartbeatManager) AddNeighbor(neighborIdentity *identity.Identity) { + if _, exists := heartbeatManager.neighborManagers[neighborIdentity.StringIdentifier]; !exists { + heartbeatManager.neighborManagers[neighborIdentity.StringIdentifier] = NewNeighborManager() + } +} + func (heartbeatManager *HeartbeatManager) InitialDislike(transactionId []byte) { heartbeatManager.initialOpinions[string(transactionId)] = false } @@ -41,16 +47,20 @@ func (heartbeatManager *HeartbeatManager) InitialLike(transactionId []byte) { } func (heartbeatManager *HeartbeatManager) GenerateHeartbeat() (result *heartbeat.Heartbeat, err errors.IdentifiableError) { - if mainStatement, mainStatementErr := heartbeatManager.GenerateMainStatement(); mainStatementErr == nil { - generatedHeartbeat := heartbeat.NewHeartbeat() - generatedHeartbeat.SetNodeId(heartbeatManager.identity.StringIdentifier) - generatedHeartbeat.SetMainStatement(mainStatement) - generatedHeartbeat.SetNeighborStatements(nil) - - if signingErr := generatedHeartbeat.Sign(heartbeatManager.identity); signingErr == nil { - result = generatedHeartbeat + if mainStatement, mainStatementErr := heartbeatManager.generateMainStatement(); mainStatementErr == nil { + if neighborStatements, neighborStatementErr := heartbeatManager.generateNeighborStatements(); neighborStatementErr == nil { + generatedHeartbeat := heartbeat.NewHeartbeat() + generatedHeartbeat.SetNodeId(heartbeatManager.identity.StringIdentifier) + generatedHeartbeat.SetMainStatement(mainStatement) + generatedHeartbeat.SetNeighborStatements(neighborStatements) + + if signingErr := generatedHeartbeat.Sign(heartbeatManager.identity); signingErr == nil { + result = generatedHeartbeat + } else { + err = signingErr + } } else { - err = signingErr + err = neighborStatementErr } } else { err = mainStatementErr @@ -59,11 +69,35 @@ func (heartbeatManager *HeartbeatManager) GenerateHeartbeat() (result *heartbeat return } -func (heartbeatManager *HeartbeatManager) GenerateMainStatement() (result *heartbeat.OpinionStatement, err errors.IdentifiableError) { +func (heartbeatManager *HeartbeatManager) ApplyHeartbeat(heartbeat *heartbeat.Heartbeat) (err errors.IdentifiableError) { + heartbeatManager.neighborManagersMutex.RLock() + defer heartbeatManager.neighborManagersMutex.RUnlock() + + if signatureValid, signatureErr := heartbeat.VerifySignature(); signatureErr == nil { + if signatureValid { + issuerId := heartbeat.GetNodeId() + + neighborManager, neighborExists := heartbeatManager.neighborManagers[issuerId] + if !neighborExists { + err = ErrUnknownNeighbor.Derive("unknown neighbor: " + issuerId) + } else { + err = neighborManager.ApplyHeartbeat(heartbeat) + } + } else { + err = ErrMalformedHeartbeat.Derive("the heartbeat has an invalid signature") + } + } else { + err = signatureErr + } + + return +} + +func (heartbeatManager *HeartbeatManager) generateMainStatement() (result *heartbeat.OpinionStatement, err errors.IdentifiableError) { mainStatement := heartbeat.NewOpinionStatement() mainStatement.SetNodeId(heartbeatManager.identity.StringIdentifier) mainStatement.SetTime(uint64(time.Now().Unix())) - mainStatement.SetToggledTransactions(heartbeatManager.GenerateToggledTransactions()) + mainStatement.SetToggledTransactions(heartbeatManager.generateToggledTransactions()) if lastAppliedStatement := heartbeatManager.statementChain.lastAppliedStatement; lastAppliedStatement != nil { mainStatement.SetPreviousStatementHash(lastAppliedStatement.GetHash()) @@ -72,7 +106,7 @@ func (heartbeatManager *HeartbeatManager) GenerateMainStatement() (result *heart if signingErr := mainStatement.Sign(heartbeatManager.identity); signingErr == nil { result = mainStatement - heartbeatManager.ResetInitialOpinions() + heartbeatManager.resetInitialOpinions() heartbeatManager.statementChain.lastAppliedStatement = mainStatement } else { err = signingErr @@ -81,7 +115,11 @@ func (heartbeatManager *HeartbeatManager) GenerateMainStatement() (result *heart return } -func (heartbeatManager *HeartbeatManager) GenerateToggledTransactions() []*heartbeat.ToggledTransaction { +func (heartbeatManager *HeartbeatManager) generateNeighborStatements() (result map[string][]*heartbeat.OpinionStatement, err errors.IdentifiableError) { + return +} + +func (heartbeatManager *HeartbeatManager) generateToggledTransactions() []*heartbeat.ToggledTransaction { toggledTransactions := make([]*heartbeat.ToggledTransaction, 0) for transactionId, liked := range heartbeatManager.initialOpinions { if !liked { @@ -97,22 +135,6 @@ func (heartbeatManager *HeartbeatManager) GenerateToggledTransactions() []*heart return toggledTransactions } -func (heartbeatManager *HeartbeatManager) ResetInitialOpinions() { +func (heartbeatManager *HeartbeatManager) resetInitialOpinions() { heartbeatManager.initialOpinions = make(map[string]bool) } - -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_test.go b/packages/ca/heartbeat_manager_test.go index 9a6d2a9a7d3a1f3f879e82dc896af1dacfac5ca1..077b9ab9f2603a76848f7cb98de661e053bc0688 100644 --- a/packages/ca/heartbeat_manager_test.go +++ b/packages/ca/heartbeat_manager_test.go @@ -16,30 +16,41 @@ func generateRandomTransactionId() (result []byte) { } func TestHeartbeatManager_GenerateHeartbeat(t *testing.T) { - transactionId1 := generateRandomTransactionId() - transactionId2 := generateRandomTransactionId() + ownIdentity := identity.GenerateRandomIdentity() + neighborIdentity := identity.GenerateRandomIdentity() - heartbeatManager := NewHeartbeatManager(identity.GenerateRandomIdentity()) - heartbeatManager.InitialDislike(transactionId1) - heartbeatManager.InitialDislike(transactionId2) + // generate first heartbeat //////////////////////////////////////////////////////////////////////////////////////// - heartbeatManager.InitialLike(generateRandomTransactionId()) + heartbeatManager1 := NewHeartbeatManager(ownIdentity) + heartbeatManager1.AddNeighbor(neighborIdentity) + heartbeatManager1.InitialDislike(generateRandomTransactionId()) + heartbeatManager1.InitialDislike(generateRandomTransactionId()) + heartbeatManager1.InitialLike(generateRandomTransactionId()) - result, err := heartbeatManager.GenerateHeartbeat() + heartbeat1, err := heartbeatManager1.GenerateHeartbeat() if err != nil { t.Error(err) return } - fmt.Println(result) + fmt.Println(heartbeat1) - result, err = heartbeatManager.GenerateHeartbeat() + heartbeatManager2 := NewHeartbeatManager(neighborIdentity) + heartbeatManager2.AddNeighbor(ownIdentity) + err = heartbeatManager2.ApplyHeartbeat(heartbeat1) if err != nil { t.Error(err) return } - fmt.Println(result) + heartbeat2, err := heartbeatManager2.GenerateHeartbeat() + if err != nil { + t.Error(err) + + return + } + + fmt.Println(heartbeat2) } diff --git a/packages/ca/neighbor_manager.go b/packages/ca/neighbor_manager.go index b9a03aa6c608f44f5538eaa674dd4ab794e0f075..afea44bc99a4927dc46b6a4028bf6ef48b6be228 100644 --- a/packages/ca/neighbor_manager.go +++ b/packages/ca/neighbor_manager.go @@ -63,13 +63,17 @@ func (neighborManager *NeighborManager) ApplyHeartbeat(heartbeat *heartbeat.Hear } // check if referenced neighbor statements are missing - for neighborId, neighborStatement := range neighborStatements { + for neighborId, statementsOfNeighbor := 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") + for _, neighborStatement := range statementsOfNeighbor { + lastAppliedNeighborStatement := neighborChain.GetLastAppliedStatement() + if lastAppliedNeighborStatement != nil && !bytes.Equal(lastAppliedNeighborStatement.GetHash(), neighborStatement.GetPreviousStatementHash()) { + return ErrMalformedHeartbeat.Derive("missing neighbor statement") + } } + } else { + // 1. check if new slot is available (not full || statement of neighbor with last connection) } }