@@ -2,6 +2,7 @@ package sphinx
22
33import (
44 "crypto/sha256"
5+ "errors"
56)
67
78const (
@@ -15,6 +16,16 @@ const (
1516// of a Hash256, and is used to detect duplicate sphinx packets.
1617type HashPrefix [HashPrefixSize ]byte
1718
19+ // errReplayLogAlreadyStarted is an error returned when Start() is called on a
20+ // ReplayLog after it is started and before it is stopped.
21+ var errReplayLogAlreadyStarted error = errors .New (
22+ "Replay log has already been started" )
23+
24+ // errReplayLogNotStarted is an error returned when methods other than Start()
25+ // are called on a ReplayLog before it is started or after it is stopped.
26+ var errReplayLogNotStarted error = errors .New (
27+ "Replay log has not been started" )
28+
1829// hashSharedSecret Sha-256 hashes the shared secret and returns the first
1930// HashPrefixSize bytes of the hash.
2031func hashSharedSecret (sharedSecret * Hash256 ) * HashPrefix {
@@ -58,3 +69,119 @@ type ReplayLog interface {
5869 // that are replays and an error if one occurs.
5970 PutBatch (* Batch ) (* ReplaySet , error )
6071}
72+
73+ // MemoryReplayLog is a simple ReplayLog implementation that stores all added
74+ // sphinx packets and processed batches in memory with no persistence.
75+ //
76+ // This is designed for use just in testing.
77+ type MemoryReplayLog struct {
78+ batches map [string ]* ReplaySet
79+ entries map [HashPrefix ]uint32
80+ }
81+
82+ // NewMemoryReplayLog constructs a new MemoryReplayLog.
83+ func NewMemoryReplayLog () * MemoryReplayLog {
84+ return & MemoryReplayLog {}
85+ }
86+
87+ // Start initializes the log and must be called before any other methods.
88+ func (rl * MemoryReplayLog ) Start () error {
89+ rl .batches = make (map [string ]* ReplaySet )
90+ rl .entries = make (map [HashPrefix ]uint32 )
91+ return nil
92+ }
93+
94+ // Stop wipes the state of the log.
95+ func (rl * MemoryReplayLog ) Stop () error {
96+ if rl .entries == nil || rl .batches == nil {
97+ return errReplayLogNotStarted
98+ }
99+
100+ rl .batches = nil
101+ rl .entries = nil
102+ return nil
103+ }
104+
105+ // Get retrieves an entry from the log given its hash prefix. It returns the
106+ // value stored and an error if one occurs. It returns ErrLogEntryNotFound
107+ // if the entry is not in the log.
108+ func (rl * MemoryReplayLog ) Get (hash * HashPrefix ) (uint32 , error ) {
109+ if rl .entries == nil || rl .batches == nil {
110+ return 0 , errReplayLogNotStarted
111+ }
112+
113+ cltv , exists := rl .entries [* hash ]
114+ if ! exists {
115+ return 0 , ErrLogEntryNotFound
116+ }
117+
118+ return cltv , nil
119+ }
120+
121+ // Put stores an entry into the log given its hash prefix and an accompanying
122+ // purposefully general type. It returns ErrReplayedPacket if the provided hash
123+ // prefix already exists in the log.
124+ func (rl * MemoryReplayLog ) Put (hash * HashPrefix , cltv uint32 ) error {
125+ if rl .entries == nil || rl .batches == nil {
126+ return errReplayLogNotStarted
127+ }
128+
129+ _ , exists := rl .entries [* hash ]
130+ if exists {
131+ return ErrReplayedPacket
132+ }
133+
134+ rl .entries [* hash ] = cltv
135+ return nil
136+ }
137+
138+ // Delete deletes an entry from the log given its hash prefix.
139+ func (rl * MemoryReplayLog ) Delete (hash * HashPrefix ) error {
140+ if rl .entries == nil || rl .batches == nil {
141+ return errReplayLogNotStarted
142+ }
143+
144+ delete (rl .entries , * hash )
145+ return nil
146+ }
147+
148+ // PutBatch stores a batch of sphinx packets into the log given their hash
149+ // prefixes and accompanying values. Returns the set of entries in the batch
150+ // that are replays and an error if one occurs.
151+ func (rl * MemoryReplayLog ) PutBatch (batch * Batch ) (* ReplaySet , error ) {
152+ if rl .entries == nil || rl .batches == nil {
153+ return nil , errReplayLogNotStarted
154+ }
155+
156+ // Return the result when the batch was first processed to provide
157+ // idempotence.
158+ replays , exists := rl .batches [string (batch .id )]
159+
160+ if ! exists {
161+ replays = NewReplaySet ()
162+ for seqNum , entry := range batch .entries {
163+ err := rl .Put (& entry .hashPrefix , entry .cltv )
164+ if err == ErrReplayedPacket {
165+ replays .Add (seqNum )
166+ continue
167+ }
168+
169+ // This would be bad because we have already updated the entries
170+ // map, but no errors other than ErrReplayedPacket should occur.
171+ if err != nil {
172+ return nil , err
173+ }
174+ }
175+
176+ replays .Merge (batch .replaySet )
177+ rl .batches [string (batch .id )] = replays
178+ }
179+
180+ batch .replaySet = replays
181+ batch .isCommitted = true
182+
183+ return replays , nil
184+ }
185+
186+ // A compile time asserting *MemoryReplayLog implements the RelayLog interface.
187+ var _ ReplayLog = (* MemoryReplayLog )(nil )
0 commit comments