diff --git a/storage/user/regValidationSig.go b/storage/user/registation.go similarity index 68% rename from storage/user/regValidationSig.go rename to storage/user/registation.go index 7440500fc680e2aee821ac761f47c8d9b201f40a..4913802216e3ca278bcb5946256725c23ac1be1c 100644 --- a/storage/user/regValidationSig.go +++ b/storage/user/registation.go @@ -8,14 +8,17 @@ package user import ( + "encoding/binary" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/xx_network/primitives/netTime" + "time" ) const currentRegValidationSigVersion = 0 const transmissionRegValidationSigKey = "transmissionRegistrationValidationSignature" const receptionRegValidationSigKey = "receptionRegistrationValidationSignature" +const registrationTimestampKey = "registrationTimestamp" // Returns the transmission Identity Validation Signature stored in RAM. May return // nil of no signature is stored @@ -33,6 +36,20 @@ func (u *User) GetReceptionRegistrationValidationSignature() []byte { return u.receptionRegValidationSig } +// Returns the registration timestamp stored in RAM as int64 +func (u *User) GetRegistrationTimestampNano() int64 { + u.rvsMux.RLock() + defer u.rvsMux.RUnlock() + return u.registrationTimestamp.UnixNano() +} + +// Returns the registration timestamp stored in RAM as +func (u *User) GetRegistrationTimestamp() time.Time { + u.rvsMux.RLock() + defer u.rvsMux.RUnlock() + return u.registrationTimestamp +} + // Loads the transmission Identity Validation Signature if it exists in the ekv func (u *User) loadTransmissionRegistrationValidationSignature() { u.rvsMux.Lock() @@ -55,6 +72,18 @@ func (u *User) loadReceptionRegistrationValidationSignature() { u.rvsMux.Unlock() } +// Loads the registration timestamp if it exists in the ekv +func (u *User) loadRegistrationTimestamp() { + u.rvsMux.Lock() + obj, err := u.kv.Get(registrationTimestampKey, + currentRegValidationSigVersion) + if err == nil { + tsNano := binary.BigEndian.Uint64(obj.Data) + u.registrationTimestamp = time.Unix(0, int64(tsNano)) + } + u.rvsMux.Unlock() +} + // Sets the Identity Validation Signature if it is not set and stores it in // the ekv func (u *User) SetTransmissionRegistrationValidationSignature(b []byte) { @@ -108,3 +137,34 @@ func (u *User) SetReceptionRegistrationValidationSignature(b []byte) { u.receptionRegValidationSig = b } + +// Sets the Registration Timestamp if it is not set and stores it in +// the ekv +func (u *User) SetRegistrationTimestamp(tsNano int64) { + u.rvsMux.Lock() + defer u.rvsMux.Unlock() + + //check if the signature already exists + if !u.registrationTimestamp.IsZero() { + jww.FATAL.Panicf("cannot overwrite existing registration timestamp") + } + + // Serialize the timestamp + tsBytes := make([]byte, 8) + binary.BigEndian.PutUint64(tsBytes, uint64(tsNano)) + + obj := &versioned.Object{ + Version: currentRegValidationSigVersion, + Timestamp: netTime.Now(), + Data: tsBytes, + } + + err := u.kv.Set(registrationTimestampKey, + currentRegValidationSigVersion, obj) + if err != nil { + jww.FATAL.Panicf("Failed to store the reception timestamp: %s", err) + } + + u.registrationTimestamp = time.Unix(0, tsNano) + +} diff --git a/storage/user/regValidationSig_test.go b/storage/user/registation_test.go similarity index 64% rename from storage/user/regValidationSig_test.go rename to storage/user/registation_test.go index 68a69f60c8a8dca24161a4ec8bc9721d096aa6a9..0011f9e7b6b85409efb74f1eb115bf6ede6fbe17 100644 --- a/storage/user/regValidationSig_test.go +++ b/storage/user/registation_test.go @@ -9,12 +9,14 @@ package user import ( "bytes" + "encoding/binary" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/ekv" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" "testing" + "time" ) // Test User GetRegistrationValidationSignature function @@ -137,3 +139,88 @@ func TestUser_loadRegistrationValidationSignature(t *testing.T) { t.Errorf("Expected sig did not match loaded. Expected: %+v, Received: %+v", sig, u.receptionRegValidationSig) } } + +// Test User's getter/setter functions for TimeStamp +func TestUser_GetRegistrationTimestamp(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + uid := id.NewIdFromString("test", id.User, t) + salt := []byte("salt") + u, err := NewUser(kv, uid, uid, salt, salt, &rsa.PrivateKey{}, &rsa.PrivateKey{}, false) + if err != nil || u == nil { + t.Errorf("Failed to create new user: %+v", err) + } + + testTime, err := time.Parse(time.RFC3339, + "2012-12-21T22:08:41+00:00") + if err != nil { + t.Fatalf("Could not parse precanned time: %v", err.Error()) + } + + // Test that User has been modified for timestamp + u.SetRegistrationTimestamp(testTime.UnixNano()) + if !testTime.Equal(u.registrationTimestamp) { + t.Errorf("SetRegistrationTimestamp did not set user's timestamp value."+ + "\n\tExpected: %s\n\tReceieved: %s", testTime.String(), u.registrationTimestamp) + } + + // Pull timestamp from kv + obj, err := u.kv.Get(registrationTimestampKey, 0) + if err != nil { + t.Errorf("Failed to get reg vaildation signature key: %+v", err) + } + + // Check if kv data is expected + unixNano := binary.BigEndian.Uint64(obj.Data) + if testTime.UnixNano() != int64(unixNano) { + t.Errorf("Timestamp pulled from kv was not expected."+ + "\n\tExpected: %d\n\tReceieved: %d", testTime.UnixNano(), unixNano) + } + + if testTime.UnixNano() != u.GetRegistrationTimestampNano() { + t.Errorf("Timestamp from GetRegistrationTimestampNano was not expected."+ + "\n\tExpected: %d\n\tReceieved: %d", testTime.UnixNano(), u.GetRegistrationTimestampNano()) + } + + if !testTime.Equal(u.GetRegistrationTimestamp()) { + t.Errorf("Timestamp from GetRegistrationTimestamp was not expected."+ + "\n\tExpected: %s\n\tReceieved: %s", testTime, u.GetRegistrationTimestamp()) + + } + +} + +// Test loading registrationTimestamp from the KV store +func TestUser_loadRegistrationTimestamp(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + uid := id.NewIdFromString("test", id.User, t) + salt := []byte("salt") + u, err := NewUser(kv, uid, uid, salt, salt, &rsa.PrivateKey{}, &rsa.PrivateKey{}, false) + if err != nil || u == nil { + t.Errorf("Failed to create new user: %+v", err) + } + + testTime, err := time.Parse(time.RFC3339, + "2012-12-21T22:08:41+00:00") + if err != nil { + t.Fatalf("Could not parse precanned time: %v", err.Error()) + } + + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, uint64(testTime.UnixNano())) + vo := &versioned.Object{ + Version: currentRegValidationSigVersion, + Timestamp: netTime.Now(), + Data: data, + } + err = kv.Set(registrationTimestampKey, + currentRegValidationSigVersion, vo) + if err != nil { + t.Errorf("Failed to set reg validation sig key in kv store: %+v", err) + } + + u.loadRegistrationTimestamp() + if !testTime.Equal(u.registrationTimestamp) { + t.Errorf("SetRegistrationTimestamp did not set user's timestamp value."+ + "\n\tExpected: %s\n\tReceieved: %s", testTime.String(), u.registrationTimestamp) + } +} diff --git a/storage/user/user.go b/storage/user/user.go index a55830ff131620f7f9f499b2a6932d616675ccda..c1779524245cd729df731243837d772ddf7b2c03 100644 --- a/storage/user/user.go +++ b/storage/user/user.go @@ -13,6 +13,7 @@ import ( "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" "sync" + "time" ) type User struct { @@ -20,7 +21,9 @@ type User struct { transmissionRegValidationSig []byte receptionRegValidationSig []byte - rvsMux sync.RWMutex + // Time in which user registered with the network + registrationTimestamp time.Time + rvsMux sync.RWMutex username string usernameMux sync.RWMutex @@ -48,6 +51,7 @@ func LoadUser(kv *versioned.KV) (*User, error) { u.loadTransmissionRegistrationValidationSignature() u.loadReceptionRegistrationValidationSignature() u.loadUsername() + u.loadRegistrationTimestamp() return u, nil }