From 2d6dc4cce2bcc250e5c41eaf246868967541e5ab Mon Sep 17 00:00:00 2001 From: Benjamin Wenger <ben@elixxir.ioo> Date: Thu, 24 Mar 2022 14:03:29 -0700 Subject: [PATCH] im progress --- api/client.go | 6 +- api/user.go | 12 +- bindings/user.go | 2 +- interfaces/networkManager.go | 85 ++++---- interfaces/params/historical.go | 37 ++++ interfaces/params/network.go | 1 + interfaces/params/rounds.go | 19 +- interfaces/user/user.go | 12 +- network/address/addressSpace.go | 40 ++-- network/address/addressSpace_test.go | 34 ++-- network/check.go | 45 +++++ network/follow.go | 96 ++++----- network/gateway/hostPool.go | 6 +- network/gateway/sender.go | 19 +- network/gateway/sender_test.go | 28 +-- network/{rounds => historical}/historical.go | 113 ++++++++--- network/identity/receptionID/IdentityUse.go | 8 +- network/identity/receptionID/identity.go | 6 +- network/identity/receptionID/registration.go | 24 +-- network/identity/receptionID/store.go | 60 ++++-- .../receptionID/store}/checkedRounds.go | 2 +- .../receptionID/store}/checkedRounds_test.go | 2 +- .../receptionID/store}/earliestRound.go | 2 +- .../receptionID/store}/unknownRounds.go | 2 +- .../receptionID/store}/unknownRounds_test.go | 2 +- network/identity/tracker.go | 136 +++++++------ network/internal/internal.go | 42 ---- network/manager.go | 116 ++++------- network/message/bundle.go | 2 +- network/message/fingerprints.go | 6 +- network/message/fingerprints_test.go | 10 +- network/message/handler.go | 6 +- network/message/inProgress.go | 10 +- network/message/inProgress_test.go | 6 +- network/message/meteredCmixMessageBuffer.go | 16 +- .../message/meteredCmixMessageBuffer_test.go | 4 +- network/message/pickup.go | 12 +- network/nodes/register.go | 4 +- network/nodes/registrar.go | 28 +-- network/nodes/registrar_test.go | 26 +-- network/{rounds => }/remoteFilters.go | 2 +- network/{rounds => }/remoteFilters_test.go | 2 +- network/rounds/check.go | 98 --------- network/rounds/get.go | 71 +++++++ network/rounds/manager.go | 56 +++-- network/rounds/retrieve.go | 51 ++--- network/rounds/retrieve_test.go | 11 +- network/rounds/roundGetter.go | 10 + .../rounds/store}/roundIdentity.go | 2 +- .../rounds/store/store.go | 191 +++--------------- network/rounds/store/uncheckedRounds.go | 176 ++++++++++++++++ .../rounds/store}/uncheckedRounds_test.go | 2 +- network/rounds/unchecked.go | 67 ++---- network/rounds/utils_test.go | 7 +- network/sendCmix.go | 16 +- network/sendCmixUtils.go | 6 +- network/sendCmix_test.go | 2 +- network/sendManyCmix.go | 30 ++- network/sendManyCmix_test.go | 2 +- single/manager_test.go | 2 +- storage/e2e/session.go | 3 +- storage/messages.go | 14 -- storage/ndf.go | 4 +- storage/regCode.go | 4 +- storage/regStatus.go | 8 +- storage/session.go | 90 +++++---- storage/user.go | 35 ---- storage/user/Info.go | 28 +++ storage/user/user.go | 10 +- storage/utility/multiStateVector_test.go | 4 +- 70 files changed, 1087 insertions(+), 1004 deletions(-) create mode 100644 interfaces/params/historical.go create mode 100644 network/check.go rename network/{rounds => historical}/historical.go (62%) rename {storage/rounds => network/identity/receptionID/store}/checkedRounds.go (99%) rename {storage/rounds => network/identity/receptionID/store}/checkedRounds_test.go (99%) rename {storage/rounds => network/identity/receptionID/store}/earliestRound.go (99%) rename {storage/rounds => network/identity/receptionID/store}/unknownRounds.go (99%) rename {storage/rounds => network/identity/receptionID/store}/unknownRounds_test.go (99%) delete mode 100644 network/internal/internal.go rename network/{rounds => }/remoteFilters.go (98%) rename network/{rounds => }/remoteFilters_test.go (99%) delete mode 100644 network/rounds/check.go create mode 100644 network/rounds/get.go create mode 100644 network/rounds/roundGetter.go rename {storage/rounds => network/rounds/store}/roundIdentity.go (98%) rename storage/rounds/uncheckedRounds.go => network/rounds/store/store.go (57%) create mode 100644 network/rounds/store/uncheckedRounds.go rename {storage/rounds => network/rounds/store}/uncheckedRounds_test.go (99%) delete mode 100644 storage/messages.go delete mode 100644 storage/user.go create mode 100644 storage/user/Info.go diff --git a/api/client.go b/api/client.go index fc89f70d3..79422ce39 100644 --- a/api/client.go +++ b/api/client.go @@ -94,7 +94,7 @@ func NewClient(ndfJSON, storageDir string, password []byte, cmixGrp, e2eGrp := decodeGroups(def) start := time.Now() protoUser := createNewUser(rngStreamGen, cmixGrp, e2eGrp) - jww.DEBUG.Printf("User generation took: %s", time.Now().Sub(start)) + jww.DEBUG.Printf("PortableUserInfo generation took: %s", time.Now().Sub(start)) _, err = checkVersionAndSetupStorage(def, storageDir, password, protoUser, cmixGrp, e2eGrp, rngStreamGen, false, registrationCode) @@ -669,7 +669,7 @@ func (c *Client) AddService(sp Service) error { // GetUser returns the current user Identity for this client. This // can be serialized into a byte stream for out-of-band sharing. -func (c *Client) GetUser() user.User { +func (c *Client) GetUser() user.Info { jww.INFO.Printf("GetUser()") return c.storage.GetUser() } @@ -940,7 +940,7 @@ func decodeGroups(ndf *ndf.NetworkDefinition) (cmixGrp, e2eGrp *cyclic.Group) { // it checks client version and creates a new storage for user data func checkVersionAndSetupStorage(def *ndf.NetworkDefinition, storageDir string, password []byte, - protoUser user.User, + protoUser user.Info, cmixGrp, e2eGrp *cyclic.Group, rngStreamGen *fastRNG.StreamGenerator, isPrecanned bool, registrationCode string) (*storage.Session, error) { // get current client version diff --git a/api/user.go b/api/user.go index 4377f1b03..5b64f0b0a 100644 --- a/api/user.go +++ b/api/user.go @@ -30,7 +30,7 @@ const ( ) // createNewUser generates an identity for cMix -func createNewUser(rng *fastRNG.StreamGenerator, cmix, e2e *cyclic.Group) user.User { +func createNewUser(rng *fastRNG.StreamGenerator, cmix, e2e *cyclic.Group) user.Info { // CMIX Keygen var transmissionRsaKey, receptionRsaKey *rsa.PrivateKey @@ -75,7 +75,7 @@ func createNewUser(rng *fastRNG.StreamGenerator, cmix, e2e *cyclic.Group) user.U jww.FATAL.Panicf(err.Error()) } - return user.User{ + return user.Info{ TransmissionID: transmissionID.DeepCopy(), TransmissionSalt: transmissionSalt, TransmissionRSA: transmissionRsaKey, @@ -140,7 +140,7 @@ func createDhKeys(rng *fastRNG.StreamGenerator, // TODO: Add precanned user code structures here. // creates a precanned user -func createPrecannedUser(precannedID uint, rng csprng.Source, cmix, e2e *cyclic.Group) user.User { +func createPrecannedUser(precannedID uint, rng csprng.Source, cmix, e2e *cyclic.Group) user.Info { // DH Keygen // FIXME: Why 256 bits? -- this is spec but not explained, it has // to do with optimizing operations on one side and still preserves @@ -164,7 +164,7 @@ func createPrecannedUser(precannedID uint, rng csprng.Source, cmix, e2e *cyclic. jww.FATAL.Panicf(err.Error()) } - return user.User{ + return user.Info{ TransmissionID: &userID, TransmissionSalt: salt, ReceptionID: &userID, @@ -178,7 +178,7 @@ func createPrecannedUser(precannedID uint, rng csprng.Source, cmix, e2e *cyclic. // createNewVanityUser generates an identity for cMix // The identity's ReceptionID is not random but starts with the supplied prefix -func createNewVanityUser(rng csprng.Source, cmix, e2e *cyclic.Group, prefix string) user.User { +func createNewVanityUser(rng csprng.Source, cmix, e2e *cyclic.Group, prefix string) user.Info { // DH Keygen // FIXME: Why 256 bits? -- this is spec but not explained, it has // to do with optimizing operations on one side and still preserves @@ -277,7 +277,7 @@ func createNewVanityUser(rng csprng.Source, cmix, e2e *cyclic.Group, prefix stri <-found close(done) wg.Wait() - return user.User{ + return user.Info{ TransmissionID: transmissionID.DeepCopy(), TransmissionSalt: transmissionSalt, TransmissionRSA: transmissionRsaKey, diff --git a/bindings/user.go b/bindings/user.go index f05245daa..c049eaeb1 100644 --- a/bindings/user.go +++ b/bindings/user.go @@ -13,7 +13,7 @@ import ( ) type User struct { - u *user.User + u *user.Info } func (u *User) GetTransmissionID() []byte { diff --git a/interfaces/networkManager.go b/interfaces/networkManager.go index 8176ec464..1fa1d5d3d 100644 --- a/interfaces/networkManager.go +++ b/interfaces/networkManager.go @@ -5,14 +5,16 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// +/*===Sending==============================================================*/ + package interfaces import ( + "gitlab.com/elixxir/client/network/gateway" "time" "gitlab.com/elixxir/client/interfaces/message" "gitlab.com/elixxir/client/interfaces/params" - "gitlab.com/elixxir/client/network/gateway" "gitlab.com/elixxir/client/stoppable" "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/network" @@ -23,44 +25,35 @@ import ( ) type NetworkManager interface { - /* Process contol */ + // Follow starts the tracking of the network in a new thread. + // Errors that occur are reported on the ClientErrorReport function if + // passed. The returned stopable can be used to stop the follower. + // Only one follower may run at a time. Follow(report ClientErrorReport) (stoppable.Stoppable, error) - // GetSender - Object that all gateway I/O goes though. It handles - // selecting gateways and proxying messages through them - GetSender() *gateway.Sender - /* Parameters and events */ - GetInstance() *network.Instance - GetHealthTracker() HealthTracker - GetEventManager() EventManager - InProgressRegistrations() int + // SendCMIX sends a "raw" CMIX message payload to the provided recipient. + // Returns the round ID of the round the payload was sent or an error + // if it fails. + SendCMIX(message format.Message, recipient *id.ID, p params.CMIX) (id.Round, ephemeral.Id, error) + SendManyCMIX(messages []message.TargetedCmixMessage, p params.CMIX) (id.Round, []ephemeral.Id, error) - CheckGarbledMessages() + /*===Accessors============================================================*/ + /* Accessors */ + // GetInstance returns the network instance object, which tracks the + // state of the network + GetInstance() *network.Instance - // GetAddressSize returns the current address size of IDs. Blocks until an - // address size is known. - GetAddressSize() uint8 + // GetInstance returns the health tracker, which using a polling or + // event api lets you determine if network following is functioning + GetHealthTracker() HealthTracker // GetVerboseRounds returns stringification of verbose round info GetVerboseRounds() string - // RegisterAddressSizeNotification returns a channel that will trigger for - // every address space size update. The provided tag is the unique ID for - // the channel. Returns an error if the tag is already used. - RegisterAddressSizeNotification(tag string) (chan uint8, error) - - // UnregisterAddressSizeNotification stops broadcasting address space size - // updates on the channel with the specified tag. - UnregisterAddressSizeNotification(tag string) - // SetPoolFilter sets the filter used to filter gateway IDs. // allows you to disable proxying through certain gateways SetPoolFilter(f gateway.Filter) - /* Message Sending */ - SendCMIX(message format.Message, recipient *id.ID, p params.CMIX) (id.Round, ephemeral.Id, error) - SendManyCMIX(messages []message.TargetedCmixMessage, p params.CMIX) (id.Round, []ephemeral.Id, error) - /* Message Receiving */ /* Identities are all network identites which the client is currently trying to pick up message on. Each identity has a default trigger @@ -128,37 +121,29 @@ type NetworkManager interface { // Will only get callbacks while the Network Follower is running. // Multiple trackTriggers can be registered TrackTriggers(func(triggers []Trigger)) + + /*Address Space*/ + // GetAddressSpace GetAddressSize returns the current address size of IDs. Blocks until an + // address size is known. + GetAddressSpace() uint8 + + // RegisterAddressSpaceNotification returns a channel that will trigger for + // every address space size update. The provided tag is the unique ID for + // the channel. Returns an error if the tag is already used. + RegisterAddressSpaceNotification(tag string) (chan uint8, error) + // UnregisterAddressSpaceNotification stops broadcasting address space size + // updates on the channel with the specified tag. + UnregisterAddressSpaceNotification(tag string) } type Preimage [32]byte -type Identity struct { +type EphemeralIdentity struct { // Identity EphId ephemeral.Id Source *id.ID } -type IdentityParams struct { - AddressSize uint8 - - // Usage variables - End time.Time // Timestamp when active polling will stop - ExtraChecks uint // Number of extra checks executed as active after the - // ID exits active - - // Polling parameters - StartValid time.Time // Timestamp when the ephID begins being valid - EndValid time.Time // Timestamp when the ephID stops being valid - - // Makes the identity not store on disk - // When an address 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 -} - type Trigger struct { Preimage Type string @@ -173,7 +158,7 @@ type MessageProcessor interface { // 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, + Process(message format.Message, receptionID EphemeralIdentity, round *mixmessages.RoundInfo) } diff --git a/interfaces/params/historical.go b/interfaces/params/historical.go new file mode 100644 index 000000000..c7bf66779 --- /dev/null +++ b/interfaces/params/historical.go @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package params + +import ( + "time" +) + +type Historical struct { + // Number of historical rounds required to automatically send a historical + // rounds query + MaxHistoricalRounds uint + // Maximum period of time a pending historical round query will wait before + // it is transmitted + HistoricalRoundsPeriod time.Duration + + // Length of historical rounds channel buffer + HistoricalRoundsBufferLen uint + + // Maximum number of times a historical round lookup will be attempted + MaxHistoricalRoundsRetries uint +} + +func GetDefaultHistorical() Historical { + return Historical{ + MaxHistoricalRounds: 100, + HistoricalRoundsPeriod: 100 * time.Millisecond, + + HistoricalRoundsBufferLen: 1000, + MaxHistoricalRoundsRetries: 3, + } +} diff --git a/interfaces/params/network.go b/interfaces/params/network.go index 370cee8e1..f29586a8f 100644 --- a/interfaces/params/network.go +++ b/interfaces/params/network.go @@ -42,6 +42,7 @@ type Network struct { Rounds Messages Rekey + Historical E2EParams E2ESessionParams } diff --git a/interfaces/params/rounds.go b/interfaces/params/rounds.go index 4cfdbb233..e7f24bffe 100644 --- a/interfaces/params/rounds.go +++ b/interfaces/params/rounds.go @@ -12,23 +12,12 @@ import ( ) type Rounds struct { - // Number of historical rounds required to automatically send a historical - // rounds query - MaxHistoricalRounds uint - // Maximum period of time a pending historical round query will wait before - // it is transmitted - HistoricalRoundsPeriod time.Duration // Number of worker threads for retrieving messages from gateways NumMessageRetrievalWorkers uint - // Length of historical rounds channel buffer - HistoricalRoundsBufferLen uint // Length of round lookup channel buffer LookupRoundsBufferLen uint - // Toggles if historical rounds should always be used - ForceHistoricalRounds bool - // Maximum number of times a historical round lookup will be attempted MaxHistoricalRoundsRetries uint @@ -46,17 +35,15 @@ type Rounds struct { //disables all attempts to pick up dropped or missed messages RealtimeOnly bool + + // Toggles if historical rounds should always be used + ForceHistoricalRounds bool } func GetDefaultRounds() Rounds { return Rounds{ - MaxHistoricalRounds: 100, - HistoricalRoundsPeriod: 100 * time.Millisecond, NumMessageRetrievalWorkers: 8, - - HistoricalRoundsBufferLen: 1000, LookupRoundsBufferLen: 2000, - ForceHistoricalRounds: false, MaxHistoricalRoundsRetries: 3, UncheckRoundPeriod: 20 * time.Second, ForceMessagePickupRetry: false, diff --git a/interfaces/user/user.go b/interfaces/user/user.go index 5dc559917..0e3c6ca03 100644 --- a/interfaces/user/user.go +++ b/interfaces/user/user.go @@ -16,7 +16,7 @@ import ( "gitlab.com/xx_network/primitives/id" ) -type User struct { +type Info struct { //General Identity TransmissionID *id.ID TransmissionSalt []byte @@ -33,7 +33,7 @@ type User struct { E2eDhPublicKey *cyclic.Int } -func (u User) GetContact() contact.Contact { +func (u Info) GetContact() contact.Contact { return contact.Contact{ ID: u.ReceptionID.DeepCopy(), DhPubKey: u.E2eDhPublicKey, @@ -41,8 +41,8 @@ func (u User) GetContact() contact.Contact { } } -func NewUserFromProto(proto *Proto) User { - return User{ +func NewUserFromProto(proto *Proto) Info { + return Info{ TransmissionID: proto.TransmissionID, TransmissionSalt: proto.TransmissionSalt, TransmissionRSA: proto.TransmissionRSA, @@ -56,8 +56,8 @@ func NewUserFromProto(proto *Proto) User { } } -func NewUserFromBackup(backup *backup.Backup) User { - return User{ +func NewUserFromBackup(backup *backup.Backup) Info { + return Info{ TransmissionID: backup.TransmissionIdentity.ComputedID, TransmissionSalt: backup.TransmissionIdentity.Salt, TransmissionRSA: backup.TransmissionIdentity.RSASigningPrivateKey, diff --git a/network/address/addressSpace.go b/network/address/addressSpace.go index 986cc8f5a..cddca9208 100644 --- a/network/address/addressSpace.go +++ b/network/address/addressSpace.go @@ -16,24 +16,32 @@ const ( // Space contains the current address space size used for creating // address IDs and the infrastructure to alert other processes when an Update // occurs. -type Space struct { +type Space interface { + GetAddressSpace() uint8 + GetAddressSpaceWithoutWait() uint8 + UpdateAddressSpace(newSize uint8) + RegisterAddressSpaceNotification(tag string) (chan uint8, error) + UnregisterAddressSpaceNotification(tag string) +} + +type space struct { size uint8 notifyMap map[string]chan uint8 cond *sync.Cond } // NewAddressSpace initialises a new AddressSpace and returns it. -func NewAddressSpace() *Space { - return &Space{ +func NewAddressSpace() Space { + return &space{ size: initSize, notifyMap: make(map[string]chan uint8), cond: sync.NewCond(&sync.Mutex{}), } } -// Get returns the current address space size. It blocks until an address space +// GetAddressSpace returns the current address space size. It blocks until an address space // size is set. -func (as *Space) Get() uint8 { +func (as *space) GetAddressSpace() uint8 { as.cond.L.Lock() defer as.cond.L.Unlock() @@ -48,19 +56,19 @@ func (as *Space) Get() uint8 { return as.size } -// GetWithoutWait returns the current address space size regardless if it has +// GetAddressSpaceWithoutWait returns the current address space size regardless if it has // been set yet. -func (as *Space) GetWithoutWait() uint8 { +func (as *space) GetAddressSpaceWithoutWait() uint8 { as.cond.L.Lock() defer as.cond.L.Unlock() return as.size } -// Update updates the address space size to the new size, if it is larger. Then, +// UpdateAddressSpace updates the address space size to the new size, if it is larger. Then, // each registered channel is notified of the Update. If this was the first time // that the address space size was set, then the conditional broadcasts to stop // blocking for all threads waiting on Get. -func (as *Space) Update(newSize uint8) { +func (as *space) UpdateAddressSpace(newSize uint8) { as.cond.L.Lock() defer as.cond.L.Unlock() @@ -90,10 +98,10 @@ func (as *Space) Update(newSize uint8) { } } -// RegisterNotification returns a channel that will trigger for every address +// RegisterAddressSpaceNotification returns a channel that will trigger for every address // space size Update. The provided tag is the unique ID for the channel. // Returns an error if the tag is already used. -func (as *Space) RegisterNotification(tag string) (chan uint8, error) { +func (as *space) RegisterAddressSpaceNotification(tag string) (chan uint8, error) { as.cond.L.Lock() defer as.cond.L.Unlock() @@ -106,9 +114,9 @@ func (as *Space) RegisterNotification(tag string) (chan uint8, error) { return as.notifyMap[tag], nil } -// UnregisterNotification stops broadcasting address space size updates on the +// UnregisterAddressSpaceNotification stops broadcasting address space size updates on the // channel with the specified tag. -func (as *Space) UnregisterNotification(tag string) { +func (as *space) UnregisterAddressSpaceNotification(tag string) { as.cond.L.Lock() defer as.cond.L.Unlock() @@ -117,7 +125,7 @@ func (as *Space) UnregisterNotification(tag string) { // NewTestAddressSpace initialises a new AddressSpace for testing with the given // size. -func NewTestAddressSpace(newSize uint8, x interface{}) *Space { +func NewTestAddressSpace(newSize uint8, x interface{}) *space { switch x.(type) { case *testing.T, *testing.M, *testing.B, *testing.PB: break @@ -126,13 +134,13 @@ func NewTestAddressSpace(newSize uint8, x interface{}) *Space { "Got %T", x) } - as := &Space{ + as := &space{ size: initSize, notifyMap: make(map[string]chan uint8), cond: sync.NewCond(&sync.Mutex{}), } - as.Update(newSize) + as.UpdateAddressSpace(newSize) return as } diff --git a/network/address/addressSpace_test.go b/network/address/addressSpace_test.go index d6c0aa921..9dc710e6e 100644 --- a/network/address/addressSpace_test.go +++ b/network/address/addressSpace_test.go @@ -32,7 +32,7 @@ func Test_addressSpace_Get(t *testing.T) { // Call get and error if it does not block wait := make(chan uint8) - go func() { wait <- as.Get() }() + go func() { wait <- as.GetAddressSpace() }() select { case size := <-wait: t.Errorf("get failed to block and returned size %d.", size) @@ -46,7 +46,7 @@ func Test_addressSpace_Get(t *testing.T) { // Call get and error if it does block wait = make(chan uint8) - go func() { wait <- as.Get() }() + go func() { wait <- as.GetAddressSpace() }() select { case size := <-wait: if size != expectedSize { @@ -63,7 +63,7 @@ func Test_addressSpace_Get_WaitBroadcast(t *testing.T) { as := NewAddressSpace() wait := make(chan uint8) - go func() { wait <- as.Get() }() + go func() { wait <- as.GetAddressSpace() }() go func() { select { @@ -82,13 +82,13 @@ func Test_addressSpace_Get_WaitBroadcast(t *testing.T) { as.cond.Broadcast() } -// Unit test of AddressSpace.GetWithoutWait. +// Unit test of AddressSpace.GetAddressSpaceWithoutWait. func Test_addressSpace_GetWithoutWait(t *testing.T) { as := NewAddressSpace() - size := as.GetWithoutWait() + size := as.GetAddressSpaceWithoutWait() if size != initSize { - t.Errorf("GetWithoutWait returned the wrong size."+ + t.Errorf("GetAddressSpaceWithoutWait returned the wrong size."+ "\nexpected: %d\nreceived: %d", initSize, size) } } @@ -99,14 +99,14 @@ func Test_addressSpace_update(t *testing.T) { expectedSize := uint8(42) // Attempt to Update to larger size - as.Update(expectedSize) + as.UpdateAddressSpace(expectedSize) if as.size != expectedSize { t.Errorf("Update failed to set the new size."+ "\nexpected: %d\nreceived: %d", expectedSize, as.size) } // Attempt to Update to smaller size - as.Update(expectedSize - 1) + as.UpdateAddressSpace(expectedSize - 1) if as.size != expectedSize { t.Errorf("Update failed to set the new size."+ "\nexpected: %d\nreceived: %d", expectedSize, as.size) @@ -122,7 +122,7 @@ func Test_addressSpace_update_GetAndChannels(t *testing.T) { // Start threads that are waiting for an Update wait := []chan uint8{make(chan uint8), make(chan uint8), make(chan uint8)} for _, waitChan := range wait { - go func(waitChan chan uint8) { waitChan <- as.Get() }(waitChan) + go func(waitChan chan uint8) { waitChan <- as.GetAddressSpace() }(waitChan) } // Wait on threads @@ -150,7 +150,7 @@ func Test_addressSpace_update_GetAndChannels(t *testing.T) { var chanID string for i := 0; i < 3; i++ { chanID = strconv.Itoa(i) - notifyChannels[chanID], err = as.RegisterNotification(chanID) + notifyChannels[chanID], err = as.RegisterAddressSpaceNotification(chanID) if err != nil { t.Errorf("Failed to regisdter channel: %+v", err) } @@ -174,13 +174,13 @@ func Test_addressSpace_update_GetAndChannels(t *testing.T) { time.Sleep(5 * time.Millisecond) // Attempt to Update to larger size - as.Update(expectedSize) + as.UpdateAddressSpace(expectedSize) wg.Wait() // Unregistered one channel and make sure it will not receive delete(notifyChannels, chanID) - as.UnregisterNotification(chanID) + as.UnregisterAddressSpaceNotification(chanID) expectedSize++ @@ -218,7 +218,7 @@ func Test_addressSpace_update_GetAndChannels(t *testing.T) { time.Sleep(5 * time.Millisecond) // Attempt to Update to larger size - as.Update(expectedSize) + as.UpdateAddressSpace(expectedSize) wg.Wait() } @@ -231,7 +231,7 @@ func Test_addressSpace_RegisterNotification(t *testing.T) { // Register channel chanID := "chanID" - sizeChan, err := as.RegisterNotification(chanID) + sizeChan, err := as.RegisterAddressSpaceNotification(chanID) if err != nil { t.Errorf("RegisterNotification returned an error: %+v", err) } @@ -257,7 +257,7 @@ func Test_addressSpace_RegisterNotification(t *testing.T) { } } -// Tests that when AddressSpace.UnregisterNotification unregisters a channel, +// Tests that when AddressSpace.UnregisterAddressSpaceNotification unregisters a channel, // it no longer can be triggered from the map. func Test_addressSpace_UnregisterNotification(t *testing.T) { as := NewAddressSpace() @@ -265,11 +265,11 @@ func Test_addressSpace_UnregisterNotification(t *testing.T) { // Register channel and then unregister it chanID := "chanID" - sizeChan, err := as.RegisterNotification(chanID) + sizeChan, err := as.RegisterAddressSpaceNotification(chanID) if err != nil { t.Errorf("RegisterNotification returned an error: %+v", err) } - as.UnregisterNotification(chanID) + as.UnregisterAddressSpaceNotification(chanID) // Wait for timeout or error if the channel receives go func() { diff --git a/network/check.go b/network/check.go new file mode 100644 index 000000000..c92f537e0 --- /dev/null +++ b/network/check.go @@ -0,0 +1,45 @@ +package network + +import ( + "encoding/binary" + "gitlab.com/elixxir/client/network/identity/receptionID/store" + "gitlab.com/xx_network/primitives/id" +) + +// Checker is a single use function which is meant to be wrapped +// and adhere to the knownRounds checker interface. it receives a round ID and +// looks up the state of that round to determine if the client has a message +// waiting in it. +// It will return true if it can conclusively determine no message exists, +// returning false and set the round to processing if it needs further +// investigation. +// Once it determines messages might be waiting in a round, it determines +// if the information about that round is already present, if it is the data is +// sent to Message Retrieval Workers, otherwise it is sent to Historical Round +// Retrieval +// false: no message +// true: message +func Checker(roundID id.Round, filters []*RemoteFilter, cr *store.CheckedRounds) bool { + // Skip checking if the round is already checked + if cr.IsChecked(roundID) { + return true + } + + //find filters that could have the round and check them + serialRid := serializeRound(roundID) + for _, filter := range filters { + if filter != nil && filter.FirstRound() <= roundID && + filter.LastRound() >= roundID { + if filter.GetFilter().Test(serialRid) { + return true + } + } + } + return false +} + +func serializeRound(roundId id.Round) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, uint64(roundId)) + return b +} diff --git a/network/follow.go b/network/follow.go index fa5386c6d..a494c4885 100644 --- a/network/follow.go +++ b/network/follow.go @@ -24,12 +24,13 @@ package network import ( "bytes" + "crypto/rand" + "encoding/binary" "fmt" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces" - "gitlab.com/elixxir/client/network/rounds" + "gitlab.com/elixxir/client/network/identity/receptionID/store" "gitlab.com/elixxir/client/stoppable" - rounds2 "gitlab.com/elixxir/client/storage/rounds" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/primitives/knownRounds" "gitlab.com/elixxir/primitives/states" @@ -78,7 +79,7 @@ func (m *manager) followNetwork(report interfaces.ClientErrorReport, stop.ToStopped() return case <-ticker.C: - m.follow(report, rng, m.Comms, stop, abandon) + m.follow(report, rng, m.comms, stop, abandon) case <-TrackTicker.C: numPolls := atomic.SwapUint64(m.tracker, 0) if m.numLatencies != 0 { @@ -92,7 +93,7 @@ func (m *manager) followNetwork(report interfaces.ClientErrorReport, numPolls, debugTrackPeriod, latencyAvg) jww.INFO.Printf(infoMsg) - m.Internal.Events.Report(1, "Polling", + m.events.Report(1, "Polling", "MetricsWithLatency", infoMsg) } else { infoMsg := fmt.Sprintf("Polled the network "+ @@ -100,7 +101,7 @@ func (m *manager) followNetwork(report interfaces.ClientErrorReport, debugTrackPeriod) jww.INFO.Printf(infoMsg) - m.Internal.Events.Report(1, "Polling", + m.events.Report(1, "Polling", "Metrics", infoMsg) } } @@ -112,7 +113,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, comms followNetworkComms, stop *stoppable.Single, abandon func(round id.Round)) { //get the identity we will poll for - identity, err := m.session.Reception().GetIdentity(rng, m.addrSpace.GetWithoutWait()) + identity, err := m.GetEphemeralIdentity(rng, m.Space.GetAddressSpaceWithoutWait()) if err != nil { jww.FATAL.Panicf("Failed to get an identity, this should be "+ "impossible: %+v", err) @@ -123,8 +124,8 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, // we want the values to be randomly generated rather than based on // actual state. if identity.Fake { - fakeEr := &rounds2.EarliestRound{} - fakeEr.Set(m.GetFakeEarliestRound()) + fakeEr := &store.EarliestRound{} + fakeEr.Set(m.getFakeEarliestRound()) identity.ER = fakeEr } @@ -136,9 +137,9 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, // Poll network updates pollReq := pb.GatewayPoll{ Partial: &pb.NDFHash{ - Hash: m.Instance.GetPartialNdf().GetHash(), + Hash: m.instance.GetPartialNdf().GetHash(), }, - LastUpdate: uint64(m.Instance.GetLastUpdateID()), + LastUpdate: uint64(m.instance.GetLastUpdateID()), ReceptionID: identity.EphId[:], StartTimestamp: identity.StartValid.UnixNano(), EndTimestamp: identity.EndValid.UnixNano(), @@ -147,7 +148,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, LastRound: uint64(identity.ER.Get()), } - result, err := m.GetSender().SendToAny(func(host *connect.Host) (interface{}, error) { + result, err := m.SendToAny(func(host *connect.Host) (interface{}, error) { jww.DEBUG.Printf("Executing poll for %v(%s) range: %s-%s(%s) from %s", identity.EphId.Int64(), identity.Source, identity.StartValid, identity.EndValid, identity.EndValid.Sub(identity.StartValid), host.GetId()) @@ -171,7 +172,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, ) } errMsg := fmt.Sprintf("Unable to poll gateway: %+v", err) - m.Internal.Events.Report(10, "Polling", "Error", errMsg) + m.events.Report(10, "Polling", "Error", errMsg) jww.ERROR.Printf(errMsg) return } @@ -190,42 +191,20 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, // NOTE: this updates the structure, AND sends events over the nodes // update channels about new and removed nodes if pollResp.PartialNDF != nil { - err = m.Instance.UpdatePartialNdf(pollResp.PartialNDF) + err = m.instance.UpdatePartialNdf(pollResp.PartialNDF) if err != nil { jww.ERROR.Printf("Unable to update partial NDF: %+v", err) return } // update gateway connections - m.GetSender().UpdateNdf(m.GetInstance().GetPartialNdf().Get()) + m.UpdateNdf(m.GetInstance().GetPartialNdf().Get()) m.session.SetNDF(m.GetInstance().GetPartialNdf().Get()) } - // Pull rate limiting parameter values from NDF - ndfRateLimitParam := m.Instance.GetPartialNdf().Get().RateLimits - ndfCapacity, ndfLeakedTokens, ndfLeakDuration := uint32(ndfRateLimitParam.Capacity), - uint32(ndfRateLimitParam.LeakedTokens), time.Duration(ndfRateLimitParam.LeakDuration) - - // Pull internal rate limiting parameters from RAM - internalRateLimitParams := m.Internal.Session.GetBucketParams().Get() - - // If any param value in our internal store does not - // match the NDF's corresponding value, update our internal store - if ndfCapacity != internalRateLimitParams.Capacity || - ndfLeakedTokens != internalRateLimitParams.LeakedTokens || - ndfLeakDuration != internalRateLimitParams.LeakDuration { - // Update internally stored params - err = m.Internal.Session.GetBucketParams(). - UpdateParams(ndfCapacity, ndfLeakedTokens, ndfLeakDuration) - if err != nil { - jww.ERROR.Printf("%+v", err) - return - } - } - // Update the address space size - if len(m.Instance.GetPartialNdf().Get().AddressSpace) != 0 { - m.addrSpace.Update(m.Instance.GetPartialNdf().Get().AddressSpace[0].Size) + if len(m.instance.GetPartialNdf().Get().AddressSpace) != 0 { + m.UpdateAddressSpace(m.instance.GetPartialNdf().Get().AddressSpace[0].Size) } // NOTE: this updates rounds and updates the tracking of the health of the network @@ -239,9 +218,10 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, continue } + marshaledTid := m.session.GetTransmissionID().Marshal() for _, clientErr := range update.ClientErrors { // If this Client appears in the ClientError - if bytes.Equal(clientErr.ClientId, m.session.GetUser().TransmissionID.Marshal()) { + if bytes.Equal(clientErr.ClientId, marshaledTid) { // Obtain relevant NodeGateway information nid, err := id.Unmarshal(clientErr.Source) @@ -249,11 +229,6 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, jww.ERROR.Printf("Unable to get NodeID: %+v", err) return } - nGw, err := m.Instance.GetNodeAndGateway(nid) - if err != nil { - jww.ERROR.Printf("Unable to get gateway: %+v", err) - return - } // Mutate the update to indicate failure due to a ClientError // FIXME: Should be able to trigger proper type of round event @@ -261,15 +236,14 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, // FIXME: before keys are deleted update.State = uint32(states.FAILED) - // delete all existing keys and trigger a re-registration with the relevant Node - m.session.Cmix().Remove(nid) - m.Instance.GetAddGatewayChan() <- nGw + //trigger a reregistration with the node + m.Registrar.TriggerNodeRegistration(nid) } } } // Trigger RoundEvents for all polled updates, including modified rounds with ClientErrors - err = m.Instance.RoundUpdates(pollResp.Updates) + err = m.instance.RoundUpdates(pollResp.Updates) if err != nil { jww.ERROR.Printf("%+v", err) return @@ -304,10 +278,10 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, } //prepare the filter objects for processing - filterList := make([]*rounds.RemoteFilter, 0, len(pollResp.Filters.Filters)) + filterList := make([]*RemoteFilter, 0, len(pollResp.Filters.Filters)) for i := range pollResp.Filters.Filters { if len(pollResp.Filters.Filters[i].Filter) != 0 { - filterList = append(filterList, rounds.NewRemoteFilter(pollResp.Filters.Filters[i])) + filterList = append(filterList, NewRemoteFilter(pollResp.Filters.Filters[i])) } } @@ -315,7 +289,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, // are messages waiting in rounds and then sends signals to the appropriate // handling threads roundChecker := func(rid id.Round) bool { - hasMessage := rounds.Checker(rid, filterList, identity.CR) + hasMessage := Checker(rid, filterList, identity.CR) if !hasMessage && m.verboseRounds != nil { m.verboseRounds.denote(rid, RoundState(NoMessageAvailable)) } @@ -375,7 +349,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, if !m.param.RealtimeOnly { roundsWithMessages2 = identity.UR.Iterate(func(rid id.Round) bool { if gwRoundsState.Checked(rid) { - return rounds.Checker(rid, filterList, identity.CR) + return Checker(rid, filterList, identity.CR) } return false }, roundsUnknown, abandon) @@ -384,7 +358,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, for _, rid := range roundsWithMessages { //denote that the round has been looked at in the tracking store if identity.CR.Check(rid) { - m.round.GetMessagesFromRound(rid, identity) + m.GetMessagesFromRound(rid, identity.EphemeralIdentity) } } @@ -396,7 +370,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, } for _, rid := range roundsWithMessages2 { - m.round.GetMessagesFromRound(rid, identity) + m.GetMessagesFromRound(rid, identity.EphemeralIdentity) } if m.verboseRounds != nil { @@ -427,3 +401,17 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, } } + +// getFakeEarliestRound generates a random earliest round for a fake identity. +func (m *manager) getFakeEarliestRound() id.Round { + b, err := csprng.Generate(8, rand.Reader) + if err != nil { + jww.FATAL.Panicf("Could not get random number: %v", err) + } + + rangeVal := binary.LittleEndian.Uint64(b) % 800 + + earliestKnown := atomic.LoadUint64(m.earliestRound) + + return id.Round(earliestKnown - rangeVal) +} diff --git a/network/gateway/hostPool.go b/network/gateway/hostPool.go index 4012868b0..2aa2b714d 100644 --- a/network/gateway/hostPool.go +++ b/network/gateway/hostPool.go @@ -115,7 +115,7 @@ func DefaultPoolParams() PoolParams { // Build and return new HostPool object func newHostPool(poolParams PoolParams, rng *fastRNG.StreamGenerator, - netDef *ndf.NetworkDefinition, getter HostManager, storage *storage.Session, + netDef *ndf.NetworkDefinition, getter HostManager, storage storage.Session, addGateway chan<- network.NodeGateway) (*HostPool, error) { var err error @@ -345,8 +345,8 @@ func (h *HostPool) UpdateNdf(ndf *ndf.NetworkDefinition) { h.ndfMux.Unlock() } -// SetFilter sets the filter used to filter gateways from the ID map. -func (h *HostPool) SetFilter(f Filter) { +// SetPoolFilter sets the filter used to filter gateways from the ID map. +func (h *HostPool) SetHostPoolFilter(f Filter) { h.filterMux.Lock() defer h.filterMux.Unlock() diff --git a/network/gateway/sender.go b/network/gateway/sender.go index 9945be6c7..b930cb31a 100644 --- a/network/gateway/sender.go +++ b/network/gateway/sender.go @@ -24,7 +24,16 @@ import ( ) // Sender Object used for sending that wraps the HostPool for providing destinations -type Sender struct { +type Sender interface { + SendToAny(sendFunc func(host *connect.Host) (interface{}, error), stop *stoppable.Single) (interface{}, error) + SendToPreferred(targets []*id.ID, sendFunc sendToPreferredFunc, + stop *stoppable.Single, timeout time.Duration) (interface{}, error) + UpdateNdf(ndf *ndf.NetworkDefinition) + SetHostPoolFilter(f Filter) + GetHostParams() connect.HostParams +} + +type sender struct { *HostPool } @@ -32,17 +41,17 @@ const RetryableError = "Nonfatal error occurred, please retry" // NewSender Create a new Sender object wrapping a HostPool object func NewSender(poolParams PoolParams, rng *fastRNG.StreamGenerator, ndf *ndf.NetworkDefinition, getter HostManager, - storage *storage.Session, addGateway chan network.NodeGateway) (*Sender, error) { + storage storage.Session, addGateway chan network.NodeGateway) (Sender, error) { hostPool, err := newHostPool(poolParams, rng, ndf, getter, storage, addGateway) if err != nil { return nil, err } - return &Sender{hostPool}, nil + return &sender{hostPool}, nil } // SendToAny Call given sendFunc to any Host in the HostPool, attempting with up to numProxies destinations -func (s *Sender) SendToAny(sendFunc func(host *connect.Host) (interface{}, error), stop *stoppable.Single) (interface{}, error) { +func (s *sender) SendToAny(sendFunc func(host *connect.Host) (interface{}, error), stop *stoppable.Single) (interface{}, error) { proxies := s.getAny(s.poolParams.ProxyAttempts, nil) for proxy := range proxies { @@ -84,7 +93,7 @@ type sendToPreferredFunc func(host *connect.Host, target *id.ID, // SendToPreferred Call given sendFunc to any Host in the HostPool, attempting // with up to numProxies destinations. Returns an error if the timeout is // reached. -func (s *Sender) SendToPreferred(targets []*id.ID, sendFunc sendToPreferredFunc, +func (s *sender) SendToPreferred(targets []*id.ID, sendFunc sendToPreferredFunc, stop *stoppable.Single, timeout time.Duration) (interface{}, error) { startTime := netTime.Now() diff --git a/network/gateway/sender_test.go b/network/gateway/sender_test.go index 3927b69ef..3e8bdc21e 100644 --- a/network/gateway/sender_test.go +++ b/network/gateway/sender_test.go @@ -60,7 +60,8 @@ func TestSender_SendToAny(t *testing.T) { } - sender, err := NewSender(params, rng, testNdf, manager, testStorage, addGwChan) + senderFace, err := NewSender(params, rng, testNdf, manager, testStorage, addGwChan) + s := senderFace.(*sender) if err != nil { t.Fatalf("Failed to create mock sender: %v", err) } @@ -72,14 +73,14 @@ func TestSender_SendToAny(t *testing.T) { t.Fatalf("Failed to unmarshal ID in mock ndf: %v", err) } - err = sender.replaceHost(gwId, uint32(index)) + err = s.replaceHost(gwId, uint32(index)) if err != nil { t.Fatalf("Failed to replace host in set-up: %v", err) } } // Test sendToAny with test interfaces - result, err := sender.SendToAny(SendToAny_HappyPath, nil) + result, err := s.SendToAny(SendToAny_HappyPath, nil) if err != nil { t.Errorf("Should not error in SendToAny happy path: %v", err) } @@ -90,12 +91,12 @@ func TestSender_SendToAny(t *testing.T) { "\n\tReceived: %v", happyPathReturn, result) } - _, err = sender.SendToAny(SendToAny_KnownError, nil) + _, err = s.SendToAny(SendToAny_KnownError, nil) if err == nil { t.Fatalf("Expected error path did not receive error") } - _, err = sender.SendToAny(SendToAny_UnknownError, nil) + _, err = s.SendToAny(SendToAny_UnknownError, nil) if err == nil { t.Fatalf("Expected error path did not receive error") } @@ -131,16 +132,17 @@ func TestSender_SendToPreferred(t *testing.T) { } - sender, err := NewSender(params, rng, testNdf, manager, testStorage, addGwChan) + sFace, err := NewSender(params, rng, testNdf, manager, testStorage, addGwChan) if err != nil { t.Fatalf("Failed to create mock sender: %v", err) } + s := sFace.(*sender) preferredIndex := 0 - preferredHost := sender.hostList[preferredIndex] + preferredHost := s.hostList[preferredIndex] // Happy path - result, err := sender.SendToPreferred([]*id.ID{preferredHost.GetId()}, + result, err := s.SendToPreferred([]*id.ID{preferredHost.GetId()}, SendToPreferred_HappyPath, nil, 250*time.Millisecond) if err != nil { t.Errorf("Should not error in SendToPreferred happy path: %v", err) @@ -153,14 +155,14 @@ func TestSender_SendToPreferred(t *testing.T) { } // Call a send which returns an error which triggers replacement - _, err = sender.SendToPreferred([]*id.ID{preferredHost.GetId()}, + _, err = s.SendToPreferred([]*id.ID{preferredHost.GetId()}, SendToPreferred_KnownError, nil, 250*time.Millisecond) if err == nil { t.Fatalf("Expected error path did not receive error") } // Check the host has been replaced - if _, ok := sender.hostMap[*preferredHost.GetId()]; ok { + if _, ok := s.hostMap[*preferredHost.GetId()]; ok { t.Errorf("Expected host %s to be removed due to error", preferredHost) } @@ -171,17 +173,17 @@ func TestSender_SendToPreferred(t *testing.T) { // get a new host to test on preferredIndex = 4 - preferredHost = sender.hostList[preferredIndex] + preferredHost = s.hostList[preferredIndex] // Unknown error return will not trigger replacement - _, err = sender.SendToPreferred([]*id.ID{preferredHost.GetId()}, + _, err = s.SendToPreferred([]*id.ID{preferredHost.GetId()}, SendToPreferred_UnknownError, nil, 250*time.Millisecond) if err == nil { t.Fatalf("Expected error path did not receive error") } // Check the host has not been replaced - if _, ok := sender.hostMap[*preferredHost.GetId()]; !ok { + if _, ok := s.hostMap[*preferredHost.GetId()]; !ok { t.Errorf("Host %s should not have been removed due on an unknown error", preferredHost) } diff --git a/network/rounds/historical.go b/network/historical/historical.go similarity index 62% rename from network/rounds/historical.go rename to network/historical/historical.go index 756dfd917..68e3984d9 100644 --- a/network/rounds/historical.go +++ b/network/historical/historical.go @@ -5,12 +5,15 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package rounds +package historical import ( "fmt" + "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/network/identity/receptionID" + "gitlab.com/elixxir/client/interfaces" + "gitlab.com/elixxir/client/interfaces/params" + "gitlab.com/elixxir/client/network/gateway" "gitlab.com/elixxir/client/stoppable" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/xx_network/comms/connect" @@ -26,42 +29,99 @@ import ( // Historical Rounds sends the output to: // - Message Retrieval Workers (/network/round/retrieve.go) -//interface to increase east of testing of historical rounds -type historicalRoundsComms interface { +type Retriever interface { + StartProcessies() *stoppable.Single + LookupHistoricalRound(rid id.Round, callback RoundResultCallback) error +} + +// manager is the controlling structure +type manager struct { + params params.Historical + + comms RoundsComms + sender gateway.Sender + events interfaces.EventManager + + c chan roundRequest +} + +//RoundsComms interface to increase east of testing of historical rounds +type RoundsComms interface { GetHost(hostId *id.ID) (*connect.Host, bool) RequestHistoricalRounds(host *connect.Host, message *pb.HistoricalRounds) (*pb.HistoricalRoundsResponse, error) } -//structure which contains a historical round lookup -type historicalRoundRequest struct { - rid id.Round - identity receptionID.IdentityUse +//RoundResultCallback is the used callback when a round is found +type RoundResultCallback func(info *pb.RoundInfo, success bool) + +//roundRequest is an internal structure which tracks a request +type roundRequest struct { + rid id.Round + RoundResultCallback numAttempts uint } -// Long running thread which process historical rounds -// Can be killed by sending a signal to the quit channel -// takes a comms interface to aid in testing -func (m *Manager) processHistoricalRounds(comm historicalRoundsComms, stop *stoppable.Single) { +func NewRetriever(param params.Historical, comms RoundsComms, + sender gateway.Sender, events interfaces.EventManager) Retriever { + return &manager{ + params: param, + comms: comms, + sender: sender, + events: events, + c: make(chan roundRequest, param.HistoricalRoundsBufferLen), + } +} + +// LookupHistoricalRound sends the lookup request to the internal handler +// and will return the result on the callback when it returns +func (m *manager) LookupHistoricalRound(rid id.Round, callback RoundResultCallback) error { + if rid == 0 { + return errors.Errorf("Cannot lookup round 0, rounds start at 1") + } + select { + case m.c <- roundRequest{ + rid: rid, + RoundResultCallback: callback, + numAttempts: 0, + }: + return nil + default: + return errors.Errorf("Cannot lookup round %d, "+ + "channel is full", rid) + } +} + +// StartProcessies starts the Long running thread which +// process historical rounds. Can be killed by sending a +// signal to the quit channel +func (m *manager) StartProcessies() *stoppable.Single { + stop := stoppable.NewSingle("TrackNetwork") + go m.processHistoricalRounds(m.comms, stop) + return stop +} + +// processHistoricalRounds is a long running thread which +// process historical rounds. Can be killed by sending +// a signal to the quit channel takes a comms interface to aid in testing +func (m *manager) processHistoricalRounds(comm RoundsComms, stop *stoppable.Single) { timerCh := make(<-chan time.Time) - rng := m.Rng.GetStream() - var roundRequests []historicalRoundRequest + var roundRequests []roundRequest for { + shouldProcess := false // wait for a quit or new round to check select { case <-stop.Quit(): - rng.Close() // return all roundRequests in the queue to the input channel so they can // be checked in the future. If the queue is full, disable them as // processing so they are picked up from the beginning for _, r := range roundRequests { select { - case m.historicalRounds <- r: + case m.c <- r: default: } } @@ -73,7 +133,7 @@ func (m *Manager) processHistoricalRounds(comm historicalRoundsComms, stop *stop shouldProcess = true } // get new round to lookup and force a lookup if - case r := <-m.historicalRounds: + case r := <-m.c: jww.DEBUG.Printf("Received and queueing round %d for "+ "historical rounds lookup", r.rid) roundRequests = append(roundRequests, r) @@ -129,9 +189,10 @@ func (m *Manager) processHistoricalRounds(comm historicalRoundsComms, stop *stop errMsg = fmt.Sprintf("Failed to retreive historical "+ "round %d on last attempt, will not try again", roundRequests[i].rid) + go roundRequests[i].RoundResultCallback(nil, false) } else { select { - case m.historicalRounds <- roundRequests[i]: + case m.c <- roundRequests[i]: errMsg = fmt.Sprintf("Failed to retreive historical "+ "round %d, will try up to %d more times", roundRequests[i].rid, m.params.MaxHistoricalRoundsRetries-roundRequests[i].numAttempts) @@ -142,26 +203,22 @@ func (m *Manager) processHistoricalRounds(comm historicalRoundsComms, stop *stop } } jww.WARN.Printf(errMsg) - m.Internal.Events.Report(5, "HistoricalRounds", + m.events.Report(5, "HistoricalRounds", "Error", errMsg) continue } - // Successfully retrieved roundRequests are sent to the Message - // Retrieval Workers - rl := roundLookup{ - roundInfo: roundInfo, - identity: roundRequests[i].identity, - } - m.lookupRoundMessages <- rl + // Successfully retrieved roundRequests are returned on the callback + go roundRequests[i].RoundResultCallback(roundInfo, true) + rids = append(rids, roundInfo.ID) } - m.Internal.Events.Report(1, "HistoricalRounds", "Metrics", + m.events.Report(1, "HistoricalRounds", "Metrics", fmt.Sprintf("Received %d historical rounds from"+ " gateway %s: %v", len(response.Rounds), gwHost, rids)) //clear the buffer now that all have been checked - roundRequests = make([]historicalRoundRequest, 0) + roundRequests = make([]roundRequest, 0) } } diff --git a/network/identity/receptionID/IdentityUse.go b/network/identity/receptionID/IdentityUse.go index 7b957be00..be8040ad9 100644 --- a/network/identity/receptionID/IdentityUse.go +++ b/network/identity/receptionID/IdentityUse.go @@ -2,7 +2,7 @@ package receptionID import ( "fmt" - "gitlab.com/elixxir/client/storage/rounds" + "gitlab.com/elixxir/client/network/identity/receptionID/store" "strconv" "strings" ) @@ -13,9 +13,9 @@ type IdentityUse struct { // Denotes if the identity is fake, in which case we do not process messages Fake bool - UR *rounds.UnknownRounds - ER *rounds.EarliestRound - CR *rounds.CheckedRounds + UR *store.UnknownRounds + ER *store.EarliestRound + CR *store.CheckedRounds } func (iu IdentityUse) GoString() string { diff --git a/network/identity/receptionID/identity.go b/network/identity/receptionID/identity.go index 74ed55577..ee7c069e9 100644 --- a/network/identity/receptionID/identity.go +++ b/network/identity/receptionID/identity.go @@ -3,9 +3,8 @@ package receptionID import ( "encoding/json" "github.com/pkg/errors" + "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/storage/versioned" - "gitlab.com/xx_network/primitives/id" - "gitlab.com/xx_network/primitives/id/ephemeral" "gitlab.com/xx_network/primitives/netTime" "strconv" "strings" @@ -17,8 +16,7 @@ const identityStorageVersion = 0 type Identity struct { // Identity - EphId ephemeral.Id - Source *id.ID + interfaces.EphemeralIdentity AddressSize uint8 // Usage variables diff --git a/network/identity/receptionID/registration.go b/network/identity/receptionID/registration.go index 92e93e3f5..536472545 100644 --- a/network/identity/receptionID/registration.go +++ b/network/identity/receptionID/registration.go @@ -4,7 +4,7 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces/params" - "gitlab.com/elixxir/client/storage/rounds" + "gitlab.com/elixxir/client/network/identity/receptionID/store" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/id/ephemeral" @@ -17,9 +17,9 @@ const knownRoundsStorageKey = "krStorage" type registration struct { Identity - UR *rounds.UnknownRounds - ER *rounds.EarliestRound - CR *rounds.CheckedRounds + UR *store.UnknownRounds + ER *store.EarliestRound + CR *store.CheckedRounds kv *versioned.KV } @@ -45,11 +45,11 @@ func newRegistration(reg Identity, kv *versioned.KV) (*registration, error) { kv: kv, } - urParams := rounds.DefaultUnknownRoundsParams() + urParams := store.DefaultUnknownRoundsParams() urParams.Stored = !reg.Ephemeral - r.UR = rounds.NewUnknownRounds(kv, urParams) - r.ER = rounds.NewEarliestRound(!reg.Ephemeral, kv) - cr, err := rounds.NewCheckedRounds(int(params.GetDefaultNetwork().KnownRoundsThreshold), kv) + r.UR = store.NewUnknownRounds(kv, urParams) + r.ER = store.NewEarliestRound(!reg.Ephemeral, kv) + cr, err := store.NewCheckedRounds(int(params.GetDefaultNetwork().KnownRoundsThreshold), kv) if err != nil { jww.FATAL.Printf("Failed to create new CheckedRounds for registration: %+v", err) } @@ -79,12 +79,12 @@ func loadRegistration(EphId ephemeral.Id, Source *id.ID, startValid time.Time, "for %s", regPrefix(EphId, Source, startValid)) } - cr, err := rounds.LoadCheckedRounds(int(params.GetDefaultNetwork().KnownRoundsThreshold), kv) + cr, err := store.LoadCheckedRounds(int(params.GetDefaultNetwork().KnownRoundsThreshold), kv) if err != nil { jww.ERROR.Printf("Making new CheckedRounds, loading of CheckedRounds "+ "failed: %+v", err) - cr, err = rounds.NewCheckedRounds(int(params.GetDefaultNetwork().KnownRoundsThreshold), kv) + cr, err = store.NewCheckedRounds(int(params.GetDefaultNetwork().KnownRoundsThreshold), kv) if err != nil { jww.FATAL.Printf("Failed to create new CheckedRounds for "+ "registration after CheckedRounds load failure: %+v", err) @@ -94,8 +94,8 @@ func loadRegistration(EphId ephemeral.Id, Source *id.ID, startValid time.Time, r := ®istration{ Identity: reg, kv: kv, - UR: rounds.LoadUnknownRounds(kv, rounds.DefaultUnknownRoundsParams()), - ER: rounds.LoadEarliestRound(kv), + UR: store.LoadUnknownRounds(kv, store.DefaultUnknownRoundsParams()), + ER: store.LoadEarliestRound(kv), CR: cr, } diff --git a/network/identity/receptionID/store.go b/network/identity/receptionID/store.go index ccea69da7..18a66096d 100644 --- a/network/identity/receptionID/store.go +++ b/network/identity/receptionID/store.go @@ -47,34 +47,39 @@ func makeIdHash(ephID ephemeral.Id, source *id.ID) idHash { } // NewStore creates a new reception store that starts empty. -func NewStore(kv *versioned.KV) *Store { - s := &Store{ - active: []*registration{}, - present: make(map[idHash]struct{}), - kv: kv.Prefix(receptionPrefix), - } +func NewOrLoadStore(kv *versioned.KV) *Store { - // Store the empty list - if err := s.save(); err != nil { - jww.FATAL.Panicf("Failed to save new reception store: %+v", err) + s, err := loadStore(kv) + if err != nil { + jww.WARN.Printf("ReceptionID store not found, creating a new one: %+v", err) + s = &Store{ + active: []*registration{}, + present: make(map[idHash]struct{}), + kv: kv.Prefix(receptionPrefix), + } + + // Store the empty list + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to save new reception store: %+v", err) + } } return s } -func LoadStore(kv *versioned.KV) *Store { +func loadStore(kv *versioned.KV) (*Store, error) { kv = kv.Prefix(receptionPrefix) // Load the versioned object for the reception list vo, err := kv.Get(receptionStoreStorageKey, receptionStoreStorageVersion) if err != nil { - jww.FATAL.Panicf("Failed to get the reception storage list: %+v", err) + return nil, errors.WithMessage(err, "Failed to get the reception storage list") } // JSON unmarshal identities list var identities []storedReference if err = json.Unmarshal(vo.Data, &identities); err != nil { - jww.FATAL.Panicf("Failed to unmarshal the stored identity list: %+v", err) + return nil, errors.WithMessage(err, "Failed to unmarshal the stored identity list") } s := &Store{ @@ -86,13 +91,13 @@ func LoadStore(kv *versioned.KV) *Store { for i, sr := range identities { s.active[i], err = loadRegistration(sr.Eph, sr.Source, sr.StartValid, s.kv) if err != nil { - jww.FATAL.Panicf("Failed to load registration for %s: %+v", - regPrefix(sr.Eph, sr.Source, sr.StartValid), err) + return nil, errors.WithMessagef(err, "failed to load registration for: %+v", + regPrefix(sr.Eph, sr.Source, sr.StartValid)) } s.present[makeIdHash(sr.Eph, sr.Source)] = struct{}{} } - return s + return s, nil } func (s *Store) save() error { @@ -229,6 +234,31 @@ func (s *Store) RemoveIdentity(ephID ephemeral.Id) { } } +func (s *Store) RemoveIdentities(source *id.ID) { + s.mux.Lock() + defer s.mux.Unlock() + + doSave := false + for i, inQuestion := range s.active { + if inQuestion.Source.Cmp(source) { + s.active = append(s.active[:i], s.active[i+1:]...) + + err := inQuestion.Delete() + if err != nil { + jww.FATAL.Panicf("Failed to delete identity: %+v", err) + } + + doSave = doSave || !inQuestion.Ephemeral + } + } + if doSave { + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to save reception store after "+ + "identity removal: %+v", err) + } + } +} + func (s *Store) SetToExpire(addressSize uint8) { s.mux.Lock() defer s.mux.Unlock() diff --git a/storage/rounds/checkedRounds.go b/network/identity/receptionID/store/checkedRounds.go similarity index 99% rename from storage/rounds/checkedRounds.go rename to network/identity/receptionID/store/checkedRounds.go index 620d4b023..e02275a14 100644 --- a/storage/rounds/checkedRounds.go +++ b/network/identity/receptionID/store/checkedRounds.go @@ -1,4 +1,4 @@ -package rounds +package store import ( "container/list" diff --git a/storage/rounds/checkedRounds_test.go b/network/identity/receptionID/store/checkedRounds_test.go similarity index 99% rename from storage/rounds/checkedRounds_test.go rename to network/identity/receptionID/store/checkedRounds_test.go index 816c6fce7..803b824de 100644 --- a/storage/rounds/checkedRounds_test.go +++ b/network/identity/receptionID/store/checkedRounds_test.go @@ -1,4 +1,4 @@ -package rounds +package store import ( "container/list" diff --git a/storage/rounds/earliestRound.go b/network/identity/receptionID/store/earliestRound.go similarity index 99% rename from storage/rounds/earliestRound.go rename to network/identity/receptionID/store/earliestRound.go index a072419f3..230bb84c3 100644 --- a/storage/rounds/earliestRound.go +++ b/network/identity/receptionID/store/earliestRound.go @@ -1,4 +1,4 @@ -package rounds +package store import ( "encoding/json" diff --git a/storage/rounds/unknownRounds.go b/network/identity/receptionID/store/unknownRounds.go similarity index 99% rename from storage/rounds/unknownRounds.go rename to network/identity/receptionID/store/unknownRounds.go index ba2661c17..d9a6cae07 100644 --- a/storage/rounds/unknownRounds.go +++ b/network/identity/receptionID/store/unknownRounds.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package rounds +package store import ( "encoding/json" diff --git a/storage/rounds/unknownRounds_test.go b/network/identity/receptionID/store/unknownRounds_test.go similarity index 99% rename from storage/rounds/unknownRounds_test.go rename to network/identity/receptionID/store/unknownRounds_test.go index 95537ded8..9b3fb6317 100644 --- a/storage/rounds/unknownRounds_test.go +++ b/network/identity/receptionID/store/unknownRounds_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package rounds +package store import ( "bytes" diff --git a/network/identity/tracker.go b/network/identity/tracker.go index 78599428b..b21608c2f 100644 --- a/network/identity/tracker.go +++ b/network/identity/tracker.go @@ -9,11 +9,11 @@ package identity import ( "encoding/json" + "io" "os" "sync" "time" - "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/network/address" @@ -30,22 +30,28 @@ const validityGracePeriod = 5 * time.Minute const TrackerListKey = "TrackerListKey" const TrackerListVersion = 0 const TimestampKey = "IDTrackingTimestamp" -const TimestampStoreVersion = 0 const ephemeralStoppable = "EphemeralCheck" const addressSpaceSizeChanTag = "ephemeralTracker" -var Forever time.Time = time.Time{} +var Forever = time.Time{} const trackedIDChanSize = 1000 const deleteIDChanSize = 1000 -type Tracker struct { +type Tracker interface { + StartProcessies() stoppable.Stoppable + AddIdentity(id *id.ID, validUntil time.Time, persistent bool) + RemoveIdentity(id *id.ID) + GetEphemeralIdentity(rng io.Reader, addressSize uint8) (receptionID.IdentityUse, error) +} + +type manager struct { tracked []trackedID store *receptionID.Store - session *storage.Session + session storage.Session newIdentity chan trackedID deleteIdentity chan *id.ID - addrSpace *address.Space + addrSpace address.Space mux *sync.Mutex } @@ -57,10 +63,9 @@ type trackedID struct { Persistent bool } -func NewTracker(session *storage.Session, addrSpace *address.Space, - kv *versioned.KV) *Tracker { +func NewOrLoadTracker(session storage.Session, addrSpace address.Space) *manager { //intilization - t := &Tracker{ + t := &manager{ tracked: make([]trackedID, 0), session: session, newIdentity: make(chan trackedID, trackedIDChanSize), @@ -68,36 +73,56 @@ func NewTracker(session *storage.Session, addrSpace *address.Space, addrSpace: addrSpace, mux: &sync.Mutex{}, } - //loading + //Load this structure err := t.load() if err != nil && os.IsNotExist(err) { - //if load fails, make a new - t.store = receptionID.NewStore(session.GetKV()) + oldTimestamp, err := getOldTimestampStore(t.session) + if err == nil { + jww.WARN.Printf("No tracked identities found, " + + "creating a new tracked identity from legacy stored timestamp") + t.tracked = append(t.tracked, trackedID{ + // make the next generation now so a generation triggers on first run + NextGeneration: netTime.Now(), + // it generated previously though oldTimestamp, denote that + LastGeneration: oldTimestamp, + Source: t.session.GetReceptionID(), + ValidUntil: Forever, + Persistent: true, + }) + } else { + jww.WARN.Printf("No tracked identities found and no legacy stored " + + "timestamp found, creating a new tracked identity from scratch") + t.tracked = append(t.tracked, trackedID{ + // make the next generation now so a generation triggers on first run + NextGeneration: netTime.Now(), + // start generation 24 hours ago to make sure all rescent ephemerals do pickups + // todo: should we go back farther? + LastGeneration: netTime.Now().Add(-time.Duration(ephemeral.Period)), + Source: t.session.GetReceptionID(), + ValidUntil: Forever, + Persistent: true, + }) + } } else if err != nil { jww.FATAL.Panicf("unable to create new Tracker: %+v", err) - } else { - t.store = receptionID.LoadStore(session.GetKV()) } - //check if there is an old timestamp, recreate the basic ID from it - //initilize the ephemeral identiies system + t.store = receptionID.NewOrLoadStore(session.GetKV()) return t } -//make the GetIdentities call on the ephemerals system public on this - // Track runs a thread which checks for past and present address ID. -func (tracker Tracker) StartProcessies() stoppable.Stoppable { +func (tracker manager) StartProcessies() stoppable.Stoppable { stop := stoppable.NewSingle(ephemeralStoppable) - go track(session, addrSpace, ourId, stop) + go tracker.track(stop) return stop } // AddIdentity adds an identity to be tracked -func (tracker *Tracker) AddIdentity(id *id.ID, validUntil time.Time, persistent bool) { +func (tracker *manager) AddIdentity(id *id.ID, validUntil time.Time, persistent bool) { tracker.newIdentity <- trackedID{ NextGeneration: netTime.Now().Add(-time.Second), LastGeneration: time.Time{}, @@ -108,14 +133,19 @@ func (tracker *Tracker) AddIdentity(id *id.ID, validUntil time.Time, persistent } // RemoveIdentity removes a currently tracked identity. -func (tracker *Tracker) RemoveIdentity(id *id.ID) { +func (tracker *manager) RemoveIdentity(id *id.ID) { tracker.deleteIdentity <- id } -func (tracker *Tracker) track(session *storage.Session, addrSpace *address.Space, ourId *id.ID, stop *stoppable.Single) { +// GetEphemeralIdentity returns an ephemeral Identity to poll the network with. +func (tracker *manager) GetEphemeralIdentity(rng io.Reader, addressSize uint8) (receptionID.IdentityUse, error) { + return tracker.store.GetIdentity(rng, addressSize) +} + +func (tracker *manager) track(stop *stoppable.Single) { // Wait until we get the ID size from the network - addressSize := addrSpace.Get() + addressSize := tracker.addrSpace.GetAddressSpace() /*wait for next event*/ trackerLoop: @@ -147,6 +177,7 @@ trackerLoop: if inQuestion.ValidUntil != Forever && identity.End.After(inQuestion.ValidUntil) { identity.End = inQuestion.ValidUntil } + identity.Ephemeral = !inQuestion.Persistent if err := tracker.store.AddIdentity(identity); err != nil { jww.FATAL.Panicf("Could not insert identity: %+v", err) } @@ -216,33 +247,25 @@ trackerLoop: if inQuestion.Source.Cmp(deleteID) { tracker.tracked = append(tracker.tracked[:i], tracker.tracked[i+1:]...) tracker.save() + tracker.store.RemoveIdentities(deleteID) break } } case <-stop.Quit(): - addrSpace.UnregisterNotification(addressSpaceSizeChanTag) + tracker.addrSpace.UnregisterAddressSpaceNotification(addressSpaceSizeChanTag) stop.ToStopped() return } } } -// checkTimestampStore performs a sanitation check of timestamp store. If a -// value has not been stored yet, then the current time is stored. -func checkTimestampStore(session *storage.Session) error { - if _, err := session.Get(TimestampKey); err != nil { - // Only generate from the last hour because this is a new ID; it could - // not yet receive messages - now, err := marshalTimestamp(netTime.Now().Add(-1 * time.Hour)) - if err != nil { - return errors.Errorf("Could not marshal new timestamp for "+ - "storage: %+v", err) - } - - return session.Set(TimestampKey, now) +func getOldTimestampStore(session storage.Session) (time.Time, error) { + lastTimestampObj, err := session.Get(TimestampKey) + if err != nil { + return time.Time{}, err } - return nil + return unmarshalTimestamp(lastTimestampObj) } // unmarshalTimestamp unmarshal the stored timestamp into a time.Time. @@ -256,18 +279,6 @@ func unmarshalTimestamp(lastTimestampObj *versioned.Object) (time.Time, error) { return lastTimestamp, err } -// marshalTimestamp marshals the timestamp and generates a storable object for -// ekv storage. -func marshalTimestamp(timeToStore time.Time) (*versioned.Object, error) { - data, err := timeToStore.MarshalBinary() - - return &versioned.Object{ - Version: TimestampStoreVersion, - Timestamp: netTime.Now(), - Data: data, - }, err -} - func generateIdentitiesOverRange(lastGeneration, generateThrough time.Time, source *id.ID, addressSize uint8) ([]receptionID.Identity, time.Time) { protoIds, err := ephemeral.GetIdsByRange( @@ -288,8 +299,10 @@ func generateIdentitiesOverRange(lastGeneration, generateThrough time.Time, for i, eid := range protoIds { // Expand the grace period for both start and end identities[i] = receptionID.Identity{ - EphId: eid.Id, - Source: source, + EphemeralIdentity: interfaces.EphemeralIdentity{ + EphId: eid.Id, + Source: source, + }, AddressSize: addressSize, End: eid.End, StartValid: eid.Start.Add(-validityGracePeriod), @@ -310,9 +323,9 @@ func generateIdentitiesOverRange(lastGeneration, generateThrough time.Time, return identities, identities[len(identities)-1].End } -func (tracker *Tracker) save() { - t.mux.Lock() - defer t.mux.Unlock() +func (tracker *manager) save() { + tracker.mux.Lock() + defer tracker.mux.Unlock() persistant := make([]trackedID, 0, len(tracker.tracked)) for i := range tracker.tracked { @@ -343,7 +356,7 @@ func (tracker *Tracker) save() { } } -func (t *Tracker) load() error { +func (t *manager) load() error { t.mux.Lock() defer t.mux.Unlock() obj, err := t.session.GetKV().Get(TrackerListKey, TrackerListVersion) @@ -351,12 +364,5 @@ func (t *Tracker) load() error { return err } - trackedID := make([]trackedID, 0) - err = json.Unmarshal(obj.Data, trackedID) - if err != nil { - return err - } - t.tracked = trackedID - - return nil + return json.Unmarshal(obj.Data, &t.tracked) } diff --git a/network/internal/internal.go b/network/internal/internal.go deleted file mode 100644 index 636330aa2..000000000 --- a/network/internal/internal.go +++ /dev/null @@ -1,42 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package internal - -import ( - "gitlab.com/elixxir/client/interfaces" - "gitlab.com/elixxir/client/network/health" - "gitlab.com/elixxir/client/storage" - "gitlab.com/elixxir/client/switchboard" - "gitlab.com/elixxir/comms/client" - "gitlab.com/elixxir/comms/network" - "gitlab.com/xx_network/primitives/id" -) - -type Internal struct { - Session *storage.Session - Switchboard *switchboard.Switchboard - //generic RNG for client - - // Comms pointer to send/recv messages - Comms *client.Comms - //contains the health tracker which keeps track of if from the client's - //perspective, the network is in good condition - Health *health.Tracker - //ID which messages are sent as - TransmissionID *id.ID - //ID which messages are received as - ReceptionID *id.ID - //contains the network instance - Instance *network.Instance - - //channels - NodeRegistration chan network.NodeGateway - - // Event Reporting - Events interfaces.EventManager -} diff --git a/network/manager.go b/network/manager.go index 42ddc1b3a..b1c97abd5 100644 --- a/network/manager.go +++ b/network/manager.go @@ -11,16 +11,14 @@ package network // and intraclient state are accessible through the context object. import ( - "crypto/rand" - "encoding/binary" "fmt" "github.com/pkg/errors" - jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/interfaces/params" "gitlab.com/elixxir/client/network/address" "gitlab.com/elixxir/client/network/gateway" "gitlab.com/elixxir/client/network/health" + "gitlab.com/elixxir/client/network/historical" "gitlab.com/elixxir/client/network/identity" "gitlab.com/elixxir/client/network/message" "gitlab.com/elixxir/client/network/nodes" @@ -30,7 +28,6 @@ import ( "gitlab.com/elixxir/comms/client" commNetwork "gitlab.com/elixxir/comms/network" "gitlab.com/elixxir/crypto/fastRNG" - "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/ndf" "math" @@ -47,9 +44,12 @@ const fakeIdentityRange = 800 // manager implements the NetworkManager interface inside context. It // controls access to network resources and implements all the communications // functions used by the client. +// CRITICAL: Manager must be private. It embeds sub moduals which +// export functions for it, but not for public consumption. By being private +// and returning ass the public interface, these can be kept private. type manager struct { //User Identity Storage - session *storage.Session + session storage.Session //generic RNG for client rng *fastRNG.StreamGenerator // comms pointer to send/recv messages @@ -62,13 +62,15 @@ type manager struct { // parameters of the network param params.Network - // handles message sending - sender *gateway.Sender //sub-managers - message.Pickup + gateway.Sender + message.Handler nodes.Registrar - round *rounds.Manager + historical.Retriever + rounds.Pickup + address.Space + identity.Tracker // Earliest tracked round earliestRound *uint64 @@ -79,18 +81,14 @@ type manager struct { numLatencies uint64 verboseRounds *RoundTracker - // Address space size - addrSpace *address.AddressSpace - // Event reporting api events interfaces.EventManager } // NewManager builds a new reception manager object using inputted key fields -func NewManager(session *storage.Session, - rng *fastRNG.StreamGenerator, events interfaces.EventManager, - comms *client.Comms, params params.Network, - ndf *ndf.NetworkDefinition) (interfaces.NetworkManager, error) { +func NewManager(params params.Network, comms *client.Comms, session storage.Session, + ndf *ndf.NetworkDefinition, rng *fastRNG.StreamGenerator, events interfaces.EventManager, +) (interfaces.NetworkManager, error) { //start network instance instance, err := commNetwork.NewInstance(comms.ProtoComms, ndf, nil, nil, commNetwork.None, params.FastPolling) @@ -105,7 +103,7 @@ func NewManager(session *storage.Session, m := manager{ param: params, tracker: &tracker, - addrSpace: address.NewAddressSpace(), + Space: address.NewAddressSpace(), events: events, earliestRound: &earliest, session: session, @@ -114,7 +112,7 @@ func NewManager(session *storage.Session, health: health.Init(instance, params.NetworkHealthTimeout), instance: instance, } - m.addrSpace.Update(18) + m.UpdateAddressSpace(18) if params.VerboseRoundTracking { m.verboseRounds = NewRoundTracker() @@ -131,24 +129,30 @@ func NewManager(session *storage.Session, // Enable optimized HostPool initialization poolParams.MaxPings = 50 poolParams.ForceConnection = true - m.sender, err = gateway.NewSender(poolParams, rng, + m.Sender, err = gateway.NewSender(poolParams, rng, ndf, comms, session, nodechan) if err != nil { return nil, err } //setup the node registrar - m.Registrar, err = nodes.LoadRegistrar(session, m.sender, m.comms, m.rng, nodechan) + m.Registrar, err = nodes.LoadRegistrar(session, m.Sender, m.comms, m.rng, nodechan) if err != nil { return nil, err } + //setup the historical rounds handler + m.Retriever = historical.NewRetriever(params.Historical, comms, m.Sender, events) + + //Set up Message Handler + m.Handler = message.NewHandler(params, m.session.GetKV(), m.events) + //set up round handler - m.round = rounds.NewManager(m.Internal, m.param.Rounds, m.Pickup.GetMessageReceptionChannel(), m.sender) + m.Pickup = rounds.NewPickup(m.param.Rounds, m.Handler.GetMessageReceptionChannel(), + m.Sender, m.Retriever, m.rng, m.instance, m.session.GetKV()) - //Set up Message Pickup - m.Pickup = message.NewPickup(params, nodechan, m.sender, m.session, m.rng, - m.events, m.comms, m.Registrar, m.instance) + //add the identity system + m.Tracker = identity.NewOrLoadTracker(m.session, m.Space) // Set upthe ability to register with new nodes when they appear m.instance.SetAddGatewayChan(nodechan) @@ -187,24 +191,23 @@ func (m *manager) Follow(report interfaces.ClientErrorReport) (stoppable.Stoppab //TODO-node remover // Start the Network Tracker - trackNetworkStopper := stoppable.NewSingle("TrackNetwork") - go m.followNetwork(report, trackNetworkStopper) - multi.Add(trackNetworkStopper) + followNetworkStopper := stoppable.NewSingle("FollowNetwork") + go m.followNetwork(report, followNetworkStopper) + multi.Add(followNetworkStopper) // Message reception - multi.Add(m.Pickup.StartProcessies()) + multi.Add(m.Handler.StartProcesses()) // Round processing - multi.Add(m.round.StartProcessors()) + multi.Add(m.Pickup.StartProcessors()) - multi.Add(identity.Track(m.session, m.addrSpace, m.ReceptionID)) + // Historical rounds processing + multi.Add(m.Retriever.StartProcessies()) - return multi, nil -} + //start the processies for the identity handler + multi.Add(m.Tracker.StartProcessies()) -// GetEventManager returns the health tracker -func (m *manager) GetEventManager() interfaces.EventManager { - return m.events + return multi, nil } // GetHealthTracker returns the health tracker @@ -217,35 +220,6 @@ func (m *manager) GetInstance() *commNetwork.Instance { return m.instance } -// GetSender returns the gateway.Sender object -func (m *manager) GetSender() *gateway.Sender { - return m.sender -} - -// GetAddressSize returns the current address space size. It blocks until an -// address space size is set. -func (m *manager) GetAddressSize() uint8 { - return m.addrSpace.Get() -} - -// RegisterAddressSizeNotification returns a channel that will trigger for every -// address space size update. The provided tag is the unique ID for the channel. -// Returns an error if the tag is already used. -func (m *manager) RegisterAddressSizeNotification(tag string) (chan uint8, error) { - return m.addrSpace.RegisterNotification(tag) -} - -// UnregisterAddressSizeNotification stops broadcasting address space size -// updates on the channel with the specified tag. -func (m *manager) UnregisterAddressSizeNotification(tag string) { - m.addrSpace.UnregisterNotification(tag) -} - -// SetPoolFilter sets the filter used to filter gateway IDs. -func (m *manager) SetPoolFilter(f gateway.Filter) { - m.sender.SetFilter(f) -} - // GetVerboseRounds returns verbose round information func (m *manager) GetVerboseRounds() string { if m.verboseRounds == nil { @@ -257,17 +231,3 @@ func (m *manager) GetVerboseRounds() string { func (m *manager) SetFakeEarliestRound(rnd id.Round) { atomic.StoreUint64(m.earliestRound, uint64(rnd)) } - -// GetFakeEarliestRound generates a random earliest round for a fake identity. -func (m *manager) GetFakeEarliestRound() id.Round { - b, err := csprng.Generate(8, rand.Reader) - if err != nil { - jww.FATAL.Panicf("Could not get random number: %v", err) - } - - rangeVal := binary.LittleEndian.Uint64(b) % 800 - - earliestKnown := atomic.LoadUint64(m.earliestRound) - - return id.Round(earliestKnown - rangeVal) -} diff --git a/network/message/bundle.go b/network/message/bundle.go index a60d070ae..b0ab15c22 100644 --- a/network/message/bundle.go +++ b/network/message/bundle.go @@ -19,5 +19,5 @@ type Bundle struct { RoundInfo *pb.RoundInfo Messages []format.Message Finish func() - Identity interfaces.Identity + Identity interfaces.EphemeralIdentity } diff --git a/network/message/fingerprints.go b/network/message/fingerprints.go index 48fc46e14..91fa906d4 100644 --- a/network/message/fingerprints.go +++ b/network/message/fingerprints.go @@ -17,7 +17,7 @@ import ( ) // FingerprintsManager is a thread-safe map, mapping format.Fingerprint's to -// a Processor object. +// a Handler object. type FingerprintsManager struct { fpMap map[id.ID]map[format.Fingerprint]interfaces.MessageProcessor sync.Mutex @@ -30,7 +30,7 @@ func newFingerprints() *FingerprintsManager { } } -// Pop returns the associated processor to the fingerprint and removes +// Pop returns the associated handler to the fingerprint and removes // it from our list. // CRITICAL: it is never ok to process a fingerprint twice. This is a security // vulnerability. @@ -54,7 +54,7 @@ func (f *FingerprintsManager) pop(clientID *id.ID, } // AddFingerprint is a thread-safe setter for the Fingerprints -// map. AddFingerprint maps the given fingerprint key to the processor +// map. AddFingerprint maps the given fingerprint key to the handler // value. If there is already an entry for this fingerprint, the // method returns with no write operation. func (f *FingerprintsManager) AddFingerprint(clientID *id.ID, diff --git a/network/message/fingerprints_test.go b/network/message/fingerprints_test.go index d7d2c4017..5459c2657 100644 --- a/network/message/fingerprints_test.go +++ b/network/message/fingerprints_test.go @@ -36,7 +36,7 @@ func TestFingerprintsManager_pop(t *testing.T) { // Construct fingerprint map fpTracker := newFingerprints() - // Construct fingerprint and processor values + // Construct fingerprint and handler values cid := id.NewIdFromString("clientID", id.User, t) fp := format.NewFingerprint([]byte("test")) mp := NewMockMsgProcessor(t) @@ -68,7 +68,7 @@ func TestFingerprintsManager_AddFingerprint(t *testing.T) { // Construct fingerprint map fpTracker := newFingerprints() - // Construct fingerprint and processor values + // Construct fingerprint and handler values cid := id.NewIdFromString("clientID", id.User, t) fp := format.NewFingerprint([]byte("test")) mp := NewMockMsgProcessor(t) @@ -99,7 +99,7 @@ func TestFingerprintsManager_DeleteFingerprint(t *testing.T) { // Construct fingerprint map fpTracker := newFingerprints() - // Construct fingerprint and processor values + // Construct fingerprint and handler values cid := id.NewIdFromString("clientID", id.User, t) fp := format.NewFingerprint([]byte("test")) mp := NewMockMsgProcessor(t) @@ -146,7 +146,7 @@ func TestFingerprintsManager_DeleteClientFingerprints(t *testing.T) { fpTracker.DeleteClientFingerprints(cid) - // Make sure every fingerprint is mapped to it's expected processor + // Make sure every fingerprint is mapped to it's expected handler if _, exists := fpTracker.fpMap[*cid]; exists { t.Fatalf("RemoveFingerprints error: failed to delete client.") } @@ -170,7 +170,7 @@ func (mock *MockMsgProcessor) MarkFingerprintUsed(_ format.Fingerprint) { return } -func (mock *MockMsgProcessor) Process(format.Message, interfaces.Identity, +func (mock *MockMsgProcessor) Process(format.Message, interfaces.EphemeralIdentity, *mixmessages.RoundInfo) { return } diff --git a/network/message/handler.go b/network/message/handler.go index 182188a61..34f4ad91e 100644 --- a/network/message/handler.go +++ b/network/message/handler.go @@ -19,7 +19,7 @@ import ( "sync" ) -func (p *pickup) handleMessages(stop *stoppable.Single) { +func (p *handler) handleMessages(stop *stoppable.Single) { for { select { case <-stop.Quit(): @@ -60,12 +60,12 @@ func (p *pickup) handleMessages(stop *stoppable.Single) { } -func (p *pickup) handleMessage(ecrMsg format.Message, bundle Bundle) bool { +func (p *handler) handleMessage(ecrMsg format.Message, bundle Bundle) bool { fingerprint := ecrMsg.GetKeyFP() identity := bundle.Identity round := bundle.RoundInfo - var receptionID interfaces.Identity + var receptionID interfaces.EphemeralIdentity // If we have a fingerprint, process it. if proc, exists := p.pop(identity.Source, fingerprint); exists { diff --git a/network/message/inProgress.go b/network/message/inProgress.go index c4e730bb8..dd28e71e9 100644 --- a/network/message/inProgress.go +++ b/network/message/inProgress.go @@ -19,11 +19,11 @@ import ( // stored on disk) and the message decryption is retried here whenever triggered. // This can be triggered through the CheckInProgressMessages on the network -// pickup and is used in the /keyExchange package on successful rekey triggering. +// handler and is used in the /keyExchange package on successful rekey triggering. // CheckInProgressMessages triggers rechecking all in progress messages if the -// queue is not full Exposed on the network pickup. -func (p *pickup) CheckInProgressMessages() { +// queue is not full Exposed on the network handler. +func (p *handler) CheckInProgressMessages() { select { case p.checkInProgress <- struct{}{}: default: @@ -33,7 +33,7 @@ func (p *pickup) CheckInProgressMessages() { // recheckInProgressRunner is a long-running thread which processes messages // that need to be checked. -func (p *pickup) recheckInProgressRunner(stop *stoppable.Single) { +func (p *handler) recheckInProgressRunner(stop *stoppable.Single) { for { select { case <-stop.Quit(): @@ -47,7 +47,7 @@ func (p *pickup) recheckInProgressRunner(stop *stoppable.Single) { } // recheckInProgress is the handler for a single run of recheck messages. -func (p *pickup) recheckInProgress() { +func (p *handler) recheckInProgress() { // Try to decrypt every garbled message, excising those whose counts are too // high for grbldMsg, ri, identity, has := p.inProcess.Next(); has; grbldMsg, ri, identity, has = p.inProcess.Next() { diff --git a/network/message/inProgress_test.go b/network/message/inProgress_test.go index c07d22744..147c69729 100644 --- a/network/message/inProgress_test.go +++ b/network/message/inProgress_test.go @@ -39,12 +39,12 @@ func (l TestListener) Name() string { func Test_pickup_CheckInProgressMessages(t *testing.T) { kv := versioned.NewKV(make(ekv.Memstore)) - p := NewPickup(params.Network{Messages: params.Messages{ + p := NewHandler(params.Network{Messages: params.Messages{ MessageReceptionBuffLen: 20, MessageReceptionWorkerPoolSize: 20, MaxChecksInProcessMessage: 20, InProcessMessageWait: time.Hour, - }}, kv, nil).(*pickup) + }}, kv, nil).(*handler) msg := makeTestFormatMessages(1)[0] cid := id.NewIdFromString("clientID", id.User, t) @@ -56,7 +56,7 @@ func Test_pickup_CheckInProgressMessages(t *testing.T) { } p.inProcess.Add(msg, &pb.RoundInfo{ID: 1, Timestamps: []uint64{0, 1, 2, 3}}, - interfaces.Identity{Source: cid}) + interfaces.EphemeralIdentity{Source: cid}) stop := stoppable.NewSingle("stop") go p.recheckInProgressRunner(stop) diff --git a/network/message/meteredCmixMessageBuffer.go b/network/message/meteredCmixMessageBuffer.go index d8fdf9c6a..321bf0e94 100644 --- a/network/message/meteredCmixMessageBuffer.go +++ b/network/message/meteredCmixMessageBuffer.go @@ -139,7 +139,7 @@ func NewOrLoadMeteredCmixMessageBuffer(kv *versioned.KV, key string) ( } func (mcmb *MeteredCmixMessageBuffer) Add(m format.Message, ri *pb.RoundInfo, - identity interfaces.Identity) (uint, time.Time) { + identity interfaces.EphemeralIdentity) (uint, time.Time) { if m.GetPrimeByteLen() == 0 { jww.FATAL.Panicf("Cannot handle a metered " + "cmix message with a length of 0") @@ -153,7 +153,7 @@ func (mcmb *MeteredCmixMessageBuffer) Add(m format.Message, ri *pb.RoundInfo, } func (mcmb *MeteredCmixMessageBuffer) AddProcessing(m format.Message, - ri *pb.RoundInfo, identity interfaces.Identity) (uint, time.Time) { + ri *pb.RoundInfo, identity interfaces.EphemeralIdentity) (uint, time.Time) { if m.GetPrimeByteLen() == 0 { jww.FATAL.Panicf("Cannot handle a metered " + "cmix message with a length of 0") @@ -167,10 +167,10 @@ func (mcmb *MeteredCmixMessageBuffer) AddProcessing(m format.Message, } func (mcmb *MeteredCmixMessageBuffer) Next() (format.Message, *pb.RoundInfo, - interfaces.Identity, bool) { + interfaces.EphemeralIdentity, bool) { m, ok := mcmb.mb.Next() if !ok { - return format.Message{}, nil, interfaces.Identity{}, false + return format.Message{}, nil, interfaces.EphemeralIdentity{}, false } msg := m.(meteredCmixMessage) @@ -197,7 +197,7 @@ func (mcmb *MeteredCmixMessageBuffer) Next() (format.Message, *pb.RoundInfo, jww.FATAL.Panicf("Failed to unmarshal round info from msg format") } - identity := interfaces.Identity{} + identity := interfaces.EphemeralIdentity{} err = json.Unmarshal(msg.Identity, &identity) if err != nil { jww.FATAL.Panicf("Failed to unmarshal identity from msg format") @@ -207,16 +207,16 @@ func (mcmb *MeteredCmixMessageBuffer) Next() (format.Message, *pb.RoundInfo, } func (mcmb *MeteredCmixMessageBuffer) Remove(m format.Message, ri *pb.RoundInfo, - identity interfaces.Identity) { + identity interfaces.EphemeralIdentity) { mcmb.mb.Succeeded(buildMsg(m, ri, identity)) } func (mcmb *MeteredCmixMessageBuffer) Failed(m format.Message, ri *pb.RoundInfo, - identity interfaces.Identity) { + identity interfaces.EphemeralIdentity) { mcmb.mb.Failed(buildMsg(m, ri, identity)) } -func buildMsg(m format.Message, ri *pb.RoundInfo, identity interfaces.Identity) meteredCmixMessage { +func buildMsg(m format.Message, ri *pb.RoundInfo, identity interfaces.EphemeralIdentity) meteredCmixMessage { if m.GetPrimeByteLen() == 0 { jww.FATAL.Panicf("Cannot handle a metered " + "cmix message with a length of 0") diff --git a/network/message/meteredCmixMessageBuffer_test.go b/network/message/meteredCmixMessageBuffer_test.go index 6e5b6046b..beeab4e9e 100644 --- a/network/message/meteredCmixMessageBuffer_test.go +++ b/network/message/meteredCmixMessageBuffer_test.go @@ -137,10 +137,10 @@ func Test_meteredCmixMessageHandler_Smoke(t *testing.T) { // AddFingerprint two messages mcmb.Add(testMsgs[0], &pb.RoundInfo{ID: 1, Timestamps: []uint64{0, 1, 2, 3}}, - interfaces.Identity{Source: id.NewIdFromString("user1", id.User, t)}) + interfaces.EphemeralIdentity{Source: id.NewIdFromString("user1", id.User, t)}) mcmb.Add(testMsgs[1], &pb.RoundInfo{ID: 2, Timestamps: []uint64{0, 1, 2, 3}}, - interfaces.Identity{Source: id.NewIdFromString("user2", id.User, t)}) + interfaces.EphemeralIdentity{Source: id.NewIdFromString("user2", id.User, t)}) msg, ri, identity, exists := mcmb.Next() if !exists { diff --git a/network/message/pickup.go b/network/message/pickup.go index 4e9195973..ce2e5b77c 100644 --- a/network/message/pickup.go +++ b/network/message/pickup.go @@ -24,7 +24,7 @@ const ( inProcessKey = "InProcessMessagesKey" ) -type Pickup interface { +type Handler interface { GetMessageReceptionChannel() chan<- Bundle StartProcesses() stoppable.Stoppable CheckInProgressMessages() @@ -40,7 +40,7 @@ type Pickup interface { DeleteClientTriggers(clientID *id.ID) } -type pickup struct { +type handler struct { param params.Network blacklistedNodes map[string]interface{} @@ -55,14 +55,14 @@ type pickup struct { TriggersManager } -func NewPickup(param params.Network, kv *versioned.KV, events interfaces.EventManager) Pickup { +func NewHandler(param params.Network, kv *versioned.KV, events interfaces.EventManager) Handler { garbled, err := NewOrLoadMeteredCmixMessageBuffer(kv, inProcessKey) if err != nil { jww.FATAL.Panicf("Failed to load or new the Garbled Messages system: %v", err) } - m := pickup{ + m := handler{ param: param, messageReception: make(chan Bundle, param.MessageReceptionBuffLen), checkInProgress: make(chan struct{}, 100), @@ -85,12 +85,12 @@ func NewPickup(param params.Network, kv *versioned.KV, events interfaces.EventMa } // GetMessageReceptionChannel gets the channel to send received messages on. -func (p *pickup) GetMessageReceptionChannel() chan<- Bundle { +func (p *handler) GetMessageReceptionChannel() chan<- Bundle { return p.messageReception } // StartProcesses starts all worker pool. -func (p *pickup) StartProcesses() stoppable.Stoppable { +func (p *handler) StartProcesses() stoppable.Stoppable { multi := stoppable.NewMulti("MessageReception") // Create the message handler workers diff --git a/network/nodes/register.go b/network/nodes/register.go index f8b39356d..3b3d0abb9 100644 --- a/network/nodes/register.go +++ b/network/nodes/register.go @@ -61,7 +61,7 @@ func registerNodes(r *registrar, stop *stoppable.Single, inProgress, attempts *s continue } - if r.Has(nid) { + if r.HasNode(nid) { jww.INFO.Printf( "Not registering node %s, already registered", nid) } @@ -116,7 +116,7 @@ func registerWithNode(sender *gateway.Sender, comms RegisterNodeCommsInterface, return err } - if r.Has(nodeID) { + if r.HasNode(nodeID) { return nil } diff --git a/network/nodes/registrar.go b/network/nodes/registrar.go index 1fa12a260..4cb6ba4af 100644 --- a/network/nodes/registrar.go +++ b/network/nodes/registrar.go @@ -31,12 +31,12 @@ var delayTable = [5]time.Duration{ type Registrar interface { StartProcesses(numParallel uint) stoppable.Stoppable - Has(nid *id.ID) bool - Remove(nid *id.ID) - GetKeys(topology *connect.Circuit) (MixCypher, error) - NumRegistered() int + HasNode(nid *id.ID) bool + RemoveNode(nid *id.ID) + GetNodeKeys(topology *connect.Circuit) (MixCypher, error) + NumRegisteredNodes() int GetInputChannel() chan<- network.NodeGateway - TriggerRegistration(nid *id.ID) + TriggerNodeRegistration(nid *id.ID) } type RegisterNodeCommsInterface interface { @@ -49,8 +49,8 @@ type registrar struct { kv *versioned.KV mux sync.RWMutex - session *storage.Session - sender *gateway.Sender + session storage.Session + sender gateway.Sender comms RegisterNodeCommsInterface rng *fastRNG.StreamGenerator @@ -59,8 +59,8 @@ type registrar struct { // LoadRegistrar loads a registrar from disk, and creates a new one if it does // not exist. -func LoadRegistrar(session *storage.Session, - sender *gateway.Sender, comms RegisterNodeCommsInterface, +func LoadRegistrar(session storage.Session, + sender gateway.Sender, comms RegisterNodeCommsInterface, rngGen *fastRNG.StreamGenerator, c chan network.NodeGateway) (Registrar, error) { kv := session.GetKV().Prefix(prefix) r := ®istrar{ @@ -114,7 +114,7 @@ func (r *registrar) StartProcesses(numParallel uint) stoppable.Stoppable { func (r *registrar) GetInputChannel() chan<- network.NodeGateway { return r.c } -func (r *registrar) TriggerRegistration(nid *id.ID) { +func (r *registrar) TriggerNodeRegistration(nid *id.ID) { r.c <- network.NodeGateway{ Node: ndf.Node{ID: nid.Marshal(), //status must be active because it is in a round @@ -124,7 +124,7 @@ func (r *registrar) TriggerRegistration(nid *id.ID) { // GetKeys returns a MixCypher for the topology and a list of nodes it did // not have a key for. If there are missing keys, then returns nil MixCypher. -func (r *registrar) GetKeys(topology *connect.Circuit) (MixCypher, error) { +func (r *registrar) GetNodeKeys(topology *connect.Circuit) (MixCypher, error) { r.mux.RLock() defer r.mux.RUnlock() @@ -158,19 +158,19 @@ func (r *registrar) GetKeys(topology *connect.Circuit) (MixCypher, error) { } // Has returns if the store has the nodes. -func (r *registrar) Has(nid *id.ID) bool { +func (r *registrar) HasNode(nid *id.ID) bool { r.mux.RLock() _, exists := r.nodes[*nid] r.mux.RUnlock() return exists } -func (r *registrar) Remove(nid *id.ID) { +func (r *registrar) RemoveNode(nid *id.ID) { r.remove(nid) } // NumRegistered returns the number of registered nodes. -func (r *registrar) NumRegistered() int { +func (r *registrar) NumRegisteredNodes() int { r.mux.RLock() defer r.mux.RUnlock() return len(r.nodes) diff --git a/network/nodes/registrar_test.go b/network/nodes/registrar_test.go index 645bbcc49..85449f529 100644 --- a/network/nodes/registrar_test.go +++ b/network/nodes/registrar_test.go @@ -77,7 +77,7 @@ func TestLoadRegistrar_Load(t *testing.T) { } circuit := connect.NewCircuit([]*id.ID{nodeId}) - keys, _ := r.GetKeys(circuit) + keys, _ := r.GetNodeKeys(circuit) if keys.(*mixCypher).keys[0].validUntil != expectedValid { t.Errorf("Unexpected valid until value loaded from store."+ "\n\tExpected: %v\n\tReceived: %v", expectedValid, keys.(*mixCypher).keys[0].validUntil) @@ -105,9 +105,9 @@ func Test_registrar_GetKeys(t *testing.T) { } circuit := connect.NewCircuit(nodeIds) - result, err := r.GetKeys(circuit) + result, err := r.GetNodeKeys(circuit) if err != nil { - t.Errorf("GetKeys returrned an error: %+v", err) + t.Errorf("GetNodeKeys returrned an error: %+v", err) } if result == nil || len(result.(*mixCypher).keys) != numIds { t.Errorf("Expected to have %d nodes keys", numIds) @@ -136,9 +136,9 @@ func Test_registrar_GetKeys_Missing(t *testing.T) { } circuit := connect.NewCircuit(nodeIds) - result, err := r.GetKeys(circuit) + result, err := r.GetNodeKeys(circuit) if err == nil { - t.Error("GetKeys did not return an error when keys should be missing.") + t.Error("GetNodeKeys did not return an error when keys should be missing.") } if result != nil { t.Errorf("Expected nil value for result due to missing keys!") @@ -157,7 +157,7 @@ func Test_registrar_Has(t *testing.T) { t.Fatal("Failed to add node's key.") } - if !r.Has(nodeId) { + if !r.HasNode(nodeId) { t.Fatal("Cannot find the node's ID that that was added.") } } @@ -168,7 +168,7 @@ func Test_registrar_Has_Not(t *testing.T) { nodeId := id.NewIdFromString("test", id.Node, t) - if r.Has(nodeId) { + if r.HasNode(nodeId) { t.Fatal("Found the node when it should not have been found.") } } @@ -177,9 +177,9 @@ func Test_registrar_NumRegistered(t *testing.T) { r := makeTestRegistrar(t) grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(2)) - if r.NumRegistered() != 0 { - t.Errorf("Unexpected NumRegistered for a new Registrar."+ - "\nexpected: %d\nreceived: %d", 0, r.NumRegistered()) + if r.NumRegisteredNodes() != 0 { + t.Errorf("Unexpected NumRegisteredNodes for a new Registrar."+ + "\nexpected: %d\nreceived: %d", 0, r.NumRegisteredNodes()) } count := 50 @@ -188,8 +188,8 @@ func Test_registrar_NumRegistered(t *testing.T) { grp.NewInt(int64(42+i)), 0, nil) } - if r.NumRegistered() != count { - t.Errorf("Unexpected NumRegistered."+ - "\nexpected: %d\nreceived: %d", count, r.NumRegistered()) + if r.NumRegisteredNodes() != count { + t.Errorf("Unexpected NumRegisteredNodes."+ + "\nexpected: %d\nreceived: %d", count, r.NumRegisteredNodes()) } } diff --git a/network/rounds/remoteFilters.go b/network/remoteFilters.go similarity index 98% rename from network/rounds/remoteFilters.go rename to network/remoteFilters.go index 6753c4110..0c88e42aa 100644 --- a/network/rounds/remoteFilters.go +++ b/network/remoteFilters.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package rounds +package network import ( jww "github.com/spf13/jwalterweatherman" diff --git a/network/rounds/remoteFilters_test.go b/network/remoteFilters_test.go similarity index 99% rename from network/rounds/remoteFilters_test.go rename to network/remoteFilters_test.go index e49043285..d9f410adb 100644 --- a/network/rounds/remoteFilters_test.go +++ b/network/remoteFilters_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package rounds +package network import ( jww "github.com/spf13/jwalterweatherman" diff --git a/network/rounds/check.go b/network/rounds/check.go deleted file mode 100644 index c88794697..000000000 --- a/network/rounds/check.go +++ /dev/null @@ -1,98 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package rounds - -import ( - "encoding/binary" - jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/network/identity/receptionID" - "gitlab.com/elixxir/client/storage/rounds" - "gitlab.com/xx_network/primitives/id" -) - -// the round checker is a single use function which is meant to be wrapped -// and adhere to the knownRounds checker interface. it receives a round ID and -// looks up the state of that round to determine if the client has a message -// waiting in it. -// It will return true if it can conclusively determine no message exists, -// returning false and set the round to processing if it needs further -// investigation. -// Once it determines messages might be waiting in a round, it determines -// if the information about that round is already present, if it is the data is -// sent to Message Retrieval Workers, otherwise it is sent to Historical Round -// Retrieval -// false: no message -// true: message -func Checker(roundID id.Round, filters []*RemoteFilter, cr *rounds.CheckedRounds) bool { - // Skip checking if the round is already checked - if cr.IsChecked(roundID) { - return true - } - - //find filters that could have the round and check them - serialRid := serializeRound(roundID) - for _, filter := range filters { - if filter != nil && filter.FirstRound() <= roundID && - filter.LastRound() >= roundID { - if filter.GetFilter().Test(serialRid) { - return true - } - } - } - return false -} - -func serializeRound(roundId id.Round) []byte { - b := make([]byte, 8) - binary.LittleEndian.PutUint64(b, uint64(roundId)) - return b -} - -func (m *Manager) GetMessagesFromRound(roundID id.Round, identity receptionID.IdentityUse) { - ri, err := m.Instance.GetRound(roundID) - if err != nil || m.params.ForceHistoricalRounds { - if m.params.ForceHistoricalRounds { - jww.WARN.Printf("Forcing use of historical rounds for round ID %d.", - roundID) - } - jww.INFO.Printf("Messages found in round %d for %d (%s), looking "+ - "up messages via historical lookup", roundID, identity.EphId.Int64(), - identity.Source) - //store the round as an unretreived round - err = m.Session.UncheckedRounds().AddRound(roundID, nil, - identity.Source, identity.EphId) - if err != nil { - jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID) - } - // If we didn't find it, send to Historical Rounds Retrieval - m.historicalRounds <- historicalRoundRequest{ - rid: roundID, - identity: identity, - numAttempts: 0, - } - } else { - jww.INFO.Printf("Messages found in round %d for %d (%s), looking "+ - "up messages via in ram lookup", roundID, identity.EphId.Int64(), - identity.Source) - //store the round as an unretreived round - if !m.params.RealtimeOnly { - err = m.Session.UncheckedRounds().AddRound(roundID, ri, - identity.Source, identity.EphId) - if err != nil { - jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID) - } - } - - // If found, send to Message Retrieval Workers - m.lookupRoundMessages <- roundLookup{ - roundInfo: ri, - identity: identity, - } - } - -} diff --git a/network/rounds/get.go b/network/rounds/get.go new file mode 100644 index 000000000..07bef0fdc --- /dev/null +++ b/network/rounds/get.go @@ -0,0 +1,71 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package rounds + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/interfaces" + pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/xx_network/primitives/id" +) + +func (m *manager) GetMessagesFromRound(roundID id.Round, identity interfaces.EphemeralIdentity) { + //get the round from the in ram store + ri, err := m.instance.GetRound(roundID) + + // If we didn't find it, send to Historical Rounds Retrieval + if err != nil || m.params.ForceHistoricalRounds { + + // store the round as an unretreived round without a round info + // This will silently do nothing if the round is + err = m.unchecked.AddRound(roundID, nil, + identity.Source, identity.EphId) + if err != nil { + jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID) + } + + if m.params.ForceHistoricalRounds { + jww.WARN.Printf("Forcing use of historical rounds for round ID %d.", + roundID) + } + + jww.INFO.Printf("Messages found in round %d for %d (%s), looking "+ + "up messages via historical lookup", roundID, identity.EphId.Int64(), + identity.Source) + + err = m.historical.LookupHistoricalRound(roundID, func(info *pb.RoundInfo, success bool) { + if !success { + + } + // If found, send to Message Retrieval Workers + m.lookupRoundMessages <- roundLookup{ + RoundInfo: info, + Identity: identity, + } + }) + } else { // if we did find it, send it to the round pickup thread + jww.INFO.Printf("Messages found in round %d for %d (%s), looking "+ + "up messages via in ram lookup", roundID, identity.EphId.Int64(), + identity.Source) + //store the round as an unretreived round + if !m.params.RealtimeOnly { + err = m.unchecked.AddRound(roundID, ri, + identity.Source, identity.EphId) + if err != nil { + jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID) + } + } + + // If found, send to Message Retrieval Workers + m.lookupRoundMessages <- roundLookup{ + RoundInfo: ri, + Identity: identity, + } + } + +} diff --git a/network/rounds/manager.go b/network/rounds/manager.go index 43d1f5f5a..bc0315bcd 100644 --- a/network/rounds/manager.go +++ b/network/rounds/manager.go @@ -8,52 +8,72 @@ package rounds import ( + "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/interfaces/params" "gitlab.com/elixxir/client/network/gateway" - "gitlab.com/elixxir/client/network/internal" + "gitlab.com/elixxir/client/network/historical" "gitlab.com/elixxir/client/network/message" + "gitlab.com/elixxir/client/network/rounds/store" "gitlab.com/elixxir/client/stoppable" + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/xx_network/primitives/id" "strconv" ) -type Manager struct { - params params.Rounds - internal.Internal - sender *gateway.Sender +type Pickup interface { + StartProcessors() stoppable.Stoppable + GetMessagesFromRound(roundID id.Round, identity interfaces.EphemeralIdentity) +} + +type manager struct { + params params.Rounds + sender gateway.Sender + session *storage.Session + + comms MessageRetrievalComms + + historical historical.Retriever + + rng *fastRNG.StreamGenerator + + instance RoundGetter - historicalRounds chan historicalRoundRequest lookupRoundMessages chan roundLookup messageBundles chan<- message.Bundle -} -func NewManager(internal internal.Internal, params params.Rounds, - bundles chan<- message.Bundle, sender *gateway.Sender) *Manager { - m := &Manager{ - params: params, + unchecked *store.UncheckedRoundStore +} - historicalRounds: make(chan historicalRoundRequest, params.HistoricalRoundsBufferLen), +func NewPickup(params params.Rounds, bundles chan<- message.Bundle, + sender gateway.Sender, historical historical.Retriever, rng *fastRNG.StreamGenerator, + instance RoundGetter, kv *versioned.KV) Pickup { + unchecked := store.NewOrLoadUncheckedStore(kv) + m := &manager{ + params: params, lookupRoundMessages: make(chan roundLookup, params.LookupRoundsBufferLen), messageBundles: bundles, sender: sender, + historical: historical, + rng: rng, + instance: instance, + unchecked: unchecked, } - m.Internal = internal return m } -func (m *Manager) StartProcessors() stoppable.Stoppable { +func (m *manager) StartProcessors() stoppable.Stoppable { multi := stoppable.NewMulti("Rounds") //start the historical rounds thread - historicalRoundsStopper := stoppable.NewSingle("ProcessHistoricalRounds") - go m.processHistoricalRounds(m.Comms, historicalRoundsStopper) - multi.Add(historicalRoundsStopper) //start the message retrieval worker pool for i := uint(0); i < m.params.NumMessageRetrievalWorkers; i++ { stopper := stoppable.NewSingle("Message Retriever " + strconv.Itoa(int(i))) - go m.processMessageRetrieval(m.Comms, stopper) + go m.processMessageRetrieval(m.comms, stopper) multi.Add(stopper) } diff --git a/network/rounds/retrieve.go b/network/rounds/retrieve.go index f5436bcde..d4f5b63aa 100644 --- a/network/rounds/retrieve.go +++ b/network/rounds/retrieve.go @@ -11,8 +11,8 @@ import ( "encoding/binary" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/network/gateway" - "gitlab.com/elixxir/client/network/identity/receptionID" "gitlab.com/elixxir/client/network/message" "gitlab.com/elixxir/client/stoppable" pb "gitlab.com/elixxir/comms/mixmessages" @@ -23,22 +23,22 @@ import ( "time" ) -type messageRetrievalComms interface { +type MessageRetrievalComms interface { GetHost(hostId *id.ID) (*connect.Host, bool) RequestMessages(host *connect.Host, message *pb.GetMessages) (*pb.GetMessagesResponse, error) } type roundLookup struct { - roundInfo *pb.RoundInfo - identity receptionID.IdentityUse + RoundInfo *pb.RoundInfo + Identity interfaces.EphemeralIdentity } const noRoundError = "does not have round %d" // processMessageRetrieval received a roundLookup request and pings the gateways -// of that round for messages for the requested identity in the roundLookup -func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, +// of that round for messages for the requested Identity in the roundLookup +func (m *manager) processMessageRetrieval(comms MessageRetrievalComms, stop *stoppable.Single) { for { @@ -47,11 +47,11 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, stop.ToStopped() return case rl := <-m.lookupRoundMessages: - ri := rl.roundInfo + ri := rl.RoundInfo jww.DEBUG.Printf("Checking for messages in round %d", ri.ID) if !m.params.RealtimeOnly { - err := m.Session.UncheckedRounds().AddRound(id.Round(ri.ID), ri, - rl.identity.Source, rl.identity.EphId) + err := m.unchecked.AddRound(id.Round(ri.ID), ri, + rl.Identity.Source, rl.Identity.EphId) if err != nil { jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", id.Round(ri.ID)) } @@ -74,7 +74,7 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, // Target the last nodes in the team first because it has // messages first, randomize other members of the team var rndBytes [32]byte - stream := m.Rng.GetStream() + stream := m.rng.GetStream() _, err := stream.Read(rndBytes[:]) stream.Close() if err != nil { @@ -106,7 +106,7 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, } } else { // Attempt to request for this gateway - bundle, err = m.getMessagesFromGateway(id.Round(ri.ID), rl.identity, comms, gwIds, stop) + bundle, err = m.getMessagesFromGateway(id.Round(ri.ID), rl.Identity, comms, gwIds, stop) // Exit if the thread has been stopped if stoppable.CheckErr(err) { @@ -126,13 +126,16 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, if len(bundle.Messages) != 0 { // If successful and there are messages, we send them to another thread - bundle.Identity = rl.identity - bundle.RoundInfo = rl.roundInfo + bundle.Identity = interfaces.EphemeralIdentity{ + EphId: rl.Identity.EphId, + Source: rl.Identity.Source, + } + bundle.RoundInfo = rl.RoundInfo m.messageBundles <- bundle jww.DEBUG.Printf("Removing round %d from unchecked store", ri.ID) if !m.params.RealtimeOnly { - err = m.Session.UncheckedRounds().Remove(id.Round(ri.ID), rl.identity.Source, rl.identity.EphId) + err = m.unchecked.Remove(id.Round(ri.ID), rl.Identity.Source, rl.Identity.EphId) if err != nil { jww.ERROR.Printf("Could not remove round %d "+ "from unchecked rounds store: %v", ri.ID, err) @@ -147,8 +150,8 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, // getMessagesFromGateway attempts to get messages from their assigned // gateway host in the round specified. If successful -func (m *Manager) getMessagesFromGateway(roundID id.Round, - identity receptionID.IdentityUse, comms messageRetrievalComms, gwIds []*id.ID, +func (m *manager) getMessagesFromGateway(roundID id.Round, + identity interfaces.EphemeralIdentity, comms MessageRetrievalComms, gwIds []*id.ID, stop *stoppable.Single) (message.Bundle, error) { start := time.Now() // Send to the gateways using backup proxies @@ -193,9 +196,9 @@ func (m *Manager) getMessagesFromGateway(roundID id.Round, jww.WARN.Printf("no messages for client %s "+ " in round %d. This happening every once in a while is normal,"+ " but can be indicative of a problem if it is consistent", - m.TransmissionID, roundID) + identity.Source, roundID) if m.params.RealtimeOnly { - err = m.Session.UncheckedRounds().Remove(roundID, identity.Source, identity.EphId) + err = m.unchecked.Remove(roundID, identity.Source, identity.EphId) if err != nil { jww.ERROR.Printf("Failed to remove round %d: %+v", roundID, err) } @@ -217,7 +220,7 @@ func (m *Manager) getMessagesFromGateway(roundID id.Round, } for i, slot := range msgs { - msg := format.NewMessage(m.Session.Cmix().GetGroup().GetP().ByteLen()) + msg := format.NewMessage(m.session.GetCmixGroup().GetP().ByteLen()) msg.SetPayloadA(slot.PayloadA) msg.SetPayloadB(slot.PayloadB) bundle.Messages[i] = msg @@ -229,13 +232,13 @@ func (m *Manager) getMessagesFromGateway(roundID id.Round, // Helper function which forces processUncheckedRounds by randomly // not looking up messages -func (m *Manager) forceMessagePickupRetry(ri *pb.RoundInfo, rl roundLookup, - comms messageRetrievalComms, gwIds []*id.ID, +func (m *manager) forceMessagePickupRetry(ri *pb.RoundInfo, rl roundLookup, + comms MessageRetrievalComms, gwIds []*id.ID, stop *stoppable.Single) (bundle message.Bundle, err error) { - rnd, _ := m.Session.UncheckedRounds().GetRound(id.Round(ri.ID), rl.identity.Source, rl.identity.EphId) + rnd, _ := m.unchecked.GetRound(id.Round(ri.ID), rl.Identity.Source, rl.Identity.EphId) if rnd.NumChecks == 0 { // Flip a coin to determine whether to pick up message - stream := m.Rng.GetStream() + stream := m.rng.GetStream() defer stream.Close() b := make([]byte, 8) _, err = stream.Read(b) @@ -253,5 +256,5 @@ func (m *Manager) forceMessagePickupRetry(ri *pb.RoundInfo, rl roundLookup, } // Attempt to request for this gateway - return m.getMessagesFromGateway(id.Round(ri.ID), rl.identity, comms, gwIds, stop) + return m.getMessagesFromGateway(id.Round(ri.ID), rl.Identity, comms, gwIds, stop) } diff --git a/network/rounds/retrieve_test.go b/network/rounds/retrieve_test.go index c6498f508..1fb3a959b 100644 --- a/network/rounds/retrieve_test.go +++ b/network/rounds/retrieve_test.go @@ -11,6 +11,7 @@ import ( "gitlab.com/elixxir/client/network/gateway" ephemeral2 "gitlab.com/elixxir/client/network/identity/receptionID" "gitlab.com/elixxir/client/network/message" + "gitlab.com/elixxir/client/network/pickup" "gitlab.com/elixxir/client/stoppable" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/crypto/fastRNG" @@ -79,7 +80,7 @@ func TestManager_ProcessMessageRetrieval(t *testing.T) { } // Send a round look up request - testManager.lookupRoundMessages <- roundLookup{ + testManager.lookupRoundMessages <- pickup.roundLookup{ roundInfo: roundInfo, identity: iu, } @@ -172,7 +173,7 @@ func TestManager_ProcessMessageRetrieval_NoRound(t *testing.T) { } // Send a round look up request - testManager.lookupRoundMessages <- roundLookup{ + testManager.lookupRoundMessages <- pickup.roundLookup{ roundInfo: roundInfo, identity: iu, } @@ -253,7 +254,7 @@ func TestManager_ProcessMessageRetrieval_FalsePositive(t *testing.T) { } // Send a round look up request - testManager.lookupRoundMessages <- roundLookup{ + testManager.lookupRoundMessages <- pickup.roundLookup{ roundInfo: roundInfo, identity: iu, } @@ -330,7 +331,7 @@ func TestManager_ProcessMessageRetrieval_Quit(t *testing.T) { } // Send a round look up request - testManager.lookupRoundMessages <- roundLookup{ + testManager.lookupRoundMessages <- pickup.roundLookup{ roundInfo: roundInfo, identity: iu, } @@ -407,7 +408,7 @@ func TestManager_ProcessMessageRetrieval_MultipleGateways(t *testing.T) { } // Send a round look up request - testManager.lookupRoundMessages <- roundLookup{ + testManager.lookupRoundMessages <- pickup.roundLookup{ roundInfo: roundInfo, identity: iu, } diff --git a/network/rounds/roundGetter.go b/network/rounds/roundGetter.go new file mode 100644 index 000000000..a319562a3 --- /dev/null +++ b/network/rounds/roundGetter.go @@ -0,0 +1,10 @@ +package rounds + +import ( + pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/xx_network/primitives/id" +) + +type RoundGetter interface { + GetRound(id id.Round) (*pb.RoundInfo, error) +} diff --git a/storage/rounds/roundIdentity.go b/network/rounds/store/roundIdentity.go similarity index 98% rename from storage/rounds/roundIdentity.go rename to network/rounds/store/roundIdentity.go index 04b083574..b63fac99a 100644 --- a/storage/rounds/roundIdentity.go +++ b/network/rounds/store/roundIdentity.go @@ -1,4 +1,4 @@ -package rounds +package store import ( "encoding/base64" diff --git a/storage/rounds/uncheckedRounds.go b/network/rounds/store/store.go similarity index 57% rename from storage/rounds/uncheckedRounds.go rename to network/rounds/store/store.go index 5d951fc0d..b3d8a8983 100644 --- a/storage/rounds/uncheckedRounds.go +++ b/network/rounds/store/store.go @@ -1,16 +1,8 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package rounds +package store import ( "bytes" "encoding/binary" - "github.com/golang/protobuf/proto" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage/versioned" @@ -20,119 +12,8 @@ import ( "gitlab.com/xx_network/primitives/netTime" "sync" "testing" - "time" -) - -const ( - uncheckedRoundVersion = 0 - roundInfoVersion = 0 - uncheckedRoundPrefix = "uncheckedRoundPrefix" - roundKeyPrefix = "roundInfo:" - - // Key to store rounds - uncheckedRoundKey = "uncheckRounds" - - // Housekeeping constant (used for serializing uint64 ie id.Round) - uint64Size = 8 - - // Maximum checks that can be performed on a round. Intended so that a round - // is checked no more than 1 week approximately (network/rounds.cappedTries + 7) - maxChecks = 14 ) -// Identity contains round identity information used in message retrieval. -// Derived from reception.Identity saving data needed for message retrieval. -type Identity struct { - EpdId ephemeral.Id - Source *id.ID -} - -// UncheckedRound contains rounds that failed on message retrieval. These rounds -// are stored for retry of message retrieval. -type UncheckedRound struct { - Info *pb.RoundInfo - Id id.Round - - Identity - // Timestamp in which round has last been checked - LastCheck time.Time - // Number of times a round has been checked - NumChecks uint64 -} - -// marshal serializes UncheckedRound r into a byte slice. -func (r UncheckedRound) marshal(kv *versioned.KV) ([]byte, error) { - buf := bytes.NewBuffer(nil) - // Store teh round info - if r.Info != nil { - if err := storeRoundInfo(kv, r.Info, r.Source, r.EpdId); err != nil { - return nil, errors.WithMessagef(err, - "failed to marshal unchecked rounds") - } - } - - // Marshal the round ID - b := make([]byte, uint64Size) - binary.LittleEndian.PutUint64(b, uint64(r.Id)) - buf.Write(b) - - // Write the round identity info - buf.Write(r.Identity.EpdId[:]) - if r.Source != nil { - buf.Write(r.Identity.Source.Marshal()) - } else { - buf.Write(make([]byte, id.ArrIDLen)) - } - - // Write the time stamp bytes - tsBytes, err := r.LastCheck.MarshalBinary() - if err != nil { - return nil, errors.WithMessage(err, "Could not marshal timestamp ") - } - b = make([]byte, uint64Size) - binary.LittleEndian.PutUint64(b, uint64(len(tsBytes))) - buf.Write(b) - buf.Write(tsBytes) - - // Write the number of tries for this round - b = make([]byte, uint64Size) - binary.LittleEndian.PutUint64(b, r.NumChecks) - buf.Write(b) - - return buf.Bytes(), nil -} - -// unmarshal deserializes round data from buff into UncheckedRound r. -func (r *UncheckedRound) unmarshal(kv *versioned.KV, buff *bytes.Buffer) error { - // Deserialize the roundInfo - r.Id = id.Round(binary.LittleEndian.Uint64(buff.Next(uint64Size))) - - // Deserialize the round identity information - copy(r.EpdId[:], buff.Next(uint64Size)) - - sourceId, err := id.Unmarshal(buff.Next(id.ArrIDLen)) - if err != nil { - return errors.WithMessagef(err, - "Failed to unmarshal round identity.source of %d", r.Id) - } - - r.Source = sourceId - - // Deserialize the timestamp bytes - timestampLen := binary.LittleEndian.Uint64(buff.Next(uint64Size)) - tsByes := buff.Next(int(timestampLen)) - if err = r.LastCheck.UnmarshalBinary(tsByes); err != nil { - return errors.WithMessagef(err, - "Failed to unmarshal round timestamp of %d", r.Id) - } - - r.NumChecks = binary.LittleEndian.Uint64(buff.Next(uint64Size)) - - r.Info, _ = loadRoundInfo(kv, r.Id, r.Source, r.EpdId) - - return nil -} - // UncheckedRoundStore stores rounds to retry for message retrieval. type UncheckedRoundStore struct { list map[roundIdentity]UncheckedRound @@ -152,6 +33,27 @@ func NewUncheckedStore(kv *versioned.KV) (*UncheckedRoundStore, error) { return urs, urs.save() } +// NewUncheckedStore is a constructor for a UncheckedRoundStore. +func NewOrLoadUncheckedStore(kv *versioned.KV) *UncheckedRoundStore { + kv = kv.Prefix(uncheckedRoundPrefix) + + urs, err := LoadUncheckedStore(kv) + if err != nil { + return urs + } + + urs = &UncheckedRoundStore{ + list: make(map[roundIdentity]UncheckedRound, 0), + kv: kv, + } + + if err = urs.save(); err != nil { + jww.FATAL.Panicf("failed to save a new unchecked round store") + } + + return urs +} + // LoadUncheckedStore loads a deserializes a UncheckedRoundStore from memory. func LoadUncheckedStore(kv *versioned.KV) (*UncheckedRoundStore, error) { @@ -183,7 +85,7 @@ func (s *UncheckedRoundStore) AddRound(rid id.Round, ri *pb.RoundInfo, stored, exists := s.list[roundId] - if !exists || stored.Info == nil { + if !exists || (stored.Info == nil && ri != nil) { newUncheckedRound := UncheckedRound{ Id: rid, Info: ri, @@ -192,7 +94,7 @@ func (s *UncheckedRoundStore) AddRound(rid id.Round, ri *pb.RoundInfo, Source: source, }, LastCheck: netTime.Now(), - NumChecks: 0, + NumChecks: stored.NumChecks, } s.list[roundId] = newUncheckedRound @@ -369,48 +271,3 @@ func (s *UncheckedRoundStore) unmarshal(data []byte) error { return nil } - -func storeRoundInfo(kv *versioned.KV, info *pb.RoundInfo, recipient *id.ID, - ephID ephemeral.Id) error { - now := netTime.Now() - - data, err := proto.Marshal(info) - if err != nil { - return errors.WithMessagef(err, - "Failed to store individual unchecked round") - } - - obj := versioned.Object{ - Version: roundInfoVersion, - Timestamp: now, - Data: data, - } - - return kv.Set( - roundKey(id.Round(info.ID), recipient, ephID), roundInfoVersion, &obj) -} - -func loadRoundInfo(kv *versioned.KV, id id.Round, recipient *id.ID, - ephID ephemeral.Id) (*pb.RoundInfo, error) { - - vo, err := kv.Get(roundKey(id, recipient, ephID), roundInfoVersion) - if err != nil { - return nil, err - } - - ri := &pb.RoundInfo{} - if err = proto.Unmarshal(vo.Data, ri); err != nil { - return nil, errors.WithMessagef(err, "Failed to unmarshal roundInfo") - } - - return ri, nil -} - -func deleteRoundInfo(kv *versioned.KV, id id.Round, recipient *id.ID, - ephID ephemeral.Id) error { - return kv.Delete(roundKey(id, recipient, ephID), roundInfoVersion) -} - -func roundKey(roundID id.Round, recipient *id.ID, ephID ephemeral.Id) string { - return roundKeyPrefix + newRoundIdentity(roundID, recipient, ephID).String() -} diff --git a/network/rounds/store/uncheckedRounds.go b/network/rounds/store/uncheckedRounds.go new file mode 100644 index 000000000..1177c1ef6 --- /dev/null +++ b/network/rounds/store/uncheckedRounds.go @@ -0,0 +1,176 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package store + +import ( + "bytes" + "encoding/binary" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" + "gitlab.com/elixxir/client/storage/versioned" + pb "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "gitlab.com/xx_network/primitives/netTime" + "time" +) + +const ( + uncheckedRoundVersion = 0 + roundInfoVersion = 0 + uncheckedRoundPrefix = "uncheckedRoundPrefix" + roundKeyPrefix = "roundInfo:" + + // Key to store rounds + uncheckedRoundKey = "uncheckRounds" + + // Housekeeping constant (used for serializing uint64 ie id.Round) + uint64Size = 8 + + // Maximum checks that can be performed on a round. Intended so that a round + // is checked no more than 1 week approximately (network/rounds.cappedTries + 7) + maxChecks = 14 +) + +// Identity contains round identity information used in message retrieval. +// Derived from reception.Identity saving data needed for message retrieval. +type Identity struct { + EpdId ephemeral.Id + Source *id.ID +} + +// UncheckedRound contains rounds that failed on message retrieval. These rounds +// are stored for retry of message retrieval. +type UncheckedRound struct { + Info *pb.RoundInfo + Id id.Round + + Identity + // Timestamp in which round has last been checked + LastCheck time.Time + // Number of times a round has been checked + NumChecks uint64 +} + +// marshal serializes UncheckedRound r into a byte slice. +func (r UncheckedRound) marshal(kv *versioned.KV) ([]byte, error) { + buf := bytes.NewBuffer(nil) + // Store teh round info + if r.Info != nil { + if err := storeRoundInfo(kv, r.Info, r.Source, r.EpdId); err != nil { + return nil, errors.WithMessagef(err, + "failed to marshal unchecked rounds") + } + } + + // Marshal the round ID + b := make([]byte, uint64Size) + binary.LittleEndian.PutUint64(b, uint64(r.Id)) + buf.Write(b) + + // Write the round identity info + buf.Write(r.Identity.EpdId[:]) + if r.Source != nil { + buf.Write(r.Identity.Source.Marshal()) + } else { + buf.Write(make([]byte, id.ArrIDLen)) + } + + // Write the time stamp bytes + tsBytes, err := r.LastCheck.MarshalBinary() + if err != nil { + return nil, errors.WithMessage(err, "Could not marshal timestamp ") + } + b = make([]byte, uint64Size) + binary.LittleEndian.PutUint64(b, uint64(len(tsBytes))) + buf.Write(b) + buf.Write(tsBytes) + + // Write the number of tries for this round + b = make([]byte, uint64Size) + binary.LittleEndian.PutUint64(b, r.NumChecks) + buf.Write(b) + + return buf.Bytes(), nil +} + +// unmarshal deserializes round data from buff into UncheckedRound r. +func (r *UncheckedRound) unmarshal(kv *versioned.KV, buff *bytes.Buffer) error { + // Deserialize the roundInfo + r.Id = id.Round(binary.LittleEndian.Uint64(buff.Next(uint64Size))) + + // Deserialize the round identity information + copy(r.EpdId[:], buff.Next(uint64Size)) + + sourceId, err := id.Unmarshal(buff.Next(id.ArrIDLen)) + if err != nil { + return errors.WithMessagef(err, + "Failed to unmarshal round identity.source of %d", r.Id) + } + + r.Source = sourceId + + // Deserialize the timestamp bytes + timestampLen := binary.LittleEndian.Uint64(buff.Next(uint64Size)) + tsByes := buff.Next(int(timestampLen)) + if err = r.LastCheck.UnmarshalBinary(tsByes); err != nil { + return errors.WithMessagef(err, + "Failed to unmarshal round timestamp of %d", r.Id) + } + + r.NumChecks = binary.LittleEndian.Uint64(buff.Next(uint64Size)) + + r.Info, _ = loadRoundInfo(kv, r.Id, r.Source, r.EpdId) + + return nil +} + +func storeRoundInfo(kv *versioned.KV, info *pb.RoundInfo, recipient *id.ID, + ephID ephemeral.Id) error { + now := netTime.Now() + + data, err := proto.Marshal(info) + if err != nil { + return errors.WithMessagef(err, + "Failed to store individual unchecked round") + } + + obj := versioned.Object{ + Version: roundInfoVersion, + Timestamp: now, + Data: data, + } + + return kv.Set( + roundKey(id.Round(info.ID), recipient, ephID), roundInfoVersion, &obj) +} + +func loadRoundInfo(kv *versioned.KV, id id.Round, recipient *id.ID, + ephID ephemeral.Id) (*pb.RoundInfo, error) { + + vo, err := kv.Get(roundKey(id, recipient, ephID), roundInfoVersion) + if err != nil { + return nil, err + } + + ri := &pb.RoundInfo{} + if err = proto.Unmarshal(vo.Data, ri); err != nil { + return nil, errors.WithMessagef(err, "Failed to unmarshal roundInfo") + } + + return ri, nil +} + +func deleteRoundInfo(kv *versioned.KV, id id.Round, recipient *id.ID, + ephID ephemeral.Id) error { + return kv.Delete(roundKey(id, recipient, ephID), roundInfoVersion) +} + +func roundKey(roundID id.Round, recipient *id.ID, ephID ephemeral.Id) string { + return roundKeyPrefix + newRoundIdentity(roundID, recipient, ephID).String() +} diff --git a/storage/rounds/uncheckedRounds_test.go b/network/rounds/store/uncheckedRounds_test.go similarity index 99% rename from storage/rounds/uncheckedRounds_test.go rename to network/rounds/store/uncheckedRounds_test.go index 7be9d4493..f38ad89a0 100644 --- a/storage/rounds/uncheckedRounds_test.go +++ b/network/rounds/store/uncheckedRounds_test.go @@ -5,7 +5,7 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package rounds +package store import ( "bytes" diff --git a/network/rounds/unchecked.go b/network/rounds/unchecked.go index c8ce4d000..1c4de699f 100644 --- a/network/rounds/unchecked.go +++ b/network/rounds/unchecked.go @@ -9,15 +9,16 @@ package rounds import ( jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/network/identity/receptionID" + "gitlab.com/elixxir/client/interfaces" + "gitlab.com/elixxir/client/network/rounds/store" "gitlab.com/elixxir/client/stoppable" - "gitlab.com/elixxir/client/storage/rounds" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" "time" ) // Constants for message retrieval backoff delays +// todo - make this a real backoff const ( tryZero = 10 * time.Second tryOne = 30 * time.Second @@ -38,18 +39,20 @@ var backOffTable = [cappedTries]time.Duration{tryZero, tryOne, tryTwo, tryThree, // Rounds will have a backoff duration in which they will be tried again. // If a round is found to be due on a periodical check, the round is sent // back to processMessageRetrieval. -func (m *Manager) processUncheckedRounds(checkInterval time.Duration, backoffTable [cappedTries]time.Duration, +// todo - make this system know which rounds are still in progress instead of just assume by time +func (m *manager) processUncheckedRounds(checkInterval time.Duration, backoffTable [cappedTries]time.Duration, stop *stoppable.Single) { ticker := time.NewTicker(checkInterval) - uncheckedRoundStore := m.Session.UncheckedRounds() + uncheckedRoundStore := m.unchecked for { select { case <-stop.Quit(): + ticker.Stop() stop.ToStopped() return case <-ticker.C: - iterator := func(rid id.Round, rnd rounds.UncheckedRound) { + iterator := func(rid id.Round, rnd store.UncheckedRound) { jww.DEBUG.Printf("checking if %d due for a message lookup", rid) // If this round is due for a round check, send the round over // to the retrieval thread. If not due, check next round. @@ -58,53 +61,19 @@ func (m *Manager) processUncheckedRounds(checkInterval time.Duration, backoffTab } jww.INFO.Printf("Round %d due for a message lookup, retrying...", rid) //check if it needs to be processed by historical Rounds - if rnd.Info == nil { - jww.INFO.Printf("Messages in round %d for %d (%s) loaded from unchecked rounds, looking "+ - "up messages via historical lookup", rnd.Id, rnd.EpdId.Int64(), - rnd.Source) - // If we didn't find it, send to Historical Rounds Retrieval - m.historicalRounds <- historicalRoundRequest{ - rid: rnd.Id, - identity: receptionID.IdentityUse{ - Identity: receptionID.Identity{ - EphId: rnd.EpdId, - Source: rnd.Source, - }, - }, - numAttempts: 0, - } - return - } else { - - // Construct roundLookup object to send - rl := roundLookup{ - roundInfo: rnd.Info, - identity: receptionID.IdentityUse{ - Identity: receptionID.Identity{ - EphId: rnd.EpdId, - Source: rnd.Source, - }, - }, - } - - // Send to processMessageRetrieval - select { - case m.lookupRoundMessages <- rl: - case <-time.After(1 * time.Second): - jww.WARN.Printf("Timing out, not retrying round %d", rl.roundInfo.ID) - } - - // Update the state of the round for next look-up (if needed) - err := uncheckedRoundStore.IncrementCheck(rid, rnd.Source, rnd.EpdId) - if err != nil { - jww.ERROR.Printf("processUncheckedRounds error: Could not "+ - "increment check attempts for round %d: %v", rid, err) - } - + m.GetMessagesFromRound(rid, interfaces.EphemeralIdentity{ + EphId: rnd.EpdId, + Source: rnd.Source, + }) + // Update the state of the round for next look-up (if needed) + err := uncheckedRoundStore.IncrementCheck(rid, rnd.Source, rnd.EpdId) + if err != nil { + jww.ERROR.Printf("processUncheckedRounds error: Could not "+ + "increment check attempts for round %d: %v", rid, err) } } // Pull and iterate through uncheckedRound list - m.Session.UncheckedRounds().IterateOverList(iterator) + m.unchecked.IterateOverList(iterator) } } } diff --git a/network/rounds/utils_test.go b/network/rounds/utils_test.go index ea2453493..5c36cbf99 100644 --- a/network/rounds/utils_test.go +++ b/network/rounds/utils_test.go @@ -12,6 +12,7 @@ import ( "gitlab.com/elixxir/client/interfaces/params" "gitlab.com/elixxir/client/network/internal" "gitlab.com/elixxir/client/network/message" + "gitlab.com/elixxir/client/network/pickup" "gitlab.com/elixxir/client/storage" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/testkeys" @@ -25,12 +26,12 @@ import ( "time" ) -func newManager(face interface{}) *Manager { +func newManager(face interface{}) *manager { sess1 := storage.InitTestingSession(face) - testManager := &Manager{ + testManager := &manager{ params: params.GetDefaultRounds(), - lookupRoundMessages: make(chan roundLookup), + lookupRoundMessages: make(chan pickup.roundLookup), messageBundles: make(chan message.Bundle), Internal: internal.Internal{ Session: sess1, diff --git a/network/sendCmix.go b/network/sendCmix.go index 28570dad1..dd0ee8be3 100644 --- a/network/sendCmix.go +++ b/network/sendCmix.go @@ -16,10 +16,10 @@ import ( "gitlab.com/elixxir/client/network/gateway" "gitlab.com/elixxir/client/network/nodes" "gitlab.com/elixxir/client/stoppable" - "gitlab.com/elixxir/client/storage" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/network" "gitlab.com/elixxir/crypto/cmix" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/elixxir/primitives/excludedRounds" "gitlab.com/elixxir/primitives/format" @@ -32,8 +32,7 @@ import ( "time" ) -// SendCMIX sends a "raw" CMIX message payload to the provided -// recipient. Note that both SendE2E and SendUnsafe call SendCMIX. +// SendCMIX sends a "raw" CMIX message payload to the provided recipient. // Returns the round ID of the round the payload was sent or an error // if it fails. func (m *manager) SendCMIX(msg format.Message, @@ -44,9 +43,9 @@ func (m *manager) SendCMIX(msg format.Message, } msgCopy := msg.Copy() - return sendCmixHelper(m.sender, msgCopy, recipient, cmixParams, m.instance, - m.session, m.Registrar, m.rng, m.events, - m.session.User().GetCryptographicIdentity().GetTransmissionID(), m.comms) + return sendCmixHelper(m.Sender, msgCopy, recipient, cmixParams, m.instance, + m.session.GetCmixGroup(), m.Registrar, m.rng, m.events, + m.session.GetTransmissionID(), m.comms) } // Helper function for sendCmix @@ -58,9 +57,9 @@ func (m *manager) SendCMIX(msg format.Message, // If the message is successfully sent, the id of the round sent it is returned, // which can be registered with the network instance to get a callback on // its status -func sendCmixHelper(sender *gateway.Sender, msg format.Message, +func sendCmixHelper(sender gateway.Sender, msg format.Message, recipient *id.ID, cmixParams params.CMIX, instance *network.Instance, - session *storage.Session, nodes nodes.Registrar, + grp *cyclic.Group, nodes nodes.Registrar, rng *fastRNG.StreamGenerator, events interfaces.EventManager, senderId *id.ID, comms SendCmixCommsInterface) (id.Round, ephemeral.Id, error) { @@ -79,7 +78,6 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, stream := rng.GetStream() defer stream.Close() - grp := session.GetCmixGroup() // flip leading bits randomly to thwart a tagging attack. // See SetGroupBits for more info diff --git a/network/sendCmixUtils.go b/network/sendCmixUtils.go index cfd2e6b49..9785eeed7 100644 --- a/network/sendCmixUtils.go +++ b/network/sendCmixUtils.go @@ -69,8 +69,8 @@ func handlePutMessageError(firstGateway *id.ID, nodes nodes.Registrar, nodeID.SetType(id.Node) // DeleteFingerprint the keys and re-register - nodes.Remove(nodeID) - nodes.TriggerRegistration(nodeID) + nodes.RemoveNode(nodeID) + nodes.TriggerNodeRegistration(nodeID) return errors.WithMessagef(err, "Failed to send to [%s] via %s "+ "due to failed authentication, retrying...", @@ -97,7 +97,7 @@ func processRound(nodes nodes.Registrar, bestRound *pb.RoundInfo, // get the keys for the round, reject if any nodes do not have keying // relationships - roundKeys, err := nodes.GetKeys(topology) + roundKeys, err := nodes.GetNodeKeys(topology) if err != nil { return nil, nil, errors.WithMessagef(err, "Failed to get keys for round %d", bestRound.ID) } diff --git a/network/sendCmix_test.go b/network/sendCmix_test.go index faaed0fac..d5e56f2df 100644 --- a/network/sendCmix_test.go +++ b/network/sendCmix_test.go @@ -109,7 +109,7 @@ func Test_attemptSendCmix(t *testing.T) { t.Errorf("%+v", errors.New(err.Error())) return } - m := message2.NewPickup(i, params.Network{Messages: params.Messages{ + m := message2.NewHandler(i, params.Network{Messages: params.Messages{ MessageReceptionBuffLen: 20, MessageReceptionWorkerPoolSize: 20, MaxChecksRetryMessage: 20, diff --git a/network/sendManyCmix.go b/network/sendManyCmix.go index ea9a3cb5c..9c60a093c 100644 --- a/network/sendManyCmix.go +++ b/network/sendManyCmix.go @@ -17,9 +17,10 @@ import ( "gitlab.com/elixxir/client/network/gateway" "gitlab.com/elixxir/client/network/nodes" "gitlab.com/elixxir/client/stoppable" - "gitlab.com/elixxir/client/storage" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/network" + "gitlab.com/elixxir/crypto/cmix" + "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/elixxir/primitives/excludedRounds" "gitlab.com/elixxir/primitives/format" @@ -36,13 +37,11 @@ import ( // with this call and can leak data about yourself. Returns the round ID of the // round the payload was sent or an error if it fails. // WARNING: Potentially Unsafe -func (m *manager) SendManyCMIX(sender *gateway.Sender, - messages []message.TargetedCmixMessage, p params.CMIX, - stop *stoppable.Single) (id.Round, []ephemeral.Id, error) { +func (m *manager) SendManyCMIX(messages []message.TargetedCmixMessage, p params.CMIX) (id.Round, []ephemeral.Id, error) { - return sendManyCmixHelper(sender, messages, p, - m.instance, m.session, m.Registrar, m.rng, m.events, - m.session.GetUser().TransmissionID, m.comms, stop) + return sendManyCmixHelper(m.Sender, messages, p, + m.instance, m.session.GetCmixGroup(), m.Registrar, m.rng, m.events, + m.session.GetTransmissionID(), m.comms) } // sendManyCmixHelper is a helper function for manager.SendManyCMIX. @@ -56,11 +55,11 @@ func (m *manager) SendManyCMIX(sender *gateway.Sender, // If the message is successfully sent, the ID of the round sent it is returned, // which can be registered with the network instance to get a callback on its // status. -func sendManyCmixHelper(sender *gateway.Sender, +func sendManyCmixHelper(sender gateway.Sender, msgs []message.TargetedCmixMessage, param params.CMIX, instance *network.Instance, - session *storage.Session, registrar nodes.Registrar, + grp *cyclic.Group, registrar nodes.Registrar, rng *fastRNG.StreamGenerator, events interfaces.EventManager, - senderId *id.ID, comms SendCmixCommsInterface, stop *stoppable.Single) ( + senderId *id.ID, comms SendCmixCommsInterface) ( id.Round, []ephemeral.Id, error) { timeStart := netTime.Now() @@ -78,6 +77,15 @@ func sendManyCmixHelper(sender *gateway.Sender, jww.INFO.Printf("[SendManyCMIX-%s]Looking for round to send cMix messages to [%s] "+ "(msgDigest: %s)", param.DebugTag, recipientString, msgDigests) + stream := rng.GetStream() + defer stream.Close() + + // flip leading bits randomly to thwart a tagging attack. + // See SetGroupBits for more info + for i := range msgs { + cmix.SetGroupBits(msgs[i].Message, grp, stream) + } + for numRoundTries := uint(0); numRoundTries < param.RoundTries; numRoundTries++ { elapsed := netTime.Since(timeStart) @@ -188,7 +196,7 @@ func sendManyCmixHelper(sender *gateway.Sender, return result, err } result, err := sender.SendToPreferred( - []*id.ID{firstGateway}, sendFunc, stop, param.SendTimeout) + []*id.ID{firstGateway}, sendFunc, param.Stop, param.SendTimeout) // Exit if the thread has been stopped if stoppable.CheckErr(err) { diff --git a/network/sendManyCmix_test.go b/network/sendManyCmix_test.go index 045dff60f..8bb69e7fe 100644 --- a/network/sendManyCmix_test.go +++ b/network/sendManyCmix_test.go @@ -109,7 +109,7 @@ func Test_attemptSendManyCmix(t *testing.T) { t.Errorf("%+v", errors.New(err.Error())) return } - m := message2.NewPickup(i, params.Network{Messages: params.Messages{ + m := message2.NewHandler(i, params.Network{Messages: params.Messages{ MessageReceptionBuffLen: 20, MessageReceptionWorkerPoolSize: 20, MaxChecksRetryMessage: 20, diff --git a/single/manager_test.go b/single/manager_test.go index 7dba96cc0..66568dfe5 100644 --- a/single/manager_test.go +++ b/single/manager_test.go @@ -51,7 +51,7 @@ func Test_newManager(t *testing.T) { if e.client != m.client || e.store != m.store || e.net != m.net || e.rng != m.rng || !reflect.DeepEqual(e.p, m.p) { - t.Errorf("NewPickup() did not return the expected new Manager."+ + t.Errorf("NewHandler() did not return the expected new Manager."+ "\nexpected: %+v\nreceived: %+v", e, m) } } diff --git a/storage/e2e/session.go b/storage/e2e/session.go index 8607c2f93..8238b090d 100644 --- a/storage/e2e/session.go +++ b/storage/e2e/session.go @@ -24,6 +24,7 @@ import ( "gitlab.com/xx_network/primitives/netTime" "math" "math/big" + "math/rand" "sync" "testing" ) @@ -550,7 +551,7 @@ func (s *Session) triggerNegotiation() bool { s.mux.Unlock() return false } - } else if s.negotiationStatus == Unconfirmed { + } else if s.negotiationStatus == Unconfirmed && rand.Uint64()%s.rekeyThreshold == 0 { // retrigger this sessions negotiation s.mux.RUnlock() s.mux.Lock() diff --git a/storage/messages.go b/storage/messages.go deleted file mode 100644 index 71c30d84d..000000000 --- a/storage/messages.go +++ /dev/null @@ -1,14 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package storage - -const ( - criticalMessagesKey = "CriticalMessages" - criticalRawMessagesKey = "CriticalRawMessages" - checkedRoundsKey = "CheckedRounds" -) diff --git a/storage/ndf.go b/storage/ndf.go index 1b081fd0f..46df6a071 100644 --- a/storage/ndf.go +++ b/storage/ndf.go @@ -15,7 +15,7 @@ import ( const ndfKey = "ndf" -func (s *Session) SetNDF(def *ndf.NetworkDefinition) { +func (s *session) SetNDF(def *ndf.NetworkDefinition) { err := utility.SaveNDF(s.kv, ndfKey, def) if err != nil { jww.FATAL.Printf("Failed to dave the NDF: %+v", err) @@ -23,7 +23,7 @@ func (s *Session) SetNDF(def *ndf.NetworkDefinition) { s.ndf = def } -func (s *Session) GetNDF() *ndf.NetworkDefinition { +func (s *session) GetNDF() *ndf.NetworkDefinition { if s.ndf != nil { return s.ndf } diff --git a/storage/regCode.go b/storage/regCode.go index da0a306d7..2a7d15f34 100644 --- a/storage/regCode.go +++ b/storage/regCode.go @@ -18,7 +18,7 @@ const regCodeKey = "regCode" const regCodeVersion = 0 // SetNDF stores a network definition json file -func (s *Session) SetRegCode(regCode string) { +func (s *session) SetRegCode(regCode string) { if err := s.Set(regCodeKey, &versioned.Object{ Version: regCodeVersion, @@ -30,7 +30,7 @@ func (s *Session) SetRegCode(regCode string) { } // Returns the stored network definition json file -func (s *Session) GetRegCode() (string, error) { +func (s *session) GetRegCode() (string, error) { regCode, err := s.Get(regCodeKey) if err != nil { return "", errors.WithMessage(err, "Failed to load the regcode") diff --git a/storage/regStatus.go b/storage/regStatus.go index dfb2619c8..90b355e75 100644 --- a/storage/regStatus.go +++ b/storage/regStatus.go @@ -56,7 +56,7 @@ func (rs RegistrationStatus) marshalBinary() []byte { } // creates a new registration status and stores it -func (s *Session) newRegStatus() error { +func (s *session) newRegStatus() error { s.regStatus = NotStarted now := netTime.Now() @@ -77,7 +77,7 @@ func (s *Session) newRegStatus() error { } // loads registration status from disk. -func (s *Session) loadRegStatus() error { +func (s *session) loadRegStatus() error { obj, err := s.Get(registrationStatusKey) if err != nil { return errors.WithMessage(err, "Failed to load registration status") @@ -88,7 +88,7 @@ func (s *Session) loadRegStatus() error { // sets the registration status to the passed status if it is greater than the // current stats, otherwise returns an error -func (s *Session) ForwardRegistrationStatus(regStatus RegistrationStatus) error { +func (s *session) ForwardRegistrationStatus(regStatus RegistrationStatus) error { s.mux.Lock() defer s.mux.Unlock() @@ -117,7 +117,7 @@ func (s *Session) ForwardRegistrationStatus(regStatus RegistrationStatus) error // sets the registration status to the passed status if it is greater than the // current stats, otherwise returns an error -func (s *Session) GetRegistrationStatus() RegistrationStatus { +func (s *session) GetRegistrationStatus() RegistrationStatus { s.mux.RLock() defer s.mux.RUnlock() return s.regStatus diff --git a/storage/session.go b/storage/session.go index 921e3792a..2e5736232 100644 --- a/storage/session.go +++ b/storage/session.go @@ -22,7 +22,6 @@ import ( "gitlab.com/elixxir/client/storage/user" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/elixxir/ekv" "gitlab.com/elixxir/primitives/version" "gitlab.com/xx_network/crypto/signature/rsa" @@ -37,32 +36,65 @@ const cmixGroupKey = "cmixGroup" const e2eGroupKey = "e2eGroup" // Session object, backed by encrypted filestore -type Session struct { - kv *versioned.KV +type Session interface { + GetClientVersion() version.Version + Get(key string) (*versioned.Object, error) + Set(key string, object *versioned.Object) error + Delete(key string) error + GetKV() *versioned.KV + GetCmixGroup() *cyclic.Group + GetE2EGroup() *cyclic.Group + ForwardRegistrationStatus(regStatus RegistrationStatus) error + GetRegistrationStatus() RegistrationStatus + SetRegCode(regCode string) + GetRegCode() (string, error) + SetNDF(def *ndf.NetworkDefinition) + GetNDF() *ndf.NetworkDefinition + GetTransmissionID() *id.ID + GetTransmissionSalt() []byte + GetReceptionID() *id.ID + GetReceptionSalt() []byte + GetReceptionRSA() *rsa.PrivateKey + GetTransmissionRSA() *rsa.PrivateKey + IsPrecanned() bool + SetUsername(username string) error + GetUsername() (string, error) + PortableUserInfo() userInterface.Info + GetTransmissionRegistrationValidationSignature() []byte + GetReceptionRegistrationValidationSignature() []byte + GetRegistrationTimestamp() time.Time + SetTransmissionRegistrationValidationSignature(b []byte) + SetReceptionRegistrationValidationSignature(b []byte) + SetRegistrationTimestamp(tsNano int64) +} - mux sync.RWMutex +type session struct { + kv *versioned.KV //memoized data + mux sync.RWMutex regStatus RegistrationStatus ndf *ndf.NetworkDefinition + + //network parameters cmixGroup *cyclic.Group e2eGroup *cyclic.Group //sub-stores - user *user.User + *user.User clientVersion *clientVersion.Store } // Initialize a new Session object -func initStore(baseDir, password string) (*Session, error) { +func initStore(baseDir, password string) (*session, error) { fs, err := ekv.NewFilestore(baseDir, password) - var s *Session + var s *session if err != nil { return nil, errors.WithMessage(err, "Failed to create storage session") } - s = &Session{ + s = &session{ kv: versioned.NewKV(fs), } @@ -70,9 +102,8 @@ func initStore(baseDir, password string) (*Session, error) { } // Creates new UserData in the session -func New(baseDir, password string, u userInterface.User, - currentVersion version.Version, cmixGrp, e2eGrp *cyclic.Group, -) (*Session, error) { +func New(baseDir, password string, u userInterface.Info, + currentVersion version.Version, cmixGrp, e2eGrp *cyclic.Group) (Session, error) { s, err := initStore(baseDir, password) if err != nil { @@ -85,7 +116,7 @@ func New(baseDir, password string, u userInterface.User, "Create new session") } - s.user, err = user.NewUser(s.kv, u.TransmissionID, u.ReceptionID, u.TransmissionSalt, + s.User, err = user.NewUser(s.kv, u.TransmissionID, u.ReceptionID, u.TransmissionSalt, u.ReceptionSalt, u.TransmissionRSA, u.ReceptionRSA, u.Precanned) if err != nil { return nil, errors.WithMessage(err, "Failed to create user") @@ -107,8 +138,7 @@ func New(baseDir, password string, u userInterface.User, } // Loads existing user data into the session -func Load(baseDir, password string, currentVersion version.Version, - rng *fastRNG.StreamGenerator) (*Session, error) { +func Load(baseDir, password string, currentVersion version.Version) (*Session, error) { s, err := initStore(baseDir, password) if err != nil { @@ -131,7 +161,7 @@ func Load(baseDir, password string, currentVersion version.Version, return nil, errors.WithMessage(err, "Failed to load client version store.") } - s.user, err = user.LoadUser(s.kv) + s.User, err = user.LoadUser(s.kv) if err != nil { return nil, errors.WithMessage(err, "Failed to load Session") } @@ -149,54 +179,44 @@ func Load(baseDir, password string, currentVersion version.Version, return s, nil } -func (s *Session) User() *user.User { - s.mux.RLock() - defer s.mux.RUnlock() - return s.user -} - // GetClientVersion returns the version of the client storage. -func (s *Session) GetClientVersion() version.Version { - s.mux.RLock() - defer s.mux.RUnlock() +func (s *session) GetClientVersion() version.Version { return s.clientVersion.Get() } // Get an object from the session -func (s *Session) Get(key string) (*versioned.Object, error) { +func (s *session) Get(key string) (*versioned.Object, error) { return s.kv.Get(key, currentSessionVersion) } // Set a value in the session -func (s *Session) Set(key string, object *versioned.Object) error { +func (s *session) Set(key string, object *versioned.Object) error { return s.kv.Set(key, currentSessionVersion, object) } // delete a value in the session -func (s *Session) Delete(key string) error { +func (s *session) Delete(key string) error { return s.kv.Delete(key, currentSessionVersion) } // GetKV returns the Session versioned.KV. -func (s *Session) GetKV() *versioned.KV { - s.mux.RLock() - defer s.mux.RUnlock() +func (s *session) GetKV() *versioned.KV { return s.kv } // GetCmixGrouo returns cMix Group -func (s *Session) GetCmixGroup() *cyclic.Group { +func (s *session) GetCmixGroup() *cyclic.Group { return s.cmixGroup } // GetE2EGrouo returns cMix Group -func (s *Session) GetE2EGroup() *cyclic.Group { +func (s *session) GetE2EGroup() *cyclic.Group { return s.e2eGroup } // Initializes a Session object wrapped around a MemStore object. // FOR TESTING ONLY -func InitTestingSession(i interface{}) *Session { +func InitTestingSession(i interface{}) Session { switch i.(type) { case *testing.T, *testing.M, *testing.B, *testing.PB: break @@ -207,7 +227,7 @@ func InitTestingSession(i interface{}) *Session { privKey, _ := rsa.LoadPrivateKeyFromPem([]byte("-----BEGIN PRIVATE KEY-----\nMIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC7Dkb6VXFn4cdp\nU0xh6ji0nTDQUyT9DSNW9I3jVwBrWfqMc4ymJuonMZbuqK+cY2l+suS2eugevWZr\ntzujFPBRFp9O14Jl3fFLfvtjZvkrKbUMHDHFehascwzrp3tXNryiRMmCNQV55TfI\nTVCv8CLE0t1ibiyOGM9ZWYB2OjXt59j76lPARYww5qwC46vS6+3Cn2Yt9zkcrGes\nkWEFa2VttHqF910TP+DZk2R5C7koAh6wZYK6NQ4S83YQurdHAT51LKGrbGehFKXq\n6/OAXCU1JLi3kW2PovTb6MZuvxEiRmVAONsOcXKu7zWCmFjuZZwfRt2RhnpcSgzf\nrarmsGM0LZh6JY3MGJ9YdPcVGSz+Vs2E4zWbNW+ZQoqlcGeMKgsIiQ670g0xSjYI\nCqldpt79gaET9PZsoXKEmKUaj6pq1d4qXDk7s63HRQazwVLGBdJQK8qX41eCdR8V\nMKbrCaOkzD5zgnEu0jBBAwdMtcigkMIk1GRv91j7HmqwryOBHryLi6NWBY3tjb4S\no9AppDQB41SH3SwNenAbNO1CXeUqN0hHX6I1bE7OlbjqI7tXdrTllHAJTyVVjenP\nel2ApMXp+LVRdDbKtwBiuM6+n+z0I7YYerxN1gfvpYgcXm4uye8dfwotZj6H2J/u\nSALsU2v9UHBzprdrLSZk2YpozJb+CQIDAQABAoICAARjDFUYpeU6zVNyCauOM7BA\ns4FfQdHReg+zApTfWHosDQ04NIc9CGbM6e5E9IFlb3byORzyevkllf5WuMZVWmF8\nd1YBBeTftKYBn2Gwa42Ql9dl3eD0wQ1gUWBBeEoOVZQ0qskr9ynpr0o6TfciWZ5m\nF50UWmUmvc4ppDKhoNwogNU/pKEwwF3xOv2CW2hB8jyLQnk3gBZlELViX3UiFKni\n/rCfoYYvDFXt+ABCvx/qFNAsQUmerurQ3Ob9igjXRaC34D7F9xQ3CMEesYJEJvc9\nGjvr5DbnKnjx152HS56TKhK8gp6vGHJz17xtWECXD3dIUS/1iG8bqXuhdg2c+2aW\nm3MFpa5jgpAawUWc7c32UnqbKKf+HI7/x8J1yqJyNeU5SySyYSB5qtwTShYzlBW/\nyCYD41edeJcmIp693nUcXzU+UAdtpt0hkXS59WSWlTrB/huWXy6kYXLNocNk9L7g\niyx0cOmkuxREMHAvK0fovXdVyflQtJYC7OjJxkzj2rWO+QtHaOySXUyinkuTb5ev\nxNhs+ROWI/HAIE9buMqXQIpHx6MSgdKOL6P6AEbBan4RAktkYA6y5EtH/7x+9V5E\nQTIz4LrtI6abaKb4GUlZkEsc8pxrkNwCqOAE/aqEMNh91Na1TOj3f0/a6ckGYxYH\npyrvwfP2Ouu6e5FhDcCBAoIBAQDcN8mK99jtrH3q3Q8vZAWFXHsOrVvnJXyHLz9V\n1Rx/7TnMUxvDX1PIVxhuJ/tmHtxrNIXOlps80FCZXGgxfET/YFrbf4H/BaMNJZNP\nag1wBV5VQSnTPdTR+Ijice+/ak37S2NKHt8+ut6yoZjD7sf28qiO8bzNua/OYHkk\nV+RkRkk68Uk2tFMluQOSyEjdsrDNGbESvT+R1Eotupr0Vy/9JRY/TFMc4MwJwOoy\ns7wYr9SUCq/cYn7FIOBTI+PRaTx1WtpfkaErDc5O+nLLEp1yOrfktl4LhU/r61i7\nfdtafUACTKrXG2qxTd3w++mHwTwVl2MwhiMZfxvKDkx0L2gxAoIBAQDZcxKwyZOy\ns6Aw7igw1ftLny/dpjPaG0p6myaNpeJISjTOU7HKwLXmlTGLKAbeRFJpOHTTs63y\ngcmcuE+vGCpdBHQkaCev8cve1urpJRcxurura6+bYaENO6ua5VzF9BQlDYve0YwY\nlbJiRKmEWEAyULjbIebZW41Z4UqVG3MQI750PRWPW4WJ2kDhksFXN1gwSnaM46KR\nPmVA0SL+RCPcAp/VkImCv0eqv9exsglY0K/QiJfLy3zZ8QvAn0wYgZ3AvH3lr9rJ\nT7pg9WDb+OkfeEQ7INubqSthhaqCLd4zwbMRlpyvg1cMSq0zRvrFpwVlSY85lW4F\ng/tgjJ99W9VZAoIBAH3OYRVDAmrFYCoMn+AzA/RsIOEBqL8kaz/Pfh9K4D01CQ/x\naqryiqqpFwvXS4fLmaClIMwkvgq/90ulvuCGXeSG52D+NwW58qxQCxgTPhoA9yM9\nVueXKz3I/mpfLNftox8sskxl1qO/nfnu15cXkqVBe4ouD+53ZjhAZPSeQZwHi05h\nCbJ20gl66M+yG+6LZvXE96P8+ZQV80qskFmGdaPozAzdTZ3xzp7D1wegJpTz3j20\n3ULKAiIb5guZNU0tEZz5ikeOqsQt3u6/pVTeDZR0dxnyFUf/oOjmSorSG75WT3sA\n0ZiR0SH5mhFR2Nf1TJ4JHmFaQDMQqo+EG6lEbAECggEAA7kGnuQ0lSCiI3RQV9Wy\nAa9uAFtyE8/XzJWPaWlnoFk04jtoldIKyzHOsVU0GOYOiyKeTWmMFtTGANre8l51\nizYiTuVBmK+JD/2Z8/fgl8dcoyiqzvwy56kX3QUEO5dcKO48cMohneIiNbB7PnrM\nTpA3OfkwnJQGrX0/66GWrLYP8qmBDv1AIgYMilAa40VdSyZbNTpIdDgfP6bU9Ily\nG7gnyF47HHPt5Cx4ouArbMvV1rof7ytCrfCEhP21Lc46Ryxy81W5ZyzoQfSxfdKb\nGyDR+jkryVRyG69QJf5nCXfNewWbFR4ohVtZ78DNVkjvvLYvr4qxYYLK8PI3YMwL\nsQKCAQB9lo7JadzKVio+C18EfNikOzoriQOaIYowNaaGDw3/9KwIhRsKgoTs+K5O\ngt/gUoPRGd3M2z4hn5j4wgeuFi7HC1MdMWwvgat93h7R1YxiyaOoCTxH1klbB/3K\n4fskdQRxuM8McUebebrp0qT5E0xs2l+ABmt30Dtd3iRrQ5BBjnRc4V//sQiwS1aC\nYi5eNYCQ96BSAEo1dxJh5RI/QxF2HEPUuoPM8iXrIJhyg9TEEpbrEJcxeagWk02y\nOMEoUbWbX07OzFVvu+aJaN/GlgiogMQhb6IiNTyMlryFUleF+9OBA8xGHqGWA6nR\nOaRA5ZbdE7g7vxKRV36jT3wvD7W+\n-----END PRIVATE KEY-----\n")) store := make(ekv.Memstore) kv := versioned.NewKV(store) - s := &Session{kv: kv} + s := &session{kv: kv} uid := id.NewIdFromString("zezima", id.User, i) u, err := user.NewUser(kv, uid, uid, []byte("salt"), []byte("salt"), privKey, privKey, false) if err != nil { @@ -222,7 +242,7 @@ func InitTestingSession(i interface{}) *Session { } u.SetRegistrationTimestamp(testTime.UnixNano()) - s.user = u + s.User = u return s } diff --git a/storage/user.go b/storage/user.go deleted file mode 100644 index e8f899092..000000000 --- a/storage/user.go +++ /dev/null @@ -1,35 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package storage - -import "gitlab.com/elixxir/client/interfaces/user" - -func (s *Session) GetUser() user.User { - s.mux.RLock() - defer s.mux.RUnlock() - ci := s.user.GetCryptographicIdentity() - return user.User{ - TransmissionID: ci.GetTransmissionID().DeepCopy(), - TransmissionSalt: copySlice(ci.GetTransmissionSalt()), - TransmissionRSA: ci.GetTransmissionRSA(), - ReceptionID: ci.GetReceptionID().DeepCopy(), - RegistrationTimestamp: s.user.GetRegistrationTimestamp().UnixNano(), - ReceptionSalt: copySlice(ci.GetReceptionSalt()), - ReceptionRSA: ci.GetReceptionRSA(), - Precanned: ci.IsPrecanned(), - E2eDhPrivateKey: s.e2e.GetDHPrivateKey().DeepCopy(), - E2eDhPublicKey: s.e2e.GetDHPublicKey().DeepCopy(), - } - -} - -func copySlice(s []byte) []byte { - n := make([]byte, len(s)) - copy(n, s) - return n -} diff --git a/storage/user/Info.go b/storage/user/Info.go new file mode 100644 index 000000000..8091bffe0 --- /dev/null +++ b/storage/user/Info.go @@ -0,0 +1,28 @@ +package user + +import "gitlab.com/elixxir/client/interfaces/user" + +func (u *User) PortableUserInfo() user.Info { + ci := u.CryptographicIdentity + return user.Info{ + TransmissionID: ci.GetTransmissionID().DeepCopy(), + TransmissionSalt: copySlice(ci.GetTransmissionSalt()), + TransmissionRSA: ci.GetTransmissionRSA(), + ReceptionID: ci.GetReceptionID().DeepCopy(), + RegistrationTimestamp: u.GetRegistrationTimestamp().UnixNano(), + ReceptionSalt: copySlice(ci.GetReceptionSalt()), + ReceptionRSA: ci.GetReceptionRSA(), + Precanned: ci.IsPrecanned(), + //fixme: set these in the e2e layer, the command line layer + //needs more logical seperation so this can be removed + E2eDhPrivateKey: nil, + E2eDhPublicKey: nil, + } + +} + +func copySlice(s []byte) []byte { + n := make([]byte, len(s)) + copy(n, s) + return n +} diff --git a/storage/user/user.go b/storage/user/user.go index c17795242..e79abf330 100644 --- a/storage/user/user.go +++ b/storage/user/user.go @@ -17,7 +17,7 @@ import ( ) type User struct { - ci *CryptographicIdentity + *CryptographicIdentity transmissionRegValidationSig []byte receptionRegValidationSig []byte @@ -37,7 +37,7 @@ func NewUser(kv *versioned.KV, transmissionID, receptionID *id.ID, transmissionS ci := newCryptographicIdentity(transmissionID, receptionID, transmissionSalt, receptionSalt, transmissionRsa, receptionRsa, isPrecanned, kv) - return &User{ci: ci, kv: kv}, nil + return &User{CryptographicIdentity: ci, kv: kv}, nil } func LoadUser(kv *versioned.KV) (*User, error) { @@ -47,7 +47,7 @@ func LoadUser(kv *versioned.KV) (*User, error) { "due to failure to load cryptographic identity") } - u := &User{ci: ci, kv: kv} + u := &User{CryptographicIdentity: ci, kv: kv} u.loadTransmissionRegistrationValidationSignature() u.loadReceptionRegistrationValidationSignature() u.loadUsername() @@ -55,7 +55,3 @@ func LoadUser(kv *versioned.KV) (*User, error) { return u, nil } - -func (u *User) GetCryptographicIdentity() *CryptographicIdentity { - return u.ci -} diff --git a/storage/utility/multiStateVector_test.go b/storage/utility/multiStateVector_test.go index 3e25cdac8..83527da50 100644 --- a/storage/utility/multiStateVector_test.go +++ b/storage/utility/multiStateVector_test.go @@ -495,7 +495,7 @@ func TestMultiStateVector_GetKeys(t *testing.T) { for state, expected := range expectedKeys { keys, err := msv.GetKeys(uint8(state)) if err != nil { - t.Errorf("GetKeys returned an error: %+v", err) + t.Errorf("GetNodeKeys returned an error: %+v", err) } if !reflect.DeepEqual(expected, keys) { t.Errorf("Incorrect keys for state %d.\nexpected: %d\nreceived: %d", @@ -517,7 +517,7 @@ func TestMultiStateVector_GetKeys_NewStateMaxError(t *testing.T) { expectedErr := fmt.Sprintf(stateMaxErr, state, msv.numStates-1) _, err = msv.GetKeys(state) if err == nil || err.Error() != expectedErr { - t.Errorf("GetKeys did not return the expected error when the state is "+ + t.Errorf("GetNodeKeys did not return the expected error when the state is "+ "larger than the max number of states.\nexpected: %s\nreceived: %v", expectedErr, err) } -- GitLab