diff --git a/cmixproto/types.pb.go b/cmixproto/types.pb.go index c0b870e2ae83e59fc0b2cffb7db46d80afedec20..d1cb16dcb305fc09a49da1002e2d909f7d40eff6 100644 --- a/cmixproto/types.pb.go +++ b/cmixproto/types.pb.go @@ -1,24 +1,37 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2018 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +// Call ./generate.sh to generate the protocol buffer code + // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc (unknown) // source: types.proto package cmixproto import ( - fmt "fmt" proto "github.com/golang/protobuf/proto" - math "math" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 type Type int32 @@ -28,11 +41,6 @@ const ( Type_NO_TYPE Type = 0 // See proto buf documentation below Type_TEXT_MESSAGE Type = 1 - // See proto buf - Type_CHANNEL_MESSAGE Type = 2 - // Nickname request and response messages - Type_NICKNAME_REQUEST Type = 6 - Type_NICKNAME_RESPONSE Type = 7 // Second field is the key data itself. This should be 2048 bits long // (according to the message length that our prime allows) and is // base64-encoded. @@ -77,478 +85,141 @@ const ( // which you can use with GET_KEY to get the keys you need to talk with // that user. Otherwise, this fourth field won't exist. Type_UDB_SEARCH_RESPONSE Type = 17 - // To get a message of this type, call the methods in the wallet. - // TODO expose these methods over the API - Type_PAYMENT_TRANSACTION Type = 20 - // See proto buf - Type_PAYMENT_RESPONSE Type = 21 - // See proto buf - Type_PAYMENT_INVOICE Type = 22 - // This message type includes only the message hash of the original - // invoice message. Since there are no fields to delimit, it's not a - // proto buffer. When the payee gets a receipt back, they know that the - // other person probably paid them, and to check the next published - // blockchain for the images of their coins to make sure. - // The wallet sends this message after receiving a PAYMENT_RESPONSE - // indicating success. - Type_PAYMENT_RECEIPT Type = 23 // End to End Rekey message types // Trigger a rekey, this message is used locally in client only Type_REKEY_TRIGGER Type = 30 // Rekey confirmation message. Sent by partner to confirm completion of a rekey Type_REKEY_CONFIRM Type = 31 - // This message type is a single fixed-length hash of the invoice - // that the client just received. The client can look up this hash in the - // inbound transaction list to display the most recently received invoice - // in the UI. - Type_PAYMENT_INVOICE_UI Type = 9000 - // This message type is a single fixed-length hash of the original invoice - // that this client sent to the paying client. The UI can look up the - // corresponding transaction in the list of completed transactions and - // display payment success on the UI. The wallet sends this message - // locally after receiving a PAYMENT_RECEIPT message. - Type_PAYMENT_RECEIPT_UI Type = 9001 -) - -var Type_name = map[int32]string{ - 0: "NO_TYPE", - 1: "TEXT_MESSAGE", - 2: "CHANNEL_MESSAGE", - 6: "NICKNAME_REQUEST", - 7: "NICKNAME_RESPONSE", - 10: "UDB_PUSH_KEY", - 11: "UDB_PUSH_KEY_RESPONSE", - 12: "UDB_GET_KEY", - 13: "UDB_GET_KEY_RESPONSE", - 14: "UDB_REGISTER", - 15: "UDB_REGISTER_RESPONSE", - 16: "UDB_SEARCH", - 17: "UDB_SEARCH_RESPONSE", - 20: "PAYMENT_TRANSACTION", - 21: "PAYMENT_RESPONSE", - 22: "PAYMENT_INVOICE", - 23: "PAYMENT_RECEIPT", - 30: "REKEY_TRIGGER", - 31: "REKEY_CONFIRM", - 9000: "PAYMENT_INVOICE_UI", - 9001: "PAYMENT_RECEIPT_UI", -} - -var Type_value = map[string]int32{ - "NO_TYPE": 0, - "TEXT_MESSAGE": 1, - "CHANNEL_MESSAGE": 2, - "NICKNAME_REQUEST": 6, - "NICKNAME_RESPONSE": 7, - "UDB_PUSH_KEY": 10, - "UDB_PUSH_KEY_RESPONSE": 11, - "UDB_GET_KEY": 12, - "UDB_GET_KEY_RESPONSE": 13, - "UDB_REGISTER": 14, - "UDB_REGISTER_RESPONSE": 15, - "UDB_SEARCH": 16, - "UDB_SEARCH_RESPONSE": 17, - "PAYMENT_TRANSACTION": 20, - "PAYMENT_RESPONSE": 21, - "PAYMENT_INVOICE": 22, - "PAYMENT_RECEIPT": 23, - "REKEY_TRIGGER": 30, - "REKEY_CONFIRM": 31, - "PAYMENT_INVOICE_UI": 9000, - "PAYMENT_RECEIPT_UI": 9001, -} - -func (x Type) String() string { - return proto.EnumName(Type_name, int32(x)) -} - -func (Type) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{0} -} - -// Use this enumeration to get specific transactions from specific transaction -// lists from the wallet -type TransactionListTag int32 - -const ( - // Transactions in this list are invoices this user made to another user - // Most importantly, they include the preimage that this user wants fulfilled, - // but the image of this preimage is what the client will send in the invoice. - // Transactions enter this list when this user invoices another user. - TransactionListTag_OUTBOUND_REQUESTS TransactionListTag = 0 - // Transactions in this list are invoices that this user received from - // other users. Most importantly, this includes the image that this user - // will fund. - TransactionListTag_INBOUND_REQUESTS TransactionListTag = 1 - // Transactions in this list are currently processing on a payment bot. - // Transactions move from INBOUND_REQUESTS to PENDING_TRANSACTIONS after - // a Pay() call. - TransactionListTag_PENDING_TRANSACTIONS TransactionListTag = 2 - // Transactions in this list are completed transactions that increased - // the value of this user's wallet. NOTE: They correspond to transactions - // originally in the OUTBOUND_REQUESTS list that went through. - TransactionListTag_COMPLETED_INBOUND_PAYMENTS TransactionListTag = 3 - // Transactions in this list are completed transactions that decreased - // the value of this user's wallet. NOTE: They correspond to transactions - // originally in the INBOUND_REQUESTS list that went through. - TransactionListTag_COMPLETED_OUTBOUND_PAYMENTS TransactionListTag = 4 ) -var TransactionListTag_name = map[int32]string{ - 0: "OUTBOUND_REQUESTS", - 1: "INBOUND_REQUESTS", - 2: "PENDING_TRANSACTIONS", - 3: "COMPLETED_INBOUND_PAYMENTS", - 4: "COMPLETED_OUTBOUND_PAYMENTS", -} - -var TransactionListTag_value = map[string]int32{ - "OUTBOUND_REQUESTS": 0, - "INBOUND_REQUESTS": 1, - "PENDING_TRANSACTIONS": 2, - "COMPLETED_INBOUND_PAYMENTS": 3, - "COMPLETED_OUTBOUND_PAYMENTS": 4, -} - -func (x TransactionListTag) String() string { - return proto.EnumName(TransactionListTag_name, int32(x)) -} - -func (TransactionListTag) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{1} -} - -// Use this enumeration to request different sort orders of transaction lists -// when getting all IDs in a transaction list -type TransactionListOrder int32 - -const ( - // Most recently initiated transaction first - TransactionListOrder_TIMESTAMP_DESCENDING TransactionListOrder = 0 - // Stalest transaction first - TransactionListOrder_TIMESTAMP_ASCENDING TransactionListOrder = 1 - // Biggest transaction first - TransactionListOrder_VALUE_DESCENDING TransactionListOrder = 2 - // Smallest transaction first - TransactionListOrder_VALUE_ASCENDING TransactionListOrder = 3 -) - -var TransactionListOrder_name = map[int32]string{ - 0: "TIMESTAMP_DESCENDING", - 1: "TIMESTAMP_ASCENDING", - 2: "VALUE_DESCENDING", - 3: "VALUE_ASCENDING", -} - -var TransactionListOrder_value = map[string]int32{ - "TIMESTAMP_DESCENDING": 0, - "TIMESTAMP_ASCENDING": 1, - "VALUE_DESCENDING": 2, - "VALUE_ASCENDING": 3, -} - -func (x TransactionListOrder) String() string { - return proto.EnumName(TransactionListOrder_name, int32(x)) -} - -func (TransactionListOrder) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{2} -} - -// Is Type.TEXT_MESSAGE -// Used for conveying simple text messages between users -type TextMessage struct { - // Terminal text foreground color. Used by the console UI - Color int32 `protobuf:"zigzag32,2,opt,name=color,proto3" json:"color,omitempty"` - // The message text itself. Varies in length - Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` - // Timestamp (Unix time in seconds) - // You can use this to display the time when the other user sent the message - // TODO Remove this when all messages have timestamps - Time int64 `protobuf:"varint,4,opt,name=time,proto3" json:"time,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *TextMessage) Reset() { *m = TextMessage{} } -func (m *TextMessage) String() string { return proto.CompactTextString(m) } -func (*TextMessage) ProtoMessage() {} -func (*TextMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{0} -} - -func (m *TextMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TextMessage.Unmarshal(m, b) -} -func (m *TextMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TextMessage.Marshal(b, m, deterministic) -} -func (m *TextMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_TextMessage.Merge(m, src) -} -func (m *TextMessage) XXX_Size() int { - return xxx_messageInfo_TextMessage.Size(m) -} -func (m *TextMessage) XXX_DiscardUnknown() { - xxx_messageInfo_TextMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_TextMessage proto.InternalMessageInfo - -func (m *TextMessage) GetColor() int32 { - if m != nil { - return m.Color - } - return 0 -} - -func (m *TextMessage) GetMessage() string { - if m != nil { - return m.Message - } - return "" -} - -func (m *TextMessage) GetTime() int64 { - if m != nil { - return m.Time - } - return 0 -} - -// Is Type.CHANNEL_MESSAGE -// This is the type of all messages that come from the channelbot. -type ChannelMessage struct { - // This is the original speaker of the channel message, who sent the - // message to the channel bot. - SpeakerID []byte `protobuf:"bytes,3,opt,name=speakerID,proto3" json:"speakerID,omitempty"` - // This is a serialized parse message under the hood. When writing a - // listener for a channel message on a client, you need to unpack the - // serialized parse message and rebroadcast it through the listeners. - Message []byte `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ChannelMessage) Reset() { *m = ChannelMessage{} } -func (m *ChannelMessage) String() string { return proto.CompactTextString(m) } -func (*ChannelMessage) ProtoMessage() {} -func (*ChannelMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{1} -} - -func (m *ChannelMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ChannelMessage.Unmarshal(m, b) -} -func (m *ChannelMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ChannelMessage.Marshal(b, m, deterministic) -} -func (m *ChannelMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_ChannelMessage.Merge(m, src) -} -func (m *ChannelMessage) XXX_Size() int { - return xxx_messageInfo_ChannelMessage.Size(m) -} -func (m *ChannelMessage) XXX_DiscardUnknown() { - xxx_messageInfo_ChannelMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_ChannelMessage proto.InternalMessageInfo - -func (m *ChannelMessage) GetSpeakerID() []byte { - if m != nil { - return m.SpeakerID +// Enum value maps for Type. +var ( + Type_name = map[int32]string{ + 0: "NO_TYPE", + 1: "TEXT_MESSAGE", + 10: "UDB_PUSH_KEY", + 11: "UDB_PUSH_KEY_RESPONSE", + 12: "UDB_GET_KEY", + 13: "UDB_GET_KEY_RESPONSE", + 14: "UDB_REGISTER", + 15: "UDB_REGISTER_RESPONSE", + 16: "UDB_SEARCH", + 17: "UDB_SEARCH_RESPONSE", + 30: "REKEY_TRIGGER", + 31: "REKEY_CONFIRM", } - return nil -} - -func (m *ChannelMessage) GetMessage() []byte { - if m != nil { - return m.Message + Type_value = map[string]int32{ + "NO_TYPE": 0, + "TEXT_MESSAGE": 1, + "UDB_PUSH_KEY": 10, + "UDB_PUSH_KEY_RESPONSE": 11, + "UDB_GET_KEY": 12, + "UDB_GET_KEY_RESPONSE": 13, + "UDB_REGISTER": 14, + "UDB_REGISTER_RESPONSE": 15, + "UDB_SEARCH": 16, + "UDB_SEARCH_RESPONSE": 17, + "REKEY_TRIGGER": 30, + "REKEY_CONFIRM": 31, } - return nil -} - -// Is Type.PAYMENT_RESPONSE -type PaymentResponse struct { - // Indicates whether the payment succeeded or failed - Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` - // Response message from the payment bot. You should display this to the - // user. - Response string `protobuf:"bytes,2,opt,name=response,proto3" json:"response,omitempty"` - // TODO Is it correct to use the whole hash? - // This is the hash of the payment message that the payment bot received. - // The client uses it to remove the correct pending transaction from the - // list of pending transactions. - ID []byte `protobuf:"bytes,3,opt,name=ID,proto3" json:"ID,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} +) -func (m *PaymentResponse) Reset() { *m = PaymentResponse{} } -func (m *PaymentResponse) String() string { return proto.CompactTextString(m) } -func (*PaymentResponse) ProtoMessage() {} -func (*PaymentResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{2} +func (x Type) Enum() *Type { + p := new(Type) + *p = x + return p } -func (m *PaymentResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PaymentResponse.Unmarshal(m, b) -} -func (m *PaymentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PaymentResponse.Marshal(b, m, deterministic) -} -func (m *PaymentResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_PaymentResponse.Merge(m, src) -} -func (m *PaymentResponse) XXX_Size() int { - return xxx_messageInfo_PaymentResponse.Size(m) -} -func (m *PaymentResponse) XXX_DiscardUnknown() { - xxx_messageInfo_PaymentResponse.DiscardUnknown(m) +func (x Type) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } -var xxx_messageInfo_PaymentResponse proto.InternalMessageInfo - -func (m *PaymentResponse) GetSuccess() bool { - if m != nil { - return m.Success - } - return false +func (Type) Descriptor() protoreflect.EnumDescriptor { + return file_types_proto_enumTypes[0].Descriptor() } -func (m *PaymentResponse) GetResponse() string { - if m != nil { - return m.Response - } - return "" +func (Type) Type() protoreflect.EnumType { + return &file_types_proto_enumTypes[0] } -func (m *PaymentResponse) GetID() []byte { - if m != nil { - return m.ID - } - return nil +func (x Type) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) } -// Is Type.PAYMENT_INVOICE -type PaymentInvoice struct { - // Timestamp (Unix time in seconds) - // Not currently used but could be useful for the user to verify the - // correctness of an invoice. - Time int64 `protobuf:"varint,1,opt,name=time,proto3" json:"time,omitempty"` - // This is a single compound coin that the invoicer wants to be funded. The - // payer should send a message to the payment bot to fund this compound, if - // they wish to pay the payee. - CreatedCoin []byte `protobuf:"bytes,2,opt,name=createdCoin,proto3" json:"createdCoin,omitempty"` - // The payee should fill this out to describe what the payment is for or - // about. - Memo string `protobuf:"bytes,3,opt,name=memo,proto3" json:"memo,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} +// Deprecated: Use Type.Descriptor instead. +func (Type) EnumDescriptor() ([]byte, []int) { + return file_types_proto_rawDescGZIP(), []int{0} +} + +var File_types_proto protoreflect.FileDescriptor + +var file_types_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, + 0x61, 0x72, 0x73, 0x65, 0x2a, 0xf9, 0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, + 0x07, 0x4e, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x45, + 0x58, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, + 0x55, 0x44, 0x42, 0x5f, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x19, + 0x0a, 0x15, 0x55, 0x44, 0x42, 0x5f, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, + 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x44, 0x42, + 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0c, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x44, + 0x42, 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, + 0x53, 0x45, 0x10, 0x0d, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x44, 0x42, 0x5f, 0x52, 0x45, 0x47, 0x49, + 0x53, 0x54, 0x45, 0x52, 0x10, 0x0e, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x44, 0x42, 0x5f, 0x52, 0x45, + 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, + 0x0f, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, 0x10, + 0x10, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, 0x5f, + 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x11, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, + 0x4b, 0x45, 0x59, 0x5f, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x10, 0x1e, 0x12, 0x11, 0x0a, + 0x0d, 0x52, 0x45, 0x4b, 0x45, 0x59, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x10, 0x1f, + 0x42, 0x0b, 0x5a, 0x09, 0x63, 0x6d, 0x69, 0x78, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_types_proto_rawDescOnce sync.Once + file_types_proto_rawDescData = file_types_proto_rawDesc +) -func (m *PaymentInvoice) Reset() { *m = PaymentInvoice{} } -func (m *PaymentInvoice) String() string { return proto.CompactTextString(m) } -func (*PaymentInvoice) ProtoMessage() {} -func (*PaymentInvoice) Descriptor() ([]byte, []int) { - return fileDescriptor_d938547f84707355, []int{3} +func file_types_proto_rawDescGZIP() []byte { + file_types_proto_rawDescOnce.Do(func() { + file_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_proto_rawDescData) + }) + return file_types_proto_rawDescData } -func (m *PaymentInvoice) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PaymentInvoice.Unmarshal(m, b) -} -func (m *PaymentInvoice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PaymentInvoice.Marshal(b, m, deterministic) -} -func (m *PaymentInvoice) XXX_Merge(src proto.Message) { - xxx_messageInfo_PaymentInvoice.Merge(m, src) -} -func (m *PaymentInvoice) XXX_Size() int { - return xxx_messageInfo_PaymentInvoice.Size(m) +var file_types_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_types_proto_goTypes = []interface{}{ + (Type)(0), // 0: parse.Type } -func (m *PaymentInvoice) XXX_DiscardUnknown() { - xxx_messageInfo_PaymentInvoice.DiscardUnknown(m) +var file_types_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } -var xxx_messageInfo_PaymentInvoice proto.InternalMessageInfo - -func (m *PaymentInvoice) GetTime() int64 { - if m != nil { - return m.Time - } - return 0 -} - -func (m *PaymentInvoice) GetCreatedCoin() []byte { - if m != nil { - return m.CreatedCoin - } - return nil -} - -func (m *PaymentInvoice) GetMemo() string { - if m != nil { - return m.Memo +func init() { file_types_proto_init() } +func file_types_proto_init() { + if File_types_proto != nil { + return } - return "" -} - -func init() { - proto.RegisterEnum("parse.Type", Type_name, Type_value) - proto.RegisterEnum("parse.TransactionListTag", TransactionListTag_name, TransactionListTag_value) - proto.RegisterEnum("parse.TransactionListOrder", TransactionListOrder_name, TransactionListOrder_value) - proto.RegisterType((*TextMessage)(nil), "parse.TextMessage") - proto.RegisterType((*ChannelMessage)(nil), "parse.ChannelMessage") - proto.RegisterType((*PaymentResponse)(nil), "parse.PaymentResponse") - proto.RegisterType((*PaymentInvoice)(nil), "parse.PaymentInvoice") -} - -func init() { proto.RegisterFile("types.proto", fileDescriptor_d938547f84707355) } - -var fileDescriptor_d938547f84707355 = []byte{ - // 622 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x54, 0xdb, 0x72, 0xda, 0x3a, - 0x14, 0x8d, 0x81, 0x5c, 0xd8, 0x10, 0x10, 0x0a, 0x39, 0xe1, 0xe4, 0x9c, 0x49, 0x18, 0x9e, 0x98, - 0x3c, 0xf4, 0xa5, 0x5f, 0xe0, 0x18, 0x05, 0x34, 0xc1, 0xb2, 0x23, 0xc9, 0x69, 0x93, 0x17, 0x8f, - 0xeb, 0x68, 0x52, 0xa6, 0xc1, 0x66, 0x6c, 0xb7, 0x93, 0xfc, 0x4b, 0x3f, 0xa0, 0xfd, 0x91, 0x7e, - 0x57, 0x47, 0x06, 0x5f, 0x26, 0x4f, 0x68, 0xaf, 0xb5, 0xb4, 0xb4, 0xd7, 0xde, 0x83, 0xa1, 0x93, - 0xbd, 0x6d, 0x54, 0xfa, 0x61, 0x93, 0xc4, 0x59, 0x8c, 0xf7, 0x37, 0x41, 0x92, 0xaa, 0xc9, 0x1d, - 0x74, 0xa4, 0x7a, 0xcd, 0x6c, 0x95, 0xa6, 0xc1, 0xb3, 0xc2, 0x43, 0xd8, 0x0f, 0xe3, 0x97, 0x38, - 0x19, 0x35, 0xc6, 0xc6, 0x74, 0xc0, 0xb7, 0x05, 0x1e, 0xc1, 0xe1, 0x7a, 0x2b, 0x18, 0x35, 0xc7, - 0xc6, 0xb4, 0xcd, 0x8b, 0x12, 0x63, 0x68, 0x65, 0xab, 0xb5, 0x1a, 0xb5, 0xc6, 0xc6, 0xb4, 0xc9, - 0xf3, 0xf3, 0x64, 0x01, 0x3d, 0xeb, 0x6b, 0x10, 0x45, 0xea, 0xa5, 0x70, 0xfd, 0x1f, 0xda, 0xe9, - 0x46, 0x05, 0xdf, 0x54, 0x42, 0x67, 0xb9, 0x43, 0x97, 0x57, 0x40, 0xdd, 0xbd, 0x95, 0x73, 0x45, - 0x39, 0xf9, 0x04, 0x7d, 0x37, 0x78, 0x5b, 0xab, 0x28, 0xe3, 0x2a, 0xdd, 0xc4, 0x51, 0xaa, 0xb4, - 0x38, 0xfd, 0x1e, 0x86, 0x2a, 0x4d, 0x47, 0xc6, 0xd8, 0x98, 0x1e, 0xf1, 0xa2, 0xc4, 0xe7, 0x70, - 0x94, 0xec, 0x54, 0x79, 0xf7, 0x6d, 0x5e, 0xd6, 0xb8, 0x07, 0x8d, 0xf2, 0xe5, 0x06, 0x9d, 0x4d, - 0x1e, 0xa1, 0xb7, 0x33, 0xa6, 0xd1, 0x8f, 0x78, 0x15, 0x56, 0x41, 0x8c, 0x2a, 0x08, 0x1e, 0x43, - 0x27, 0x4c, 0x54, 0x90, 0xa9, 0x27, 0x2b, 0x5e, 0x45, 0xb9, 0x69, 0x97, 0xd7, 0x21, 0x7d, 0x6b, - 0xad, 0xd6, 0xf1, 0x6e, 0x2a, 0xf9, 0xf9, 0xea, 0x4f, 0x13, 0x5a, 0xf2, 0x6d, 0xa3, 0x70, 0x07, - 0x0e, 0x99, 0xe3, 0xcb, 0x07, 0x97, 0xa0, 0x3d, 0x8c, 0xa0, 0x2b, 0xc9, 0x67, 0xe9, 0xdb, 0x44, - 0x08, 0x73, 0x4e, 0x90, 0x81, 0x4f, 0xa0, 0x6f, 0x2d, 0x4c, 0xc6, 0xc8, 0xb2, 0x04, 0x1b, 0x78, - 0x08, 0x88, 0x51, 0xeb, 0x96, 0x99, 0x36, 0xf1, 0x39, 0xb9, 0xf3, 0x88, 0x90, 0xe8, 0x00, 0x9f, - 0xc2, 0xa0, 0x86, 0x0a, 0xd7, 0x61, 0x82, 0xa0, 0x43, 0xed, 0xe9, 0xcd, 0xae, 0x7d, 0xd7, 0x13, - 0x0b, 0xff, 0x96, 0x3c, 0x20, 0xc0, 0xff, 0xc2, 0x69, 0x1d, 0xa9, 0xc4, 0x1d, 0xdc, 0x87, 0x8e, - 0xa6, 0xe6, 0x44, 0xe6, 0xda, 0x2e, 0x1e, 0xc1, 0xb0, 0x06, 0x54, 0xd2, 0xe3, 0xc2, 0x97, 0x93, - 0x39, 0x15, 0x92, 0x70, 0xd4, 0x2b, 0x7c, 0x0b, 0xa4, 0x12, 0xf7, 0x71, 0x0f, 0x40, 0x53, 0x82, - 0x98, 0xdc, 0x5a, 0x20, 0x84, 0xcf, 0xe0, 0xa4, 0xaa, 0x2b, 0xe1, 0x40, 0x13, 0xae, 0xf9, 0x60, - 0x13, 0x26, 0x7d, 0xc9, 0x4d, 0x26, 0x4c, 0x4b, 0x52, 0x87, 0xa1, 0xa1, 0xce, 0x5c, 0x10, 0xa5, - 0xfc, 0x54, 0x8f, 0xa7, 0x40, 0x29, 0xbb, 0x77, 0xa8, 0x45, 0xd0, 0x3f, 0x75, 0x90, 0x13, 0x8b, - 0x50, 0x57, 0xa2, 0x33, 0x3c, 0x80, 0x63, 0x4e, 0x74, 0x04, 0xc9, 0xe9, 0x7c, 0x4e, 0x38, 0xba, - 0xa8, 0x20, 0xcb, 0x61, 0x37, 0x94, 0xdb, 0xe8, 0x12, 0x9f, 0x01, 0x7e, 0xe7, 0xe7, 0x7b, 0x14, - 0xfd, 0xba, 0xa9, 0x13, 0x3b, 0x4f, 0x4d, 0xfc, 0xbe, 0xb9, 0xfa, 0x69, 0x00, 0x96, 0x49, 0x10, - 0xa5, 0x41, 0x98, 0xad, 0xe2, 0x68, 0xb9, 0x4a, 0x33, 0x19, 0x3c, 0xeb, 0x65, 0x38, 0x9e, 0xbc, - 0x76, 0x3c, 0x36, 0x2b, 0x56, 0x24, 0xd0, 0x9e, 0x4e, 0x41, 0xd9, 0x3b, 0xd4, 0xd0, 0x43, 0x76, - 0x09, 0x9b, 0x51, 0x36, 0xaf, 0x87, 0x16, 0xa8, 0x81, 0x2f, 0xe0, 0xdc, 0x72, 0x6c, 0x77, 0x49, - 0x24, 0x99, 0xf9, 0xc5, 0xcd, 0x5d, 0x23, 0x02, 0x35, 0xf1, 0x25, 0xfc, 0x57, 0xf1, 0xe5, 0x83, - 0xa5, 0xa0, 0x75, 0x95, 0xc1, 0xf0, 0x5d, 0x77, 0x4e, 0xf2, 0xa4, 0xf4, 0x9f, 0x75, 0x28, 0xa9, - 0x4d, 0x84, 0x34, 0x6d, 0xd7, 0x9f, 0x11, 0x61, 0x6d, 0xdf, 0x47, 0x7b, 0x7a, 0x03, 0x15, 0x63, - 0x96, 0x84, 0xa1, 0x7b, 0xbf, 0x37, 0x97, 0x1e, 0xa9, 0xcb, 0x1b, 0x7a, 0xd8, 0x5b, 0xb4, 0x92, - 0x36, 0xaf, 0x3b, 0x8f, 0xed, 0x70, 0xbd, 0x7a, 0xcd, 0xbf, 0x21, 0x5f, 0x0e, 0xf2, 0x9f, 0x8f, - 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x3f, 0xff, 0xd7, 0x16, 0x59, 0x04, 0x00, 0x00, + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_types_proto_rawDesc, + NumEnums: 1, + NumMessages: 0, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_types_proto_goTypes, + DependencyIndexes: file_types_proto_depIdxs, + EnumInfos: file_types_proto_enumTypes, + }.Build() + File_types_proto = out.File + file_types_proto_rawDesc = nil + file_types_proto_goTypes = nil + file_types_proto_depIdxs = nil } diff --git a/cmixproto/types.proto b/cmixproto/types.proto index d64600bbe301fad6852519326b6d2648d9ab4372..141ad7b831546b07ecc4e89bc4be802c00755df0 100644 --- a/cmixproto/types.proto +++ b/cmixproto/types.proto @@ -19,13 +19,6 @@ enum Type { // See proto buf documentation below TEXT_MESSAGE = 1; - // See proto buf - CHANNEL_MESSAGE = 2; - - - // Nickname request and response messages - NICKNAME_REQUEST = 6; - NICKNAME_RESPONSE = 7; // None of the UDB message types are proto bufs because I haven't had time // to migrate UDB fully to the new systems yet. @@ -106,143 +99,17 @@ enum Type { // All of the seeds and compounds are in an ordered list, and they get // categorized and processed on the payment bot. - // To get a message of this type, call the methods in the wallet. - // TODO expose these methods over the API - PAYMENT_TRANSACTION = 20; - // See proto buf - PAYMENT_RESPONSE = 21; - // See proto buf - PAYMENT_INVOICE = 22; - // This message type includes only the message hash of the original - // invoice message. Since there are no fields to delimit, it's not a - // proto buffer. When the payee gets a receipt back, they know that the - // other person probably paid them, and to check the next published - // blockchain for the images of their coins to make sure. - // The wallet sends this message after receiving a PAYMENT_RESPONSE - // indicating success. - PAYMENT_RECEIPT = 23; - // End to End Rekey message types // Trigger a rekey, this message is used locally in client only REKEY_TRIGGER = 30; // Rekey confirmation message. Sent by partner to confirm completion of a rekey REKEY_CONFIRM = 31; - - // These are specialized messages that convey the information that - // the UI needs to know once the wallet's finished updating. They shouldn't - // go over the network. Types 9000-9999 are reserved for messages like this. - - // This message type is a single fixed-length hash of the invoice - // that the client just received. The client can look up this hash in the - // inbound transaction list to display the most recently received invoice - // in the UI. - PAYMENT_INVOICE_UI = 9000; - // This message type is a single fixed-length hash of the original invoice - // that this client sent to the paying client. The UI can look up the - // corresponding transaction in the list of completed transactions and - // display payment success on the UI. The wallet sends this message - // locally after receiving a PAYMENT_RECEIPT message. - PAYMENT_RECEIPT_UI = 9001; -} - -// Use this enumeration to get specific transactions from specific transaction -// lists from the wallet -enum TransactionListTag { - - // Transactions in this list are invoices this user made to another user - // Most importantly, they include the preimage that this user wants fulfilled, - // but the image of this preimage is what the client will send in the invoice. - // Transactions enter this list when this user invoices another user. - OUTBOUND_REQUESTS = 0; - - // Transactions in this list are invoices that this user received from - // other users. Most importantly, this includes the image that this user - // will fund. - INBOUND_REQUESTS = 1; - - // Transactions in this list are currently processing on a payment bot. - // Transactions move from INBOUND_REQUESTS to PENDING_TRANSACTIONS after - // a Pay() call. - PENDING_TRANSACTIONS = 2; - - // Transactions in this list are completed transactions that increased - // the value of this user's wallet. NOTE: They correspond to transactions - // originally in the OUTBOUND_REQUESTS list that went through. - COMPLETED_INBOUND_PAYMENTS = 3; - - // Transactions in this list are completed transactions that decreased - // the value of this user's wallet. NOTE: They correspond to transactions - // originally in the INBOUND_REQUESTS list that went through. - COMPLETED_OUTBOUND_PAYMENTS = 4; } -// Use this enumeration to request different sort orders of transaction lists -// when getting all IDs in a transaction list -enum TransactionListOrder { - // Most recently initiated transaction first - TIMESTAMP_DESCENDING = 0; - // Stalest transaction first - TIMESTAMP_ASCENDING = 1; - // Biggest transaction first - VALUE_DESCENDING = 2; - // Smallest transaction first - VALUE_ASCENDING = 3; -} - -// Text message types - -// Is Type.TEXT_MESSAGE -// Used for conveying simple text messages between users -message TextMessage { - // Terminal text foreground color. Used by the console UI - sint32 color = 2; - // The message text itself. Varies in length - string message = 3; - // Timestamp (Unix time in seconds) - // You can use this to display the time when the other user sent the message - // TODO Remove this when all messages have timestamps - int64 time = 4; -} +message RekeyTrigger { + // PublicKey used in the registration + bytes publicKey = 1; + bytes SessionID = 2; -// Is Type.CHANNEL_MESSAGE -// This is the type of all messages that come from the channelbot. -message ChannelMessage { - // This is the original speaker of the channel message, who sent the - // message to the channel bot. - bytes speakerID = 3; - // This is a serialized parse message under the hood. When writing a - // listener for a channel message on a client, you need to unpack the - // serialized parse message and rebroadcast it through the listeners. - bytes message = 4; -} - -// Payment message types - -// Is Type.PAYMENT_RESPONSE -message PaymentResponse { - // Indicates whether the payment succeeded or failed - bool success = 1; - // Response message from the payment bot. You should display this to the - // user. - string response = 2; - // TODO Is it correct to use the whole hash? - // This is the hash of the payment message that the payment bot received. - // The client uses it to remove the correct pending transaction from the - // list of pending transactions. - bytes ID = 3; -} -// Is Type.PAYMENT_INVOICE -message PaymentInvoice { - // Timestamp (Unix time in seconds) - // Not currently used but could be useful for the user to verify the - // correctness of an invoice. - int64 time = 1; - // This is a single compound coin that the invoicer wants to be funded. The - // payer should send a message to the payment bot to fund this compound, if - // they wish to pay the payee. - bytes createdCoin = 2; - // The payee should fill this out to describe what the payment is for or - // about. - string memo = 3; } diff --git a/context/networkManager.go b/context/networkManager.go index 910cd11241f90c934eea954ad72f583dd23d9ad1..595abefb364f0aa6f631600f41b721ec8b5da69d 100644 --- a/context/networkManager.go +++ b/context/networkManager.go @@ -1,17 +1,17 @@ package context import ( + "gitlab.com/elixxir/client/context/params" + "gitlab.com/elixxir/client/context/stoppable" "gitlab.com/elixxir/comms/network" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" ) type NetworkManager interface { - SendE2E(m Message) ([]id.Round, error) + SendE2E(m Message, e2eP params.E2E, cmixP params.CMIX) ([]id.Round, error) SendUnsafe(m Message) ([]id.Round, error) SendCMIX(message format.Message) (id.Round, error) - GetRekeyChan() chan id.ID GetInstance() *network.Instance - //placeholder to stop active threads - Kill() bool + Stoppable() stoppable.Stoppable } diff --git a/context/params/CMIX.go b/context/params/CMIX.go new file mode 100644 index 0000000000000000000000000000000000000000..cefc970ce4dc1f1756805fd9d22b0a37775ec934 --- /dev/null +++ b/context/params/CMIX.go @@ -0,0 +1,9 @@ +package params + +type CMIX struct { + Retries uint +} + +func GetDefaultCMIX() CMIX { + return CMIX{Retries: 3} +} diff --git a/context/params/E2E.go b/context/params/E2E.go new file mode 100644 index 0000000000000000000000000000000000000000..2d0d0ee9cd3b9f24aaa4f3c499ae9fdfb77ec279 --- /dev/null +++ b/context/params/E2E.go @@ -0,0 +1,29 @@ +package params + +import "fmt" + +type E2E struct { + Type SendType +} + +func GetDefaultE2E() E2E { + return E2E{Type: Standard} +} + +type SendType uint8 + +const ( + Standard SendType = 0 + KeyExchange SendType = 1 +) + +func (st SendType) String() string { + switch st { + case Standard: + return "Standard" + case KeyExchange: + return "KeyExchange" + default: + return fmt.Sprintf("Unknown SendType %v", uint8(st)) + } +} diff --git a/context/stoppable/multi.go b/context/stoppable/multi.go index 986787d5055ebc24115f7468ea554877fa0c391e..f30c484758a5689e6fd0388b6b996f6bf764a9e0 100644 --- a/context/stoppable/multi.go +++ b/context/stoppable/multi.go @@ -1,7 +1,9 @@ package stoppable import ( + "fmt" "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" "sync" "sync/atomic" "time" @@ -65,10 +67,10 @@ func (m *Multi) Close(timeout time.Duration) error { wg := &sync.WaitGroup{} - for _, stopable := range m.stoppables { + for _, stoppable := range m.stoppables { wg.Add(1) go func() { - if stopable.Close(timeout) != nil { + if stoppable.Close(timeout) != nil { atomic.AddUint32(&numErrors, 1) } wg.Done() @@ -80,8 +82,10 @@ func (m *Multi) Close(timeout time.Duration) error { atomic.StoreUint32(&m.running, 0) if numErrors > 0 { - return errors.Errorf("MultiStopper %s failed to close "+ + errStr := fmt.Sprintf("MultiStopper %s failed to close "+ "%v/%v stoppers", m.name, numErrors, len(m.stoppables)) + jww.ERROR.Println(errStr) + return errors.New(errStr) } return nil diff --git a/context/utility/trackResults.go b/context/utility/trackResults.go new file mode 100644 index 0000000000000000000000000000000000000000..200615a6eb15db6704bba6b4bf5bfe05ee3661da --- /dev/null +++ b/context/utility/trackResults.go @@ -0,0 +1,23 @@ +package utility + +import ( + ds "gitlab.com/elixxir/comms/network/dataStructures" + "gitlab.com/elixxir/primitives/states" +) + +// Function to track the results of events. It returns true if the collection of +// events resolved well, and then a count of how many rounds failed and how +// many roundEvents timed out. +func TrackResults(resultsCh chan ds.EventReturn, numResults int) (bool, int, int) { + numTimeOut, numRoundFail := 0, 0 + for numResponses := 0; numResponses < numResults; numResponses++ { + er := <-resultsCh + if er.TimedOut { + numTimeOut++ + } else if states.Round(er.RoundInfo.State) == states.FAILED { + numRoundFail++ + } + } + + return (numTimeOut + numRoundFail) > 0, numRoundFail, numTimeOut +} diff --git a/go.mod b/go.mod index a7b3c09e61d611f872e96573ca65bc965c18159f..6d29483ab5a4dd8fdc78fbb6dbf97243d0562731 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.6.2 - gitlab.com/elixxir/comms v0.0.0-20200827193018-c0911a1a1ec0 + gitlab.com/elixxir/comms v0.0.0-20200828220824-6e9c8abca9bc gitlab.com/elixxir/crypto v0.0.0-20200827170914-14227f20900c gitlab.com/elixxir/ekv v0.1.1 gitlab.com/elixxir/primitives v0.0.0-20200827170420-5d50351f99b4 diff --git a/go.sum b/go.sum index 456e23218b6fda4856698cd2ea23165f3f313b63..bd7749dbf83ec7bfa18f59a8a169948b67762d0f 100644 --- a/go.sum +++ b/go.sum @@ -171,6 +171,8 @@ gitlab.com/elixxir/comms v0.0.0-20200827170208-d1f872422b7e h1:BEBxLOW6yMdT53rBv gitlab.com/elixxir/comms v0.0.0-20200827170208-d1f872422b7e/go.mod h1:HW3Ige10aeJeyb2fcQ/YOBPiyzY/4jHau1Cj6/1WBHc= gitlab.com/elixxir/comms v0.0.0-20200827193018-c0911a1a1ec0 h1:C0P5VqrgGcTuV9kCicxakHLSUFN3qNk55O/nrBmY0fk= gitlab.com/elixxir/comms v0.0.0-20200827193018-c0911a1a1ec0/go.mod h1:HW3Ige10aeJeyb2fcQ/YOBPiyzY/4jHau1Cj6/1WBHc= +gitlab.com/elixxir/comms v0.0.0-20200828220824-6e9c8abca9bc h1:+Plp0oXKNTvHUZkoYusFyLELlyXgAKn3O5I3389xv+A= +gitlab.com/elixxir/comms v0.0.0-20200828220824-6e9c8abca9bc/go.mod h1:HW3Ige10aeJeyb2fcQ/YOBPiyzY/4jHau1Cj6/1WBHc= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4 h1:28ftZDeYEko7xptCZzeFWS1Iam95dj46TWFVVlKmw6A= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= gitlab.com/elixxir/crypto v0.0.0-20200805174804-bdf909f2a16d/go.mod h1:cu6uNoANVLV0J6HyTL6KqVtVyh9SHU1RjJhytYlsbVQ= diff --git a/io/keyExchange/init.go b/io/keyExchange/init.go new file mode 100644 index 0000000000000000000000000000000000000000..7159d57803c07649e28612d4bf6b6dc9372593ae --- /dev/null +++ b/io/keyExchange/init.go @@ -0,0 +1,15 @@ +package keyExchange + +import ( + "gitlab.com/elixxir/client/context" + "gitlab.com/elixxir/client/context/stoppable" + "gitlab.com/elixxir/primitives/switchboard" +) + +func Init(ctx *context.Context) stoppable.Stoppable { + + //register the rekey request thread + rekeyRequestCh := make(chan switchboard.Item, 10) + ctx.Switchboard.RegisterChannel() + +} diff --git a/io/keyExchange/rekey.go b/io/keyExchange/rekey.go index 9e1334bfcea646ecd8c0e20455097f8cc20b0cad..45092934626f433f1b76ea395062cd90d25d028c 100644 --- a/io/keyExchange/rekey.go +++ b/io/keyExchange/rekey.go @@ -1,33 +1,60 @@ package keyExchange import ( + "github.com/pkg/errors" "gitlab.com/elixxir/client/context" + "gitlab.com/elixxir/client/context/params" + "gitlab.com/elixxir/client/context/utility" "gitlab.com/elixxir/client/storage/e2e" + ds "gitlab.com/elixxir/comms/network/dataStructures" "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/primitives/id" + "gitlab.com/elixxir/primitives/states" jww "github.com/spf13/jwalterweatherman" + "time" ) -type KeyExchangeSend interface { - SendRekey(m context.Message) ([]id.Round, error) +func CheckKeyExchanges(ctx *context.Context, manager *e2e.Manager) { + sessions := manager.TriggerNegotiations() + for _, ses := range sessions { + locakSes := ses + go trigger(ctx, manager, locakSes) + } } -func Rekey(ctx *context.Context, partner *id.ID, kx KeyExchangeSend) { - e2eStore := ctx.Session.E2e() - - //get the key manager - manager, err := e2eStore.GetPartner(partner) - if err != nil { - jww.ERROR.Printf("Failed to Rekey with %s: Failed to retrieve "+ - "key manager: %s", partner, err) +// There are two types of key negotiations that can be triggered, creating a new +// session and negotiation, or resenting a negotiation for an already created +// session. They run the same negotiation, the former does it on a newly created +// session while the latter on an extand +func trigger(ctx *context.Context, manager *e2e.Manager, session *e2e.Session) { + var negotiatingSession *e2e.Session + switch session.ConfirmationStatus() { + // If the passed session is triggering a negotiation on a new session to + // replace itself, then create the session + case e2e.NewSessionTriggered: + //create the session, pass a nil private key to generate a new one + negotiatingSession = manager.NewSendSession(nil, e2e.GetDefaultSessionParams()) + //move the state of the triggering session forward + session.SetNegotiationStatus(e2e.NewSessionCreated) + // If the session has not successfully negotiated, redo its negotiation + case e2e.Unconfirmed: + negotiatingSession = session + default: + jww.FATAL.Panicf("Session %s provided invalid e2e "+ + "negotiating status: %s", session, session.ConfirmationStatus()) } - //create the session - session, err := manager.NewSendSession(nil, e2e.GetDefaultSessionParams()) + // send the rekey notification to the partner + err := negotiate(ctx, negotiatingSession) + // if sending the negotiation fails, revert the state of the session to + // unconfirmed so it will be triggered in the future if err != nil { - jww.ERROR.Printf("Failed to Rekey with %s: Failed to make new "+ - "session: %s", partner, err) + jww.ERROR.Printf("Failed to do Key Negotiation: %s", err) + negotiatingSession.SetNegotiationStatus(e2e.Unconfirmed) } +} + +func negotiate(ctx *context.Context, session *e2e.Session) error { + e2eStore := ctx.Session.E2e() //generate public key pubKey := diffieHellman.GeneratePublicKey(session.GetMyPrivKey(), @@ -35,14 +62,59 @@ func Rekey(ctx *context.Context, partner *id.ID, kx KeyExchangeSend) { //send session m := context.Message{ - Recipient: partner, + Recipient: session.GetPartner(), Payload: pubKey.Bytes(), MessageType: 42, } - rounds, err := kx.SendRekey(m) + //send the message under the key exchange + e2eParams := params.GetDefaultE2E() + e2eParams.Type = params.KeyExchange + cmixParams := params.GetDefaultCMIX() + cmixParams.Retries = 20 + + rounds, err := ctx.Manager.SendE2E(m, e2eParams, cmixParams) + // If the send fails, returns the error so it can be handled. The caller + // should ensure the calling session is in a state where the Rekey will + // be triggered next time a key is used if err != nil { + return errors.Errorf("Failed to send the key negotation message "+ + "for %s: %s", session, err) + } + + //create the runner which will handle the result of sending the messages + sendResults := make(chan ds.EventReturn, len(rounds)) + + //Register the event for all rounds + roundEvents := ctx.Manager.GetInstance().GetRoundEvents() + for _, r := range rounds { + roundEvents.AddRoundEventChan(r, sendResults, 1*time.Minute, + states.COMPLETED, states.FAILED) + } + //Start the thread which will handle the outcome of the send + go trackNegotiationResult(sendResults, len(rounds), session) +} + +func trackNegotiationResult(resultsCh chan ds.EventReturn, numResults int, session *e2e.Session) { + success, numTimeOut, numRoundFail := utility.TrackResults(resultsCh, numResults) + + // If a single partition of the Key Negotiation request does not + // transmit, the partner cannot read the result. Log the error and set + // the session as unconfirmed so it will re-trigger the negotiation + if !success { + jww.ERROR.Printf("Key Negotiation for %s failed to "+ + "transmit %v/%v paritions: %v round failures, %v timeouts", + session, numRoundFail+numTimeOut, numResults, numRoundFail, + numTimeOut) + session.SetNegotiationStatus(e2e.Unconfirmed) + return } + // otherwise, the transmission is a success and this should be denoted + // in the session and the log + jww.INFO.Printf("Key Negotiation transmission for %s sucesfull", + session) + session.SetNegotiationStatus(e2e.Sent) } + diff --git a/storage/cmix/key.go b/storage/cmix/key.go index f2078b678e1d78902337663ce4cede06b5fddeff..2042bc56975504f3d9bfdda2a5ba4738a656b2bd 100644 --- a/storage/cmix/key.go +++ b/storage/cmix/key.go @@ -1,6 +1,7 @@ package cmix import ( + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/primitives/id" @@ -70,9 +71,11 @@ func (k *key) save() error { } // deletes the key from the versioned keystore -func (k *key) delete(kv *versioned.KV, id *id.ID) error { +func (k *key) delete(kv *versioned.KV, id *id.ID) { key := keyKey(id) - return kv.Delete(key) + if err := kv.Delete(key); err != nil { + jww.FATAL.Panicf("Failed to delete key %s: %s", k, err) + } } // makes a binary representation of the given key in the keystore @@ -86,6 +89,12 @@ func (k *key) unmarshal(b []byte) error { return k.k.GobDecode(b) } +// Adheres to the stringer interface +func (k *key) String() string { + return k.storeKey + +} + // generates the key used in the keystore for the given key func keyKey(id *id.ID) string { return "nodeKey:" + id.String() diff --git a/storage/cmix/store.go b/storage/cmix/store.go index 9ca51397c7cd572a343484f217e0bd43e6158c0d..561968779f2a2b4701f001867e656fac70c6c1e0 100644 --- a/storage/cmix/store.go +++ b/storage/cmix/store.go @@ -17,6 +17,7 @@ import ( "gitlab.com/xx_network/primitives/id" "sync" "time" + jww "github.com/spf13/jwalterweatherman" ) const currentStoreVersion = 0 @@ -95,37 +96,38 @@ func LoadStore(kv *versioned.KV) (*Store, error) { // adds the key for a round to the cmix storage object. Saves the updated list // of nodes and the key to disk -func (s *Store) Add(nid *id.ID, k *cyclic.Int) error { +func (s *Store) Add(nid *id.ID, k *cyclic.Int) { s.mux.Lock() defer s.mux.Unlock() nodekey, err := NewKey(s.kv, k, nid) if err != nil { - return err + jww.FATAL.Panicf("Failed to make nodeKey for %s: %s", nid, err) } s.nodes[*nid] = nodekey - return s.save() + if err = s.save(); err != nil { + jww.FATAL.Panicf("Failed to save nodeKey list for %s: %s", nid, err) + } } // Remove a Node key from the nodes map and save -func (s *Store) Remove(nid *id.ID) error { +func (s *Store) Remove(nid *id.ID) { s.mux.Lock() defer s.mux.Unlock() nodekey, ok := s.nodes[*nid] if !ok { - return errors.New("Cannot remove, no key with given ID found") + return } - err := nodekey.delete(s.kv, nid) - if err != nil { - return err - } + nodekey.delete(s.kv, nid) delete(s.nodes, *nid) - return s.save() + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed make nodeKey for %s: %s", nid, err) + } } //Returns a RoundKeys for the topology and a list of nodes it did not have a key for diff --git a/storage/e2e/confirmation.go b/storage/e2e/confirmation.go deleted file mode 100644 index 72ba4060e2d03f49a54608f7d898a8eecbce7ac7..0000000000000000000000000000000000000000 --- a/storage/e2e/confirmation.go +++ /dev/null @@ -1,9 +0,0 @@ -package e2e - -type Confirmation uint8 - -const ( - Unconfimed Confirmation = 0 - InProgress = 1 - Confirmed = 2 -) diff --git a/storage/e2e/key.go b/storage/e2e/key.go index 4dfa8396553735ae77bec55075d83d40a77b66f5..539a681fb476ab0d1b9494840483b239c5b25994 100644 --- a/storage/e2e/key.go +++ b/storage/e2e/key.go @@ -95,8 +95,8 @@ func (k *Key) Decrypt(msg format.Message) (format.Message, error) { } // Sets the key as used -func (k *Key) denoteUse() error { - return k.session.useKey(k.keyNum) +func (k *Key) denoteUse() { + k.session.useKey(k.keyNum) } // Generates the key and returns it diff --git a/storage/e2e/manager.go b/storage/e2e/manager.go index 1606ef8ca5b8f7c53c1e4001284555c44f4cf37e..26a437d9a1fc98ff2f907555d97feb3fe2042175 100644 --- a/storage/e2e/manager.go +++ b/storage/e2e/manager.go @@ -2,8 +2,10 @@ package e2e import ( "github.com/pkg/errors" + "gitlab.com/elixxir/client/context/params" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/primitives/id" + jww "github.com/spf13/jwalterweatherman" ) type Manager struct { @@ -17,7 +19,7 @@ type Manager struct { // create the manager and its first send and receive sessions func newManager(ctx *context, partnerID *id.ID, myPrivKey *cyclic.Int, - partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) (*Manager, error) { + partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) *Manager { m := &Manager{ ctx: ctx, partner: partnerID, @@ -26,31 +28,15 @@ func newManager(ctx *context, partnerID *id.ID, myPrivKey *cyclic.Int, m.send = NewSessionBuff(m, "send") m.receive = NewSessionBuff(m, "receive") - sendSession, err := newSession(m, myPrivKey, partnerPubKey, sendParams, Send) - if err != nil { - return nil, errors.WithMessage(err, - "Failed to create the send session") - } + sendSession := newSession(m, myPrivKey, partnerPubKey, sendParams, Send) - err = m.send.AddSession(sendSession) - if err != nil { - return nil, errors.WithMessage(err, - "Failed to add the send session to buffer") - } + m.send.AddSession(sendSession) - receiveSession, err := newSession(m, myPrivKey, partnerPubKey, receiveParams, Receive) - if err != nil { - return nil, errors.WithMessage(err, - "Failed to create the receive session") - } + receiveSession := newSession(m, myPrivKey, partnerPubKey, receiveParams, Receive) - err = m.receive.AddSession(receiveSession) - if err != nil { - return nil, errors.WithMessage(err, - "Failed to add the receive session to buffer") - } + m.receive.AddSession(receiveSession) - return m, nil + return m } //loads a manager and all buffers and sessions from disk @@ -87,57 +73,57 @@ func (m *Manager) GetPartnerID() *id.ID { // creates a new receive session using the latest private key this user has sent // and the new public key received from the partner. -func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int, params SessionParams) error { +func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int, params SessionParams) *Session { //find your last confirmed private key - myPrivKey := m.send.GetNewestConfirmed().GetMyPrivKey() + myPrivKey := m.send.GetNewestRekeyableSession().GetMyPrivKey() //create the session - session, err := newSession(m, myPrivKey, partnerPubKey, params, Receive) - - if err != nil { - return err - } + session := newSession(m, myPrivKey, partnerPubKey, params, Receive) //add the session to the buffer - err = m.receive.AddSession(session) - if err != nil { - //delete the session if it failed to add to the buffer - session.Delete() - } + m.receive.AddSession(session) - return err + return session } // creates a new receive session using the latest public key received from the // partner and a mew private key for the user // passing in a private key is optional. a private key will be generated if // none is passed -func (m *Manager) NewSendSession(myPrivKey *cyclic.Int, params SessionParams) (*Session, error) { +func (m *Manager) NewSendSession(myPrivKey *cyclic.Int, params SessionParams) *Session { //find the latest public key from the other party - partnerPubKey := m.receive.GetNewestConfirmed().partnerPubKey + partnerPubKey := m.receive.GetNewestRekeyableSession().partnerPubKey - session, err := newSession(m, myPrivKey, partnerPubKey, params, Send) - if err != nil { - return nil, err - } + //create the session + session := newSession(m, myPrivKey, partnerPubKey, params, Send) - //add the session to the buffer - err = m.send.AddSession(session) - if err != nil { - //delete the session if it failed to add to the buffer - session.Delete() - return nil, err - } + //add the session to the send session buffer and return + m.send.AddSession(session) - return session, nil + return session } -// gets the session buffer for message reception -func (m *Manager) GetSendingSession() *Session { - return m.send.GetSessionForSending() +// gets the correct session to send with depending on the type of send +func (m *Manager) GetSendingSession(st params.SendType) *Session { + switch st { + case params.Standard: + return m.send.GetSessionForSending() + case params.KeyExchange: + return m.send.GetNewestRekeyableSession() + default: + jww.ERROR.Printf("Cannot get session for invalid Send Type: %s", + st) + } + + return nil } // Confirms a send session is known about by the partner func (m *Manager) Confirm(sid SessionID) error { return m.send.Confirm(sid) } + +// returns a list of key exchange operations if any are necessary +func (m *Manager) TriggerNegotiations() []*Session { + return m.send.TriggerNegotiation() +} diff --git a/storage/e2e/negotiation.go b/storage/e2e/negotiation.go new file mode 100644 index 0000000000000000000000000000000000000000..ad6431c3cbce34f816b661118f3782d8fdcab331 --- /dev/null +++ b/storage/e2e/negotiation.go @@ -0,0 +1,37 @@ +package e2e + +import "fmt" + +// Fix-me: this solution is incompatible with offline sending, when that is +// added, a session which has not been confirmed will never trigger the +// creation of new session, the Unconfirmed->Confirmed and +// Confirmed->NewSessionCreated most likely need to be two separate enums +// tracked separately +type Negotiation uint8 + +const ( + Unconfirmed Negotiation = 0 + Sending = 1 + Sent = 2 + Confirmed = 3 + NewSessionTriggered = 4 + NewSessionCreated = 5 +) + +//Adherence to stringer interface +func (c Negotiation) String() string { + switch c { + case Unconfirmed: + return "Unconfirmed" + case Sending: + return "Sending" + case Sent: + return "Sent" + case Confirmed: + return "Confirmed" + default: + return fmt.Sprintf("Unknown Negotiation %v", uint8(c)) + } +} + +type exchangeType uint8 diff --git a/storage/e2e/session.go b/storage/e2e/session.go index 876d384dccdc2d738289d395a047f5fea05efe11..1129eb5921d1c1ef8a1bb378ce1977eb1daa5bc7 100644 --- a/storage/e2e/session.go +++ b/storage/e2e/session.go @@ -2,6 +2,7 @@ package e2e import ( "encoding/json" + "fmt" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage/versioned" @@ -10,6 +11,7 @@ import ( dh "gitlab.com/elixxir/crypto/diffieHellman" "gitlab.com/elixxir/crypto/e2e" "gitlab.com/elixxir/crypto/hash" + "gitlab.com/xx_network/primitives/id" "sync" "time" ) @@ -34,7 +36,7 @@ type Session struct { partnerPubKey *cyclic.Int //denotes if the other party has confirmed this key - confirmStatus Confirmation + negotiationStatus Negotiation // Value of the counter at which a rekey is triggered ttl uint32 @@ -72,27 +74,32 @@ type SessionDisk struct { /*CONSTRUCTORS*/ //Generator which creates all keys and structures -func newSession(manager *Manager, myPrivKey *cyclic.Int, partnerPubKey *cyclic.Int, params SessionParams, t SessionType) (*Session, error) { - session := &Session{ - params: params, - manager: manager, - t: t, - myPrivKey: myPrivKey, - partnerPubKey: partnerPubKey, - confirmed: t == Receive, +func newSession(manager *Manager, myPrivKey *cyclic.Int, partnerPubKey *cyclic.Int, params SessionParams, t SessionType) *Session { + + confirmation := Unconfirmed + if t == Receive { + confirmation = Confirmed } - err := session.generate() - if err != nil { - return nil, err + + session := &Session{ + params: params, + manager: manager, + t: t, + myPrivKey: myPrivKey, + partnerPubKey: partnerPubKey, + negotiationStatus: confirmation, } - err = session.save() + session.generate() + + err := session.save() if err != nil { - return nil, err + jww.FATAL.Printf("Failed to make new session for Partner %s: %s", + manager.partner, err) } - return session, nil + return session } // Load session and state vector from kv and populate runtime fields @@ -189,6 +196,11 @@ func (s *Session) GetID() SessionID { return sid } +// returns the ID of the partner for this session +func (s *Session) GetPartner() *id.ID { + return s.manager.partner +} + //ekv functions func (s *Session) marshal() ([]byte, error) { sd := SessionDisk{} @@ -198,7 +210,17 @@ func (s *Session) marshal() ([]byte, error) { sd.BaseKey = s.baseKey.Bytes() sd.MyPrivKey = s.myPrivKey.Bytes() sd.PartnerPubKey = s.partnerPubKey.Bytes() - sd.Confirmed = s.confirmed + + // assume in progress confirmations and session creations have failed on + // reset, therefore do not store their pending progress + if s.negotiationStatus == Sending { + sd.Confirmation = uint8(Unconfirmed) + } else if s.negotiationStatus == NewSessionTriggered { + sd.Confirmation = uint8(Confirmed) + } else { + sd.Confirmation = uint8(s.negotiationStatus) + } + sd.TTL = s.ttl return json.Marshal(&sd) @@ -221,7 +243,7 @@ func (s *Session) unmarshal(b []byte) error { s.baseKey = grp.NewIntFromBytes(sd.BaseKey) s.myPrivKey = grp.NewIntFromBytes(sd.MyPrivKey) s.partnerPubKey = grp.NewIntFromBytes(sd.PartnerPubKey) - s.confirmed = sd.Confirmed + s.negotiationStatus = Negotiation(sd.Confirmation) s.ttl = sd.TTL statesKey := makeStateVectorKey(keyEKVPrefix, s.GetID()) @@ -262,50 +284,160 @@ func (s *Session) PopReKey() (*Key, error) { // returns the state of the session, which denotes if the Session is active, // functional but in need of a rekey, empty of send key, or empty of rekeys func (s *Session) Status() Status { - if s.keyState.GetNumAvailable() == 0 { + // copy the num available so it stays consistent as this function does its + // checks + numAvailable := s.keyState.GetNumAvailable() + + if numAvailable == 0 { return RekeyEmpty - } else if s.keyState.GetNumAvailable() <= uint32(s.params.NumRekeys) { + } else if numAvailable <= uint32(s.params.NumRekeys) { return Empty - } else if s.keyState.GetNumAvailable() <= s.keyState.GetNumKeys()-s.ttl { + // do not need to make a copy of getNumKeys becasue it is static and + // only used once + } else if numAvailable <= s.keyState.GetNumKeys()-s.ttl { return RekeyNeeded } else { return Active } } -// returns the state of the session, which denotes if the Session is active, -// functional but in need of a rekey, empty of send key, or empty of rekeys -func (s *Session) IsReKeyNeeded() bool { - return s.keyState.GetNumAvailable() == s.ttl +// Sets the negotiation status, this tracks the state of the key negotiation, +// only certain movements are allowed +// Unconfirmed <--> Sending --> Sent --> Confirmed <--> NewSessionTriggered --> NewSessionCreated +// +// Saves the session unless the status is sending so that on reload the rekey +// will be redone if it was in the process of sending + +// Moving from Unconfirmed to Sending and from Confirmed to NewSessionTriggered +// is handled by Session.triggerNegotiation() which is called by the +// Manager as part of Manager.TriggerNegotiations() and will be rejected +// from this function + +var legalStateChanges = [][]bool{ + {false, false, false, false, false, false}, + {true, false, true, true, false, false}, + {false, false, false, true, false, false}, + {false, false, false, false, false, false}, + {false, false, false, true, false, true}, + {false, false, false, false, false, false}, +} + +func (s *Session) SetNegotiationStatus(status Negotiation) { + s.mux.Lock() + defer s.mux.Unlock() + //only allow the correct state changes to propagate + if !legalStateChanges[s.negotiationStatus][status] { + jww.FATAL.Panicf("Negotiation status change from %s to %s "+ + "is not valid", s.negotiationStatus, status) + } + + // the states of Sending and NewSessionTriggered are not saved to disk when + // moved from Unconfirmed or Confirmed respectively so the actions are + // re-triggered if there is a crash and reload. As a result, a save when + // reverting states is unnecessary + save := !((s.negotiationStatus == Sending && status == Unconfirmed) || + (s.negotiationStatus == NewSessionTriggered && status == Confirmed)) + + //change the state + s.negotiationStatus = status + + //save the status if appropriate + if save { + if err := s.save(); err != nil { + jww.FATAL.Printf("Failed to save Session %s when moving from %s to %s") + } + } +} + +// This function, in a mostly thread safe manner, checks if the session needs a +// negotiation, returns if it does while updating the session to denote the +// negotiation was triggered +// WARNING: This function relies on proper action by the caller for data safety. +// When triggering the creation of a new session (the first case) it does not +// store to disk the fact that it has triggered the session. This is because +// every session should only trigger one other session and in the event that +// session trigger does not resolve before a crash, by not storing it the +// trigger will automatically happen again when reloading after the crash. +// In order to ensure the session creation is not triggered again after the +// reload, it is the responsibility of the caller to call +// Session.SetConfirmationStatus(NewSessionCreated) . +func (s *Session) triggerNegotiation() bool { + // Due to the fact that a read lock cannot be transitioned to a + // write lock, the state checks need to happen a second time because it + // is possible for another thread to take the read lock and update the + // state between this thread releasing it and regaining it again. In this + // case, such double locking is preferable because the majority of the time, + // the checked cases will turn out to be false. + s.mux.RLock() + //trigger a rekey to create a new session + if s.keyState.GetNumAvailable() >= s.ttl && s.negotiationStatus == Confirmed { + s.mux.RUnlock() + s.mux.Lock() + if s.keyState.GetNumAvailable() >= s.ttl && s.negotiationStatus == Confirmed { + s.negotiationStatus = NewSessionTriggered + // no save is make after the update because we do not want this state + // saved to disk. The caller will shortly execute the operation, + // and then move to the next state. If a crash occurs before, by not + // storing this state this operation will be repeated after reload + // The save function has been modified so if another call causes a + // save, "NewSessionTriggerd" will be overwritten with "Confirmed" + // in the saved data. + s.mux.Unlock() + return true + } else { + s.mux.Unlock() + return false + } + // retrigger this sessions negotiation + } else if s.negotiationStatus == Unconfirmed { + s.mux.RUnlock() + s.mux.Lock() + if s.negotiationStatus == Unconfirmed { + s.negotiationStatus = Sending + // no save is make after the update because we do not want this state + // saved to disk. The caller will shortly execute the operation, + // and then move to the next state. If a crash occurs before, by not + // storing this state this operation will be repeated after reload + // The save function has been modified so if another call causes a + // save, "Sending" will be overwritten with "Unconfirmed" + // in the saved data. + s.mux.Unlock() + return true + } else { + s.mux.Unlock() + return false + } + } + s.mux.RUnlock() + return true } // checks if the session has been confirmed -func (s *Session) IsConfirmed() bool { +func (s *Session) ConfirmationStatus() Negotiation { s.mux.RLock() defer s.mux.RUnlock() - return s.confirmed + return s.negotiationStatus } -/*PRIVATE*/ +// checks if the session has been confirmed +func (s *Session) IsConfirmed() bool { + c := s.ConfirmationStatus() + return c >= Confirmed +} -// Sets the confirm bool. this is set when the partner is certain to share the -// session. It should be called immediately for receive keys and only on rekey -// confirmation for send keys. Confirmation can only be made by the sessionBuffer -// because it is used to keep track of active sessions for rekey as well -func (s *Session) confirm() error { - s.mux.Lock() - defer s.mux.Unlock() - s.confirmed = true - return s.save() +func (s *Session) String() string { + return fmt.Sprintf("{Partner: %s, ID: %s}", + s.manager.partner, s.GetID()) } -func (s *Session) useKey(keynum uint32) error { - return s.keyState.Use(keynum) +/*PRIVATE*/ +func (s *Session) useKey(keynum uint32) { + s.keyState.Use(keynum) } // generates keys from the base data stored in the session object. // myPrivKey will be generated if not present -func (s *Session) generate() error { +func (s *Session) generate() { grp := s.manager.ctx.grp //generate private key if it is not present @@ -335,7 +467,7 @@ func (s *Session) generate() error { var err error s.keyState, err = newStateVector(s.manager.ctx, makeStateVectorKey(keyEKVPrefix, s.GetID()), numKeys) if err != nil { - return errors.WithMessage(err, "Failed key generation") + jww.FATAL.Printf("Failed key generation: %s", err) } //register keys for reception if this is a reception session @@ -343,8 +475,6 @@ func (s *Session) generate() error { //register keys s.manager.ctx.fa.add(s.getUnusedKeys()) } - - return nil } //returns key objects for all unused keys diff --git a/storage/e2e/sessionBuff.go b/storage/e2e/sessionBuff.go index 547027133789575b5706d1c8913c3f018b6af456..26616158703396d12836a2f07a75eda0fdb458ee 100644 --- a/storage/e2e/sessionBuff.go +++ b/storage/e2e/sessionBuff.go @@ -121,16 +121,21 @@ func (sb *sessionBuff) unmarshal(b []byte) error { return nil } -func (sb *sessionBuff) AddSession(s *Session) error { +func (sb *sessionBuff) AddSession(s *Session) { sb.mux.Lock() defer sb.mux.Unlock() sb.addSession(s) - return sb.save() + if err := sb.save(); err != nil { + key := makeSessionBuffKey(sb.keyPrefix, sb.manager.partner) + jww.FATAL.Printf("Failed to save Session Buffer %s after "+ + "adding session %s: %s", key, s, err) + } + + return } func (sb *sessionBuff) addSession(s *Session) { - sb.sessions = append([]*Session{s}, sb.sessions...) sb.sessionByID[s.GetID()] = s return @@ -147,9 +152,9 @@ func (sb *sessionBuff) GetNewest() *Session { // returns the session which is most likely to be successful for sending func (sb *sessionBuff) GetSessionForSending() *Session { - sb.mux.RLock() - defer sb.mux.RUnlock() - if len(sb.sessions) == 0 { + //dont need to take the lock due to the use of a copy of the buffer + sessions := sb.getInternalBufferShallowCopy() + if len(sessions) == 0 { return nil } @@ -157,9 +162,9 @@ func (sb *sessionBuff) GetSessionForSending() *Session { var unconfirmedActive []*Session var unconfirmedRekey []*Session - for _, s := range sb.sessions { + for _, s := range sessions { status := s.Status() - confirmed := s.confirmed + confirmed := s.IsConfirmed() if status == Active && confirmed { //always return the first confirmed active, happy path return s @@ -184,21 +189,38 @@ func (sb *sessionBuff) GetSessionForSending() *Session { return nil } -func (sb *sessionBuff) GetNewestConfirmed() *Session { - sb.mux.RLock() - defer sb.mux.RUnlock() - if len(sb.sessions) == 0 { +// returns a list of session that need rekeys. Nil instances mean a new rekey +// from scratch +func (sb *sessionBuff) TriggerNegotiation() []*Session { + //dont need to take the lock due to the use of a copy of the buffer + sessions := sb.getInternalBufferShallowCopy() + var instructions []*Session + for _, ses := range sessions { + if ses.triggerNegotiation() { + instructions = append(instructions, ses) + } + } + return instructions +} + +// returns the newest session which can be used to start a key negotiation +func (sb *sessionBuff) GetNewestRekeyableSession() *Session { + //dont need to take the lock due to the use of a copy of the buffer + sessions := sb.getInternalBufferShallowCopy() + + if len(sessions) == 0 { return nil } for _, s := range sb.sessions { - status := s.Status() - confirmed := s.confirmed - if status != RekeyEmpty && confirmed { + // This looks like it might not be thread safe, I think it is because + // the failure mode is it skips to a lower key to rekey with, which is + // always valid. It isn't clear it can fail though because we are + // accessing the data in the same order it would be written (i think) + if s.Status() != RekeyEmpty && s.IsConfirmed() { return s } } - return nil } @@ -219,20 +241,28 @@ func (sb *sessionBuff) Confirm(id SessionID) error { return errors.Errorf("Could not confirm session %s, does not exist", s.GetID()) } - err := s.confirm() - if err != nil { - jww.FATAL.Panicf("Failed to confirm session "+ - "%s for %s: %s", s.GetID(), sb.manager.partner, err.Error()) - } + s.SetNegotiationStatus(Confirmed) + + sb.clean() + + return nil +} - return sb.clean() +// adding or removing a session is always done via replacing the entire +// slice, this allow us to copy the slice under the read lock and do the +// rest of the work while not taking the lock +func (sb *sessionBuff) getInternalBufferShallowCopy() []*Session { + sb.mux.RLock() + defer sb.mux.RUnlock() + return sb.sessions } -func (sb *sessionBuff) clean() error { +func (sb *sessionBuff) clean() { numConfirmed := uint(0) var newSessions []*Session + editsMade := false for _, s := range sb.sessions { if s.IsConfirmed() { @@ -241,16 +271,23 @@ func (sb *sessionBuff) clean() error { if numConfirmed > maxUnconfirmed { delete(sb.sessionByID, s.GetID()) s.Delete() - - break + editsMade = true + continue } } newSessions = append(newSessions, s) } - sb.sessions = newSessions + //only do the update and save if changes occured + if editsMade { + sb.sessions = newSessions - return sb.save() + if err := sb.save(); err != nil { + key := makeSessionBuffKey(sb.keyPrefix, sb.manager.partner) + jww.FATAL.Printf("Failed to save Session Buffer %s after "+ + "clean: %s", key, err) + } + } } func makeSessionBuffKey(keyPrefix string, partnerID *id.ID) string { diff --git a/storage/e2e/session_test.go b/storage/e2e/session_test.go index 46de8d439ba15dc915a6fa3fc9db1d9ccbeeda2b..9c7f69a76e4de260ce7ebd70594465cdc8641fd8 100644 --- a/storage/e2e/session_test.go +++ b/storage/e2e/session_test.go @@ -35,10 +35,7 @@ func TestSession_generate_noPrivateKeyReceive(t *testing.T) { } //run the generate command - err := s.generate() - if err != nil { - t.Fatal(err) - } + s.generate() //check that it generated a private key if s.myPrivKey == nil { @@ -100,10 +97,7 @@ func TestSession_generate_PrivateKeySend(t *testing.T) { } //run the generate command - err := s.generate() - if err != nil { - t.Fatal(err) - } + s.generate() //check that it generated a private key if s.myPrivKey.Cmp(myPrivKey) != 0 { @@ -141,11 +135,9 @@ func TestNewSession(t *testing.T) { // Make a test session to easily populate all the fields sessionA, _ := makeTestSession(t) // Make a new session with the variables we got from makeTestSession - sessionB, err := newSession(sessionA.manager, sessionA.myPrivKey, sessionA.partnerPubKey, sessionA.params, sessionA.t) - if err != nil { - t.Fatal(err) - } - err = cmpSerializedFields(sessionA, sessionB) + sessionB := newSession(sessionA.manager, sessionA.myPrivKey, sessionA.partnerPubKey, sessionA.params, sessionA.t) + + err := cmpSerializedFields(sessionA, sessionB) if err != nil { t.Error(err) } @@ -239,7 +231,7 @@ func TestSession_Serialization(t *testing.T) { // compare fields also represented in SessionDisk // fields not represented in SessionDisk shouldn't be expected to be populated by Unmarshal func cmpSerializedFields(a *Session, b *Session) error { - if a.confirmed != b.confirmed { + if a.negotiationStatus != b.negotiationStatus { return errors.New("confirmed differed") } if a.t != b.t { @@ -406,11 +398,11 @@ func TestSession_GetMyPrivKey(t *testing.T) { // Shows that IsConfirmed returns whether the session is confirmed func TestSession_IsConfirmed(t *testing.T) { s, _ := makeTestSession(t) - s.confirmed = false + s.negotiationStatus = Unconfirmed if s.IsConfirmed() { t.Error("s was confirmed when it shouldn't have been") } - s.confirmed = true + s.negotiationStatus = Confirmed if !s.IsConfirmed() { t.Error("s wasn't confirmed when it should have been") } @@ -500,9 +492,9 @@ func makeTestSession(t *testing.T) (*Session, *context) { manager: &Manager{ ctx: ctx, }, - t: Receive, - confirmed: true, - ttl: 5, + t: Receive, + negotiationStatus: Confirmed, + ttl: 5, } var err error s.keyState, err = newStateVector(ctx, makeStateVectorKey(keyEKVPrefix, s.GetID()), 1024) diff --git a/storage/e2e/stateVector.go b/storage/e2e/stateVector.go index f93619700e94ee0b37c1c0d1837a86ddbf531835..3844705e7f1cc95e77a4175a8d339be98f314d55 100644 --- a/storage/e2e/stateVector.go +++ b/storage/e2e/stateVector.go @@ -2,10 +2,12 @@ package e2e import ( "encoding/json" + "fmt" "github.com/pkg/errors" "gitlab.com/elixxir/client/storage/versioned" "sync" "time" + jww "github.com/spf13/jwalterweatherman" ) const currentStateVectorVersion = 0 @@ -85,7 +87,7 @@ func (sv *stateVector) save() error { return sv.ctx.kv.Set(sv.key, &obj) } -func (sv *stateVector) Use(keynum uint32) error { +func (sv *stateVector) Use(keynum uint32) { sv.mux.Lock() defer sv.mux.Unlock() @@ -100,7 +102,9 @@ func (sv *stateVector) Use(keynum uint32) error { sv.numAvailable-- - return sv.save() + if err := sv.save(); err != nil { + jww.FATAL.Printf("Failed to save %s on Use(): %s", sv, err) + } } func (sv *stateVector) GetNumAvailable() uint32 { @@ -136,7 +140,11 @@ func (sv *stateVector) Next() (uint32, error) { sv.nextAvailable() sv.numAvailable-- - return next, sv.save() + if err := sv.save(); err != nil { + jww.FATAL.Printf("Failed to save %s on Next(): %s", sv, err) + } + + return next, nil } @@ -176,6 +184,12 @@ func (sv *stateVector) GetUsedKeyNums() []uint32 { return keyNums } +//Adheres to the stringer interface +func (sv *stateVector) String() string { + return fmt.Sprintf("stateVector: %s", sv.key) +} + + // finds the next used state and sets that as firstAvailable. This does not // execute a store and a store must be executed after. func (sv *stateVector) nextAvailable() { diff --git a/storage/e2e/stateVector_test.go b/storage/e2e/stateVector_test.go index a447786cdb5f50a5feecc0a71e49a011f66d2369..b6cff62f08e4c178e3c94f480b98f4072d6745a2 100644 --- a/storage/e2e/stateVector_test.go +++ b/storage/e2e/stateVector_test.go @@ -124,10 +124,7 @@ func TestStateVector_Use(t *testing.T) { for numCalls := range keyNums { // These calls to Use won't set nextAvailable, because the first keyNum set - err := sv.Use(keyNums[numCalls]) - if err != nil { - t.Fatal(err) - } + sv.Use(keyNums[numCalls]) if !reflect.DeepEqual(expectedVect[numCalls], sv.vect) { t.Errorf("sv.vect differed from expected at index %v", numCalls) fmt.Println(sv.vect) diff --git a/storage/e2e/store.go b/storage/e2e/store.go index dac6ec27090b039b06c2e754fd341c7efb1c5c9d..cef07d447abbda92a2aca1a90a3699e39610d840 100644 --- a/storage/e2e/store.go +++ b/storage/e2e/store.go @@ -127,19 +127,20 @@ func (s *Store) save() error { } func (s *Store) AddPartner(partnerID *id.ID, myPrivKey *cyclic.Int, - partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) error { + partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) { s.mux.Lock() defer s.mux.Unlock() - m, err := newManager(&s.context, partnerID, myPrivKey, partnerPubKey, sendParams, receiveParams) - - if err != nil { - return err - } + m := newManager(&s.context, partnerID, myPrivKey, partnerPubKey, sendParams, receiveParams) s.managers[*partnerID] = m - return s.save() + if err := s.save(); err != nil { + jww.FATAL.Printf("Failed to add Parter %s: Save of store "+ + "failed: %s", partnerID, err) + } + + return } func (s *Store) GetPartner(partnerID *id.ID) (*Manager, error) { @@ -272,11 +273,7 @@ func (f *fingerprints) Pop(fingerprint format.Fingerprint) (*Key, error) { delete(f.toKey, fingerprint) - err := key.denoteUse() - - if err != nil { - return nil, err - } + key.denoteUse() key.fp = &fingerprint