Skip to content
Snippets Groups Projects
Commit 498f7ad5 authored by Richard T. Carback III's avatar Richard T. Carback III
Browse files

Implement Triggers and Fingerprints manager objects, WIP handler2 function implementation.

parent aabbc7bb
No related branches found
No related tags found
4 merge requests!510Release,!207WIP: Client Restructure,!203Symmetric broadcast,!187Xx 3829/triggers
...@@ -8,16 +8,18 @@ ...@@ -8,16 +8,18 @@
package interfaces package interfaces
import ( import (
"time"
"gitlab.com/elixxir/client/interfaces/message" "gitlab.com/elixxir/client/interfaces/message"
"gitlab.com/elixxir/client/interfaces/params" "gitlab.com/elixxir/client/interfaces/params"
"gitlab.com/elixxir/client/network/gateway" "gitlab.com/elixxir/client/network/gateway"
"gitlab.com/elixxir/client/stoppable" "gitlab.com/elixxir/client/stoppable"
"gitlab.com/elixxir/comms/mixmessages"
"gitlab.com/elixxir/comms/network" "gitlab.com/elixxir/comms/network"
"gitlab.com/elixxir/crypto/e2e" "gitlab.com/elixxir/crypto/e2e"
"gitlab.com/elixxir/primitives/format" "gitlab.com/elixxir/primitives/format"
"gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/id"
"gitlab.com/xx_network/primitives/id/ephemeral" "gitlab.com/xx_network/primitives/id/ephemeral"
"time"
) )
type NetworkManager interface { type NetworkManager interface {
...@@ -115,12 +117,12 @@ type NetworkManager interface { ...@@ -115,12 +117,12 @@ type NetworkManager interface {
// type - a descriptive string of the trigger. Generally used in notifications // type - a descriptive string of the trigger. Generally used in notifications
// source - a byte buffer of related data. Generally used in notifications. // source - a byte buffer of related data. Generally used in notifications.
// Example: Sender ID // Example: Sender ID
AddTrigger(trigger Trigger, response MessageProcessorTrigger) error AddTrigger(trigger Trigger, response MessageProcessor) error
// RemoveTrigger - If only a single response is associated with the preimage, the entire // RemoveTrigger - If only a single response is associated with the preimage, the entire
// preimage is removed. If there is more than one response, only the given response is removed // preimage is removed. If there is more than one response, only the given response is removed
// if nil is passed in for response, all triggers for the preimage will be removed // if nil is passed in for response, all triggers for the preimage will be removed
RemoveTrigger(preimage []byte, response MessageProcessorTrigger) error RemoveTrigger(preimage Preimage, response MessageProcessor) error
// TrackTriggers - Registers a callback which will get called every time triggers change. // TrackTriggers - Registers a callback which will get called every time triggers change.
// It will receive the triggers list every time it is modified. // It will receive the triggers list every time it is modified.
...@@ -129,6 +131,8 @@ type NetworkManager interface { ...@@ -129,6 +131,8 @@ type NetworkManager interface {
TrackTriggers(func(triggers []Trigger)) TrackTriggers(func(triggers []Trigger))
} }
type Preimage [32]byte
type Identity struct { type Identity struct {
// Identity // Identity
EphId ephemeral.Id EphId ephemeral.Id
...@@ -147,24 +151,30 @@ type IdentityParams struct { ...@@ -147,24 +151,30 @@ type IdentityParams struct {
EndValid time.Time // Timestamp when the ephID stops being valid EndValid time.Time // Timestamp when the ephID stops being valid
// Makes the identity not store on disk // Makes the identity not store on disk
// When an ephemeral identity is deleted, all fingerprints & triggers
// associated with it also delete.
// TODO: This should not be confused with EphID for checking
// when messages are for the the user. That's a different type
// of Ephemeral in this context.
Ephemeral bool Ephemeral bool
} }
type Trigger struct { type Trigger struct {
Preimage []byte Preimage
Type string Type string
Source []byte Source []byte
}
type MessageProcessorFP interface {
Process(message format.Message, fingerprint format.Fingerprint)
MarkFingerprintUsed(fingerprint format.Fingerprint)
} }
type MessageProcessorTrigger interface { type MessageProcessor interface {
Process(message format.Message, preimage []byte, Type string, source []byte) // Process decrypts and hands off the message to its internal down
Equals(trigger MessageProcessorTrigger) bool // stream message processing system.
String() string // CRITICAL: Fingerprints should never be used twice. Process must
// denote, in long term storage, usage of a fingerprint and that
// fingerprint must not be added again during application load.
// It is a security vulnerability to reuse a fingerprint. It leaks
// privacy and can lead to compromise of message contents and integrity.
Process(message format.Message, receptionID Identity,
round *mixmessages.RoundInfo)
} }
//type Ratchet interface { //type Ratchet interface {
......
///////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
///////////////////////////////////////////////////////////////////////////////
package network
import (
"github.com/pkg/errors"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/primitives/format"
"sync"
)
// Processor is an object which ties an interfaces.MessageProcessorFP
// to a lock. This prevents the processor from being used in multiple
// different threads.
type Processor struct {
interfaces.MessageProcessorFP
sync.Mutex
}
// NewFingerprints is a constructor function for the Processor object.
func newProcessor(mp interfaces.MessageProcessorFP) *Processor {
return &Processor{
MessageProcessorFP: mp,
Mutex: sync.Mutex{},
}
}
// Fingerprints is a thread-safe map, mapping format.Fingerprint's to
// a Processor object.
type Fingerprints struct {
fingerprints map[format.Fingerprint]*Processor
sync.RWMutex
}
// NewFingerprints is a constructor function for the Fingerprints tracker.
func NewFingerprints() *Fingerprints {
return &Fingerprints{
fingerprints: make(map[format.Fingerprint]*Processor),
RWMutex: sync.RWMutex{},
}
}
// Get is a thread-safe getter for the Fingerprints map. Get returns the mapped
// processor and true (representing that it exists in the map) if the provided
// fingerprint has an entry. Otherwise, Get returns nil and false.
func (f *Fingerprints) Get(fingerprint format.Fingerprint) (*Processor, bool) {
f.RLock()
defer f.RUnlock()
fp, exists := f.fingerprints[fingerprint]
if !exists {
return nil, false
}
return fp, true
}
// AddFingerprint is a thread-safe setter for the Fingerprints map. AddFingerprint
// maps the given fingerprint key to the processor value. If there is already
// an entry for this fingerprint, the method returns with no write operation.
func (f *Fingerprints) AddFingerprint(fingerprint format.Fingerprint,
processor interfaces.MessageProcessorFP) {
f.Lock()
defer f.Unlock()
f.addFingerprint(fingerprint, processor)
}
// AddFingerprints is a thread-safe setter for multiple entries into
// the Fingerprints map. If there is not a 1:1 relationship between
// fingerprints and processors slices (i.e. the lengths of these slices
// are equivalent), an error will be returned.
// Otherwise, each fingerprint is written to the associated processor.
// If there is already an entry for the given fingerprint/processor pair,
// no write operation will occur for this pair.
func (f *Fingerprints) AddFingerprints(fps []format.Fingerprint,
processors []interfaces.MessageProcessorFP) error {
f.Lock()
defer f.Unlock()
if len(fps) != len(processors) {
return errors.Errorf("Canot perform a batch add when there are "+
"not an equal amount of fingerprints and processors. "+
"Given %d fingerprints and %d processors.", len(fps), len(processors))
}
for i, fp := range fps {
f.addFingerprint(fp, processors[i])
}
return nil
}
// addFingerprint is a non-thread-safe helper function which writes a Processor
// to the given fingerprint key. If an entry already exists for this fingerprint key,
// no write operation occurs.
func (f *Fingerprints) addFingerprint(fingerprint format.Fingerprint,
processor interfaces.MessageProcessorFP) {
if _, exists := f.fingerprints[fingerprint]; exists {
return
}
newMsgProc := newProcessor(processor)
f.fingerprints[fingerprint] = newMsgProc
}
// RemoveFingerprint is a thread-safe deletion operation on the Fingerprints map.
// It will remove the entry for the given fingerprint from the map.
func (f *Fingerprints) RemoveFingerprint(fingerprint format.Fingerprint) {
f.Lock()
defer f.Unlock()
delete(f.fingerprints, fingerprint)
}
// RemoveFingerprints is a thread-safe batch deletion operation on the Fingerprints map.
// It will remove the entries for the given fingerprints from the map.
func (f *Fingerprints) RemoveFingerprints(fingerprint []format.Fingerprint) {
f.Lock()
defer f.Unlock()
for _, fp := range fingerprint {
delete(f.fingerprints, fp)
}
}
///////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC //
// //
// Use of this source code is governed by a license that can be found in the //
// LICENSE file //
///////////////////////////////////////////////////////////////////////////////
package message
import (
"sync"
"github.com/pkg/errors"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/primitives/format"
"gitlab.com/xx_network/primitives/id"
)
// FingerprintsManager is a thread-safe map, mapping format.Fingerprint's to
// a Processor object.
type FingerprintsManager struct {
fpMap map[id.ID]map[format.Fingerprint]interfaces.MessageProcessor
sync.Mutex
}
// NewFingerprints is a constructor function for the Fingerprints tracker.
func NewFingerprints() *FingerprintsManager {
return &FingerprintsManager{
fpMap: make(map[id.ID]map[format.Fingerprint]interfaces.MessageProcessor),
}
}
// Pop returns the associated processor to the fingerprint and removes
// it from our list.
// CRITICAL: it is never ok to process a fingerprint twice. This is a security
// vulnerability.
func (f *FingerprintsManager) pop(clientID *id.ID,
fingerprint format.Fingerprint) (
interfaces.MessageProcessor, bool) {
f.Lock()
defer f.Unlock()
cid := *clientID
if idFpmap, exists := f.fpMap[cid]; exists {
if proc, exists := idFpmap[fingerprint]; exists {
delete(f.fpMap[cid], fingerprint)
if len(f.fpMap[cid]) == 0 {
delete(f.fpMap, cid)
}
return proc, true
}
}
return nil, false
}
// AddFingerprint is a thread-safe setter for the Fingerprints
// map. AddFingerprint maps the given fingerprint key to the processor
// value. If there is already an entry for this fingerprint, the
// method returns with no write operation.
func (f *FingerprintsManager) Add(clientID *id.ID,
fingerprint format.Fingerprint,
mp interfaces.MessageProcessor) error {
f.Lock()
defer f.Unlock()
cid := *clientID
if _, exists := f.fpMap[cid]; !exists {
f.fpMap[cid] = make(
map[format.Fingerprint]interfaces.MessageProcessor)
}
if _, exists := f.fpMap[cid][fingerprint]; exists {
return errors.Errorf("fingerprint %s already exists",
fingerprint)
}
f.fpMap[cid][fingerprint] = mp
return nil
}
// Delete is a thread-safe deletion operation on the Fingerprints map.
// It will remove the entry for the given fingerprint from the map.
func (f *FingerprintsManager) Delete(clientID *id.ID,
fingerprint format.Fingerprint) {
f.Lock()
defer f.Unlock()
cid := *clientID
if _, exists := f.fpMap[cid]; exists {
if _, exists = f.fpMap[cid][fingerprint]; exists {
delete(f.fpMap[cid], fingerprint)
}
if len(f.fpMap[cid]) == 0 {
delete(f.fpMap, cid)
}
}
}
// DeleteClient is a thread-safe deletion operation on the Fingerprints map.
// It will remove all entres for the given clientID from the map.
func (f *FingerprintsManager) DeleteClient(clientID *id.ID,
fingerprint format.Fingerprint) {
f.Lock()
defer f.Unlock()
cid := *clientID
delete(f.fpMap, cid)
}
File moved
...@@ -9,7 +9,10 @@ package message ...@@ -9,7 +9,10 @@ package message
import ( import (
"fmt" "fmt"
"time"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/interfaces"
"gitlab.com/elixxir/client/interfaces/message" "gitlab.com/elixxir/client/interfaces/message"
"gitlab.com/elixxir/client/interfaces/preimage" "gitlab.com/elixxir/client/interfaces/preimage"
"gitlab.com/elixxir/client/stoppable" "gitlab.com/elixxir/client/stoppable"
...@@ -19,7 +22,6 @@ import ( ...@@ -19,7 +22,6 @@ import (
"gitlab.com/elixxir/primitives/format" "gitlab.com/elixxir/primitives/format"
"gitlab.com/elixxir/primitives/states" "gitlab.com/elixxir/primitives/states"
"gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/id"
"time"
) )
func (m *Manager) handleMessages(stop *stoppable.Single) { func (m *Manager) handleMessages(stop *stoppable.Single) {
...@@ -162,95 +164,35 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge ...@@ -162,95 +164,35 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge
} }
} }
func (m *Manager) handleMessage2(ecrMsg format.Message, bundle Bundle) {
fingerprint := ecrMsg.GetKeyFP()
msgDigest := ecrMsg.Digest()
identity := bundle.Identity
round := bundle.RoundInfo
newID := // todo use new id systme from ticket
{
ID id.ID
ephID ephemeral.Id
}
// If we have a fingerprint, process it.
messageProc, exists := m.fingerprints.Pop(fingerprint) {
// note scope here is all broken, fix...
m.fingers.Lock()
defer m.fingersUnlock()
mp, ok := m.fingers[fingerprint]
if ok {
mp.MarkFingerprintUsed(fingerprint)
delete(m.fingers, fingerprint)
return mp, true
}
return nil, false
}
if exists {
// in progress is a future project.
// m.inprogress.Add({fingerprint, ecrMsg, newID, round})
go messageProc.Process(ecrMsg, newID, round)
return
}
triggerProc, trigger, exists := m.triggers.Lookup(
ecrMsg.GetIdentityFP(), ecrMsgContents)
if exists {
go triggerProc.Process(ecrMsg, newID, round, trigger)
return
} else {
// TODO: delete this else block because it should not be needed.
jww.INFO.Printf("checking backup %v", preimage.MakeDefault(identity.Source))
// //if it doesnt exist, check against the default fingerprint for the identity
// forMe = fingerprint2.CheckIdentityFP(ecrMsg.GetIdentityFP(),
// ecrMsgContents, preimage.MakeDefault(identity.Source))
}
if jww.GetLogThreshold() == jww.LevelTrace {
expectedFP := fingerprint2.IdentityFP(ecrMsgContents,
preimage.MakeDefault(identity.Source))
jww.TRACE.Printf("Message for %d (%s) failed identity "+
"check: %v (expected-default) vs %v (received)",
identity.EphId,
identity.Source, expectedFP, ecrMsg.GetIdentityFP())
}
im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+
"msgDigest: %s, not determined to be for client", ecrMsg.GetKeyFP(), bundle.Round, ecrMsg.Digest())
m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
m.Session.GetGarbledMessages().Add(ecrMsg)
}
func (m *Manager) handleMessage2(ecrMsg format.Message, bundle Bundle) { func (m *Manager) handleMessage2(ecrMsg format.Message, bundle Bundle) {
fingerprint := ecrMsg.GetKeyFP() fingerprint := ecrMsg.GetKeyFP()
msgDigest := ecrMsg.Digest() // msgDigest := ecrMsg.Digest()
identity := bundle.Identity identity := bundle.Identity
round := bundle.RoundInfo round := bundle.RoundInfo
newID := // todo use new id systme from ticket // newID := *id.ID{} // todo use new id systme from ticket
{ // {
ID id.ID // ID id.ID
ephID ephemeral.Id // ephID ephemeral.Id
} // }
var receptionID interfaces.Identity
// If we have a fingerprint, process it. // If we have a fingerprint, process it.
//Lock if proc, exists := m.pop(fingerprint); exists {
messageProc, fpLock, exists := m.fingerprints[fingerprint] proc.Process(ecrMsg, receptionID, round)
// Unlock
if exists {
fpLock.Lock()
defer fpLock.Unlock()
messageProc.MarkFingerprintUsed(fingerprint)
messageProc.Process(ecrMsg, newID, round)
delete(m.fingerprints, fingerprint)
return return
} }
triggerProc, trigger, exists := m.triggers.Lookup( triggers, exists := m.get(ecrMsg.GetIdentityFP(), ecrMsg.GetContents())
ecrMsg.GetIdentityFP(), ecrMsgContents)
if exists { if exists {
go triggerProc.Process(ecrMsg, newID, round, trigger) for _, t := range triggers {
go t.Process(ecrMsg, receptionID, round)
}
if len(triggers) == 0 {
jww.ERROR.Printf("empty trigger list for %s",
ecrMsg.GetIdentityFP()) // get preimage
)
}
return return
} else { } else {
// TODO: delete this else block because it should not be needed. // TODO: delete this else block because it should not be needed.
...@@ -261,7 +203,7 @@ func (m *Manager) handleMessage2(ecrMsg format.Message, bundle Bundle) { ...@@ -261,7 +203,7 @@ func (m *Manager) handleMessage2(ecrMsg format.Message, bundle Bundle) {
} }
if jww.GetLogThreshold() == jww.LevelTrace { if jww.GetLogThreshold() == jww.LevelTrace {
expectedFP := fingerprint2.IdentityFP(ecrMsgContents, expectedFP := fingerprint2.IdentityFP(ecrMsg.GetContents(),
preimage.MakeDefault(identity.Source)) preimage.MakeDefault(identity.Source))
jww.TRACE.Printf("Message for %d (%s) failed identity "+ jww.TRACE.Printf("Message for %d (%s) failed identity "+
"check: %v (expected-default) vs %v (received)", "check: %v (expected-default) vs %v (received)",
...@@ -269,7 +211,8 @@ func (m *Manager) handleMessage2(ecrMsg format.Message, bundle Bundle) { ...@@ -269,7 +211,8 @@ func (m *Manager) handleMessage2(ecrMsg format.Message, bundle Bundle) {
identity.Source, expectedFP, ecrMsg.GetIdentityFP()) identity.Source, expectedFP, ecrMsg.GetIdentityFP())
} }
im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+ im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+
"msgDigest: %s, not determined to be for client", ecrMsg.GetKeyFP(), bundle.Round, ecrMsg.Digest()) "msgDigest: %s, not determined to be for client",
ecrMsg.GetKeyFP(), bundle.Round, ecrMsg.Digest())
m.Internal.Events.Report(1, "MessageReception", "Garbled", im) m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
m.Session.GetGarbledMessages().Add(ecrMsg) m.Session.GetGarbledMessages().Add(ecrMsg)
} }
...@@ -10,6 +10,7 @@ package message ...@@ -10,6 +10,7 @@ package message
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/interfaces/params" "gitlab.com/elixxir/client/interfaces/params"
"gitlab.com/elixxir/client/network/gateway" "gitlab.com/elixxir/client/network/gateway"
...@@ -31,6 +32,9 @@ type Manager struct { ...@@ -31,6 +32,9 @@ type Manager struct {
nodeRegistration chan network.NodeGateway nodeRegistration chan network.NodeGateway
networkIsHealthy chan bool networkIsHealthy chan bool
triggerGarbled chan struct{} triggerGarbled chan struct{}
FingerprintsManager
TriggersManager
} }
func NewManager(internal internal.Internal, param params.Network, func NewManager(internal internal.Internal, param params.Network,
...@@ -54,6 +58,9 @@ func NewManager(internal internal.Internal, param params.Network, ...@@ -54,6 +58,9 @@ func NewManager(internal internal.Internal, param params.Network,
} }
m.blacklistedNodes[string(decodedId)] = nil m.blacklistedNodes[string(decodedId)] = nil
} }
m.FingerprintsManager = *NewFingerprints()
m.TriggersManager = *NewTriggers()
return &m return &m
} }
......
...@@ -5,14 +5,15 @@ ...@@ -5,14 +5,15 @@
// LICENSE file // // LICENSE file //
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
package network package message
import ( import (
"encoding/base64" "sync"
"github.com/pkg/errors" "github.com/pkg/errors"
"gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/interfaces"
fingerprint2 "gitlab.com/elixxir/crypto/fingerprint" "gitlab.com/elixxir/crypto/fingerprint"
"sync" "gitlab.com/xx_network/primitives/id"
) )
/* Trigger - predefined hash based tags appended to all cmix messages /* Trigger - predefined hash based tags appended to all cmix messages
...@@ -35,17 +36,17 @@ Triggers are ephemeral to the session. When starting a new client, all triggers ...@@ -35,17 +36,17 @@ Triggers are ephemeral to the session. When starting a new client, all triggers
re-added before StartNetworkFollower is called. re-added before StartNetworkFollower is called.
*/ */
type Triggers struct { type TriggersManager struct {
triggers map[string][]*Trigger tmap map[id.ID]map[interfaces.Preimage][]trigger
sync.RWMutex sync.Mutex
} }
type Trigger struct { type trigger struct {
interfaces.Trigger interfaces.Trigger
interfaces.MessageProcessorTrigger interfaces.MessageProcessor
} }
func NewTriggers() *Triggers { func NewTriggers() *TriggersManager {
// todo: implement me // todo: implement me
return nil return nil
} }
...@@ -64,23 +65,21 @@ func NewTriggers() *Triggers { ...@@ -64,23 +65,21 @@ func NewTriggers() *Triggers {
// - privatizing the state-changing methods // - privatizing the state-changing methods
// - leaking lookup on this layer and migrating the state modifiation methods // - leaking lookup on this layer and migrating the state modifiation methods
// a layer down in a sepearate package // a layer down in a sepearate package
func (t *Triggers) Lookup(receivedIdentityFp, func (t *TriggersManager) get(clientID *id.ID, receivedIdentityFp,
ecrMsgContents []byte) (triggers []*Trigger, forMe bool) { ecrMsgContents []byte) ([]trigger,
t.RLock() bool) {
defer t.RUnlock() t.Lock()
defer t.Unlock()
for preimage, triggerList := range t.triggers { cid := *clientID
preimageBytes, err := unmarshalPreimage(preimage)
if err != nil { triggers, exists := t.tmap[cid]
// fixme: panic here?, An error here would mean there's a bad if !exists {
// key-value pair in the map (specifically the preimage-key is bad, return nil, false
// as it should be base64 encoded). }
}
// fixme, there probably needs to be a small refactor. for pi, triggerList := range triggers {
// Terminology and variable names are being used misused. For example: if fingerprint.CheckIdentityFP(receivedIdentityFp,
// phrases like tag, preimage and identityFP are being used ecrMsgContents, pi[:]) {
// interchangeably in the code and it's getting unwieldy.
if fingerprint2.CheckIdentityFP(receivedIdentityFp, ecrMsgContents, preimageBytes) {
return triggerList, true return triggerList, true
} }
} }
...@@ -95,77 +94,78 @@ func (t *Triggers) Lookup(receivedIdentityFp, ...@@ -95,77 +94,78 @@ func (t *Triggers) Lookup(receivedIdentityFp,
// type - a descriptive string of the trigger. Generally used in notifications // type - a descriptive string of the trigger. Generally used in notifications
// source - a byte buffer of related data. Generally used in notifications. // source - a byte buffer of related data. Generally used in notifications.
// Example: Sender ID // Example: Sender ID
func (t *Triggers) Add(trigger interfaces.Trigger, func (t *TriggersManager) Add(clientID *id.ID, newTrigger interfaces.Trigger,
response interfaces.MessageProcessorTrigger) error { response interfaces.MessageProcessor) {
t.Lock() t.Lock()
defer t.Unlock() defer t.Unlock()
marshalledPreimage := marshalPreimage(trigger.Preimage) newEntry := trigger{
Trigger: newTrigger,
MessageProcessor: response,
}
newTrigger := &Trigger{ cid := *clientID
Trigger: trigger, if _, exists := t.tmap[cid]; !exists {
MessageProcessorTrigger: response, t.tmap[cid] = make(map[interfaces.Preimage][]trigger)
} }
if existingTriggers, exists := t.triggers[marshalledPreimage]; exists { pi := newTrigger.Preimage
// fixme Should there be a check if this response exists already? if existingTriggers, exists := t.tmap[cid][pi]; exists {
t.triggers[marshalledPreimage] = append(existingTriggers, newTrigger) t.tmap[cid][pi] = append(existingTriggers, newEntry)
return nil
} }
t.triggers[marshalledPreimage] = []*Trigger{newTrigger} t.tmap[cid][pi] = []trigger{newEntry}
return nil
} }
// RemoveTrigger - If only a single response is associated with the preimage, // Delete - If only a single response is associated with the preimage,
// the entire preimage is removed. If there is more than one response, only // the entire preimage is removed. If there is more than one response, only
// the given response is removed. If nil is passed in for response, // the given response is removed. If nil is passed in for response,
// all triggers for the preimage will be removed. // all triggers for the preimage will be removed.
func (t *Triggers) RemoveTrigger(preimage []byte, func (t *TriggersManager) Delete(clientID *id.ID, preimage interfaces.Preimage,
response interfaces.MessageProcessorTrigger) error { response interfaces.MessageProcessor) error {
t.Lock() t.Lock()
defer t.Unlock() defer t.Unlock()
marshalledPreimage := marshalPreimage(preimage) if response == nil {
return errors.Errorf("response cannot be nil when deleting")
}
triggers, exists := t.triggers[marshalledPreimage] cid := *clientID
idTmap, exists := t.tmap[cid]
if !exists { if !exists {
return errors.Errorf("No trigger with preimage %q found", return nil
marshalledPreimage)
} }
if response == nil { triggers, exists := idTmap[preimage]
delete(t.triggers, marshalledPreimage) if !exists {
return nil return nil
} }
for _, trigger := range triggers { if len(triggers) == 1 && triggers[0].MessageProcessor == response {
if trigger.Equals(response) { if len(idTmap) == 1 {
delete(t.triggers, marshalPreimage(trigger.Preimage)) delete(t.tmap, cid)
return nil } else {
delete(t.tmap[cid], preimage)
} }
} }
return errors.Errorf("No response (%q) exists with preimage %q", for idx, cur := range triggers {
response.String(), marshalledPreimage) if cur.MessageProcessor == response {
} t.tmap[cid][preimage] = append(triggers[:idx],
triggers[idx+1:]...)
// fixme: maybe make preimage a type or struct and place this in primitives? return nil
}
}
// marshalPreimage is a helper which encodes the preimage byte data to return nil
// a base64 encoded string.
func marshalPreimage(pi []byte) string {
return base64.StdEncoding.EncodeToString(pi)
} }
// unmarshalPreimage is a helper which decodes the preimage base64 string to // DeleteClient - delete the mapping associated with an ID
// bytes. func (t *TriggersManager) DeleteClient(clientID *id.ID) {
func unmarshalPreimage(data string) ([]byte, error) { t.Lock()
decoded, err := base64.StdEncoding.DecodeString(data) defer t.Unlock()
if err != nil {
return nil, err
}
return decoded, nil delete(t.tmap, *clientID)
} }
File moved
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment