diff options
author | Brian Picciano <mediocregopher@gmail.com> | 2021-08-01 17:54:53 -0600 |
---|---|---|
committer | Brian Picciano <mediocregopher@gmail.com> | 2021-08-01 17:54:53 -0600 |
commit | 069ee93de17579230ef749d5804df7a0ac350ac5 (patch) | |
tree | 0beac3e94e237f1382f35650a2f455a79c63f03c /srv/pow/store.go | |
parent | 6bec0e3964f03f776841cec5e639784222a33958 (diff) |
implemented PoW backend
Diffstat (limited to 'srv/pow/store.go')
-rw-r--r-- | srv/pow/store.go | 92 |
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 +} |