summaryrefslogtreecommitdiff
path: root/src/pow/store.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pow/store.go')
-rw-r--r--src/pow/store.go92
1 files changed, 92 insertions, 0 deletions
diff --git a/src/pow/store.go b/src/pow/store.go
new file mode 100644
index 0000000..0b5e7d0
--- /dev/null
+++ b/src/pow/store.go
@@ -0,0 +1,92 @@
+package pow
+
+import (
+ "errors"
+ "sync"
+ "time"
+
+ "github.com/tilinna/clock"
+)
+
+// ErrSeedSolved is used to indicate a seed has already been solved.
+var ErrSeedSolved = errors.New("seed already solved")
+
+// Store is used to track information related to proof-of-work challenges and
+// solutions.
+type Store interface {
+
+ // MarkSolved will return ErrSeedSolved if the seed was already marked. The
+ // seed will be cleared from the Store once expiresAt is reached.
+ MarkSolved(seed []byte, expiresAt time.Time) error
+
+ Close() error
+}
+
+type inMemStore struct {
+ clock clock.Clock
+
+ m map[string]time.Time
+ l sync.Mutex
+ closeCh chan struct{}
+ spinLoopCh chan struct{} // only used by tests
+}
+
+const inMemStoreGCPeriod = 5 * time.Second
+
+// NewMemoryStore initializes and returns an in-memory Store implementation.
+func NewMemoryStore(clock clock.Clock) Store {
+ s := &inMemStore{
+ clock: clock,
+ m: map[string]time.Time{},
+ closeCh: make(chan struct{}),
+ spinLoopCh: make(chan struct{}, 1),
+ }
+ go s.spin(s.clock.NewTicker(inMemStoreGCPeriod))
+ return s
+}
+
+func (s *inMemStore) spin(ticker *clock.Ticker) {
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ticker.C:
+ now := s.clock.Now()
+
+ s.l.Lock()
+ for seed, expiresAt := range s.m {
+ if !now.Before(expiresAt) {
+ delete(s.m, seed)
+ }
+ }
+ s.l.Unlock()
+
+ case <-s.closeCh:
+ return
+ }
+
+ select {
+ case s.spinLoopCh <- struct{}{}:
+ default:
+ }
+ }
+}
+
+func (s *inMemStore) MarkSolved(seed []byte, expiresAt time.Time) error {
+ seedStr := string(seed)
+
+ s.l.Lock()
+ defer s.l.Unlock()
+
+ if _, ok := s.m[seedStr]; ok {
+ return ErrSeedSolved
+ }
+
+ s.m[seedStr] = expiresAt
+ return nil
+}
+
+func (s *inMemStore) Close() error {
+ close(s.closeCh)
+ return nil
+}