summaryrefslogtreecommitdiff
path: root/srv/pow/store.go
diff options
context:
space:
mode:
authorBrian Picciano <mediocregopher@gmail.com>2021-08-01 17:54:53 -0600
committerBrian Picciano <mediocregopher@gmail.com>2021-08-01 17:54:53 -0600
commit069ee93de17579230ef749d5804df7a0ac350ac5 (patch)
tree0beac3e94e237f1382f35650a2f455a79c63f03c /srv/pow/store.go
parent6bec0e3964f03f776841cec5e639784222a33958 (diff)
implemented PoW backend
Diffstat (limited to 'srv/pow/store.go')
-rw-r--r--srv/pow/store.go92
1 files changed, 92 insertions, 0 deletions
diff --git a/srv/pow/store.go b/srv/pow/store.go
new file mode 100644
index 0000000..0b5e7d0
--- /dev/null
+++ b/srv/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
+}