diff --git a/cmix.go b/cmix.go new file mode 100644 index 0000000000000000000000000000000000000000..06ab7d0f9a35a7d1070711496d6ca1cb892a258f --- /dev/null +++ b/cmix.go @@ -0,0 +1 @@ +package main diff --git a/storage/cmix/key.go b/storage/cmix/key.go new file mode 100644 index 0000000000000000000000000000000000000000..389482e8b62cfa21ef1fecad8d614532bc7a5930 --- /dev/null +++ b/storage/cmix/key.go @@ -0,0 +1,76 @@ +package cmix + +import ( + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/xx_network/primitives/id" + "time" +) + +const currentKeyVersion = 0 + +type key struct { + k *cyclic.Int +} + +// loads the key for the given node id from the versioned keystore +func loadKey(kv *versioned.KV, id *id.ID) (*key, error) { + k := &key{} + + key := keyKey(id) + + obj, err := kv.Get(key) + if err != nil { + return nil, err + } + + err = k.unmarshal(obj.Data) + + if err != nil { + return nil, err + } + + return k, nil +} + +// saves the key as the key for the given node ID in the passed keystore +func (k *key) save(kv *versioned.KV, id *id.ID) error { + now := time.Now() + + data, err := k.marshal() + if err != nil { + return err + } + + obj := versioned.Object{ + Version: currentKeyVersion, + Timestamp: now, + Data: data, + } + + key := keyKey(id) + + return kv.Set(key, &obj) +} + +// deletes the key from the versioned keystore +func (k *key) delete(kv *versioned.KV, id *id.ID) error { + key := keyKey(id) + return kv.Delete(key) +} + +// makes a binary representation of the given key in the keystore +func (k *key) marshal() ([]byte, error) { + return k.k.GobEncode() +} + +// resets the data of the key from the binary representation of the key passed in +func (k *key) unmarshal(b []byte) error { + k.k = &cyclic.Int{} + return k.k.GobDecode(b) +} + +// generates the key used in the keystore for the given key +func keyKey(id *id.ID) string { + return "nodeKey:" + id.String() +} diff --git a/storage/cmix/roundKeys.go b/storage/cmix/roundKeys.go new file mode 100644 index 0000000000000000000000000000000000000000..71888b095f9f2a15733704b9bedb1e5d762d36c2 --- /dev/null +++ b/storage/cmix/roundKeys.go @@ -0,0 +1,28 @@ +package cmix + +import ( + "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/crypto/cmix" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/hash" + "gitlab.com/elixxir/primitives/format" +) + +type RoundKeys []*cyclic.Int + +// Encrypts the given message for CMIX +// Panics if the passed message format +func (rk RoundKeys) Encrypt(grp *cyclic.Group, msg format.Message, + salt []byte) (format.Message, [][]byte, error) { + + ecrMsg := cmix.ClientEncrypt(grp, msg, salt, rk) + + h, err := hash.NewCMixHash() + if err != nil { + globals.Log.ERROR.Printf("Cound not get hash for KMAC generation: %+v", h) + } + + KMAC := cmix.GenerateKMACs(salt, rk, h) + + return ecrMsg, KMAC +} diff --git a/storage/cmix/roundKeys_test.go b/storage/cmix/roundKeys_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d9e4b5b5a9a5c252fadaf3417194140f6ecc45c0 --- /dev/null +++ b/storage/cmix/roundKeys_test.go @@ -0,0 +1,7 @@ +package cmix + +import "testing" + +func TestRoundKeys_Encrypt_Consistancy(t *testing.T) { + +} diff --git a/storage/cmix/store.go b/storage/cmix/store.go new file mode 100644 index 0000000000000000000000000000000000000000..21cd6b563adbfc377162c9983900178eb8a7c19f --- /dev/null +++ b/storage/cmix/store.go @@ -0,0 +1,159 @@ +package cmix + +import ( + "encoding/json" + "github.com/pkg/errors" + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" + "sync" + "time" +) + +const currentStoreVersion = 0 +const storeKey = "cmixKeyStore" + +type Store struct { + nodes map[id.ID]*key + + kv *versioned.KV + + mux sync.RWMutex +} + +// returns a new cmix storage object +func NewStore(kv *versioned.KV) *Store { + return &Store{ + nodes: make(map[id.ID]*key), + kv: kv, + } +} + +// loads the cmix storage object +func LoadStore(kv *versioned.KV) (*Store, error) { + s := NewStore(kv) + + obj, err := kv.Get(storeKey) + if err != nil { + return nil, err + } + + err = s.unmarshal(obj.Data) + + if err != nil { + return nil, err + } + + return s, nil +} + +// adds the key for a round to the cmix storage object. Saves the updated list +// of nodes and the key to disk +func (s *Store) Add(nid *id.ID, k *cyclic.Int) error { + s.mux.Lock() + defer s.mux.Unlock() + + nodekey := &key{k: k} + err := nodekey.save(s.kv, nid) + if err != nil { + return err + } + + s.nodes[*nid] = nodekey + return s.save() +} + +// removes the key from the cmix storage object. Saves an updates node list to +// +func (s *Store) Remove(nid *id.ID, k *cyclic.Int) error { + s.mux.Lock() + defer s.mux.Unlock() + + nodekey, ok := s.nodes[*nid] + if !ok { + return errors.New("Cannot remove, no key with given ID found") + } + + err := nodekey.delete(s.kv, nid) + if err != nil { + return err + } + + delete(s.nodes, *nid) + + return nil +} + +//Returns a RoundKeys for the topology and a list of nodes it did not have a key for +func (s *Store) GetRoundKeys(topology *connect.Circuit) (RoundKeys, []*id.ID) { + s.mux.RLock() + defer s.mux.RUnlock() + + var missingNodes []*id.ID + + rk := RoundKeys(make([]*cyclic.Int, topology.Len())) + + for i := 0; i < topology.Len(); i++ { + nid := topology.GetNodeAtIndex(i) + k, ok := s.nodes[*nid] + if !ok { + missingNodes = append(missingNodes, nid) + } else { + rk[i] = k.k + } + } + + return rk, missingNodes +} + +// stores the cmix store +func (s *Store) save() error { + now := time.Now() + + data, err := s.marshal() + if err != nil { + return err + } + + obj := versioned.Object{ + Version: currentStoreVersion, + Timestamp: now, + Data: data, + } + + return s.kv.Set(storeKey, &obj) +} + +// builds a byte representation of the store +func (s *Store) marshal() ([]byte, error) { + nodes := make([]id.ID, len(s.nodes)) + + index := 0 + for nid, _ := range s.nodes { + nodes[index] = nid + } + + return json.Marshal(&nodes) +} + +// restores the data for a store from the byte representation of the store +func (s *Store) unmarshal(b []byte) error { + var nodes []id.ID + + err := json.Unmarshal(b, &nodes) + if err != nil { + return err + } + + for _, nid := range nodes { + k, err := loadKey(s.s, &nid) + if err != nil { + return errors.WithMessagef(err, "could not load node key for %s", &nid) + } + s.nodes[nid] = k + } + + return nil +} diff --git a/storage/contact.go b/storage/contact.go index a6fae356bb2f2bed691069d0130c845cb58dadc9..4eb415dd264f2e4eae89c8825642ccdbbd6c837d 100644 --- a/storage/contact.go +++ b/storage/contact.go @@ -9,6 +9,7 @@ package storage import ( "encoding/json" "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/xx_network/primitives/id" "time" ) @@ -42,7 +43,7 @@ func (s *Session) saveContacts() error { if err != nil { return err } - obj := VersionedObject{ + obj := versioned.Object{ Version: currentContactVersion, Timestamp: time.Now(), Data: data, @@ -59,7 +60,7 @@ func (s *Session) updateContact(record *Contact) error { // GetContactByEmail reads contact information from disk func (s *Session) GetContactByEmail(email string) (*Contact, error) { - key := MakeKeyWithPrefix("Contact", email) + key := versioned.MakeKeyWithPrefix("Contact", email) obj, err := s.Get(key) if err != nil { @@ -85,12 +86,12 @@ func (s *Session) SetContactByEmail(email string, record *Contact) error { return err } - key := MakeKeyWithPrefix("Contact", email) + key := versioned.MakeKeyWithPrefix("Contact", email) data, err := json.Marshal(record) if err != nil { return err } - obj := VersionedObject{ + obj := versioned.Object{ Version: currentContactVersion, Timestamp: time.Now(), Data: data, @@ -122,6 +123,6 @@ func (s *Session) DeleteContactByID(ID *id.ID) error { return err } - key := MakeKeyWithPrefix("Contact", record.Email) + key := versioned.MakeKeyWithPrefix("Contact", record.Email) return s.Delete(key) } diff --git a/storage/contact_test.go b/storage/contact_test.go index 95e99655acd3c700c28cda9e260ba132a511d6c9..1787252e0022149595dba2181ef86b75695c0dca 100644 --- a/storage/contact_test.go +++ b/storage/contact_test.go @@ -7,6 +7,7 @@ package storage import ( + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/ekv" "gitlab.com/xx_network/primitives/id" "reflect" @@ -16,7 +17,7 @@ import ( // Show that all fields of a searched user record get stored func TestSession_Contact(t *testing.T) { store := make(ekv.Memstore) - session := &Session{kv: NewVersionedKV(store)} + session := &Session{kv: versioned.NewKV(store)} session.loadAllContacts() expectedRecord := &Contact{ diff --git a/e2e/context.go b/storage/e2e/context.go similarity index 66% rename from e2e/context.go rename to storage/e2e/context.go index 771646565aa1f75e8069cb05917c974ca27da0b1..7792eb424739484f5c77e42786a10e82e6c69cf2 100644 --- a/e2e/context.go +++ b/storage/e2e/context.go @@ -1,7 +1,7 @@ package e2e import ( - "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/cyclic" ) @@ -10,5 +10,5 @@ type context struct { grp *cyclic.Group - kv *storage.Session + kv *versioned.KV } diff --git a/e2e/fingerprintAccess.go b/storage/e2e/fingerprintAccess.go similarity index 100% rename from e2e/fingerprintAccess.go rename to storage/e2e/fingerprintAccess.go diff --git a/e2e/key.go b/storage/e2e/key.go similarity index 100% rename from e2e/key.go rename to storage/e2e/key.go diff --git a/e2e/key_test.go b/storage/e2e/key_test.go similarity index 100% rename from e2e/key_test.go rename to storage/e2e/key_test.go diff --git a/e2e/manager.go b/storage/e2e/manager.go similarity index 100% rename from e2e/manager.go rename to storage/e2e/manager.go diff --git a/e2e/params.go b/storage/e2e/params.go similarity index 100% rename from e2e/params.go rename to storage/e2e/params.go diff --git a/e2e/params_test.go b/storage/e2e/params_test.go similarity index 100% rename from e2e/params_test.go rename to storage/e2e/params_test.go diff --git a/e2e/session.go b/storage/e2e/session.go similarity index 99% rename from e2e/session.go rename to storage/e2e/session.go index 15e3e0640308bbce86c6da04e6e4017ad1771f06..f4cd7b20ea6884e3d6fe69c07d8e7774e09586b9 100644 --- a/e2e/session.go +++ b/storage/e2e/session.go @@ -3,7 +3,7 @@ package e2e import ( "encoding/json" "errors" - "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/csprng" "gitlab.com/elixxir/crypto/cyclic" dh "gitlab.com/elixxir/crypto/diffieHellman" @@ -116,7 +116,7 @@ func (s *Session) save() error { return err } - obj := storage.VersionedObject{ + obj := versioned.Object{ Version: currentSessionVersion, Timestamp: now, Data: data, diff --git a/e2e/sessionBuff.go b/storage/e2e/sessionBuff.go similarity index 98% rename from e2e/sessionBuff.go rename to storage/e2e/sessionBuff.go index f5cba0e8e5117d4b7ec11d114aba887b2298c8f4..523b5e6f4a1b635149a6e6a6e4bcd1166f705d2c 100644 --- a/e2e/sessionBuff.go +++ b/storage/e2e/sessionBuff.go @@ -4,10 +4,10 @@ import ( "encoding/base64" "encoding/json" "github.com/pkg/errors" - "gitlab.com/elixxir/client/storage" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/xx_network/primitives/id" "sync" - jww "github.com/spf13/jwalterweatherman" "time" ) @@ -68,7 +68,7 @@ func (sb *sessionBuff) save() error { return err } - obj := storage.VersionedObject{ + obj := versioned.Object{ Version: currentSessionBuffVersion, Timestamp: now, Data: data, diff --git a/e2e/sessionID.go b/storage/e2e/sessionID.go similarity index 100% rename from e2e/sessionID.go rename to storage/e2e/sessionID.go diff --git a/e2e/sessionType.go b/storage/e2e/sessionType.go similarity index 100% rename from e2e/sessionType.go rename to storage/e2e/sessionType.go diff --git a/e2e/sessionType_test.go b/storage/e2e/sessionType_test.go similarity index 100% rename from e2e/sessionType_test.go rename to storage/e2e/sessionType_test.go diff --git a/e2e/session_test.go b/storage/e2e/session_test.go similarity index 94% rename from e2e/session_test.go rename to storage/e2e/session_test.go index c3c785d314ccdb27d73ded75575fac5fbb30e499..c178e1970a36133040893e83ababa470d0271886 100644 --- a/e2e/session_test.go +++ b/storage/e2e/session_test.go @@ -2,8 +2,10 @@ package e2e import ( "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/csprng" dh "gitlab.com/elixxir/crypto/diffieHellman" + "gitlab.com/elixxir/ekv" "testing" ) @@ -19,7 +21,7 @@ func TestSession_generate_noPrivateKeyReceive(t *testing.T) { ctx := &context{ fa: &fps, grp: grp, - kv: storage.InitMem(t), + kv: versioned.NewKV(make(ekv.Memstore)), } //build the session @@ -80,7 +82,7 @@ func TestSession_generate_PrivateKeySend(t *testing.T) { ctx := &context{ fa: &fps, grp: grp, - kv: storage.InitMem(t), + kv: versioned.NewKV(make(ekv.Memstore)), } //build the session diff --git a/e2e/stateVector.go b/storage/e2e/stateVector.go similarity index 98% rename from e2e/stateVector.go rename to storage/e2e/stateVector.go index 14cc72bf5c5470a75c17b0b3f38991b7b3131aec..491b205ae92e5ad59ea366eecc9a98a20a2a9e66 100644 --- a/e2e/stateVector.go +++ b/storage/e2e/stateVector.go @@ -3,7 +3,7 @@ package e2e import ( "encoding/json" "github.com/pkg/errors" - "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/versioned" "sync" "time" ) @@ -76,7 +76,7 @@ func (sv *stateVector) save() error { return err } - obj := storage.VersionedObject{ + obj := versioned.Object{ Version: currentStateVectorVersion, Timestamp: now, Data: data, diff --git a/e2e/stateVector_test.go b/storage/e2e/stateVector_test.go similarity index 95% rename from e2e/stateVector_test.go rename to storage/e2e/stateVector_test.go index 57dcf28b02e879fa487c811d532fdaacc4a86d2e..6435e18764ce81772f9c4362262bb3a553743d25 100644 --- a/e2e/stateVector_test.go +++ b/storage/e2e/stateVector_test.go @@ -3,6 +3,8 @@ package e2e import ( "fmt" "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/ekv" "math/bits" "reflect" "testing" @@ -23,7 +25,7 @@ func TestStateVector_GetNumAvailable(t *testing.T) { func TestStateVector_GetNumKeys(t *testing.T) { ctx := context{ - kv: storage.InitMem(t), + kv: versioned.NewKV(make(ekv.Memstore)), } const numKeys = 32 sv := newStateVector(&ctx, "key", numKeys) @@ -41,7 +43,7 @@ func TestStateVector_Next(t *testing.T) { expectedFirstAvail := []uint32{139, 145, 300, 360, 420, 761, 868, 875, 893, 995} ctx := context{ - kv: storage.InitMem(t), + kv: versioned.NewKV(make(ekv.Memstore)), } const numKeys = 1000 sv := newStateVector(&ctx, "key", numKeys) @@ -94,7 +96,7 @@ func TestStateVector_Use(t *testing.T) { keyNums := []uint32{139, 145, 300, 360, 420, 761, 868, 875, 893, 995} ctx := context{ - kv: storage.InitMem(t), + kv: versioned.NewKV(make(ekv.Memstore)), } const numKeys = 1000 sv := newStateVector(&ctx, "key", numKeys) @@ -130,7 +132,7 @@ func TestStateVector_Used(t *testing.T) { keyNums := []uint32{139, 145, 300, 360, 420, 761, 868, 875, 893, 995} ctx := context{ - kv: storage.InitMem(t), + kv: versioned.NewKV(make(ekv.Memstore)), } const numKeys = 1000 sv := newStateVector(&ctx, "key", numKeys) @@ -158,7 +160,7 @@ func TestStateVector_GetUsedKeyNums(t *testing.T) { keyNums := []uint32{139, 145, 300, 360, 420, 761, 868, 875, 893, 995} ctx := context{ - kv: storage.InitMem(t), + kv: versioned.NewKV(make(ekv.Memstore)), } const numKeys = 1000 sv := newStateVector(&ctx, "key", numKeys) @@ -179,7 +181,7 @@ func TestStateVector_GetUnusedKeyNums(t *testing.T) { keyNums := []uint32{139, 145, 300, 360, 420, 761, 868, 875, 893, 995} ctx := context{ - kv: storage.InitMem(t), + kv: versioned.NewKV(make(ekv.Memstore)), } const numKeys = 1000 sv := newStateVector(&ctx, "key", numKeys) @@ -203,7 +205,7 @@ func TestLoadStateVector(t *testing.T) { keyNums := []uint32{139, 145, 300, 360, 420, 761, 868, 875, 893, 995} const numKeys = 1000 ctx := context{ - kv: storage.InitMem(t), + kv: versioned.NewKV(make(ekv.Memstore)), } sv := newStateVector(&ctx, "key", numKeys) sv.vect = []uint64{0xffffffffffffffff, 0xffffffffffffffff, 0xfffffffffffdf7ff, 0xffffffffffffffff, 0xffffefffffffffff, 0xfffffeffffffffff, 0xffffffefffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xfdffffffffffffff, 0xffffffffffffffff, 0xdffff7efffffffff, 0xffffffffffffffff, 0xfffffff7ffffffff} diff --git a/e2e/status.go b/storage/e2e/status.go similarity index 100% rename from e2e/status.go rename to storage/e2e/status.go diff --git a/e2e/status_test.go b/storage/e2e/status_test.go similarity index 100% rename from e2e/status_test.go rename to storage/e2e/status_test.go diff --git a/e2e/store.go b/storage/e2e/store.go similarity index 93% rename from e2e/store.go rename to storage/e2e/store.go index 23ac8115311baa76f5dbf51af2e530cbbe4a908b..b2cef1c8b8d33abc1fda97fe37ecb4a47420cef0 100644 --- a/e2e/store.go +++ b/storage/e2e/store.go @@ -4,7 +4,7 @@ import ( "encoding/json" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" @@ -12,7 +12,7 @@ import ( "time" ) -const storeKey = "keyStore" +const storeKey = "e2eKeyStore" const currentStoreVersion = 0 type Store struct { @@ -24,7 +24,7 @@ type Store struct { context } -func NewStore(grp *cyclic.Group, kv *storage.Session) *Store { +func NewStore(grp *cyclic.Group, kv *versioned.KV) *Store { fingerprints := newFingerprints() return &Store{ managers: make(map[id.ID]*Manager), @@ -38,7 +38,7 @@ func NewStore(grp *cyclic.Group, kv *storage.Session) *Store { } -func LoadStore(grp *cyclic.Group, kv *storage.Session) (*Store, error) { +func LoadStore(grp *cyclic.Group, kv *versioned.KV) (*Store, error) { s := NewStore(grp, kv) obj, err := kv.Get(storeKey) @@ -63,7 +63,7 @@ func (s *Store) save() error { return err } - obj := storage.VersionedObject{ + obj := versioned.Object{ Version: currentStoreVersion, Timestamp: now, Data: data, diff --git a/storage/registration.go b/storage/registration.go index 3b624867ab47b1968a1c07d8bd7362300afa6b9c..5ffd0b8fcb9503c37e14223f96be1819b4baa155 100644 --- a/storage/registration.go +++ b/storage/registration.go @@ -8,6 +8,7 @@ package storage import ( "encoding/json" "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/storage/versioned" "time" ) @@ -17,7 +18,7 @@ var currentRegistrationVersion = uint64(0) // key-value store func (s *Session) SetRegValidationSig(newVal []byte) error { // Construct the versioned object - vo := &VersionedObject{ + vo := &versioned.Object{ Version: currentRegistrationVersion, Timestamp: time.Now(), Data: newVal, @@ -59,7 +60,7 @@ func (s *Session) SetRegState(newVal int64) error { return err } - obj := VersionedObject{ + obj := versioned.Object{ Version: currentRegistrationVersion, Timestamp: time.Now(), Data: data, diff --git a/storage/session.go b/storage/session.go index 130fcde1fd6e0761687de8b3960b79d5f2256e94..3639610b398ba49488064ba5a4265b80a88f6f8d 100644 --- a/storage/session.go +++ b/storage/session.go @@ -12,6 +12,7 @@ import ( "bytes" "encoding/gob" "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/client/user" "gitlab.com/elixxir/ekv" "gitlab.com/xx_network/comms/connect" @@ -23,7 +24,7 @@ import ( // Session object, backed by encrypted filestore type Session struct { - kv *VersionedKV + kv *versioned.KV userData *UserData mux sync.Mutex @@ -38,7 +39,7 @@ func Init(baseDir, password string) (*Session, error) { var s *Session if err == nil { s = &Session{ - kv: NewVersionedKV(fs), + kv: versioned.NewKV(fs), } } @@ -53,16 +54,16 @@ func InitMem(t *testing.T) *Session { panic("cannot use a memstore not for testing") } store := make(ekv.Memstore) - return &Session{kv: NewVersionedKV(store)} + return &Session{kv: versioned.NewKV(store)} } // Get an object from the session -func (s *Session) Get(key string) (*VersionedObject, error) { +func (s *Session) Get(key string) (*versioned.Object, error) { return s.kv.Get(key) } // Set a value in the session -func (s *Session) Set(key string, object *VersionedObject) error { +func (s *Session) Set(key string, object *versioned.Object) error { return s.kv.Set(key, object) } @@ -82,7 +83,7 @@ func (s *Session) GetLastMessageId() (string, error) { // Set the LastMessageID in the Session func (s *Session) SetLastMessageId(id string) error { - vo := &VersionedObject{ + vo := &versioned.Object{ Timestamp: time.Now(), Data: []byte(id), } @@ -109,7 +110,7 @@ func (s *Session) GetNodeKeys() (map[string]user.NodeKeys, error) { } // Store the new map - vo := &VersionedObject{ + vo := &versioned.Object{ Timestamp: time.Now(), Data: nodeKeysBuffer.Bytes(), } @@ -173,7 +174,7 @@ func (s *Session) PushNodeKey(id *id.ID, key user.NodeKeys) error { err = enc.Encode(nodeKeys) // Insert the map back into the Session - vo := &VersionedObject{ + vo := &versioned.Object{ Timestamp: time.Now(), Data: nodeKeysBuffer.Bytes(), } @@ -195,6 +196,6 @@ func InitTestingSession(i interface{}) *Session { } store := make(ekv.Memstore) - return &Session{kv: NewVersionedKV(store)} + return &Session{kv: versioned.NewKV(store)} } diff --git a/storage/session_test.go b/storage/session_test.go index d20a3149808b763b710749e2b9ffa2e1f70b7bb2..048fa870c126ac51bc44c27494f2054c8397c0a1 100644 --- a/storage/session_test.go +++ b/storage/session_test.go @@ -8,6 +8,7 @@ package storage import ( "bytes" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/client/user" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/large" @@ -35,7 +36,7 @@ func initTest(t *testing.T) *Session { func TestSession_Smoke(t *testing.T) { s := initTest(t) - err := s.Set("testkey", &VersionedObject{ + err := s.Set("testkey", &versioned.Object{ Version: 0, Timestamp: time.Now(), Data: []byte("test"), diff --git a/storage/userdata.go b/storage/userdata.go index 266c0d4f37fed0aa348febb19ffff3c83ef9f0b8..c5845715f78396fa5ad7a3d56758cdfb16514590 100644 --- a/storage/userdata.go +++ b/storage/userdata.go @@ -9,6 +9,7 @@ package storage import ( "bytes" "encoding/gob" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" @@ -91,7 +92,7 @@ func (s *Session) CommitUserData(data *UserData) error { return err } - obj := &VersionedObject{ + obj := &versioned.Object{ Version: currentUserDataVersion, Timestamp: time.Now(), Data: userDataBuffer.Bytes(), diff --git a/storage/userdata_test.go b/storage/userdata_test.go index d182f217229e72f57b5b92a221a41c689308f282..deb27edcfac15e617c669881cef05520b8b1f026 100644 --- a/storage/userdata_test.go +++ b/storage/userdata_test.go @@ -2,6 +2,7 @@ package storage import ( "bytes" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/large" "gitlab.com/elixxir/crypto/signature/rsa" @@ -40,7 +41,7 @@ func TestSession_CommitUserData(t *testing.T) { // Create a session backed by memory store := make(ekv.Memstore) - vkv := NewVersionedKV(store) + vkv := versioned.NewKV(store) session := Session{kv: vkv} err = session.CommitUserData(expectedData) if err != nil { diff --git a/storage/versionedkv.go b/storage/versioned/kv.go similarity index 67% rename from storage/versionedkv.go rename to storage/versioned/kv.go index cf73e4362f1b4595507dd6a4eec9015d13268701..0afe0b5e191de1b530e4d8f448ecf07d491d6edb 100644 --- a/storage/versionedkv.go +++ b/storage/versioned/kv.go @@ -4,7 +4,7 @@ // All rights reserved. / //////////////////////////////////////////////////////////////////////////////// -package storage +package versioned import ( "encoding/json" @@ -22,10 +22,10 @@ func MakeKeyWithPrefix(dataType string, uniqueID string) string { return fmt.Sprintf("%s%s%s", dataType, prefixKeySeparator, uniqueID) } -// VersionedObject is used by VersionedKeyValue to keep track of +// Object is used by VersionedKeyValue to keep track of // versioning and time of storage -type VersionedObject struct { - // Used to determine version upgrade, if any +type Object struct { + // Used to determine version Upgrade, if any Version uint64 // Set when this object is written @@ -35,19 +35,19 @@ type VersionedObject struct { Data []byte } -// Unmarshal deserializes a VersionedObject from a byte slice. It's used to +// Unmarshal deserializes a Object from a byte slice. It's used to // make these storable in a KeyValue. -// VersionedObject exports all fields and they have simple types, so +// Object exports all fields and they have simple types, so // json.Unmarshal works fine. -func (v *VersionedObject) Unmarshal(data []byte) error { +func (v *Object) Unmarshal(data []byte) error { return json.Unmarshal(data, v) } -// Marshal serializes a VersionedObject into a byte slice. It's used to +// Marshal serializes a Object into a byte slice. It's used to // make these storable in a KeyValue. -// VersionedObject exports all fields and they have simple types, so +// Object exports all fields and they have simple types, so // json.Marshal works fine. -func (v *VersionedObject) Marshal() []byte { +func (v *Object) Marshal() []byte { d, err := json.Marshal(v) // Not being to marshal this simple object means something is really // wrong @@ -58,33 +58,33 @@ func (v *VersionedObject) Marshal() []byte { } // Upgrade functions must be of this type -type upgrade func(key string, oldObject *VersionedObject) (*VersionedObject, +type Upgrade func(key string, oldObject *Object) (*Object, error) -// VersionedKV stores versioned data and upgrade functions -type VersionedKV struct { - upgradeTable map[string]upgrade +// KV stores versioned data and Upgrade functions +type KV struct { + upgradeTable map[string]Upgrade data ekv.KeyValue } // Create a versioned key/value store backed by something implementing KeyValue -func NewVersionedKV(data ekv.KeyValue) *VersionedKV { - newKV := new(VersionedKV) - // Add new upgrade functions to this upgrade table - newKV.upgradeTable = make(map[string]upgrade) - // All upgrade functions should upgrade to the latest version. You can - // call older upgrade functions if you need to. Upgrade functions don't +func NewKV(data ekv.KeyValue) *KV { + newKV := new(KV) + // Add new Upgrade functions to this Upgrade table + newKV.upgradeTable = make(map[string]Upgrade) + // All Upgrade functions should Upgrade to the latest version. You can + // call older Upgrade functions if you need to. Upgrade functions don't // change the key or store the upgraded version of the data in the // key/value store. There's no mechanism built in for this -- users // should always make the key prefix before calling Set, and if they // want the upgraded data persisted they should call Set with the // upgraded data. newKV.upgradeTable[MakeKeyWithPrefix("test", "")] = func(key string, - oldObject *VersionedObject) (*VersionedObject, error) { + oldObject *Object) (*Object, error) { if oldObject.Version == 1 { return oldObject, nil } - return &VersionedObject{ + return &Object{ Version: 1, // Upgrade functions don't need to update // the timestamp @@ -99,18 +99,18 @@ func NewVersionedKV(data ekv.KeyValue) *VersionedKV { // Get gets and upgrades data stored in the key/value store // Make sure to inspect the version returned in the versioned object -func (v *VersionedKV) Get(key string) (*VersionedObject, error) { +func (v *KV) Get(key string) (*Object, error) { // Get raw data - result := VersionedObject{} + result := Object{} err := v.data.Get(key, &result) if err != nil { return nil, err } // If the key starts with a version tag that we can find in the table, - // we should call that function to upgrade it + // we should call that function to Upgrade it for version, upgrade := range v.upgradeTable { if strings.HasPrefix(key, version) { - // We should run this upgrade function + // We should run this Upgrade function // The user of this function must update the key // based on the version returned in this // versioned object! @@ -121,13 +121,13 @@ func (v *VersionedKV) Get(key string) (*VersionedObject, error) { } // Delete removes a given key from the data store -func (v *VersionedKV) Delete(key string) error { +func (v *KV) Delete(key string) error { return nil } // Set upserts new data into the storage // When calling this, you are responsible for prefixing the key with the correct // type optionally unique id! Call MakeKeyWithPrefix() to do so. -func (v *VersionedKV) Set(key string, object *VersionedObject) error { +func (v *KV) Set(key string, object *Object) error { return v.data.Set(key, object) } diff --git a/storage/versionedkv_test.go b/storage/versioned/kv_test.go similarity index 85% rename from storage/versionedkv_test.go rename to storage/versioned/kv_test.go index e58e8b73156b45187e762c3b92f88a1f97b51936..2570d2e94a989d85b9d075faf1989e2d381d4217 100644 --- a/storage/versionedkv_test.go +++ b/storage/versioned/kv_test.go @@ -4,7 +4,7 @@ // All rights reserved. / //////////////////////////////////////////////////////////////////////////////// -package storage +package versioned import ( "bytes" @@ -16,7 +16,7 @@ import ( // Shows that all fields can be serialized/deserialized correctly using json func TestVersionedObject_MarshalUnmarshal(t *testing.T) { - original := VersionedObject{ + original := Object{ Version: 8, Timestamp: time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC), Data: []byte("original text"), @@ -24,7 +24,7 @@ func TestVersionedObject_MarshalUnmarshal(t *testing.T) { marshalled := original.Marshal() - unmarshalled := VersionedObject{} + unmarshalled := Object{} err := unmarshalled.Unmarshal(marshalled) if err != nil { // Should never happen @@ -37,10 +37,10 @@ func TestVersionedObject_MarshalUnmarshal(t *testing.T) { t.Logf("%+v", unmarshalled) } -// VersionedKV Get should call the upgrade function when it's available +// KV Get should call the Upgrade function when it's available func TestVersionedKV_Get_Err(t *testing.T) { kv := make(ekv.Memstore) - vkv := NewVersionedKV(kv) + vkv := NewKV(kv) key := MakeKeyWithPrefix("test", "12345") result, err := vkv.Get(key) if err == nil { @@ -53,13 +53,13 @@ func TestVersionedKV_Get_Err(t *testing.T) { } } -// Test versioned KV upgrade path +// Test versioned KV Upgrade path func TestVersionedKV_Get_Upgrade(t *testing.T) { // Set up a dummy KV with the required data kv := make(ekv.Memstore) - vkv := NewVersionedKV(kv) + vkv := NewKV(kv) key := MakeKeyWithPrefix("test", "12345") - original := VersionedObject{ + original := Object{ Version: 0, Timestamp: time.Now(), Data: []byte("not upgraded"), @@ -74,19 +74,19 @@ func TestVersionedKV_Get_Upgrade(t *testing.T) { } if !bytes.Equal(result.Data, []byte("this object was upgraded from v0 to v1")) { - t.Errorf("upgrade should have overwritten data."+ + t.Errorf("Upgrade should have overwritten data."+ " result data: %q", result.Data) } } -// Test Get without upgrade path +// Test Get without Upgrade path func TestVersionedKV_Get(t *testing.T) { // Set up a dummy KV with the required data kv := make(ekv.Memstore) - vkv := NewVersionedKV(kv) + vkv := NewKV(kv) originalVersion := uint64(1) key := MakeKeyWithPrefix("test", "12345") - original := VersionedObject{ + original := Object{ Version: originalVersion, Timestamp: time.Now(), Data: []byte("not upgraded"), @@ -100,7 +100,7 @@ func TestVersionedKV_Get(t *testing.T) { err) } if !bytes.Equal(result.Data, []byte("not upgraded")) { - t.Errorf("upgrade should not have overwritten data."+ + t.Errorf("Upgrade should not have overwritten data."+ " result data: %q", result.Data) } } @@ -108,10 +108,10 @@ func TestVersionedKV_Get(t *testing.T) { // Test that Set puts data in the store func TestVersionedKV_Set(t *testing.T) { kv := make(ekv.Memstore) - vkv := NewVersionedKV(kv) + vkv := NewKV(kv) originalVersion := uint64(1) key := MakeKeyWithPrefix("test", "12345") - original := VersionedObject{ + original := Object{ Version: originalVersion, Timestamp: time.Now(), Data: []byte("not upgraded"),