|
| 1 | +package sphinx |
| 2 | + |
| 3 | +import "errors" |
| 4 | + |
| 5 | +// ErrAlreadyCommitted signals that an entry could not be added to the |
| 6 | +// batch because it has already been persisted. |
| 7 | +var ErrAlreadyCommitted = errors.New("cannot add to batch after committing") |
| 8 | + |
| 9 | +// Batch is an object used to incrementally construct a set of entries to add to |
| 10 | +// the replay log. After construction is completed, it can be added to the log |
| 11 | +// using the PutBatch method. |
| 12 | +type Batch struct { |
| 13 | + // isCommitted denotes whether or not this batch has been successfully |
| 14 | + // written to disk. |
| 15 | + isCommitted bool |
| 16 | + |
| 17 | + // id is a unique, caller chosen identifier for this batch. |
| 18 | + id []byte |
| 19 | + |
| 20 | + // entries stores the set of all potential entries that might get |
| 21 | + // written to the replay log. Some entries may be skipped after |
| 22 | + // examining the on-disk content at the time of commit.. |
| 23 | + entries map[uint16]batchEntry |
| 24 | + |
| 25 | + // replayCache is an in memory lookup-table, which stores the hash |
| 26 | + // prefix of entries already added to this batch. This allows a quick |
| 27 | + // mechanism for intra-batch duplicate detection. |
| 28 | + replayCache map[HashPrefix]struct{} |
| 29 | + |
| 30 | + // replaySet contains the sequence numbers of all entries that were |
| 31 | + // detected as replays. The set is finalized upon writing the batch to |
| 32 | + // disk, and merges replays detected by the replay cache and on-disk |
| 33 | + // replay log. |
| 34 | + replaySet *ReplaySet |
| 35 | +} |
| 36 | + |
| 37 | +// NewBatch initializes an object for constructing a set of entries to |
| 38 | +// atomically add to a replay log. Batches are identified by byte slice, which |
| 39 | +// allows the caller to safely process the same batch twice and get an |
| 40 | +// idempotent result. |
| 41 | +func NewBatch(id []byte) *Batch { |
| 42 | + return &Batch{ |
| 43 | + id: id, |
| 44 | + entries: make(map[uint16]batchEntry), |
| 45 | + replayCache: make(map[HashPrefix]struct{}), |
| 46 | + replaySet: NewReplaySet(), |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +// Put inserts a hash-prefix/CLTV pair into the current batch. This method only |
| 51 | +// returns an error in the event that the batch was already committed to disk. |
| 52 | +// Decisions regarding whether or not a particular sequence number is a replay |
| 53 | +// is ultimately reported via the batch's ReplaySet after committing to disk. |
| 54 | +func (b *Batch) Put(seqNum uint16, hashPrefix *HashPrefix, cltv uint32) error { |
| 55 | + // Abort if this batch was already written to disk. |
| 56 | + if b.isCommitted { |
| 57 | + return ErrAlreadyCommitted |
| 58 | + } |
| 59 | + |
| 60 | + // Check to see if this hash prefix is already included in this batch. |
| 61 | + // If so, we will opportunistically mark this index as replayed. |
| 62 | + if _, ok := b.replayCache[*hashPrefix]; ok { |
| 63 | + b.replaySet.Add(seqNum) |
| 64 | + return nil |
| 65 | + } |
| 66 | + |
| 67 | + // Otherwise, this is a distinct hash prefix for this batch. Add it to |
| 68 | + // our list of entries that we will try to write to disk. Each of these |
| 69 | + // entries will be checked again during the commit to see if any other |
| 70 | + // on-disk entries contain the same hash prefix. |
| 71 | + b.entries[seqNum] = batchEntry{ |
| 72 | + hashPrefix: *hashPrefix, |
| 73 | + cltv: cltv, |
| 74 | + } |
| 75 | + |
| 76 | + // Finally, add this hash prefix to our in-memory replay cache, this |
| 77 | + // will be consulted upon further adds to check for duplicates in the |
| 78 | + // same batch. |
| 79 | + b.replayCache[*hashPrefix] = struct{}{} |
| 80 | + |
| 81 | + return nil |
| 82 | +} |
| 83 | + |
| 84 | +// batchEntry is a tuple of a secret's hash prefix and the corresponding CLTV at |
| 85 | +// which the onion blob from which the secret was derived expires. |
| 86 | +type batchEntry struct { |
| 87 | + hashPrefix HashPrefix |
| 88 | + cltv uint32 |
| 89 | +} |
0 commit comments