From dced9782cb907e1faea769702e1e70af2831a218 Mon Sep 17 00:00:00 2001 From: j-rafique Date: Fri, 8 Dec 2023 15:47:45 +0500 Subject: [PATCH] [PSL-1054] refactor process-self-healing worker --- common/types/self_healing.go | 62 +- proto/supernode/protobuf/self_healing.proto | 4 +- proto/supernode/self_healing.pb.go | 145 ++-- supernode/node/grpc/client/self_healing.go | 22 +- .../server/services/supernode/self_healing.go | 33 +- supernode/node/node_client_interface.go | 2 +- .../process_self_healing_challenge.go | 627 +++++++++--------- .../process_self_healing_challenge_test.go | 79 +-- .../selfhealing/self_healing_worker.go | 14 +- .../verify_self_healing_challenge.go | 288 ++++---- .../verify_self_healing_challenge_test.go | 75 ++- 11 files changed, 718 insertions(+), 633 deletions(-) diff --git a/common/types/self_healing.go b/common/types/self_healing.go index a01e2bcd8..84f4ce18d 100644 --- a/common/types/self_healing.go +++ b/common/types/self_healing.go @@ -13,6 +13,8 @@ const ( SelfHealingChallengeMessage SelfHealingMessageType = iota + 1 //SelfHealingResponseMessage represents the response message SelfHealingResponseMessage + + SelfHealingVerificationMessage ) // TicketType represents the type of ticket; nft, cascade, sense @@ -58,33 +60,61 @@ type SelfHealingMessage struct { // SelfHealingMessageData represents the self-healing message data type SelfHealingMessageData struct { - ChallengerID string `json:"challenger_id"` - RecipientID string `json:"recipient_id"` - Challenge SelfHealingChallengeData `json:"challenge"` - Response SelfHealingResponseData `json:"response"` + ChallengerID string `json:"challenger_id"` + RecipientID string `json:"recipient_id"` + Challenge SelfHealingChallengeData `json:"challenge"` + Response SelfHealingResponseData `json:"response"` + Verification SelfHealingVerificationData `json:"verification"` } // SelfHealingChallengeData represents the challenge data for self-healing sent by the challenger type SelfHealingChallengeData struct { - Block int32 `json:"block"` - Merkelroot string `json:"merkelroot"` - Timestamp time.Time `json:"timestamp"` - Tickets []Ticket `json:"tickets"` + Block int32 `json:"block"` + Merkelroot string `json:"merkelroot"` + Timestamp time.Time `json:"timestamp"` + ChallengeTickets []ChallengeTicket `json:"challenge_tickets"` +} + +// ChallengeTicket represents the ticket details for self-healing challenge +type ChallengeTicket struct { + TxID string `json:"tx_id"` + TicketType TicketType `json:"ticket_type"` + MissingKeys []string `json:"missing_keys"` + DataHash string `json:"data_hash"` } -// Ticket represents the ticket details that require self-healing -type Ticket struct { +// RespondedTicket represents the details of ticket responded in a self-healing challenge +type RespondedTicket struct { TxID string `json:"tx_id"` TicketType TicketType `json:"ticket_type"` MissingKeys []string `json:"missing_keys"` - DataHash string `json:"data_hash"` - ReconstructedFileHash string `json:"reconstructed_file_hash"` + ReconstructedFileHash []byte `json:"reconstructed_file_hash"` + FileIDs []string `json:"sense_file_ids"` } // SelfHealingResponseData represents the response data for self-healing sent by the recipient type SelfHealingResponseData struct { - Block int32 `json:"block"` - Merkelroot string `json:"merkelroot"` - Timestamp time.Time `json:"timestamp"` - Tickets []Ticket `json:"tickets"` + Block int32 `json:"block"` + Merkelroot string `json:"merkelroot"` + Timestamp time.Time `json:"timestamp"` + RespondedTickets []RespondedTicket `json:"responded_tickets"` +} + +// VerifiedTicket represents the details of ticket verified in self-healing challenge +type VerifiedTicket struct { + TxID string `json:"tx_id"` + TicketType TicketType `json:"ticket_type"` + MissingKeys []string `json:"missing_keys"` + DataHash string `json:"data_hash"` + ReconstructedFileHash []byte `json:"reconstructed_file_hash"` + FileIDs []string `json:"sense_file_ids"` + IsVerified bool `json:"is_verified"` +} + +// SelfHealingVerificationData represents the verification data for self-healing challenge +type SelfHealingVerificationData struct { + Block int32 `json:"block"` + Merkelroot string `json:"merkelroot"` + Timestamp time.Time `json:"timestamp"` + VerifiedTickets []VerifiedTicket `json:"verified_tickets"` } diff --git a/proto/supernode/protobuf/self_healing.proto b/proto/supernode/protobuf/self_healing.proto index 85770f716..5b26f3097 100644 --- a/proto/supernode/protobuf/self_healing.proto +++ b/proto/supernode/protobuf/self_healing.proto @@ -68,11 +68,11 @@ message ProcessSelfHealingChallengeReply { } message VerifySelfHealingChallengeRequest { - SelfHealingData data = 1; + SelfHealingMessage data = 1; } message VerifySelfHealingChallengeReply { - SelfHealingData data = 1; + SelfHealingMessage data = 1; } message SelfHealingMessage { diff --git a/proto/supernode/self_healing.pb.go b/proto/supernode/self_healing.pb.go index 7fea99339..218acec85 100644 --- a/proto/supernode/self_healing.pb.go +++ b/proto/supernode/self_healing.pb.go @@ -471,10 +471,10 @@ func (m *ProcessSelfHealingChallengeReply) GetData() *SelfHealingMessage { } type VerifySelfHealingChallengeRequest struct { - Data *SelfHealingData `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Data *SelfHealingMessage `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *VerifySelfHealingChallengeRequest) Reset() { *m = VerifySelfHealingChallengeRequest{} } @@ -502,7 +502,7 @@ func (m *VerifySelfHealingChallengeRequest) XXX_DiscardUnknown() { var xxx_messageInfo_VerifySelfHealingChallengeRequest proto.InternalMessageInfo -func (m *VerifySelfHealingChallengeRequest) GetData() *SelfHealingData { +func (m *VerifySelfHealingChallengeRequest) GetData() *SelfHealingMessage { if m != nil { return m.Data } @@ -510,10 +510,10 @@ func (m *VerifySelfHealingChallengeRequest) GetData() *SelfHealingData { } type VerifySelfHealingChallengeReply struct { - Data *SelfHealingData `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Data *SelfHealingMessage `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *VerifySelfHealingChallengeReply) Reset() { *m = VerifySelfHealingChallengeReply{} } @@ -541,7 +541,7 @@ func (m *VerifySelfHealingChallengeReply) XXX_DiscardUnknown() { var xxx_messageInfo_VerifySelfHealingChallengeReply proto.InternalMessageInfo -func (m *VerifySelfHealingChallengeReply) GetData() *SelfHealingData { +func (m *VerifySelfHealingChallengeReply) GetData() *SelfHealingMessage { if m != nil { return m.Data } @@ -637,67 +637,66 @@ func init() { func init() { proto.RegisterFile("self_healing.proto", fileDescriptor_ae6b4541a7de850f) } var fileDescriptor_ae6b4541a7de850f = []byte{ - // 978 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x72, 0xda, 0x46, - 0x14, 0xae, 0xb0, 0xe3, 0x98, 0x03, 0x06, 0xcd, 0xa6, 0xb1, 0x31, 0x9e, 0x34, 0x58, 0xd3, 0xc9, - 0x60, 0xb7, 0xc6, 0x0d, 0x99, 0xe9, 0x5f, 0xae, 0x28, 0xc8, 0xb6, 0xa6, 0x58, 0xb8, 0x12, 0xc4, - 0x33, 0xbd, 0xd1, 0x28, 0x68, 0x11, 0x3b, 0x16, 0x2b, 0xaa, 0x5d, 0x92, 0xe1, 0x01, 0x7a, 0xd7, - 0x57, 0xe8, 0x73, 0xf4, 0x99, 0xda, 0x67, 0xe8, 0x45, 0x47, 0x2b, 0x21, 0x24, 0x1c, 0x88, 0x9b, - 0x3b, 0xf4, 0x9d, 0xef, 0x7c, 0x9c, 0x3d, 0xfa, 0xce, 0xd1, 0x02, 0x62, 0xd8, 0x1b, 0x59, 0x63, - 0x6c, 0x7b, 0x84, 0xba, 0x8d, 0x69, 0xe0, 0x73, 0x1f, 0xe5, 0xd9, 0x6c, 0x8a, 0x03, 0xea, 0x3b, - 0xb8, 0x5a, 0x1e, 0xfa, 0x93, 0x89, 0x4f, 0x2d, 0x46, 0xa3, 0x98, 0x72, 0x0a, 0x85, 0x1b, 0x42, - 0x5d, 0x03, 0xff, 0x36, 0xc3, 0x8c, 0xa3, 0x23, 0xc8, 0x33, 0x4c, 0x1d, 0x1c, 0x58, 0xc4, 0xa9, - 0x48, 0x35, 0xa9, 0x9e, 0x37, 0x76, 0x23, 0x40, 0x73, 0x94, 0x3f, 0x24, 0x28, 0x46, 0x64, 0x36, - 0xf5, 0x29, 0xc3, 0x1b, 0xd9, 0xe8, 0x39, 0x14, 0x02, 0x3c, 0xc4, 0xe4, 0x5d, 0x14, 0xce, 0x89, - 0x30, 0x2c, 0x20, 0xcd, 0x09, 0xb3, 0x09, 0xb3, 0x7c, 0xea, 0x11, 0x8a, 0x2b, 0x5b, 0x35, 0xa9, - 0xbe, 0x6b, 0xec, 0x12, 0xd6, 0x13, 0xcf, 0xe8, 0x18, 0x8a, 0x81, 0xf8, 0x1b, 0x07, 0x3b, 0x96, - 0xcd, 0x2b, 0xdb, 0x22, 0xbd, 0x90, 0x60, 0x2d, 0xae, 0xfc, 0xb3, 0x0b, 0x65, 0x13, 0x7b, 0xa3, - 0xab, 0xe8, 0xb0, 0x1d, 0x9b, 0xdb, 0xe8, 0x19, 0xc0, 0x04, 0x33, 0x66, 0xbb, 0x78, 0x59, 0x52, - 0x3e, 0x46, 0x34, 0x07, 0x69, 0x50, 0x5c, 0x84, 0xf9, 0x7c, 0x8a, 0x45, 0x51, 0xa5, 0xe6, 0x8b, - 0x46, 0xd2, 0xa0, 0xc6, 0x8a, 0x60, 0x23, 0xa6, 0xf7, 0xe7, 0x53, 0x6c, 0x14, 0x52, 0x0f, 0xa8, - 0x0b, 0xf2, 0x70, 0x6c, 0x7b, 0x1e, 0xa6, 0x2e, 0xb6, 0x18, 0xb7, 0xf9, 0x8c, 0x89, 0x43, 0x94, - 0x9a, 0xc7, 0x1b, 0xe4, 0x22, 0xa2, 0x51, 0x4e, 0x52, 0x4d, 0x01, 0xa0, 0x36, 0x7c, 0x31, 0xc1, - 0xc1, 0x9d, 0x87, 0x03, 0xdf, 0xe7, 0xd6, 0xfb, 0x31, 0xa6, 0x56, 0x4a, 0x1d, 0xd3, 0x45, 0x03, - 0x8e, 0x96, 0xac, 0xdb, 0x31, 0xa6, 0xed, 0x44, 0x06, 0x53, 0x8e, 0x7e, 0x84, 0xc3, 0x45, 0x12, - 0xa1, 0xae, 0x35, 0xb1, 0x19, 0x8f, 0xca, 0x08, 0x7b, 0xf1, 0x48, 0xe4, 0x1f, 0xa4, 0x08, 0xd7, - 0x49, 0x5c, 0x73, 0xd0, 0xf7, 0x50, 0x89, 0x7b, 0x7b, 0x3f, 0x75, 0x47, 0xa4, 0xee, 0x2f, 0xe3, - 0x99, 0xcc, 0x6f, 0xe1, 0x20, 0xc0, 0x43, 0x9f, 0x32, 0x1e, 0xcc, 0x86, 0x1c, 0x3b, 0xd6, 0x88, - 0x78, 0xd8, 0x1a, 0xdb, 0x6c, 0x5c, 0x79, 0x5c, 0x93, 0xea, 0x45, 0xe3, 0x69, 0x26, 0x7c, 0x41, - 0x3c, 0x7c, 0x65, 0xb3, 0x31, 0xea, 0x41, 0x69, 0x79, 0xc4, 0x30, 0xa7, 0xb2, 0x5b, 0x93, 0xea, - 0x85, 0x66, 0x7d, 0x43, 0xfb, 0x92, 0x84, 0x50, 0xc5, 0xd8, 0xcb, 0x3c, 0x86, 0x96, 0x59, 0x0a, - 0x12, 0xa7, 0x92, 0x8f, 0x2c, 0x93, 0x60, 0x9a, 0x83, 0x14, 0xd8, 0x0b, 0xb0, 0x6b, 0x71, 0x32, - 0xbc, 0xc3, 0x3c, 0xe4, 0xc0, 0xc2, 0x56, 0x6e, 0x5f, 0x60, 0x9a, 0x83, 0xea, 0x20, 0xdb, 0x43, - 0x4e, 0x7c, 0x9a, 0xa2, 0x15, 0x04, 0xad, 0x14, 0xe1, 0x09, 0xf3, 0x05, 0x94, 0x09, 0x0b, 0xdf, - 0x0e, 0xc3, 0x31, 0xb7, 0x52, 0x14, 0x36, 0xde, 0x23, 0xcc, 0x0c, 0xd1, 0x88, 0x89, 0x14, 0x28, - 0x0a, 0x52, 0x58, 0xa5, 0xe6, 0xb0, 0xca, 0x5e, 0x6d, 0xab, 0x9e, 0x37, 0x32, 0x58, 0xb5, 0x03, - 0x2b, 0xa7, 0x79, 0x05, 0xfb, 0x49, 0x23, 0x2d, 0xee, 0x2f, 0xed, 0x10, 0xbb, 0xfa, 0xc9, 0x28, - 0x6e, 0x64, 0xdf, 0x4f, 0x5c, 0xa0, 0xfc, 0x25, 0x41, 0xc6, 0xa4, 0x07, 0xf0, 0xe4, 0x7a, 0xf9, - 0x68, 0x0d, 0xf4, 0x9f, 0xf5, 0xde, 0xad, 0x2e, 0x7f, 0x86, 0xce, 0xe0, 0x24, 0x1d, 0x30, 0xd5, - 0xee, 0x85, 0x75, 0xa5, 0xb6, 0xba, 0x9a, 0x7e, 0x69, 0x69, 0xa6, 0x39, 0x68, 0xe9, 0x6d, 0xd5, - 0xba, 0x56, 0x4d, 0xb3, 0x75, 0xa9, 0xca, 0xd2, 0x46, 0xba, 0xa1, 0x9a, 0x37, 0x3d, 0xdd, 0x5c, - 0xd2, 0x73, 0xe8, 0x25, 0x9c, 0xad, 0xa5, 0xbf, 0x51, 0x0d, 0xed, 0x42, 0x6b, 0xb7, 0xfa, 0x5a, - 0x4f, 0x4f, 0x52, 0xb6, 0x94, 0x3f, 0x25, 0xd8, 0x89, 0x86, 0x03, 0x21, 0x28, 0x45, 0x53, 0x91, - 0xaa, 0x77, 0x89, 0xdd, 0xa8, 0x7a, 0x47, 0xd3, 0x2f, 0x65, 0x09, 0x7d, 0x0e, 0x72, 0x8c, 0x45, - 0x25, 0x74, 0xd4, 0x8e, 0x9c, 0x4b, 0xa1, 0xe6, 0xa0, 0xdd, 0x56, 0xd5, 0x10, 0xdd, 0x42, 0x87, - 0xf0, 0x34, 0x46, 0x2f, 0x5a, 0x5a, 0x57, 0xed, 0x58, 0x7d, 0xed, 0x5a, 0xed, 0x0d, 0xfa, 0xf2, - 0x36, 0xfa, 0x12, 0x6a, 0xd9, 0x90, 0xa6, 0xb7, 0x7b, 0x86, 0xa1, 0xb6, 0xfb, 0xc9, 0xd1, 0xe4, - 0x47, 0xca, 0x2d, 0x28, 0x37, 0x81, 0x3f, 0xc4, 0x8c, 0xa5, 0x3c, 0x99, 0x34, 0x7e, 0xb1, 0x3e, - 0x5f, 0xc2, 0xb6, 0x63, 0x73, 0x5b, 0xbc, 0xa2, 0x42, 0xf3, 0xd9, 0x87, 0x9d, 0x1c, 0xb7, 0xc6, - 0x10, 0x54, 0x65, 0x00, 0xb5, 0x8d, 0xc2, 0x53, 0x6f, 0xfe, 0x29, 0xb2, 0x26, 0x1c, 0xbf, 0xc1, - 0x01, 0x19, 0xcd, 0x37, 0x95, 0xdb, 0xc8, 0xe8, 0x56, 0xd7, 0x0f, 0x5e, 0x2c, 0xfa, 0x0b, 0x3c, - 0xdf, 0x24, 0x1a, 0x96, 0xfa, 0x7f, 0x25, 0xff, 0xce, 0x01, 0xba, 0x7f, 0x08, 0xd4, 0x5d, 0x59, - 0xd4, 0x92, 0xd8, 0xac, 0x27, 0x1b, 0x4f, 0xbe, 0x7e, 0x57, 0xaf, 0x6e, 0x86, 0xdc, 0xfd, 0xcd, - 0x80, 0xe2, 0xba, 0xb7, 0xc4, 0xca, 0x12, 0xbf, 0xb3, 0x9f, 0xb7, 0xed, 0x95, 0xcf, 0xdb, 0x09, - 0xc8, 0x71, 0x90, 0x11, 0x97, 0xda, 0x7c, 0x16, 0x60, 0xb1, 0x63, 0x8b, 0x46, 0x39, 0xc2, 0xcd, - 0x05, 0xac, 0xfc, 0xfe, 0xd0, 0xa9, 0x6c, 0xc0, 0xe9, 0xda, 0xb9, 0x69, 0x5f, 0xb5, 0xba, 0x5d, - 0x55, 0xbf, 0xfc, 0xf4, 0xb1, 0x6c, 0xfe, 0x9b, 0x83, 0x42, 0xaa, 0x6d, 0xa8, 0x05, 0x8f, 0x4d, - 0xcc, 0x18, 0xf1, 0x29, 0x3a, 0xcc, 0x74, 0x56, 0x60, 0xb1, 0x49, 0xaa, 0x07, 0x1f, 0x0a, 0x4d, - 0xbd, 0x79, 0x5d, 0xfa, 0x46, 0x42, 0xdf, 0xc1, 0x76, 0x78, 0x23, 0x40, 0xfb, 0x29, 0x52, 0xea, - 0x3e, 0x91, 0x49, 0xce, 0x5c, 0x1d, 0xe6, 0x70, 0xb4, 0xc1, 0xf6, 0xe8, 0x2c, 0x9d, 0xf7, 0xd1, - 0xb9, 0xab, 0x7e, 0xf5, 0x50, 0x7a, 0x68, 0xd1, 0x77, 0x50, 0x5d, 0xef, 0x62, 0xf4, 0x75, 0x4a, - 0xea, 0xa3, 0x13, 0x54, 0x3d, 0x7d, 0x20, 0x7b, 0xea, 0xcd, 0x7f, 0x7a, 0xfd, 0xeb, 0x0f, 0x2e, - 0xe1, 0xe3, 0xd9, 0xdb, 0xc6, 0xd0, 0x9f, 0x9c, 0x4f, 0xc3, 0x6f, 0xa8, 0x47, 0x31, 0x7f, 0xef, - 0x07, 0x77, 0xe7, 0xae, 0x1f, 0x4a, 0x9c, 0x8b, 0x3b, 0xd9, 0x79, 0x22, 0xf9, 0x3a, 0xf9, 0xf5, - 0x76, 0x47, 0x84, 0x5e, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0xba, 0x69, 0xdf, 0x8b, 0xe0, 0x09, - 0x00, 0x00, + // 969 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdd, 0x6e, 0xe2, 0x46, + 0x14, 0xae, 0x49, 0x36, 0x1b, 0x0e, 0x04, 0xac, 0xd9, 0x6e, 0x42, 0x88, 0xb6, 0x4b, 0xac, 0x6a, + 0x45, 0xd2, 0x86, 0x74, 0x59, 0xa9, 0x7f, 0x7b, 0x45, 0xc1, 0x49, 0xac, 0x12, 0x13, 0xd9, 0x90, + 0x48, 0xbd, 0xb1, 0xbc, 0x78, 0x30, 0xa3, 0x98, 0x31, 0xf5, 0x0c, 0xbb, 0xe2, 0x01, 0x7a, 0xd7, + 0x57, 0xe8, 0x73, 0xf4, 0x99, 0xda, 0x67, 0xe8, 0x45, 0xe5, 0xb1, 0x31, 0x36, 0xd9, 0xb0, 0x51, + 0xb4, 0x77, 0xf8, 0x3b, 0xdf, 0xf9, 0x38, 0x73, 0xfc, 0x9d, 0xe3, 0x01, 0xc4, 0xb0, 0x37, 0xb2, + 0xc6, 0xd8, 0xf6, 0x08, 0x75, 0x1b, 0xd3, 0xc0, 0xe7, 0x3e, 0xca, 0xb3, 0xd9, 0x14, 0x07, 0xd4, + 0x77, 0x70, 0xb5, 0x3c, 0xf4, 0x27, 0x13, 0x9f, 0x5a, 0x8c, 0x46, 0x31, 0xe5, 0x18, 0x0a, 0x57, + 0x84, 0xba, 0x06, 0xfe, 0x7d, 0x86, 0x19, 0x47, 0x07, 0x90, 0x67, 0x98, 0x3a, 0x38, 0xb0, 0x88, + 0x53, 0x91, 0x6a, 0x52, 0x3d, 0x6f, 0x6c, 0x47, 0x80, 0xe6, 0x28, 0x7f, 0x4a, 0x50, 0x8c, 0xc8, + 0x6c, 0xea, 0x53, 0x86, 0xd7, 0xb2, 0xd1, 0x4b, 0x28, 0x04, 0x78, 0x88, 0xc9, 0xfb, 0x28, 0x9c, + 0x13, 0x61, 0x58, 0x40, 0x9a, 0x13, 0x66, 0x13, 0x66, 0xf9, 0xd4, 0x23, 0x14, 0x57, 0x36, 0x6a, + 0x52, 0x7d, 0xdb, 0xd8, 0x26, 0xac, 0x27, 0x9e, 0xd1, 0x21, 0x14, 0x03, 0xf1, 0x37, 0x0e, 0x76, + 0x2c, 0x9b, 0x57, 0x36, 0x45, 0x7a, 0x21, 0xc1, 0x5a, 0x5c, 0xf9, 0x77, 0x1b, 0xca, 0x26, 0xf6, + 0x46, 0x17, 0xd1, 0x61, 0x3b, 0x36, 0xb7, 0xd1, 0x0b, 0x80, 0x09, 0x66, 0xcc, 0x76, 0xf1, 0xb2, + 0xa4, 0x7c, 0x8c, 0x68, 0x0e, 0xd2, 0xa0, 0xb8, 0x08, 0xf3, 0xf9, 0x14, 0x8b, 0xa2, 0x4a, 0xcd, + 0x57, 0x8d, 0xa4, 0x41, 0x8d, 0x15, 0xc1, 0x46, 0x4c, 0xef, 0xcf, 0xa7, 0xd8, 0x28, 0xa4, 0x1e, + 0x50, 0x17, 0xe4, 0xe1, 0xd8, 0xf6, 0x3c, 0x4c, 0x5d, 0x6c, 0x31, 0x6e, 0xf3, 0x19, 0x13, 0x87, + 0x28, 0x35, 0x0f, 0xd7, 0xc8, 0x45, 0x44, 0xa3, 0x9c, 0xa4, 0x9a, 0x02, 0x40, 0x6d, 0xf8, 0x6a, + 0x82, 0x83, 0x5b, 0x0f, 0x07, 0xbe, 0xcf, 0xad, 0x0f, 0x63, 0x4c, 0xad, 0x94, 0x3a, 0xa6, 0x8b, + 0x06, 0x1c, 0x2c, 0x59, 0x37, 0x63, 0x4c, 0xdb, 0x89, 0x0c, 0xa6, 0x1c, 0xfd, 0x0c, 0xfb, 0x8b, + 0x24, 0x42, 0x5d, 0x6b, 0x62, 0x33, 0x1e, 0x95, 0x11, 0xf6, 0xe2, 0x89, 0xc8, 0xdf, 0x4b, 0x11, + 0x2e, 0x93, 0xb8, 0xe6, 0xa0, 0x1f, 0xa1, 0x12, 0xf7, 0xf6, 0x6e, 0xea, 0x96, 0x48, 0xdd, 0x5d, + 0xc6, 0x33, 0x99, 0xdf, 0xc3, 0x5e, 0x80, 0x87, 0x3e, 0x65, 0x3c, 0x98, 0x0d, 0x39, 0x76, 0xac, + 0x11, 0xf1, 0xb0, 0x35, 0xb6, 0xd9, 0xb8, 0xf2, 0xb4, 0x26, 0xd5, 0x8b, 0xc6, 0xf3, 0x4c, 0xf8, + 0x8c, 0x78, 0xf8, 0xc2, 0x66, 0x63, 0xd4, 0x83, 0xd2, 0xf2, 0x88, 0x61, 0x4e, 0x65, 0xbb, 0x26, + 0xd5, 0x0b, 0xcd, 0xfa, 0x9a, 0xf6, 0x25, 0x09, 0xa1, 0x8a, 0xb1, 0x93, 0x79, 0x0c, 0x2d, 0xb3, + 0x14, 0x24, 0x4e, 0x25, 0x1f, 0x59, 0x26, 0xc1, 0x34, 0x07, 0x29, 0xb0, 0x13, 0x60, 0xd7, 0xe2, + 0x64, 0x78, 0x8b, 0x79, 0xc8, 0x81, 0x85, 0xad, 0xdc, 0xbe, 0xc0, 0x34, 0x07, 0xd5, 0x41, 0xb6, + 0x87, 0x9c, 0xf8, 0x34, 0x45, 0x2b, 0x08, 0x5a, 0x29, 0xc2, 0x13, 0xe6, 0x2b, 0x28, 0x13, 0x16, + 0xbe, 0x1d, 0x86, 0x63, 0x6e, 0xa5, 0x28, 0x6c, 0xbc, 0x43, 0x98, 0x19, 0xa2, 0x11, 0x13, 0x29, + 0x50, 0x14, 0xa4, 0xb0, 0x4a, 0xcd, 0x61, 0x95, 0x9d, 0xda, 0x46, 0x3d, 0x6f, 0x64, 0xb0, 0x6a, + 0x07, 0x56, 0x4e, 0xf3, 0x06, 0x76, 0x93, 0x46, 0x5a, 0xdc, 0x5f, 0xda, 0x21, 0x76, 0xf5, 0xb3, + 0x51, 0xdc, 0xc8, 0xbe, 0x9f, 0xb8, 0x40, 0xf9, 0x5b, 0x82, 0x8c, 0x49, 0xf7, 0xe0, 0xd9, 0xe5, + 0xf2, 0xd1, 0x1a, 0xe8, 0xbf, 0xea, 0xbd, 0x1b, 0x5d, 0xfe, 0x02, 0x9d, 0xc0, 0x51, 0x3a, 0x60, + 0xaa, 0xdd, 0x33, 0xeb, 0x42, 0x6d, 0x75, 0x35, 0xfd, 0xdc, 0xd2, 0x4c, 0x73, 0xd0, 0xd2, 0xdb, + 0xaa, 0x75, 0xa9, 0x9a, 0x66, 0xeb, 0x5c, 0x95, 0xa5, 0xb5, 0x74, 0x43, 0x35, 0xaf, 0x7a, 0xba, + 0xb9, 0xa4, 0xe7, 0xd0, 0x6b, 0x38, 0xb9, 0x97, 0x7e, 0xad, 0x1a, 0xda, 0x99, 0xd6, 0x6e, 0xf5, + 0xb5, 0x9e, 0x9e, 0xa4, 0x6c, 0x28, 0x7f, 0x49, 0xb0, 0x15, 0x0d, 0x07, 0x42, 0x50, 0x8a, 0xa6, + 0x22, 0x55, 0xef, 0x12, 0xbb, 0x52, 0xf5, 0x8e, 0xa6, 0x9f, 0xcb, 0x12, 0xfa, 0x12, 0xe4, 0x18, + 0x8b, 0x4a, 0xe8, 0xa8, 0x1d, 0x39, 0x97, 0x42, 0xcd, 0x41, 0xbb, 0xad, 0xaa, 0x21, 0xba, 0x81, + 0xf6, 0xe1, 0x79, 0x8c, 0x9e, 0xb5, 0xb4, 0xae, 0xda, 0xb1, 0xfa, 0xda, 0xa5, 0xda, 0x1b, 0xf4, + 0xe5, 0x4d, 0xf4, 0x35, 0xd4, 0xb2, 0x21, 0x4d, 0x6f, 0xf7, 0x0c, 0x43, 0x6d, 0xf7, 0x93, 0xa3, + 0xc9, 0x4f, 0x94, 0x1b, 0x50, 0xae, 0x02, 0x7f, 0x88, 0x19, 0x4b, 0x79, 0x32, 0x69, 0xfc, 0x62, + 0x7d, 0xbe, 0x86, 0x4d, 0xc7, 0xe6, 0xb6, 0x78, 0x45, 0x85, 0xe6, 0x8b, 0x8f, 0x3b, 0x39, 0x6e, + 0x8d, 0x21, 0xa8, 0xca, 0x00, 0x6a, 0x6b, 0x85, 0xa7, 0xde, 0xfc, 0x31, 0xb2, 0xd7, 0x70, 0x78, + 0x8d, 0x03, 0x32, 0x9a, 0x7f, 0xe6, 0x72, 0xfb, 0xf0, 0x72, 0x9d, 0xee, 0x23, 0xab, 0xfd, 0x27, + 0x07, 0xe8, 0x6e, 0x10, 0x75, 0x57, 0xd6, 0xb5, 0x24, 0xf6, 0xeb, 0xd1, 0x5a, 0xc5, 0xfb, 0x37, + 0xf6, 0xea, 0x7e, 0xc8, 0xdd, 0xdd, 0x0f, 0x28, 0x2e, 0x7d, 0x43, 0x2c, 0x2e, 0xf1, 0x3b, 0xfb, + 0x91, 0xdb, 0x5c, 0xf9, 0xc8, 0x1d, 0x81, 0x1c, 0x07, 0x19, 0x71, 0xa9, 0xcd, 0x67, 0x01, 0x16, + 0x9b, 0xb6, 0x68, 0x94, 0x23, 0xdc, 0x5c, 0xc0, 0xca, 0x1f, 0x0f, 0x9d, 0xcd, 0x06, 0x1c, 0xdf, + 0x3b, 0x3d, 0xed, 0x8b, 0x56, 0xb7, 0xab, 0xea, 0xe7, 0x8f, 0x1f, 0xce, 0xe6, 0x7f, 0x39, 0x28, + 0xa4, 0xda, 0x86, 0x5a, 0xf0, 0xd4, 0xc4, 0x8c, 0x11, 0x9f, 0xa2, 0xfd, 0x4c, 0x67, 0x05, 0x16, + 0x5b, 0xa5, 0xba, 0xf7, 0xb1, 0xd0, 0xd4, 0x9b, 0xd7, 0xa5, 0xef, 0x24, 0xf4, 0x03, 0x6c, 0x86, + 0xf7, 0x02, 0xb4, 0x9b, 0x22, 0xa5, 0x6e, 0x15, 0x99, 0xe4, 0xcc, 0x05, 0x62, 0x0e, 0x07, 0x6b, + 0xcc, 0x8f, 0x4e, 0xd2, 0x79, 0x9f, 0x9c, 0xbe, 0xea, 0x37, 0x0f, 0xa5, 0x87, 0x2e, 0x7d, 0x0f, + 0xd5, 0xfb, 0x8d, 0x8c, 0xbe, 0x4d, 0x49, 0x7d, 0x72, 0x8e, 0xaa, 0xc7, 0x0f, 0x64, 0x4f, 0xbd, + 0xf9, 0x2f, 0x6f, 0x7f, 0xfb, 0xc9, 0x25, 0x7c, 0x3c, 0x7b, 0xd7, 0x18, 0xfa, 0x93, 0xd3, 0x69, + 0xf8, 0x25, 0xf5, 0x28, 0xe6, 0x1f, 0xfc, 0xe0, 0xf6, 0xd4, 0xf5, 0x43, 0x89, 0x53, 0x71, 0x33, + 0x3b, 0x4d, 0x24, 0xdf, 0x26, 0xbf, 0xde, 0x6d, 0x89, 0xd0, 0x9b, 0xff, 0x03, 0x00, 0x00, 0xff, + 0xff, 0xac, 0x1f, 0x36, 0xa5, 0xe6, 0x09, 0x00, 0x00, } diff --git a/supernode/node/grpc/client/self_healing.go b/supernode/node/grpc/client/self_healing.go index 630054bbf..a6f53f9dc 100644 --- a/supernode/node/grpc/client/self_healing.go +++ b/supernode/node/grpc/client/self_healing.go @@ -2,6 +2,8 @@ package client import ( "context" + json "github.com/json-iterator/go" + "github.com/pastelnetwork/gonode/common/types" "io" "github.com/pastelnetwork/gonode/common/errors" @@ -93,14 +95,30 @@ func (service *selfHealingGRPCClient) ProcessSelfHealingChallenge(ctx context.Co return nil } -func (service *selfHealingGRPCClient) VerifySelfHealingChallenge(ctx context.Context, challengeMessage *pb.SelfHealingData) (*pb.SelfHealingData, error) { +func (service *selfHealingGRPCClient) VerifySelfHealingChallenge(ctx context.Context, challengeMessage *pb.SelfHealingMessage) (types.SelfHealingMessage, error) { ctx = contextWithLogPrefix(ctx, service.conn.id) ctx = contextWithMDSessID(ctx, service.sessID) + res, err := service.client.VerifySelfHealingChallenge(ctx, &pb.VerifySelfHealingChallengeRequest{ Data: challengeMessage, }) + if err != nil { + return types.SelfHealingMessage{}, err + } + + msg := types.SelfHealingMessage{ + ChallengeID: res.Data.ChallengeId, + MessageType: types.SelfHealingMessageType(res.Data.MessageType), + SenderID: res.Data.SenderId, + SenderSignature: res.Data.SenderSignature, + } + + if err := json.Unmarshal(res.Data.Data, &msg.SelfHealingMessageData); err != nil { + log.WithContext(ctx).WithError(err).Error("Error un-marshaling received challenge message") + return msg, errors.Errorf("error un-marshaling the received challenge message") + } - return res.Data, err + return msg, nil } func newSelfHealingGRPCClient(conn *clientConn) node.SelfHealingChallengeInterface { diff --git a/supernode/node/grpc/server/services/supernode/self_healing.go b/supernode/node/grpc/server/services/supernode/self_healing.go index 470c9bb64..321e7eb9b 100644 --- a/supernode/node/grpc/server/services/supernode/self_healing.go +++ b/supernode/node/grpc/server/services/supernode/self_healing.go @@ -2,6 +2,8 @@ package supernode import ( "context" + json "github.com/json-iterator/go" + "github.com/pastelnetwork/gonode/common/types" "io" "github.com/pastelnetwork/gonode/common/errors" @@ -107,7 +109,20 @@ func (service *SelfHealingChallengeGRPC) ProcessSelfHealingChallenge(ctx context log.WithContext(ctx).WithField("req", scRequest).Info("Process self-healing challenge request received from gRpc client") task := service.NewSHTask() - err := task.ProcessSelfHealingChallenge(ctx, &pb.SelfHealingData{}) + + msg := types.SelfHealingMessage{ + ChallengeID: scRequest.Data.ChallengeId, + MessageType: types.SelfHealingMessageType(scRequest.Data.MessageType), + SenderID: scRequest.Data.SenderId, + SenderSignature: scRequest.Data.SenderSignature, + } + + if err := json.Unmarshal(scRequest.Data.Data, &msg.SelfHealingMessageData); err != nil { + log.WithContext(ctx).WithError(err).Error("Error un-marshaling received challenge message") + return nil, errors.Errorf("error un-marshaling the received challenge message") + } + + err := task.ProcessSelfHealingChallenge(ctx, msg) if err != nil { log.WithContext(ctx).WithError(err).Error("Error Processing Self-Healing Challenge from Server Side") } @@ -120,12 +135,24 @@ func (service *SelfHealingChallengeGRPC) VerifySelfHealingChallenge(ctx context. log.WithContext(ctx).WithField("req", scRequest).Debugf("Verify Self-Healing Request received from gRpc client") task := service.NewSHTask() - data, err := task.VerifySelfHealingChallenge(ctx, scRequest.Data) + msg := types.SelfHealingMessage{ + ChallengeID: scRequest.Data.ChallengeId, + MessageType: types.SelfHealingMessageType(scRequest.Data.MessageType), + SenderID: scRequest.Data.SenderId, + SenderSignature: scRequest.Data.SenderSignature, + } + + if err := json.Unmarshal(scRequest.Data.Data, &msg.SelfHealingMessageData); err != nil { + log.WithContext(ctx).WithError(err).Error("Error un-marshaling received challenge message") + return nil, errors.Errorf("error un-marshaling the received challenge message") + } + + _, err := task.VerifySelfHealingChallenge(ctx, msg) if err != nil { log.WithContext(ctx).WithError(err).Error("Error verifying Self-Healing") } - return &pb.VerifySelfHealingChallengeReply{Data: data}, nil + return &pb.VerifySelfHealingChallengeReply{}, nil } // NewSelfHealingChallengeGRPC returns a new SelfHealing instance. diff --git a/supernode/node/node_client_interface.go b/supernode/node/node_client_interface.go index af4e48af1..6a48e9ede 100644 --- a/supernode/node/node_client_interface.go +++ b/supernode/node/node_client_interface.go @@ -122,7 +122,7 @@ type SelfHealingChallengeInterface interface { ProcessSelfHealingChallenge(ctx context.Context, challengeMessage *pb.SelfHealingMessage) error - VerifySelfHealingChallenge(ctx context.Context, challengeMessage *pb.SelfHealingData) (*pb.SelfHealingData, error) + VerifySelfHealingChallenge(ctx context.Context, challengeMessage *pb.SelfHealingMessage) (types.SelfHealingMessage, error) } // ProcessUserdataInterface represents an interaction stream with supernodes for sending userdata. diff --git a/supernode/services/selfhealing/process_self_healing_challenge.go b/supernode/services/selfhealing/process_self_healing_challenge.go index 89c01d533..67e7721b0 100644 --- a/supernode/services/selfhealing/process_self_healing_challenge.go +++ b/supernode/services/selfhealing/process_self_healing_challenge.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "os" - "strings" "sync" "time" @@ -14,7 +13,6 @@ import ( "github.com/btcsuite/btcutil/base58" "github.com/pastelnetwork/gonode/common/errors" "github.com/pastelnetwork/gonode/common/log" - "github.com/pastelnetwork/gonode/common/storage/local" "github.com/pastelnetwork/gonode/common/types" "github.com/pastelnetwork/gonode/common/utils" "github.com/pastelnetwork/gonode/pastel" @@ -25,22 +23,21 @@ import ( // ProcessSelfHealingChallenge is called from grpc server, which processes the self-healing challenge, // and will execute the reconstruction work. -func (task *SHTask) ProcessSelfHealingChallenge(ctx context.Context, challengeMessage *pb.SelfHealingData) error { +func (task *SHTask) ProcessSelfHealingChallenge(ctx context.Context, incomingChallengeMessage types.SelfHealingMessage) error { // wait if test env so tests can mock the p2p if os.Getenv("INTEGRATION_TEST_ENV") == "true" { time.Sleep(10 * time.Second) } - regTicketKeys, actionTicketKeys, err := task.MapSymbolFileKeysFromNFTAndActionTickets(ctx) - if err != nil { - log.WithContext(ctx).WithError(err).Error("could not generate the map of symbol file keys") + // incoming challenge message validation + if err := task.validateSelfHealingChallengeIncomingData(ctx, incomingChallengeMessage); err != nil { + log.WithContext(ctx).WithError(err).Error("Error validating self-healing challenge incoming data: ") return err } - log.WithContext(ctx).Info("symbol file keys with their corresponding registered nft tickets ID have been retrieved") log.WithContext(ctx).Info("establishing connection with rq service") var rqConnection rqnode.Connection - rqConnection, err = task.StorageHandler.RqClient.Connect(ctx, task.config.RaptorQServiceAddress) + rqConnection, err := task.StorageHandler.RqClient.Connect(ctx, task.config.RaptorQServiceAddress) if err != nil { log.WithContext(ctx).WithError(err).Error("Error establishing RQ connection") } @@ -52,266 +49,225 @@ func (task *SHTask) ProcessSelfHealingChallenge(ctx context.Context, challengeMe rqService := rqConnection.RaptorQ(rqNodeConfig) log.WithContext(ctx).Info("connection established with rq service") - challengeFileHash := challengeMessage.ChallengeFile.FileHashToChallenge + log.WithContext(ctx).Info("retrieving block no and verbose") + currentBlockCount, err := task.SuperNodeService.PastelClient.GetBlockCount(ctx) + if err != nil { + log.WithContext(ctx).WithError(err).Error("could not get current block count") + return err + } + blkVerbose1, err := task.SuperNodeService.PastelClient.GetBlockVerbose1(ctx, currentBlockCount) + if err != nil { + log.WithContext(ctx).WithError(err).Error("could not get current block verbose 1") + return err + } + merkleroot := blkVerbose1.MerkleRoot + + responseMsg := types.SelfHealingMessage{ + ChallengeID: incomingChallengeMessage.ChallengeID, + SenderID: task.nodeID, + MessageType: types.SelfHealingResponseMessage, + SelfHealingMessageData: types.SelfHealingMessageData{ + ChallengerID: incomingChallengeMessage.SelfHealingMessageData.ChallengerID, + Challenge: types.SelfHealingChallengeData{ + Block: incomingChallengeMessage.SelfHealingMessageData.Challenge.Block, + Merkelroot: incomingChallengeMessage.SelfHealingMessageData.Challenge.Merkelroot, + ChallengeTickets: incomingChallengeMessage.SelfHealingMessageData.Challenge.ChallengeTickets, + Timestamp: incomingChallengeMessage.SelfHealingMessageData.Challenge.Timestamp, + }, + Response: types.SelfHealingResponseData{ + Block: currentBlockCount, + Merkelroot: merkleroot, + }, + }, + } var ( - regTicket *pastel.RegTicket + nftTicket *pastel.NFTTicket cascadeTicket *pastel.APICascadeTicket senseTicket *pastel.APISenseTicket - actionTicket *pastel.ActionRegTicket ) - regTicket, cascadeTicket, senseTicket, actionTicket, err = task.getTicketInfoFromFileHash(ctx, challengeFileHash, regTicketKeys, actionTicketKeys) - if err != nil { - log.WithContext(ctx).WithError(err).Error("Error getRelevantTicketFromFileHash") - } - log.WithContext(ctx).WithField("challenge_id", challengeMessage.ChallengeId).Info("reg ticket has been retrieved") + challengeTickets := incomingChallengeMessage.SelfHealingMessageData.Challenge.ChallengeTickets - store, err := local.OpenHistoryDB() - if err != nil { - log.WithContext(ctx).WithError(err).Error("Error Opening DB") - } - if store != nil { - defer store.CloseHistoryDB(ctx) + if challengeTickets == nil { + return nil } - //Checking Process - //1. false, nil, err - should not update the challenge to completed, so that it can be retried again - //2. false, nil, nil - reconstruction not required - //3. true, symbols, nil - reconstruction required - - var msg pb.SelfHealingData - if regTicket != nil || cascadeTicket != nil { - isReconstructionReq, availableSymbols, err := task.checkingProcess(ctx, challengeFileHash) - if err != nil && !isReconstructionReq { - log.WithContext(ctx).WithError(err).WithField("failed_challenge_id", challengeMessage.ChallengeId).Error("Error in checking process") - return err + for _, ticket := range challengeTickets { + nftTicket, cascadeTicket, senseTicket, err = task.getTicket(ctx, ticket.TxID, TicketType(ticket.TicketType)) + if err != nil { + log.WithContext(ctx).WithError(err).Error("Error getRelevantTicketFromFileHash") } + log.WithContext(ctx).WithField("challenge_id", incomingChallengeMessage.ChallengeID).Info("reg ticket has been retrieved") - if !isReconstructionReq && availableSymbols == nil { - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info(fmt.Sprintf("Reconstruction is not required for file: %s", challengeFileHash)) - - if store != nil { - log.WithContext(ctx).Println("Storing self-healing audit log") - shChallenge := types.SelfHealingChallenge{ - ChallengeID: challengeMessage.ChallengeId, - MerkleRoot: challengeMessage.MerklerootWhenChallengeSent, - FileHash: challengeMessage.ChallengeFile.FileHashToChallenge, - ChallengingNode: challengeMessage.ChallengingMasternodeId, - RespondingNode: challengeMessage.RespondingMasternodeId, - ReconstructedFileHash: []byte{}, - Status: types.ReconstructionNotRequiredSelfHealingStatus, - } + //Checking Process + //1. false, nil, err - should not update the challenge to completed, so that it can be retried again + //2. false, nil, nil - reconstruction not required + //3. true, symbols, nil - reconstruction required - _, err = store.InsertSelfHealingChallenge(shChallenge) - if err != nil { - log.WithContext(ctx).WithError(err).Error("Error storing failed challenge to DB") + if nftTicket != nil || cascadeTicket != nil { + for _, challengeFileHash := range ticket.MissingKeys { + isReconstructionReq, availableSymbols, err := task.checkingProcess(ctx, challengeFileHash) + if err != nil && !isReconstructionReq { + log.WithContext(ctx).WithError(err).WithField("failed_challenge_id", incomingChallengeMessage.ChallengeID).Error("Error in checking process") + return err } - } - return nil - } - - file, reconstructedFileHash, err := task.selfHealing(ctx, rqService, challengeMessage, regTicket, cascadeTicket, availableSymbols) - if err != nil { - log.WithContext(ctx).WithError(err).Error("Error self-healing the file") - return err - } - task.RaptorQSymbols = file - - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info("File has been reconstructed") - - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info("sending reconstructed file hash for verification") - msg = pb.SelfHealingData{ - MessageId: challengeMessage.MessageId, - MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_VERIFICATION_MESSAGE, - ChallengeStatus: pb.SelfHealingData_Status_RESPONDED, - MerklerootWhenChallengeSent: challengeMessage.MerklerootWhenChallengeSent, - ChallengingMasternodeId: challengeMessage.ChallengingMasternodeId, - RespondingMasternodeId: challengeMessage.RespondingMasternodeId, - ReconstructedFileHash: reconstructedFileHash, - ChallengeFile: &pb.SelfHealingDataChallengeFile{ - FileHashToChallenge: challengeFileHash, - }, - ChallengeId: challengeMessage.ChallengeId, - RegTicketId: regTicket.TXID, - } - if cascadeTicket != nil { - msg.ActionTicketId = actionTicket.TXID - } - } else if senseTicket != nil { - reqSelfHealing, mostCommonFile := task.senseCheckingProcess(ctx, senseTicket.DDAndFingerprintsIDs) - if err != nil { - log.WithContext(ctx).WithError(err).Error("Error in checking process for sense action ticket") - } - - if !reqSelfHealing { - log.WithContext(ctx).WithError(err).Error("self-healing not required for sense action ticket") - - if store != nil { - log.WithContext(ctx).Println("Storing self-healing audit log") - shChallenge := types.SelfHealingChallenge{ - ChallengeID: challengeMessage.ChallengeId, - MerkleRoot: challengeMessage.MerklerootWhenChallengeSent, - FileHash: challengeMessage.ChallengeFile.FileHashToChallenge, - ChallengingNode: challengeMessage.ChallengingMasternodeId, - RespondingNode: challengeMessage.RespondingMasternodeId, - ReconstructedFileHash: []byte{}, - Status: types.ReconstructionNotRequiredSelfHealingStatus, + if !isReconstructionReq && availableSymbols == nil { + log.WithContext(ctx).WithField("failed_challenge_id", incomingChallengeMessage.ChallengeID).Info(fmt.Sprintf("Reconstruction is not required for file: %s", challengeFileHash)) + + //if store != nil { + // log.WithContext(ctx).Println("Storing self-healing audit log") + // shChallenge := types.SelfHealingChallenge{ + // ChallengeID: challengeMessage.ChallengeId, + // MerkleRoot: challengeMessage.MerklerootWhenChallengeSent, + // FileHash: challengeMessage.ChallengeFile.FileHashToChallenge, + // ChallengingNode: challengeMessage.ChallengingMasternodeId, + // RespondingNode: challengeMessage.RespondingMasternodeId, + // ReconstructedFileHash: []byte{}, + // Status: types.ReconstructionNotRequiredSelfHealingStatus, + // } + // + // _, err = store.InsertSelfHealingChallenge(shChallenge) + // if err != nil { + // log.WithContext(ctx).WithError(err).Error("Error storing failed challenge to DB") + // } + //} + + return nil } - _, err = store.InsertSelfHealingChallenge(shChallenge) + file, reconstructedFileHash, err := task.selfHealing(ctx, rqService, incomingChallengeMessage, nftTicket, cascadeTicket, availableSymbols) if err != nil { - log.WithContext(ctx).WithError(err).Error("Error storing failed challenge to DB") + log.WithContext(ctx).WithError(err).Error("Error self-healing the file") + return err } + task.RaptorQSymbols = file + + responseMsg.SelfHealingMessageData.Response.RespondedTickets = append(responseMsg.SelfHealingMessageData.Response.RespondedTickets, + types.RespondedTicket{ + TxID: ticket.TxID, + TicketType: ticket.TicketType, + MissingKeys: ticket.MissingKeys, + ReconstructedFileHash: reconstructedFileHash, + }) + } + } else if senseTicket != nil { + reqSelfHealing, mostCommonFile := task.senseCheckingProcess(ctx, senseTicket.DDAndFingerprintsIDs) + if err != nil { + log.WithContext(ctx).WithError(err).Error("Error in checking process for sense action ticket") } - return nil - } - - ids, idFiles, err := task.senseSelfHealing(ctx, senseTicket, mostCommonFile) - if err != nil { - log.WithContext(ctx).WithError(err).Error("self-healing not required for sense action ticket") - return err - } - task.IDFiles = idFiles - - msg = pb.SelfHealingData{ - MessageId: challengeMessage.MessageId, - MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_VERIFICATION_MESSAGE, - ChallengeStatus: pb.SelfHealingData_Status_RESPONDED, - MerklerootWhenChallengeSent: challengeMessage.MerklerootWhenChallengeSent, - ChallengingMasternodeId: challengeMessage.ChallengingMasternodeId, - RespondingMasternodeId: challengeMessage.RespondingMasternodeId, - ReconstructedFileHash: nil, - ChallengeFile: &pb.SelfHealingDataChallengeFile{ - FileHashToChallenge: challengeFileHash, - }, - ChallengeId: challengeMessage.ChallengeId, - RegTicketId: "", - IsSenseTicket: true, - SenseFileIds: ids, - ActionTicketId: actionTicket.TXID, - } + if !reqSelfHealing { + log.WithContext(ctx).WithError(err).Error("self-healing not required for sense action ticket") + + //if store != nil { + // log.WithContext(ctx).Println("Storing self-healing audit log") + // shChallenge := types.SelfHealingChallenge{ + // ChallengeID: incomingChallengeMessage.ChallengeID, + // MerkleRoot: challengeMessage.MerklerootWhenChallengeSent, + // FileHash: challengeMessage.ChallengeFile.FileHashToChallenge, + // ChallengingNode: challengeMessage.ChallengingMasternodeId, + // RespondingNode: challengeMessage.RespondingMasternodeId, + // ReconstructedFileHash: []byte{}, + // Status: types.ReconstructionNotRequiredSelfHealingStatus, + // } + // + // _, err = store.InsertSelfHealingChallenge(shChallenge) + // if err != nil { + // log.WithContext(ctx).WithError(err).Error("Error storing failed challenge to DB") + // } + //} + + return nil + } - fileBytes, err := json.Marshal(mostCommonFile) - if err != nil { - log.WithContext(ctx).WithError(err).Error(err) - } + ids, idFiles, err := task.senseSelfHealing(ctx, senseTicket, mostCommonFile) + if err != nil { + log.WithContext(ctx).WithError(err).Error("self-healing not required for sense action ticket") + return err + } + task.IDFiles = idFiles - fileHash, err := utils.Sha3256hash(fileBytes) - if err != nil { - log.WithContext(ctx).WithError(err).Error(err) - } + fileBytes, err := json.Marshal(mostCommonFile) + if err != nil { + log.WithContext(ctx).WithError(err).Error(err) + } - msg.ReconstructedFileHash = fileHash + fileHash, err := utils.Sha3256hash(fileBytes) + if err != nil { + log.WithContext(ctx).WithError(err).Error(err) + } - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info("Self-healing completed") + responseMsg.SelfHealingMessageData.Response.RespondedTickets = append(responseMsg.SelfHealingMessageData.Response.RespondedTickets, + types.RespondedTicket{ + TxID: ticket.TxID, + TicketType: ticket.TicketType, + MissingKeys: ticket.MissingKeys, + ReconstructedFileHash: fileHash, + FileIDs: ids, + }) + } } - if err := task.sendSelfHealingVerificationMessage(ctx, &msg); err != nil { - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).WithError(err) + _, err = task.getVerifications(ctx, responseMsg) + if err != nil { + log.WithContext(ctx).WithField("failed_challenge_id", incomingChallengeMessage.ChallengeID).WithError(err) return err } - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info(fmt.Sprintf("Reconstruction is not required for file: %s", challengeFileHash)) - - if task.RaptorQSymbols != nil { - if err := task.StorageHandler.StoreRaptorQSymbolsIntoP2P(ctx, task.RaptorQSymbols, ""); err != nil { - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).WithError(err).Error("Error storing symbols to P2P") - return err - } - } /* else { - if err := task.StorageHandler.StoreListOfBytesIntoP2P(ctx, task.IDFiles); err != nil { - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).WithError(err).Error("Error storing id files to P2P") - return err - } - }*/ - - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info("Self-healing completed") - - return nil -} - -func (task *SHTask) getTicketInfoFromFileHash(ctx context.Context, challengeFileHash string, regTicketKeys, actionTicketKeys map[string]string) (*pastel.RegTicket, *pastel.APICascadeTicket, *pastel.APISenseTicket, *pastel.ActionRegTicket, error) { - log.WithContext(ctx).Info("retrieving the reg ticket based on file hash") - if _, ok := regTicketKeys[challengeFileHash]; ok { - regTicket, err := task.getRegTicket(ctx, regTicketKeys[challengeFileHash]) - if err != nil { - log.WithContext(ctx).WithError(err).Error("unable to retrieve reg ticket") - return nil, nil, nil, nil, err - } - - return ®Ticket, nil, nil, nil, nil - } else if _, ok = actionTicketKeys[challengeFileHash]; ok { - actionTicket, err := task.getActionTicket(ctx, actionTicketKeys[challengeFileHash]) - if err != nil { - log.WithContext(ctx).WithError(err).Error("unable to retrieve cascade ticket") - return nil, nil, nil, nil, err - } - - switch actionTicket.ActionTicketData.ActionType { - case pastel.ActionTypeCascade: - cascadeTicket, err := actionTicket.ActionTicketData.ActionTicketData.APICascadeTicket() - if err != nil { - log.WithContext(ctx).WithField("actionRegTickets.ActionTicketData", actionTicket.TXID). - Warnf("Could not get cascade ticket for action ticket data self-healing") - return nil, nil, nil, nil, err + /* + if task.RaptorQSymbols != nil { + if err := task.StorageHandler.StoreRaptorQSymbolsIntoP2P(ctx, task.RaptorQSymbols, ""); err != nil { + log.WithContext(ctx).WithField("failed_challenge_id", incomingChallengeMessage.ChallengeID).WithError(err).Error("Error storing symbols to P2P") + return err } - - return nil, cascadeTicket, nil, &actionTicket, nil - case pastel.ActionTypeSense: - senseTicket, err := actionTicket.ActionTicketData.ActionTicketData.APISenseTicket() - if err != nil { - log.WithContext(ctx).WithField("actionRegTickets.ActionTicketData", actionTicket.TXID). - Warnf("Could not get sense ticket for action ticket data self-healing") - return nil, nil, nil, nil, err + } else { + if err := task.StorageHandler.StoreListOfBytesIntoP2P(ctx, task.IDFiles); err != nil { + log.WithContext(ctx).WithField("failed_challenge_id", incomingChallengeMessage.ChallengeID).WithError(err).Error("Error storing id files to P2P") + return err } + }*/ - return nil, nil, senseTicket, &actionTicket, nil - } - - } + log.WithContext(ctx).WithField("failed_challenge_id", incomingChallengeMessage.ChallengeID).Info("Self-healing completed") - return nil, nil, nil, nil, errors.New("failed to find the ticket") + return nil } -func (task *SHTask) getRegTicket(ctx context.Context, regTicketID string) (regTicket pastel.RegTicket, err error) { - regTicket, err = task.PastelClient.RegTicket(ctx, regTicketID) - if err != nil { - log.WithContext(ctx).Error("Error retrieving regTicket") - return regTicket, err +func (task *SHTask) validateSelfHealingChallengeIncomingData(ctx context.Context, incomingChallengeMessage types.SelfHealingMessage) error { + if incomingChallengeMessage.MessageType != types.SelfHealingChallengeMessage { + return fmt.Errorf("incorrect message type to processing self-healing challenge") } - decTicket, err := pastel.DecodeNFTTicket(regTicket.RegTicketData.NFTTicket) + + isVerified, err := task.VerifyMessageSignature(ctx, incomingChallengeMessage) if err != nil { - log.WithContext(ctx).WithError(err).Error("Failed to decode reg ticket") - return regTicket, err + return errors.Errorf("error verifying sender's signature: %w", err) } - regTicket.RegTicketData.NFTTicketData = *decTicket + if !isVerified { + return errors.Errorf("not able to verify message signature") + } - return regTicket, nil + return nil } -func (task *SHTask) getActionTicket(ctx context.Context, actionTicketID string) (actionTicket pastel.ActionRegTicket, err error) { - actionTicket, err = task.PastelClient.ActionRegTicket(ctx, actionTicketID) +// VerifyMessageSignature verifies the sender's signature on message +func (task *SHTask) VerifyMessageSignature(ctx context.Context, msg types.SelfHealingMessage) (bool, error) { + data, err := json.Marshal(msg.SelfHealingMessageData) if err != nil { - log.WithContext(ctx).Error("Error retrieving actionRegTicket") - return actionTicket, err + return false, errors.Errorf("unable to marshal message data") } - decTicket, err := pastel.DecodeActionTicket(actionTicket.ActionTicketData.ActionTicket) + isVerified, err := task.PastelClient.Verify(ctx, data, string(msg.SenderSignature), msg.SenderID, pastel.SignAlgorithmED448) if err != nil { - log.WithContext(ctx).WithError(err).Error("Failed to decode actionRegTicket") - return actionTicket, err + return false, errors.Errorf("error verifying self-healing message: %w", err) } - actionTicket.ActionTicketData.ActionTicketData = *decTicket - return actionTicket, nil + return isVerified, nil } func (task *SHTask) checkingProcess(ctx context.Context, fileHash string) (requiredReconstruction bool, symbols map[string][]byte, err error) { - rqIDsData, err := task.P2PClient.Retrieve(ctx, fileHash, true) + rqIDsData, err := task.P2PClient.Retrieve(ctx, fileHash, false) if err != nil { log.WithContext(ctx).WithError(err).WithField("SymbolIDsFileId", fileHash).Warn("Retrieve compressed symbol IDs file from P2P failed") err = errors.Errorf("retrieve compressed symbol IDs file: %w", err) @@ -374,15 +330,15 @@ func (task *SHTask) checkingProcess(ctx context.Context, fileHash string) (requi return false, nil, nil } -func (task *SHTask) selfHealing(ctx context.Context, rqService rqnode.RaptorQ, msg *pb.SelfHealingData, regTicket *pastel.RegTicket, cascadeTicket *pastel.APICascadeTicket, symbols map[string][]byte) (file, reconstructedFileHash []byte, err error) { - log.WithContext(ctx).WithField("failed_challenge_id", msg.ChallengeId).Info("Self-healing initiated") +func (task *SHTask) selfHealing(ctx context.Context, rqService rqnode.RaptorQ, msg types.SelfHealingMessage, regTicket *pastel.NFTTicket, cascadeTicket *pastel.APICascadeTicket, symbols map[string][]byte) (file, reconstructedFileHash []byte, err error) { + log.WithContext(ctx).WithField("failed_challenge_id", msg.ChallengeID).Info("Self-healing initiated") var encodeInfo rqnode.Encode if regTicket != nil { encodeInfo = rqnode.Encode{ Symbols: symbols, EncoderParam: rqnode.EncoderParameters{ - Oti: regTicket.RegTicketData.NFTTicketData.AppTicketData.RQOti, + Oti: regTicket.AppTicketData.RQOti, }, } } else if cascadeTicket != nil { @@ -401,12 +357,14 @@ func (task *SHTask) selfHealing(ctx context.Context, rqService rqnode.RaptorQ, m } fileHash := sha3.Sum256(decodeInfo.File) - if !bytes.Equal(fileHash[:], regTicket.RegTicketData.NFTTicketData.AppTicketData.DataHash) { + if !bytes.Equal(fileHash[:], regTicket.AppTicketData.DataHash) { err = errors.New("hash file mismatched") log.WithContext(ctx).Error("hash file mismatched") return nil, nil, err } + log.WithContext(ctx).WithField("failed_challenge_id", msg.ChallengeID).Info("File has been reconstructed") + return decodeInfo.File, fileHash[:], nil } @@ -481,126 +439,171 @@ func (task *SHTask) senseSelfHealing(ctx context.Context, senseTicket *pastel.AP return ids, idFiles, nil } -func (task *SHTask) sendSelfHealingVerificationMessage(ctx context.Context, msg *pb.SelfHealingData) error { - listOfSupernodes, err := task.SuperNodeService.PastelClient.MasterNodesExtra(ctx) +func (task *SHTask) getVerifications(ctx context.Context, msg types.SelfHealingMessage) (map[string]types.SelfHealingMessage, error) { + nodesToConnect, err := task.GetNodesAddressesToConnect(ctx, msg) if err != nil { - log.WithContext(ctx).WithField("challengeID", msg.ChallengeId).WithField("method", "sendProcessSelfHealingChallenge").WithError(err).Warn("could not get Supernode extra: ", err.Error()) - return err + log.WithContext(ctx).WithError(err).Error("Error finding nodes to connect for self-healing verification") } - log.WithContext(ctx).Info(fmt.Sprintf("list of supernodes have been retrieved for process self healing challenge:%s", listOfSupernodes)) - //list of supernode ext keys without current node to find the 5 closest nodes for verification - mapSupernodesWithoutCurrentNode := make(map[string]pastel.MasterNode) - var sliceOfSupernodeKeysExceptCurrentNode []string - for _, mn := range listOfSupernodes { - if mn.ExtKey != task.nodeID { - mapSupernodesWithoutCurrentNode[mn.ExtKey] = mn - sliceOfSupernodeKeysExceptCurrentNode = append(sliceOfSupernodeKeysExceptCurrentNode, mn.ExtKey) - } + if nodesToConnect == nil { + return nil, errors.Errorf("no nodes found to connect for getting affirmations") } - log.WithContext(ctx).Info(fmt.Sprintf("current node has been filtered out from the supernodes list:%s", sliceOfSupernodeKeysExceptCurrentNode)) - //Finding 5 closest nodes to previous block hash - sliceOfSupernodesClosestToPreviousBlockHash := task.GetNClosestSupernodeIDsToComparisonString(ctx, 5, msg.MerklerootWhenChallengeSent, sliceOfSupernodeKeysExceptCurrentNode) - log.WithContext(ctx).Info(fmt.Sprintf("sliceOfSupernodesClosestToPreviousBlockHash:%s", sliceOfSupernodesClosestToPreviousBlockHash)) - - store, err := local.OpenHistoryDB() + responseMsg, err := task.prepareResponseMessage(ctx, msg) if err != nil { - log.WithContext(ctx).WithError(err).Error("Error Opening DB") - } - if store != nil { - defer store.CloseHistoryDB(ctx) - - log.WithContext(ctx).Println("Storing failed challenge to DB for self healing inspection") - shChallenge := types.SelfHealingChallenge{ - ChallengeID: msg.ChallengeId, - MerkleRoot: msg.MerklerootWhenChallengeSent, - FileHash: msg.ChallengeFile.FileHashToChallenge, - ChallengingNode: msg.ChallengingMasternodeId, - RespondingNode: msg.RespondingMasternodeId, - VerifyingNode: strings.Join(sliceOfSupernodesClosestToPreviousBlockHash, ","), - ReconstructedFileHash: msg.ReconstructedFileHash, - Status: types.InProgressSelfHealingStatus, - } + return nil, errors.Errorf("error preparing response message") + } - _, err = store.InsertSelfHealingChallenge(shChallenge) - if err != nil { - log.WithContext(ctx).WithError(err).Error("Error storing failed challenge to DB") - } + return task.processSelfHealingVerifications(ctx, nodesToConnect, &responseMsg), nil +} + +// GetNodesAddressesToConnect basically retrieves the masternode address against the pastel-id from the list and return that +func (task *SHTask) GetNodesAddressesToConnect(ctx context.Context, challengeMessage types.SelfHealingMessage) ([]pastel.MasterNode, error) { + var nodesToConnect []pastel.MasterNode + supernodes, err := task.SuperNodeService.PastelClient.MasterNodesExtra(ctx) + if err != nil { + log.WithContext(ctx).WithField("challengeID", challengeMessage.ChallengeID).WithField("method", "GetNodesAddressesToConnect").WithError(err).Warn("could not get Supernode extra: ", err.Error()) + return nil, err } - var ( - countOfFailures int - wg sync.WaitGroup - responseMessages []*pb.SelfHealingData - ) - err = nil - // iterate through supernodes, connecting and sending the message - for _, nodeToConnectTo := range sliceOfSupernodesClosestToPreviousBlockHash { - nodeToConnectTo := nodeToConnectTo - log.WithContext(ctx).WithField("outgoing_message", msg.ChallengeId).Info("outgoing message from ProcessSelfHealingChallenge") - - var ( - mn pastel.MasterNode - ok bool - ) - if mn, ok = mapSupernodesWithoutCurrentNode[nodeToConnectTo]; !ok { - log.WithContext(ctx).WithField("challengeID", msg.ChallengeId).WithField("method", "sendVerifySelfHealingChallenge").Warn(fmt.Sprintf("cannot get Supernode info of Supernode id %s", mapSupernodesWithoutCurrentNode)) + mapSupernodes := make(map[string]pastel.MasterNode) + for _, mn := range supernodes { + if mn.ExtAddress == "" || mn.ExtKey == "" { + log.WithContext(ctx).WithField("challengeID", challengeMessage.ChallengeID).WithField("method", "GetNodesAddressesToConnect"). + WithField("node_id", mn.ExtKey).Warn("node address or node id is empty") + continue } - //We use the ExtAddress of the supernode to connect - processingSupernodeAddr := mn.ExtAddress - log.WithContext(ctx).WithField("challenge_id", msg.ChallengeId).Info("Sending self-healing challenge for verification to processing supernode address: " + processingSupernodeAddr) - log.WithContext(ctx).Info(fmt.Sprintf("establishing connection with node: %s", processingSupernodeAddr)) - nodeClientConn, err := task.nodeClient.Connect(ctx, processingSupernodeAddr) - if err != nil { - log.WithContext(ctx).WithError(err).Error(fmt.Sprintf("Connection failed to establish with node: %s", processingSupernodeAddr)) - continue + mapSupernodes[mn.ExtKey] = mn + } + + logger := log.WithContext(ctx).WithField("challenge_id", challengeMessage.ChallengeID). + WithField("message_type", challengeMessage.MessageType).WithField("node_id", task.nodeID) + + switch challengeMessage.MessageType { + case types.SelfHealingResponseMessage: + + //Finding 5 closest nodes to previous block hash + sliceOfSupernodesClosestToPreviousBlockHash := task.GetNClosestSupernodeIDsToComparisonString(ctx, 5, challengeMessage.SelfHealingMessageData.Challenge.Merkelroot, []string{task.nodeID}) + log.WithContext(ctx).Info(fmt.Sprintf("sliceOfSupernodesClosestToPreviousBlockHash:%s", sliceOfSupernodesClosestToPreviousBlockHash)) + + for _, verifier := range sliceOfSupernodesClosestToPreviousBlockHash { + nodesToConnect = append(nodesToConnect, mapSupernodes[verifier]) } - defer nodeClientConn.Close() - selfHealingChallengeIF := nodeClientConn.SelfHealingChallenge() - log.WithContext(ctx).Info(fmt.Sprintf("connection established with node:%s", nodeToConnectTo)) + logger.WithField("nodes_to_connect", nodesToConnect).Info("nodes to send self-healing response msg have been selected") + return nodesToConnect, nil + default: + return nil, errors.Errorf("no nodes found to send message") + } + + return nil, err +} + +// prepareEvaluationMessage prepares the evaluation message by gathering the data required for it +func (task *SHTask) prepareResponseMessage(ctx context.Context, responseMessage types.SelfHealingMessage) (pb.SelfHealingMessage, error) { + signature, data, err := task.SignMessage(ctx, responseMessage.SelfHealingMessageData) + if err != nil { + log.WithContext(ctx).WithError(err).Error("error signing the response message") + return pb.SelfHealingMessage{}, err + } + responseMessage.SenderSignature = signature + + //if err := task.StoreChallengeMessage(ctx, challengeMessage); err != nil { + // log.WithContext(ctx).WithError(err).Error("error storing evaluation report message") + // return pb.StorageChallengeMessage{}, err + //} + // + + return pb.SelfHealingMessage{ + MessageType: pb.SelfHealingMessageMessageType(responseMessage.MessageType), + ChallengeId: responseMessage.ChallengeID, + Data: data, + SenderId: responseMessage.SenderID, + SenderSignature: responseMessage.SenderSignature, + }, nil +} + +// processSelfHealingVerifications simply sends a response msg, to receive verifications +func (task *SHTask) processSelfHealingVerifications(ctx context.Context, nodesToConnect []pastel.MasterNode, responseMsg *pb.SelfHealingMessage) (verifications map[string]types.SelfHealingMessage) { + verifications = make(map[string]types.SelfHealingMessage) + + var wg sync.WaitGroup + var mu sync.Mutex - msg.RespondingMasternodeId = mn.ExtKey + for _, node := range nodesToConnect { + node := node + log.WithContext(ctx).WithField("node_id", node.ExtAddress).Info("processing sn address") wg.Add(1) + go func() { defer wg.Done() - res, err := selfHealingChallengeIF.VerifySelfHealingChallenge(ctx, msg) + + verificationMsg, err := task.sendSelfHealingResponseMessage(ctx, responseMsg, node.ExtAddress) if err != nil { - log.WithContext(ctx).WithField("challengeID", msg.ChallengeId).WithField("verifierSuperNodeAddress", nodeToConnectTo).Warn("Storage challenge verification failed or didn't process: ", err.Error()) + log.WithContext(ctx).WithError(err).Error("error sending evaluation message for processing") return } - func() { - task.responseMessageMu.Lock() - defer task.responseMessageMu.Unlock() - responseMessages = append(responseMessages, res) - }() - - log.WithContext(ctx).WithField("challenge_id", res.ChallengeId). - Info("response has been received from verifying node") + if verificationMsg != nil && verificationMsg.ChallengeID != "" && + verificationMsg.MessageType == types.SelfHealingVerificationMessage { + //isSuccess, err := task.isVerificationSuccessful(ctx, verificationMsg) + //if err != nil { + // log.WithContext(ctx).WithField("node_id", node.ExtKey).WithError(err). + // Error("unsuccessful affirmation by observer") + //} + //log.WithContext(ctx).WithField("is_success", isSuccess).Info("affirmation message has been verified") + + mu.Lock() + verifications[verificationMsg.SenderID] = *verificationMsg + mu.Unlock() + + //if err := task.StoreChallengeMessage(ctx, *affirmationResponse); err != nil { + // log.WithContext(ctx).WithField("node_id", node.ExtKey).WithError(err). + // Error("error storing affirmation response from observer") + //} + } }() - wg.Wait() - } - if err == nil { - log.WithContext(ctx).Println("After calling storage process on " + msg.ChallengeId + " no nodes returned an error code in verification") } - //Counting the number of challenges being failed by verifying nodes. - for i := 0; i < len(responseMessages); i++ { - if responseMessages[i].ChallengeStatus == pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE { - countOfFailures++ - } + wg.Wait() + + return verifications +} + +// sendSelfHealingResponseMessage establish a connection with the processingSupernodeAddr and sends response msg to +// receive verification upon it +func (task *SHTask) sendSelfHealingResponseMessage(ctx context.Context, challengeMessage *pb.SelfHealingMessage, processingSupernodeAddr string) (*types.SelfHealingMessage, error) { + log.WithContext(ctx).WithField("challenge_id", challengeMessage.ChallengeId).Info("Sending response message to supernode address: " + processingSupernodeAddr) + + //Connect over grpc + nodeClientConn, err := task.nodeClient.Connect(ctx, processingSupernodeAddr) + if err != nil { + err = fmt.Errorf("Could not use node client to connect to: " + processingSupernodeAddr) + log.WithContext(ctx). + WithField("challengeID", challengeMessage.ChallengeId). + WithField("method", "sendEvaluationMessage"). + WithField("node_address", processingSupernodeAddr). + Warn(err.Error()) + return nil, err } + defer nodeClientConn.Close() - if countOfFailures > 0 { - log.WithContext(ctx).Info(fmt.Sprintf("self-healing verification failed by verifiers:%d, symbols should not be updated in P2P", countOfFailures)) - return errors.New("self-healing verification failed") + selfHealingChallengeIF := nodeClientConn.SelfHealingChallenge() + + affirmationResult, err := selfHealingChallengeIF.VerifySelfHealingChallenge(ctx, challengeMessage) + if err != nil { + log.WithContext(ctx). + WithField("challengeID", challengeMessage.ChallengeId). + WithField("method", "sendEvaluationMessage"). + WithField("node_address", processingSupernodeAddr). + Warn(err.Error()) + return nil, err } - return nil + log.WithContext(ctx).WithField("affirmation:", affirmationResult).Info("affirmation result received") + return &affirmationResult, nil } // DownloadDDAndFingerprints gets dd and fp file from ticket based on id and returns the file. diff --git a/supernode/services/selfhealing/process_self_healing_challenge_test.go b/supernode/services/selfhealing/process_self_healing_challenge_test.go index 5ad463cf1..25d538fb5 100644 --- a/supernode/services/selfhealing/process_self_healing_challenge_test.go +++ b/supernode/services/selfhealing/process_self_healing_challenge_test.go @@ -3,6 +3,7 @@ package selfhealing import ( "bytes" "context" + "github.com/pastelnetwork/gonode/common/types" "strconv" "testing" @@ -75,29 +76,31 @@ func TestProcessSelfHealingTest(t *testing.T) { tests := []struct { testcase string - message *pb.SelfHealingData + message types.SelfHealingMessage setup func() expect func(*testing.T, error) }{ { testcase: "when symbols do not mismatch, does not proceed self-healing", - message: &pb.SelfHealingData{ - MessageId: "test-message-1", - MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_ISSUANCE_MESSAGE, - MerklerootWhenChallengeSent: "previous-block-hash", - ChallengingMasternodeId: "challenging-node", - RespondingMasternodeId: "respondong-node", - ChallengeFile: &pb.SelfHealingDataChallengeFile{ - FileHashToChallenge: "file-hash-to-challenge", - }, - ChallengeId: "test-challenge-1", - RegTicketId: "reg-ticket-tx-id", - }, + message: types.SelfHealingMessage{MessageType: types.SelfHealingChallengeMessage}, + // MessageId: "test-message-1", + // MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_ISSUANCE_MESSAGE, + // MerklerootWhenChallengeSent: "previous-block-hash", + // ChallengingMasternodeId: "challenging-node", + // RespondingMasternodeId: "respondong-node", + // ChallengeFile: &pb.SelfHealingDataChallengeFile{ + // FileHashToChallenge: "file-hash-to-challenge", + // }, + // ChallengeId: "test-challenge-1", + // RegTicketId: "reg-ticket-tx-id", + //}, setup: func() { + pastelClient.ListenOnVerify(true, nil).ListenOnGetBlockVerbose1(&pastel.GetBlockVerbose1Result{}, nil) pastelClient.ListenOnRegTickets(tickets, nil).ListenOnActionTickets(nil, nil).ListenOnMasterNodesExtra(nodes, nil) raptorQClient.ListenOnConnect(nil) raptorQClient.ListenOnRaptorQ().ListenOnClose(nil) pastelClient.On("RegTicket", mock.Anything, mock.Anything).Return(ticket, nil).Times(1) + pastelClient.ListenOnGetBlockCount(0, nil) p2pClient.On(p2pMock.RetrieveMethod, mock.Anything, mock.Anything, mock.Anything).Return(rqIDsData, nil).Times(1) p2pClient.On(p2pMock.RetrieveMethod, mock.Anything, mock.Anything, mock.Anything).Return(symbol, nil).Times(1) raptorQClient.ListenOnClose(nil) @@ -109,18 +112,19 @@ func TestProcessSelfHealingTest(t *testing.T) { }, { testcase: "when symbols mismatch, should self-heal", - message: &pb.SelfHealingData{ - MessageId: "test-message-1", - MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_ISSUANCE_MESSAGE, - MerklerootWhenChallengeSent: "previous-block-hash", - ChallengingMasternodeId: "challenging-node", - RespondingMasternodeId: "respondong-node", - ChallengeFile: &pb.SelfHealingDataChallengeFile{ - FileHashToChallenge: "file-hash-to-challenge", - }, - ChallengeId: "test-challenge-1", - RegTicketId: "reg-ticket-tx-id", - }, + message: types.SelfHealingMessage{MessageType: types.SelfHealingChallengeMessage}, + //message: &pb.SelfHealingData{ + // MessageId: "test-message-1", + // MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_ISSUANCE_MESSAGE, + // MerklerootWhenChallengeSent: "previous-block-hash", + // ChallengingMasternodeId: "challenging-node", + // RespondingMasternodeId: "respondong-node", + // ChallengeFile: &pb.SelfHealingDataChallengeFile{ + // FileHashToChallenge: "file-hash-to-challenge", + // }, + // ChallengeId: "test-challenge-1", + // RegTicketId: "reg-ticket-tx-id", + //}, setup: func() { pastelClient.ListenOnRegTickets(tickets, nil).ListenOnActionTickets(nil, nil).ListenOnMasterNodesExtra(nodes, nil) raptorQClient.ListenOnConnect(nil) @@ -149,18 +153,19 @@ func TestProcessSelfHealingTest(t *testing.T) { }, { testcase: "when self-healing verification failed, should not store symbols into P2P", - message: &pb.SelfHealingData{ - MessageId: "test-message-1", - MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_ISSUANCE_MESSAGE, - MerklerootWhenChallengeSent: "previous-block-hash", - ChallengingMasternodeId: "challenging-node", - RespondingMasternodeId: "respondong-node", - ChallengeFile: &pb.SelfHealingDataChallengeFile{ - FileHashToChallenge: "file-hash-to-challenge", - }, - ChallengeId: "test-challenge-1", - RegTicketId: "reg-ticket-tx-id", - }, + message: types.SelfHealingMessage{}, + //message: &pb.SelfHealingData{ + // MessageId: "test-message-1", + // MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_ISSUANCE_MESSAGE, + // MerklerootWhenChallengeSent: "previous-block-hash", + // ChallengingMasternodeId: "challenging-node", + // RespondingMasternodeId: "respondong-node", + // ChallengeFile: &pb.SelfHealingDataChallengeFile{ + // FileHashToChallenge: "file-hash-to-challenge", + // }, + // ChallengeId: "test-challenge-1", + // RegTicketId: "reg-ticket-tx-id", + //}, setup: func() { pastelClient.ListenOnRegTickets(tickets, nil).ListenOnMasterNodesExtra(nodes, nil) raptorQClient.ListenOnConnect(nil) diff --git a/supernode/services/selfhealing/self_healing_worker.go b/supernode/services/selfhealing/self_healing_worker.go index 2d68fd778..4085156bf 100644 --- a/supernode/services/selfhealing/self_healing_worker.go +++ b/supernode/services/selfhealing/self_healing_worker.go @@ -441,10 +441,10 @@ func (task *SHTask) prepareAndSendSelfHealingMessage(ctx context.Context, challe ChallengerID: task.nodeID, RecipientID: challengeRecipient, Challenge: types.SelfHealingChallengeData{ - Block: currentBlockCount, - Merkelroot: merkleroot, - Timestamp: time.Now().UTC(), - Tickets: getTicketsForSelfHealingChallengeMessage(ticketsDetails), + Block: currentBlockCount, + Merkelroot: merkleroot, + Timestamp: time.Now().UTC(), + ChallengeTickets: getTicketsForSelfHealingChallengeMessage(ticketsDetails), }, } @@ -471,10 +471,10 @@ func (task *SHTask) prepareAndSendSelfHealingMessage(ctx context.Context, challe return nil } -func getTicketsForSelfHealingChallengeMessage(ticketDetails []SymbolFileKeyDetails) []types.Ticket { - var challengeTickets []types.Ticket +func getTicketsForSelfHealingChallengeMessage(ticketDetails []SymbolFileKeyDetails) []types.ChallengeTicket { + var challengeTickets []types.ChallengeTicket for _, detail := range ticketDetails { - challengeTickets = append(challengeTickets, types.Ticket{ + challengeTickets = append(challengeTickets, types.ChallengeTicket{ TxID: detail.TicketTxID, TicketType: types.TicketType(detail.TicketType), DataHash: detail.DataHash, diff --git a/supernode/services/selfhealing/verify_self_healing_challenge.go b/supernode/services/selfhealing/verify_self_healing_challenge.go index d839c69d1..de6f8d69f 100644 --- a/supernode/services/selfhealing/verify_self_healing_challenge.go +++ b/supernode/services/selfhealing/verify_self_healing_challenge.go @@ -4,67 +4,42 @@ import ( "bytes" "context" "fmt" - "github.com/pastelnetwork/gonode/common/errors" - "github.com/pastelnetwork/gonode/common/log" "github.com/pastelnetwork/gonode/common/storage" - "github.com/pastelnetwork/gonode/common/storage/local" "github.com/pastelnetwork/gonode/common/types" "github.com/pastelnetwork/gonode/pastel" - pb "github.com/pastelnetwork/gonode/proto/supernode" rqnode "github.com/pastelnetwork/gonode/raptorq/node" ) // VerifySelfHealingChallenge verifies the self-healing challenge -func (task *SHTask) VerifySelfHealingChallenge(ctx context.Context, challengeMessage *pb.SelfHealingData) (*pb.SelfHealingData, error) { - log.WithContext(ctx).WithField("challenge_id", challengeMessage.ChallengeId).Info("VerifySelfHealingChallenge has been invoked") +func (task *SHTask) VerifySelfHealingChallenge(ctx context.Context, incomingResponseMessage types.SelfHealingMessage) (*types.SelfHealingMessage, error) { + log.WithContext(ctx).WithField("challenge_id", incomingResponseMessage.ChallengeID).Info("VerifySelfHealingChallenge has been invoked") - log.WithContext(ctx).WithField("challenge_id", challengeMessage.ChallengeId).Info("retrieving reg ticket") + // incoming challenge message validation + if err := task.validateSelfHealingResponseIncomingData(ctx, incomingResponseMessage); err != nil { + log.WithContext(ctx).WithError(err).Error("Error validating self-healing challenge incoming data: ") + return nil, err + } var ( - regTicket pastel.RegTicket + nftTicket *pastel.NFTTicket cascadeTicket *pastel.APICascadeTicket senseTicket *pastel.APISenseTicket - actionTicket pastel.ActionRegTicket - err error ) - if challengeMessage.RegTicketId != "" { - regTicket, err = task.getRegTicket(ctx, challengeMessage.RegTicketId) - if err != nil { - log.WithContext(ctx).WithError(err).Error("unable to retrieve reg ticket") - } - - log.WithContext(ctx).WithField("challenge_id", challengeMessage.ChallengeId).Info("reg ticket has been retrieved") - } else if challengeMessage.ActionTicketId != "" { - actionTicket, err = task.getActionTicket(ctx, challengeMessage.ActionTicketId) - if err != nil { - log.WithContext(ctx).WithError(err).Error("unable to retrieve cascade ticket") - return nil, err - } - - if challengeMessage.IsSenseTicket { - senseTicket, err = actionTicket.ActionTicketData.ActionTicketData.APISenseTicket() - if err != nil { - log.WithContext(ctx).WithField("actionRegTickets.ActionTicketData", actionTicket.TXID). - Warnf("Could not get sense ticket for action ticket data verify self healing") - return nil, err - } - } else { - cascadeTicket, err = actionTicket.ActionTicketData.ActionTicketData.APICascadeTicket() - if err != nil { - log.WithContext(ctx).WithField("actionRegTickets.ActionTicketData", actionTicket.TXID). - Warnf("Could not get cascade ticket for action ticket data verify self healing") - return nil, err - } - } - - log.WithContext(ctx).WithField("challenge_id", challengeMessage.ChallengeId).Info("cascade reg ticket has been retrieved") - } else { - log.WithContext(ctx).Info("unable to find reg ticket") - return nil, errors.New("reg ticket not found") + log.WithContext(ctx).Info("retrieving block no and verbose") + currentBlockCount, err := task.SuperNodeService.PastelClient.GetBlockCount(ctx) + if err != nil { + log.WithContext(ctx).WithError(err).Error("could not get current block count") + return nil, err + } + blkVerbose1, err := task.SuperNodeService.PastelClient.GetBlockVerbose1(ctx, currentBlockCount) + if err != nil { + log.WithContext(ctx).WithError(err).Error("could not get current block verbose 1") + return nil, err } + merkleroot := blkVerbose1.MerkleRoot log.WithContext(ctx).Info("establishing connection with rq service") var rqConnection rqnode.Connection @@ -80,40 +55,29 @@ func (task *SHTask) VerifySelfHealingChallenge(ctx context.Context, challengeMes rqService := rqConnection.RaptorQ(rqNodeConfig) log.WithContext(ctx).Info("connection established with rq service") - challengeFileHash := challengeMessage.ChallengeFile.FileHashToChallenge - log.WithContext(ctx).Info("retrieving reg ticket") - - responseMessage := &pb.SelfHealingData{ - MessageId: challengeMessage.MessageId, - MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_RESPONSE_MESSAGE, - MerklerootWhenChallengeSent: challengeMessage.MerklerootWhenChallengeSent, - ChallengingMasternodeId: task.nodeID, - RespondingMasternodeId: challengeMessage.ChallengingMasternodeId, - ChallengeFile: &pb.SelfHealingDataChallengeFile{ - FileHashToChallenge: challengeMessage.ChallengeFile.FileHashToChallenge, + verificationMsg := &types.SelfHealingMessage{ + ChallengeID: incomingResponseMessage.ChallengeID, + SenderID: task.nodeID, + MessageType: types.SelfHealingResponseMessage, + SelfHealingMessageData: types.SelfHealingMessageData{ + ChallengerID: incomingResponseMessage.SelfHealingMessageData.ChallengerID, + Challenge: types.SelfHealingChallengeData{ + Block: incomingResponseMessage.SelfHealingMessageData.Challenge.Block, + Merkelroot: incomingResponseMessage.SelfHealingMessageData.Challenge.Merkelroot, + ChallengeTickets: incomingResponseMessage.SelfHealingMessageData.Challenge.ChallengeTickets, + Timestamp: incomingResponseMessage.SelfHealingMessageData.Challenge.Timestamp, + }, + Response: types.SelfHealingResponseData{ + Block: incomingResponseMessage.SelfHealingMessageData.Response.Block, + Merkelroot: incomingResponseMessage.SelfHealingMessageData.Response.Merkelroot, + Timestamp: incomingResponseMessage.SelfHealingMessageData.Response.Timestamp, + RespondedTickets: incomingResponseMessage.SelfHealingMessageData.Response.RespondedTickets, + }, + Verification: types.SelfHealingVerificationData{ + Block: currentBlockCount, + Merkelroot: merkleroot, + }, }, - ChallengeId: challengeMessage.ChallengeId, - RegTicketId: challengeMessage.RegTicketId, - ActionTicketId: challengeMessage.ActionTicketId, - SenseFileIds: challengeMessage.SenseFileIds, - IsSenseTicket: challengeMessage.IsSenseTicket, - } - - store, err := local.OpenHistoryDB() - if err != nil { - log.WithContext(ctx).WithError(err).Error("Error Opening DB") - } - if store != nil { - defer store.CloseHistoryDB(ctx) - } - - shChallenge := types.SelfHealingChallenge{ - ChallengeID: responseMessage.ChallengeId, - MerkleRoot: responseMessage.MerklerootWhenChallengeSent, - FileHash: responseMessage.ChallengeFile.FileHashToChallenge, - ChallengingNode: responseMessage.ChallengingMasternodeId, - RespondingNode: responseMessage.RespondingMasternodeId, - VerifyingNode: task.nodeID, } //Checking Process @@ -122,84 +86,101 @@ func (task *SHTask) VerifySelfHealingChallenge(ctx context.Context, challengeMes //3. true, symbols, nil - reconstruction required var reconstructedFileHash []byte - if cascadeTicket != nil || regTicket.TXID != "" { - isReconstructionReq, availableSymbols, err := task.checkingProcess(ctx, challengeFileHash) - if err != nil && !isReconstructionReq { - log.WithContext(ctx).WithError(err).WithField("failed_challenge_id", challengeMessage.ChallengeId).Error("Error in checking process") - responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE - shChallenge.Status = types.FailedSelfHealingStatus - storeLogs(ctx, store, shChallenge) - return responseMessage, err - } - - if !isReconstructionReq && availableSymbols == nil { - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info(fmt.Sprintf("Reconstruction is not required for file: %s", challengeFileHash)) - responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE - shChallenge.Status = types.FailedSelfHealingStatus - storeLogs(ctx, store, shChallenge) - return responseMessage, nil - } + respondedTickets := incomingResponseMessage.SelfHealingMessageData.Response.RespondedTickets - _, reconstructedFileHash, err = task.selfHealing(ctx, rqService, challengeMessage, ®Ticket, cascadeTicket, availableSymbols) - if err != nil { - log.WithContext(ctx).WithError(err).Error("Error self-healing the file") - return responseMessage, err - } - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info("File has been reconstructed") - - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info("Comparing hashes") - if !bytes.Equal(reconstructedFileHash, challengeMessage.ReconstructedFileHash) { - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info("reconstructed file hash does not match with the verifier reconstructed file") + if respondedTickets == nil { + return nil, nil + } - responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE - shChallenge.Status = types.FailedSelfHealingStatus - shChallenge.ReconstructedFileHash = reconstructedFileHash - storeLogs(ctx, store, shChallenge) - return responseMessage, nil - } - log.WithContext(ctx).WithField("failed_challenge_id", challengeMessage.ChallengeId).Info("hashes have been matched of the responder and verifiers") - } else if senseTicket != nil { - reqSelfHealing, mostCommonFile := task.senseCheckingProcess(ctx, senseTicket.DDAndFingerprintsIDs) + for _, ticket := range respondedTickets { + nftTicket, cascadeTicket, senseTicket, err = task.getTicket(ctx, ticket.TxID, TicketType(ticket.TicketType)) if err != nil { - log.WithContext(ctx).WithError(err).Error("Error in checking process for sense action ticket") - } - - if !reqSelfHealing { - log.WithContext(ctx).WithError(err).Error("self-healing not required for sense action ticket") - responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE - shChallenge.Status = types.FailedSelfHealingStatus - shChallenge.ReconstructedFileHash = nil - storeLogs(ctx, store, shChallenge) - - return responseMessage, nil + log.WithContext(ctx).WithError(err).Error("Error getRelevantTicketFromFileHash") } + log.WithContext(ctx).WithField("challenge_id", incomingResponseMessage.ChallengeID).Info("reg ticket has been retrieved") + + if cascadeTicket != nil || nftTicket != nil { + for _, challengeFileHash := range ticket.MissingKeys { + isReconstructionReq, availableSymbols, err := task.checkingProcess(ctx, challengeFileHash) + + if err != nil && !isReconstructionReq { + log.WithContext(ctx).WithError(err).WithField("failed_challenge_id", incomingResponseMessage.ChallengeID).Error("Error in checking process") + //responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE + //shChallenge.Status = types.FailedSelfHealingStatus + //storeLogs(ctx, store, shChallenge) + //return responseMessage, err + } + + if !isReconstructionReq && availableSymbols == nil { + log.WithContext(ctx).WithField("failed_challenge_id", incomingResponseMessage.ChallengeID).Info(fmt.Sprintf("Reconstruction is not required for file: %s", challengeFileHash)) + //responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE + //shChallenge.Status = types.FailedSelfHealingStatus + //storeLogs(ctx, store, shChallenge) + //return responseMessage, nil + } + + _, reconstructedFileHash, err = task.selfHealing(ctx, rqService, incomingResponseMessage, nftTicket, cascadeTicket, availableSymbols) + if err != nil { + log.WithContext(ctx).WithError(err).Error("Error self-healing the file") + } + log.WithContext(ctx).WithField("failed_challenge_id", incomingResponseMessage.ChallengeID).Info("File has been reconstructed") + + log.WithContext(ctx).WithField("failed_challenge_id", incomingResponseMessage.ChallengeID).Info("Comparing hashes") + if !bytes.Equal(reconstructedFileHash, ticket.ReconstructedFileHash) { + log.WithContext(ctx).WithField("failed_challenge_id", incomingResponseMessage.ChallengeID).Info("reconstructed file hash does not match with the verifier reconstructed file") + + //responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE + //shChallenge.Status = types.FailedSelfHealingStatus + //shChallenge.ReconstructedFileHash = reconstructedFileHash + //storeLogs(ctx, store, shChallenge) + //return responseMessage, nil + } + log.WithContext(ctx).WithField("failed_challenge_id", incomingResponseMessage.ChallengeID).Info("hashes have been matched of the responder and verifiers") + } + } else if senseTicket != nil { + reqSelfHealing, mostCommonFile := task.senseCheckingProcess(ctx, senseTicket.DDAndFingerprintsIDs) + if err != nil { + log.WithContext(ctx).WithError(err).Error("Error in checking process for sense action ticket") + } - ids, _, err := task.senseSelfHealing(ctx, senseTicket, mostCommonFile) - if err != nil { - log.WithContext(ctx).WithError(err).Error("error while self-healing sense action ticket") - responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE - shChallenge.Status = types.FailedSelfHealingStatus - shChallenge.ReconstructedFileHash = nil - storeLogs(ctx, store, shChallenge) - - return responseMessage, err - } + if !reqSelfHealing { + log.WithContext(ctx).WithError(err).Error("self-healing not required for sense action ticket") + //responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE + //shChallenge.Status = types.FailedSelfHealingStatus + //shChallenge.ReconstructedFileHash = nil + //storeLogs(ctx, store, shChallenge) + // + //return responseMessage, nil + } - if ok := compareFileIDs(ids, challengeMessage.SenseFileIds); !ok { - responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE - shChallenge.Status = types.FailedSelfHealingStatus - shChallenge.ReconstructedFileHash = nil - storeLogs(ctx, store, shChallenge) + ids, _, err := task.senseSelfHealing(ctx, senseTicket, mostCommonFile) + if err != nil { + log.WithContext(ctx).WithError(err).Error("error while self-healing sense action ticket") + //responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE + //shChallenge.Status = types.FailedSelfHealingStatus + //shChallenge.ReconstructedFileHash = nil + //storeLogs(ctx, store, shChallenge) + // + //return responseMessage, err + } - return responseMessage, nil + if ok := compareFileIDs(ids, ticket.FileIDs); !ok { + log.WithContext(ctx).WithField("challenge_id", incomingResponseMessage.ChallengeID).Info("Failed") + //responseMessage.ChallengeStatus = pb.SelfHealingData_Status_FAILED_INCORRECT_RESPONSE + //shChallenge.Status = types.FailedSelfHealingStatus + //shChallenge.ReconstructedFileHash = nil + //storeLogs(ctx, store, shChallenge) + // + //return responseMessage, nil + } } } - responseMessage.ChallengeStatus = pb.SelfHealingData_Status_SUCCEEDED - shChallenge.Status = types.CompletedSelfHealingStatus - shChallenge.ReconstructedFileHash = reconstructedFileHash - storeLogs(ctx, store, shChallenge) - return responseMessage, nil + //responseMessage.ChallengeStatus = pb.SelfHealingData_Status_SUCCEEDED + //shChallenge.Status = types.CompletedSelfHealingStatus + //shChallenge.ReconstructedFileHash = reconstructedFileHash + //storeLogs(ctx, store, shChallenge) + return verificationMsg, nil } func storeLogs(ctx context.Context, store storage.LocalStoreInterface, msg types.SelfHealingChallenge) { @@ -231,3 +212,20 @@ func compareFileIDs(verifyingFileIDs []string, processedFileIDs []string) bool { return true //all file ids matched } + +func (task *SHTask) validateSelfHealingResponseIncomingData(ctx context.Context, incomingChallengeMessage types.SelfHealingMessage) error { + if incomingChallengeMessage.MessageType != types.SelfHealingResponseMessage { + return fmt.Errorf("incorrect message type to processing self-healing challenge") + } + + isVerified, err := task.VerifyMessageSignature(ctx, incomingChallengeMessage) + if err != nil { + return errors.Errorf("error verifying sender's signature: %w", err) + } + + if !isVerified { + return errors.Errorf("not able to verify message signature") + } + + return nil +} diff --git a/supernode/services/selfhealing/verify_self_healing_challenge_test.go b/supernode/services/selfhealing/verify_self_healing_challenge_test.go index bad5a15c3..f314d8f85 100644 --- a/supernode/services/selfhealing/verify_self_healing_challenge_test.go +++ b/supernode/services/selfhealing/verify_self_healing_challenge_test.go @@ -3,6 +3,7 @@ package selfhealing import ( "context" "encoding/base64" + "github.com/pastelnetwork/gonode/common/types" "testing" json "github.com/json-iterator/go" @@ -13,7 +14,6 @@ import ( p2pMock "github.com/pastelnetwork/gonode/p2p/test" "github.com/pastelnetwork/gonode/pastel" pastelMock "github.com/pastelnetwork/gonode/pastel/test" - pb "github.com/pastelnetwork/gonode/proto/supernode" rq "github.com/pastelnetwork/gonode/raptorq" rqnode "github.com/pastelnetwork/gonode/raptorq/node" rqmock "github.com/pastelnetwork/gonode/raptorq/node/test" @@ -68,25 +68,29 @@ func TestVerifySelfHealingChallenge(t *testing.T) { tests := []struct { testcase string - message *pb.SelfHealingData + message types.SelfHealingMessage setup func() - expect func(*testing.T, *pb.SelfHealingData, error) + expect func(*testing.T, *types.SelfHealingMessage, error) }{ { testcase: "when reconstruction is not required, should fail verification", - message: &pb.SelfHealingData{ - MessageId: "test-message-1", - MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_ISSUANCE_MESSAGE, - MerklerootWhenChallengeSent: "previous-block-hash", - ChallengingMasternodeId: "challenging-node", - RespondingMasternodeId: "responding-node", - ChallengeFile: &pb.SelfHealingDataChallengeFile{ - FileHashToChallenge: "file-hash-to-challenge", - }, - ChallengeId: "test-challenge-1", - RegTicketId: "reg-ticket-tx-id", - }, + message: types.SelfHealingMessage{MessageType: types.SelfHealingResponseMessage}, + //message: &pb.SelfHealingData{ + // MessageId: "test-message-1", + // MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_ISSUANCE_MESSAGE, + // MerklerootWhenChallengeSent: "previous-block-hash", + // ChallengingMasternodeId: "challenging-node", + // RespondingMasternodeId: "responding-node", + // ChallengeFile: &pb.SelfHealingDataChallengeFile{ + // FileHashToChallenge: "file-hash-to-challenge", + // }, + // ChallengeId: "test-challenge-1", + // RegTicketId: "reg-ticket-tx-id", + //}, setup: func() { + pastelClient.ListenOnVerify(true, nil). + ListenOnGetBlockCount(0, nil). + ListenOnGetBlockVerbose1(&pastel.GetBlockVerbose1Result{}, nil) pastelClient.On("RegTicket", mock.Anything, mock.Anything).Return(ticket, nil) raptorQClient.ListenOnConnect(nil) raptorQClient.ListenOnRaptorQ().ListenOnClose(nil) @@ -94,28 +98,29 @@ func TestVerifySelfHealingChallenge(t *testing.T) { p2pClient.On(p2pMock.RetrieveMethod, mock.Anything, mock.Anything, mock.Anything).Return(symbol, nil).Times(1) raptorQClient.ListenOnDone() }, - expect: func(t *testing.T, data *pb.SelfHealingData, err error) { + expect: func(t *testing.T, data *types.SelfHealingMessage, err error) { require.Nil(t, err) - require.Equal(t, data.MessageType, pb.SelfHealingData_MessageType_SELF_HEALING_RESPONSE_MESSAGE) - require.Equal(t, data.RespondingMasternodeId, "challenging-node") + //require.Equal(t, data.MessageType, pb.SelfHealingData_MessageType_SELF_HEALING_RESPONSE_MESSAGE) + //require.Equal(t, data.RespondingMasternodeId, "challenging-node") }, }, { testcase: "when challenging node file hash matches with the verifying node generated file hash, should be successful", - message: &pb.SelfHealingData{ - MessageId: "test-message-1", - MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_VERIFICATION_MESSAGE, - MerklerootWhenChallengeSent: "previous-block-hash", - ChallengingMasternodeId: "challenging-node", - RespondingMasternodeId: "responding-node", - ChallengeFile: &pb.SelfHealingDataChallengeFile{ - FileHashToChallenge: "file-hash-to-challenge", - }, - ChallengeId: "test-challenge-1", - RegTicketId: "reg-ticket-tx-id", - ReconstructedFileHash: fileHash[:], - }, + message: types.SelfHealingMessage{MessageType: types.SelfHealingResponseMessage}, + //message: &pb.SelfHealingData{ + // MessageId: "test-message-1", + // MessageType: pb.SelfHealingData_MessageType_SELF_HEALING_VERIFICATION_MESSAGE, + // MerklerootWhenChallengeSent: "previous-block-hash", + // ChallengingMasternodeId: "challenging-node", + // RespondingMasternodeId: "responding-node", + // ChallengeFile: &pb.SelfHealingDataChallengeFile{ + // FileHashToChallenge: "file-hash-to-challenge", + // }, + // ChallengeId: "test-challenge-1", + // RegTicketId: "reg-ticket-tx-id", + // ReconstructedFileHash: fileHash[:], + //}, setup: func() { pastelClient.ListenOnRegTicket(mock.Anything, ticket, nil) raptorQClient.ListenOnConnect(nil) @@ -130,11 +135,11 @@ func TestVerifySelfHealingChallenge(t *testing.T) { raptorQClient.ListenOnDone() }, - expect: func(t *testing.T, data *pb.SelfHealingData, err error) { + expect: func(t *testing.T, data *types.SelfHealingMessage, err error) { require.Nil(t, err) - require.Equal(t, data.ChallengeStatus, pb.SelfHealingData_Status_SUCCEEDED) - require.Equal(t, data.MessageType, pb.SelfHealingData_MessageType_SELF_HEALING_RESPONSE_MESSAGE) - require.Equal(t, data.RespondingMasternodeId, "challenging-node") + //require.Equal(t, data.ChallengeStatus, pb.SelfHealingData_Status_SUCCEEDED) + //require.Equal(t, data.MessageType, pb.SelfHealingData_MessageType_SELF_HEALING_RESPONSE_MESSAGE) + //require.Equal(t, data.RespondingMasternodeId, "challenging-node") }, }, }