mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-04 18:52:44 +02:00
htlcswitch: Move DecayedLog definition from sphinx package.
This moves over the implementation deleted in https://github.com/lightningnetwork/lightning-onion/pull/22 to structure package dependencies better.
This commit is contained in:
289
htlcswitch/decayedlog_test.go
Normal file
289
htlcswitch/decayedlog_test.go
Normal file
@@ -0,0 +1,289 @@
|
||||
package htlcswitch
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lightning-onion"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
)
|
||||
|
||||
const (
|
||||
cltv uint32 = 100000
|
||||
)
|
||||
|
||||
// startup sets up the DecayedLog and possibly the garbage collector.
|
||||
func startup(notifier bool) (sphinx.ReplayLog, *mockNotifier,
|
||||
*sphinx.HashPrefix, error) {
|
||||
|
||||
var log sphinx.ReplayLog
|
||||
var chainNotifier *mockNotifier
|
||||
if notifier {
|
||||
|
||||
// Create the MockNotifier which triggers the garbage collector
|
||||
chainNotifier = &mockNotifier{
|
||||
epochChan: make(chan *chainntnfs.BlockEpoch, 1),
|
||||
}
|
||||
|
||||
// Initialize the DecayedLog object
|
||||
log = NewDecayedLog("tempdir", chainNotifier)
|
||||
} else {
|
||||
// Initialize the DecayedLog object
|
||||
log = NewDecayedLog("tempdir", nil)
|
||||
}
|
||||
|
||||
// Open the channeldb (start the garbage collector)
|
||||
err := log.Start()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Create a HashPrefix identifier for a packet. Instead of actually
|
||||
// generating an ECDH secret and hashing it, simulate with random bytes.
|
||||
// This is used as a key to retrieve the cltv value.
|
||||
var hashedSecret sphinx.HashPrefix
|
||||
_, err = rand.Read(hashedSecret[:])
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
return log, chainNotifier, &hashedSecret, nil
|
||||
}
|
||||
|
||||
// shutdown deletes the temporary directory that the test database uses
|
||||
// and handles closing the database.
|
||||
func shutdown(dir string, d sphinx.ReplayLog) {
|
||||
d.Stop()
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
// TestDecayedLogGarbageCollector tests the ability of the garbage collector
|
||||
// to delete expired cltv values every time a block is received. Expired cltv
|
||||
// values are cltv values that are < current block height.
|
||||
func TestDecayedLogGarbageCollector(t *testing.T) {
|
||||
d, notifier, hashedSecret, err := startup(true)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to start up DecayedLog: %v", err)
|
||||
}
|
||||
defer shutdown("tempdir", d)
|
||||
|
||||
// Store <hashedSecret, cltv> in the sharedHashBucket.
|
||||
err = d.Put(hashedSecret, cltv)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to store in channeldb: %v", err)
|
||||
}
|
||||
|
||||
// Wait for database write (GC is in a goroutine)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Send block notifications to garbage collector. The garbage collector
|
||||
// should remove the entry by block 100001.
|
||||
|
||||
// Send block 100000
|
||||
notifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||
Height: 100000,
|
||||
}
|
||||
|
||||
// Assert that hashedSecret is still in the sharedHashBucket
|
||||
val, err := d.Get(hashedSecret)
|
||||
if err != nil {
|
||||
t.Fatalf("Get failed - received an error upon Get: %v", err)
|
||||
}
|
||||
|
||||
if val != cltv {
|
||||
t.Fatalf("GC incorrectly deleted CLTV")
|
||||
}
|
||||
|
||||
// Send block 100001 (expiry block)
|
||||
notifier.epochChan <- &chainntnfs.BlockEpoch{
|
||||
Height: 100001,
|
||||
}
|
||||
|
||||
// Wait for database write (GC is in a goroutine)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Assert that hashedSecret is not in the sharedHashBucket
|
||||
val, err = d.Get(hashedSecret)
|
||||
if err == nil {
|
||||
t.Fatalf("CLTV was not deleted")
|
||||
}
|
||||
if err != sphinx.ErrLogEntryNotFound {
|
||||
t.Fatalf("Get failed - received unexpected error upon Get: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecayedLogPersistentGarbageCollector tests the persistence property of
|
||||
// the garbage collector. The garbage collector will be restarted immediately and
|
||||
// a block that expires the stored CLTV value will be sent to the ChainNotifier.
|
||||
// We test that this causes the <hashedSecret, CLTV> pair to be deleted even
|
||||
// on GC restarts.
|
||||
func TestDecayedLogPersistentGarbageCollector(t *testing.T) {
|
||||
d, _, hashedSecret, err := startup(true)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to start up DecayedLog: %v", err)
|
||||
}
|
||||
defer shutdown("tempdir", d)
|
||||
|
||||
// Store <hashedSecret, cltv> in the sharedHashBucket
|
||||
if err = d.Put(hashedSecret, cltv); err != nil {
|
||||
t.Fatalf("Unable to store in channeldb: %v", err)
|
||||
}
|
||||
|
||||
// Wait for database write (GC is in a goroutine)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Shut down DecayedLog and the garbage collector along with it.
|
||||
d.Stop()
|
||||
|
||||
d2, notifier2, hashedSecret2, err := startup(true)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to restart DecayedLog: %v", err)
|
||||
}
|
||||
defer shutdown("tempdir", d2)
|
||||
|
||||
// Send a block notification to the garbage collector that expires
|
||||
// the stored CLTV.
|
||||
notifier2.epochChan <- &chainntnfs.BlockEpoch{
|
||||
Height: int32(100001),
|
||||
}
|
||||
|
||||
// Wait for database write (GC is in a goroutine)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// Assert that hashedSecret is not in the sharedHashBucket
|
||||
_, err = d2.Get(hashedSecret2)
|
||||
if err == nil {
|
||||
t.Fatalf("CLTV was not deleted")
|
||||
}
|
||||
if err != sphinx.ErrLogEntryNotFound {
|
||||
t.Fatalf("Get failed - received unexpected error upon Get: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecayedLogInsertionAndRetrieval inserts a cltv value into the
|
||||
// sharedHashBucket and then deletes it and finally asserts that we can no
|
||||
// longer retrieve it.
|
||||
func TestDecayedLogInsertionAndDeletion(t *testing.T) {
|
||||
d, _, hashedSecret, err := startup(false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to start up DecayedLog: %v", err)
|
||||
}
|
||||
defer shutdown("tempdir", d)
|
||||
|
||||
// Store <hashedSecret, cltv> in the sharedHashBucket.
|
||||
err = d.Put(hashedSecret, cltv)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to store in channeldb: %v", err)
|
||||
}
|
||||
|
||||
// Delete hashedSecret from the sharedHashBucket.
|
||||
err = d.Delete(hashedSecret)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to delete from channeldb: %v", err)
|
||||
}
|
||||
|
||||
// Assert that hashedSecret is not in the sharedHashBucket
|
||||
_, err = d.Get(hashedSecret)
|
||||
if err == nil {
|
||||
t.Fatalf("CLTV was not deleted")
|
||||
}
|
||||
if err != sphinx.ErrLogEntryNotFound {
|
||||
t.Fatalf("Get failed - received unexpected error upon Get: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecayedLogStartAndStop tests for persistence. The DecayedLog is started,
|
||||
// a cltv value is stored in the sharedHashBucket, and then it the DecayedLog
|
||||
// is stopped. The DecayedLog is then started up again and we test that the
|
||||
// cltv value is indeed still stored in the sharedHashBucket. We then delete
|
||||
// the cltv value and check that it persists upon startup.
|
||||
func TestDecayedLogStartAndStop(t *testing.T) {
|
||||
d, _, hashedSecret, err := startup(false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to start up DecayedLog: %v", err)
|
||||
}
|
||||
defer shutdown("tempdir", d)
|
||||
|
||||
// Store <hashedSecret, cltv> in the sharedHashBucket.
|
||||
err = d.Put(hashedSecret, cltv)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to store in channeldb: %v", err)
|
||||
}
|
||||
|
||||
// Shutdown the DecayedLog's channeldb
|
||||
d.Stop()
|
||||
|
||||
d2, _, hashedSecret2, err := startup(false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to restart DecayedLog: %v", err)
|
||||
}
|
||||
defer shutdown("tempdir", d2)
|
||||
|
||||
// Retrieve the stored cltv value given the hashedSecret key.
|
||||
value, err := d2.Get(hashedSecret)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve from channeldb: %v", err)
|
||||
}
|
||||
|
||||
// Check that the original cltv value matches the retrieved cltv
|
||||
// value.
|
||||
if cltv != value {
|
||||
t.Fatalf("Value retrieved doesn't match value stored")
|
||||
}
|
||||
|
||||
// Delete hashedSecret from sharedHashBucket
|
||||
err = d2.Delete(hashedSecret2)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to delete from channeldb: %v", err)
|
||||
}
|
||||
|
||||
// Shutdown the DecayedLog's channeldb
|
||||
d2.Stop()
|
||||
|
||||
d3, _, hashedSecret3, err := startup(false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to restart DecayedLog: %v", err)
|
||||
}
|
||||
defer shutdown("tempdir", d3)
|
||||
|
||||
// Assert that hashedSecret is not in the sharedHashBucket
|
||||
_, err = d3.Get(hashedSecret3)
|
||||
if err == nil {
|
||||
t.Fatalf("CLTV was not deleted")
|
||||
}
|
||||
if err != sphinx.ErrLogEntryNotFound {
|
||||
t.Fatalf("Get failed - received unexpected error upon Get: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecayedLogStorageAndRetrieval stores a cltv value and then retrieves it
|
||||
// via the nested sharedHashBucket and finally asserts that the original stored
|
||||
// and retrieved cltv values are equal.
|
||||
func TestDecayedLogStorageAndRetrieval(t *testing.T) {
|
||||
d, _, hashedSecret, err := startup(false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to start up DecayedLog: %v", err)
|
||||
}
|
||||
defer shutdown("tempdir", d)
|
||||
|
||||
// Store <hashedSecret, cltv> in the sharedHashBucket
|
||||
err = d.Put(hashedSecret, cltv)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to store in channeldb: %v", err)
|
||||
}
|
||||
|
||||
// Retrieve the stored cltv value given the hashedSecret key.
|
||||
value, err := d.Get(hashedSecret)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve from channeldb: %v", err)
|
||||
}
|
||||
|
||||
// If the original cltv value does not match the value retrieved,
|
||||
// then the test failed.
|
||||
if cltv != value {
|
||||
t.Fatalf("Value retrieved doesn't match value stored")
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user