Skip to content

Commit

Permalink
Add contextstore implementation
Browse files Browse the repository at this point in the history
The goal of contextstore is to simplify context recovery when
working with modules like Crypto and Hasher. Currently, using
these modules requires creating a new protobuf type each time the
user wants to verify a signature or compute a hash. This is
associated with a lot of boilerplate code. ContextStore allows
saving the context locally in the module and only sending an
automatically generated id in a standard ContextStoreOrigin
protobuf.
  • Loading branch information
xosmig committed Jun 20, 2022
1 parent ccb8064 commit 2a7a7f5
Show file tree
Hide file tree
Showing 7 changed files with 619 additions and 353 deletions.
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@ require (
gopkg.in/alecthomas/kingpin.v2 v2.2.6
)

require github.com/stretchr/testify v1.7.0

require (
github.com/DataDog/zstd v1.4.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de // indirect
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
Expand All @@ -34,6 +37,7 @@ require (
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/tinylru v1.1.0 // indirect
Expand Down
17 changes: 17 additions & 0 deletions pkg/contextstore/contextstore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package contextstore

// ContextStore can be used to store arbitrary data under an automatically deterministically generated unique id.
type ContextStore[T any] interface {
Store(t T) ItemID
Recover(id ItemID) T
Dispose(id ItemID)
RecoverAndDispose(id ItemID) T
}

// ItemID is used to uniquely identify entries of the ContextStore.
type ItemID uint64

// Pb returns the protobuf representation of ItemID.
func (i ItemID) Pb() uint64 {
return uint64(i)
}
25 changes: 25 additions & 0 deletions pkg/contextstore/contextstore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package contextstore

import (
"github.com/stretchr/testify/assert"
"testing"
)

func TestContextStore_SimpleTest(t *testing.T) {
cs := NewSequentialContextStore[string]()
helloID := cs.Store("Hello")
worldID := cs.Store("World")

assert.Equal(t, "World", cs.Recover(worldID))
assert.Equal(t, "Hello", cs.Recover(helloID))

cs.Dispose(worldID)
assert.Panics(t, func() {
cs.Recover(worldID)
})

assert.Equal(t, "Hello", cs.RecoverAndDispose(helloID))
assert.Panics(t, func() {
cs.RecoverAndDispose(helloID)
})
}
41 changes: 41 additions & 0 deletions pkg/contextstore/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package contextstore

import (
"github.com/filecoin-project/mir/pkg/pb/eventpb"
t "github.com/filecoin-project/mir/pkg/types"
)

// Origin returns a ContextStoreOrigin protobuf containing the given id.
func Origin(itemID ItemID) *eventpb.ContextStoreOrigin {
return &eventpb.ContextStoreOrigin{ItemID: itemID.Pb()}
}

// SignOrigin returns a SignOrigin protobuf containing moduleID and contextstore.Origin(itemID).
func SignOrigin(moduleID t.ModuleID, itemID ItemID) *eventpb.SignOrigin {
return &eventpb.SignOrigin{
Module: moduleID.Pb(),
Type: &eventpb.SignOrigin_Contextstore{
Contextstore: Origin(itemID),
},
}
}

// SigVerOrigin returns a SigVerOrigin protobuf containing moduleID and contextstore.Origin(itemID).
func SigVerOrigin(moduleID t.ModuleID, itemID ItemID) *eventpb.SigVerOrigin {
return &eventpb.SigVerOrigin{
Module: moduleID.Pb(),
Type: &eventpb.SigVerOrigin_Contextstore{
Contextstore: Origin(itemID),
},
}
}

// HashOrigin returns a HashOrigin protobuf containing moduleID and contextstore.Origin(itemID).
func HashOrigin(moduleID t.ModuleID, itemID ItemID) *eventpb.HashOrigin {
return &eventpb.HashOrigin{
Module: moduleID.Pb(),
Type: &eventpb.HashOrigin_Contextstore{
Contextstore: Origin(itemID),
},
}
}
49 changes: 49 additions & 0 deletions pkg/contextstore/sequential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package contextstore

import "fmt"

type sequentialContextStoreImpl[T any] struct {
nextID ItemID
storage map[ItemID]T
}

// NewSequentialContextStore creates an empty ContextStore that can only be accessed sequentially.
func NewSequentialContextStore[T any]() ContextStore[T] {
return &sequentialContextStoreImpl[T]{
storage: make(map[ItemID]T),
}
}

// Store stores the given data in the ContextStore and returns a unique id.
// The data can be later recovered or disposed of using this id.
func (s *sequentialContextStoreImpl[T]) Store(t T) ItemID {
id := s.nextID
s.nextID++

s.storage[id] = t
return id
}

// Recover returns the data stored under the provided id.
// Note that the data will continue to exist in the ContextStore.
// In order to dispose of the data, call s.Dispose(id) or s.RecoverAndDispose(id).
func (s *sequentialContextStoreImpl[T]) Recover(id ItemID) T {
item, present := s.storage[id]
if !present {
panic(fmt.Errorf("item with id '%v' is not present in the ContextStore", id))
}

return item
}

// Dispose removes the data from the ContextStore.
func (s *sequentialContextStoreImpl[T]) Dispose(id ItemID) {
delete(s.storage, id)
}

// RecoverAndDispose returns the data stored under the provided id and removes it from the ContextStore.
func (s *sequentialContextStoreImpl[T]) RecoverAndDispose(id ItemID) T {
t := s.Recover(id)
s.Dispose(id)
return t
}
Loading

0 comments on commit 2a7a7f5

Please sign in to comment.