make Messenger.Timesource public (#1837)

This commit is contained in:
Adam Babik 2020-02-07 12:30:26 +01:00 committed by GitHub
parent e1dee26ba9
commit d27a507e0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 112 additions and 84 deletions

View File

@ -160,6 +160,33 @@ func (c *Chat) updateChatFromProtocolGroup(g *v1protocol.Group) {
c.MembershipUpdates = g.Events() c.MembershipUpdates = g.Events()
} }
// NextClockAndTimestamp returns the next clock value
// and the current timestamp
func (c *Chat) NextClockAndTimestamp(timesource TimeSource) (uint64, uint64) {
clock := c.LastClockValue
timestamp := timesource.GetCurrentTime()
if clock == 0 || clock < timestamp {
clock = timestamp
} else {
clock = clock + 1
}
return clock, timestamp
}
func (c *Chat) UpdateFromMessage(message *Message, timesource TimeSource) error {
c.Timestamp = int64(timesource.GetCurrentTime())
if c.LastClockValue <= message.Clock {
jsonMessage, err := json.Marshal(message)
if err != nil {
return err
}
c.LastClockValue = message.Clock
c.LastMessage = jsonMessage
}
return nil
}
// ChatMembershipUpdate represent an event on membership of the chat // ChatMembershipUpdate represent an event on membership of the chat
type ChatMembershipUpdate struct { type ChatMembershipUpdate struct {
// Unique identifier for the event // Unique identifier for the event
@ -207,14 +234,14 @@ func oneToOneChatID(publicKey *ecdsa.PublicKey) string {
return types.EncodeHex(crypto.FromECDSAPub(publicKey)) return types.EncodeHex(crypto.FromECDSAPub(publicKey))
} }
func OneToOneFromPublicKey(pk *ecdsa.PublicKey, timesource ClockValueTimesource) *Chat { func OneToOneFromPublicKey(pk *ecdsa.PublicKey, timesource TimeSource) *Chat {
chatID := types.EncodeHex(crypto.FromECDSAPub(pk)) chatID := types.EncodeHex(crypto.FromECDSAPub(pk))
newChat := CreateOneToOneChat(chatID[:8], pk, timesource) newChat := CreateOneToOneChat(chatID[:8], pk, timesource)
return &newChat return &newChat
} }
func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey, timesource ClockValueTimesource) Chat { func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey, timesource TimeSource) Chat {
return Chat{ return Chat{
ID: oneToOneChatID(publicKey), ID: oneToOneChatID(publicKey),
Name: name, Name: name,
@ -224,7 +251,7 @@ func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey, timesource Cloc
} }
} }
func CreatePublicChat(name string, timesource ClockValueTimesource) Chat { func CreatePublicChat(name string, timesource TimeSource) Chat {
return Chat{ return Chat{
ID: name, ID: name,
Name: name, Name: name,
@ -235,7 +262,7 @@ func CreatePublicChat(name string, timesource ClockValueTimesource) Chat {
} }
} }
func createGroupChat(timesource ClockValueTimesource) Chat { func CreateGroupChat(timesource TimeSource) Chat {
return Chat{ return Chat{
Active: true, Active: true,
Color: chatColors[rand.Intn(len(chatColors))], Color: chatColors[rand.Intn(len(chatColors))],
@ -276,37 +303,6 @@ func stringSliceToPublicKeys(slice []string, prefixed bool) ([]*ecdsa.PublicKey,
return result, nil return result, nil
} }
type ClockValueTimesource interface {
GetCurrentTime() uint64
}
// NextClockAndTimestamp returns the next clock value
// and the current timestamp
func (c *Chat) NextClockAndTimestamp(timesource ClockValueTimesource) (uint64, uint64) {
clock := c.LastClockValue
timestamp := timesource.GetCurrentTime()
if clock == 0 || clock < timestamp {
clock = timestamp
} else {
clock = clock + 1
}
return clock, timestamp
}
func (c *Chat) UpdateFromMessage(message *Message, timesource ClockValueTimesource) error {
c.Timestamp = int64(timesource.GetCurrentTime())
if c.LastClockValue <= message.Clock {
jsonMessage, err := json.Marshal(message)
if err != nil {
return err
}
c.LastClockValue = message.Clock
c.LastMessage = jsonMessage
}
return nil
}
func stringSliceContains(slice []string, item string) bool { func stringSliceContains(slice []string, item string) bool {
for _, s := range slice { for _, s := range slice {
if s == item { if s == item {

View File

@ -9,7 +9,7 @@ import (
"github.com/status-im/status-go/protocol/identity/identicon" "github.com/status-im/status-go/protocol/identity/identicon"
) )
func extendMessageFromChat(message *Message, chat *Chat, key *ecdsa.PublicKey, timesource ClockValueTimesource) error { func extendMessageFromChat(message *Message, chat *Chat, key *ecdsa.PublicKey, timesource TimeSource) error {
clock, timestamp := chat.NextClockAndTimestamp(timesource) clock, timestamp := chat.NextClockAndTimestamp(timesource)
message.LocalChatID = chat.ID message.LocalChatID = chat.ID

View File

@ -59,7 +59,7 @@ func (m *MessageHandler) HandleMembershipUpdate(messageState *ReceivedMessageSta
if !group.IsMember(contactIDFromPublicKey(&m.identity.PublicKey)) { if !group.IsMember(contactIDFromPublicKey(&m.identity.PublicKey)) {
return errors.New("can't create a new group chat without us being a member") return errors.New("can't create a new group chat without us being a member")
} }
newChat := createGroupChat(messageState.Timesource) newChat := CreateGroupChat(messageState.Timesource)
chat = &newChat chat = &newChat
} else { } else {
@ -550,7 +550,7 @@ func (m *MessageHandler) HandleDeclineRequestTransaction(messageState *ReceivedM
return m.handleCommandMessage(messageState, oldMessage) return m.handleCommandMessage(messageState, oldMessage)
} }
func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat, timesource ClockValueTimesource) (*Chat, error) { func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat, timesource TimeSource) (*Chat, error) {
if message.SigPubKey == nil { if message.SigPubKey == nil {
m.logger.Error("public key can't be empty") m.logger.Error("public key can't be empty")
return nil, errors.New("received a message with empty public key") return nil, errors.New("received a message with empty public key")

View File

@ -126,6 +126,8 @@ type config struct {
type Option func(*config) error type Option func(*config) error
// WithSystemMessagesTranslations is required for Group Chats which are currently disabled.
// nolint: unused
func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option { func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option {
return func(c *config) error { return func(c *config) error {
c.systemMessagesTranslations = t c.systemMessagesTranslations = t
@ -629,7 +631,7 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string,
var response MessengerResponse var response MessengerResponse
logger := m.logger.With(zap.String("site", "CreateGroupChatWithMembers")) logger := m.logger.With(zap.String("site", "CreateGroupChatWithMembers"))
logger.Info("Creating group chat", zap.String("name", name), zap.Any("members", members)) logger.Info("Creating group chat", zap.String("name", name), zap.Any("members", members))
chat := createGroupChat(m.getTimesource()) chat := CreateGroupChat(m.getTimesource())
group, err := v1protocol.NewGroupWithCreator(name, m.identity) group, err := v1protocol.NewGroupWithCreator(name, m.identity)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1699,8 +1701,8 @@ type ReceivedMessageState struct {
ExistingMessagesMap map[string]bool ExistingMessagesMap map[string]bool
// Response to the client // Response to the client
Response *MessengerResponse Response *MessengerResponse
// Timesource is a timesource for clock values/timestamps // Timesource is a time source for clock values/timestamps.
Timesource ClockValueTimesource Timesource TimeSource
} }
func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filter][]*types.Message) (*MessengerResponse, error) { func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filter][]*types.Message) (*MessengerResponse, error) {
@ -2806,6 +2808,10 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
return &response, nil return &response, nil
} }
func (m *Messenger) getTimesource() ClockValueTimesource { func (m *Messenger) getTimesource() TimeSource {
return m.transport return m.transport
} }
func (m *Messenger) Timesource() TimeSource {
return m.getTimesource()
}

12
protocol/timesource.go Normal file
View File

@ -0,0 +1,12 @@
package protocol
// TimeSource provides a unified way of getting the current time.
// The intention is to always use a synchronized time source
// between all components of the protocol.
//
// This is required by Whisper and Waku protocols
// which rely on a fact that all peers
// have a synchronized time source.
type TimeSource interface {
GetCurrentTime() uint64
}

View File

@ -160,6 +160,33 @@ func (c *Chat) updateChatFromProtocolGroup(g *v1protocol.Group) {
c.MembershipUpdates = g.Events() c.MembershipUpdates = g.Events()
} }
// NextClockAndTimestamp returns the next clock value
// and the current timestamp
func (c *Chat) NextClockAndTimestamp(timesource TimeSource) (uint64, uint64) {
clock := c.LastClockValue
timestamp := timesource.GetCurrentTime()
if clock == 0 || clock < timestamp {
clock = timestamp
} else {
clock = clock + 1
}
return clock, timestamp
}
func (c *Chat) UpdateFromMessage(message *Message, timesource TimeSource) error {
c.Timestamp = int64(timesource.GetCurrentTime())
if c.LastClockValue <= message.Clock {
jsonMessage, err := json.Marshal(message)
if err != nil {
return err
}
c.LastClockValue = message.Clock
c.LastMessage = jsonMessage
}
return nil
}
// ChatMembershipUpdate represent an event on membership of the chat // ChatMembershipUpdate represent an event on membership of the chat
type ChatMembershipUpdate struct { type ChatMembershipUpdate struct {
// Unique identifier for the event // Unique identifier for the event
@ -207,14 +234,14 @@ func oneToOneChatID(publicKey *ecdsa.PublicKey) string {
return types.EncodeHex(crypto.FromECDSAPub(publicKey)) return types.EncodeHex(crypto.FromECDSAPub(publicKey))
} }
func OneToOneFromPublicKey(pk *ecdsa.PublicKey, timesource ClockValueTimesource) *Chat { func OneToOneFromPublicKey(pk *ecdsa.PublicKey, timesource TimeSource) *Chat {
chatID := types.EncodeHex(crypto.FromECDSAPub(pk)) chatID := types.EncodeHex(crypto.FromECDSAPub(pk))
newChat := CreateOneToOneChat(chatID[:8], pk, timesource) newChat := CreateOneToOneChat(chatID[:8], pk, timesource)
return &newChat return &newChat
} }
func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey, timesource ClockValueTimesource) Chat { func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey, timesource TimeSource) Chat {
return Chat{ return Chat{
ID: oneToOneChatID(publicKey), ID: oneToOneChatID(publicKey),
Name: name, Name: name,
@ -224,7 +251,7 @@ func CreateOneToOneChat(name string, publicKey *ecdsa.PublicKey, timesource Cloc
} }
} }
func CreatePublicChat(name string, timesource ClockValueTimesource) Chat { func CreatePublicChat(name string, timesource TimeSource) Chat {
return Chat{ return Chat{
ID: name, ID: name,
Name: name, Name: name,
@ -235,7 +262,7 @@ func CreatePublicChat(name string, timesource ClockValueTimesource) Chat {
} }
} }
func createGroupChat(timesource ClockValueTimesource) Chat { func CreateGroupChat(timesource TimeSource) Chat {
return Chat{ return Chat{
Active: true, Active: true,
Color: chatColors[rand.Intn(len(chatColors))], Color: chatColors[rand.Intn(len(chatColors))],
@ -276,37 +303,6 @@ func stringSliceToPublicKeys(slice []string, prefixed bool) ([]*ecdsa.PublicKey,
return result, nil return result, nil
} }
type ClockValueTimesource interface {
GetCurrentTime() uint64
}
// NextClockAndTimestamp returns the next clock value
// and the current timestamp
func (c *Chat) NextClockAndTimestamp(timesource ClockValueTimesource) (uint64, uint64) {
clock := c.LastClockValue
timestamp := timesource.GetCurrentTime()
if clock == 0 || clock < timestamp {
clock = timestamp
} else {
clock = clock + 1
}
return clock, timestamp
}
func (c *Chat) UpdateFromMessage(message *Message, timesource ClockValueTimesource) error {
c.Timestamp = int64(timesource.GetCurrentTime())
if c.LastClockValue <= message.Clock {
jsonMessage, err := json.Marshal(message)
if err != nil {
return err
}
c.LastClockValue = message.Clock
c.LastMessage = jsonMessage
}
return nil
}
func stringSliceContains(slice []string, item string) bool { func stringSliceContains(slice []string, item string) bool {
for _, s := range slice { for _, s := range slice {
if s == item { if s == item {

View File

@ -9,7 +9,7 @@ import (
"github.com/status-im/status-go/protocol/identity/identicon" "github.com/status-im/status-go/protocol/identity/identicon"
) )
func extendMessageFromChat(message *Message, chat *Chat, key *ecdsa.PublicKey, timesource ClockValueTimesource) error { func extendMessageFromChat(message *Message, chat *Chat, key *ecdsa.PublicKey, timesource TimeSource) error {
clock, timestamp := chat.NextClockAndTimestamp(timesource) clock, timestamp := chat.NextClockAndTimestamp(timesource)
message.LocalChatID = chat.ID message.LocalChatID = chat.ID

View File

@ -59,7 +59,7 @@ func (m *MessageHandler) HandleMembershipUpdate(messageState *ReceivedMessageSta
if !group.IsMember(contactIDFromPublicKey(&m.identity.PublicKey)) { if !group.IsMember(contactIDFromPublicKey(&m.identity.PublicKey)) {
return errors.New("can't create a new group chat without us being a member") return errors.New("can't create a new group chat without us being a member")
} }
newChat := createGroupChat(messageState.Timesource) newChat := CreateGroupChat(messageState.Timesource)
chat = &newChat chat = &newChat
} else { } else {
@ -550,7 +550,7 @@ func (m *MessageHandler) HandleDeclineRequestTransaction(messageState *ReceivedM
return m.handleCommandMessage(messageState, oldMessage) return m.handleCommandMessage(messageState, oldMessage)
} }
func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat, timesource ClockValueTimesource) (*Chat, error) { func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat, timesource TimeSource) (*Chat, error) {
if message.SigPubKey == nil { if message.SigPubKey == nil {
m.logger.Error("public key can't be empty") m.logger.Error("public key can't be empty")
return nil, errors.New("received a message with empty public key") return nil, errors.New("received a message with empty public key")

View File

@ -126,6 +126,8 @@ type config struct {
type Option func(*config) error type Option func(*config) error
// WithSystemMessagesTranslations is required for Group Chats which are currently disabled.
// nolint: unused
func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option { func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option {
return func(c *config) error { return func(c *config) error {
c.systemMessagesTranslations = t c.systemMessagesTranslations = t
@ -629,7 +631,7 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string,
var response MessengerResponse var response MessengerResponse
logger := m.logger.With(zap.String("site", "CreateGroupChatWithMembers")) logger := m.logger.With(zap.String("site", "CreateGroupChatWithMembers"))
logger.Info("Creating group chat", zap.String("name", name), zap.Any("members", members)) logger.Info("Creating group chat", zap.String("name", name), zap.Any("members", members))
chat := createGroupChat(m.getTimesource()) chat := CreateGroupChat(m.getTimesource())
group, err := v1protocol.NewGroupWithCreator(name, m.identity) group, err := v1protocol.NewGroupWithCreator(name, m.identity)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1699,8 +1701,8 @@ type ReceivedMessageState struct {
ExistingMessagesMap map[string]bool ExistingMessagesMap map[string]bool
// Response to the client // Response to the client
Response *MessengerResponse Response *MessengerResponse
// Timesource is a timesource for clock values/timestamps // Timesource is a time source for clock values/timestamps.
Timesource ClockValueTimesource Timesource TimeSource
} }
func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filter][]*types.Message) (*MessengerResponse, error) { func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filter][]*types.Message) (*MessengerResponse, error) {
@ -2806,6 +2808,10 @@ func (m *Messenger) ValidateTransactions(ctx context.Context, addresses []types.
return &response, nil return &response, nil
} }
func (m *Messenger) getTimesource() ClockValueTimesource { func (m *Messenger) getTimesource() TimeSource {
return m.transport return m.transport
} }
func (m *Messenger) Timesource() TimeSource {
return m.getTimesource()
}

View File

@ -0,0 +1,12 @@
package protocol
// TimeSource provides a unified way of getting the current time.
// The intention is to always use a synchronized time source
// between all components of the protocol.
//
// This is required by Whisper and Waku protocols
// which rely on a fact that all peers
// have a synchronized time source.
type TimeSource interface {
GetCurrentTime() uint64
}