package htlcswitch
import (
"crypto/rand"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/chainntnfs"
)
const (
cltv uint32 = 100000
)
func tempDecayedLogPath(t *testing.T) string {
dir, err := ioutil.TempDir("", "decayedlog")
if err != nil {
t.Fatalf("unable to create temporary decayed log dir: %v", err)
}
return filepath.Join(dir, "sphinxreplay.db")
}
func startup(dbPath string, notifier bool) (sphinx.ReplayLog, *mockNotifier,
*sphinx.HashPrefix, error) {
var log sphinx.ReplayLog
var chainNotifier *mockNotifier
if notifier {
chainNotifier = &mockNotifier{
epochChan: make(chan *chainntnfs.BlockEpoch, 1),
}
log = NewDecayedLog(dbPath, chainNotifier)
} else {
log = NewDecayedLog(dbPath, nil)
}
err := log.Start()
if err != nil {
return nil, nil, nil, err
}
var hashedSecret sphinx.HashPrefix
_, err = rand.Read(hashedSecret[:])
if err != nil {
return nil, nil, nil, err
}
return log, chainNotifier, &hashedSecret, nil
}
func shutdown(dir string, d sphinx.ReplayLog) {
d.Stop()
os.RemoveAll(dir)
}
func TestDecayedLogGarbageCollector(t *testing.T) {
t.Parallel()
dbPath := tempDecayedLogPath(t)
d, notifier, hashedSecret, err := startup(dbPath, true)
if err != nil {
t.Fatalf("Unable to start up DecayedLog: %v", err)
}
defer shutdown(dbPath, d)
err = d.Put(hashedSecret, cltv)
if err != nil {
t.Fatalf("Unable to store in channeldb: %v", err)
}
time.Sleep(500 * time.Millisecond)
notifier.epochChan <- &chainntnfs.BlockEpoch{
Height: 100000,
}
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")
}
notifier.epochChan <- &chainntnfs.BlockEpoch{
Height: 100001,
}
time.Sleep(500 * time.Millisecond)
_, 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)
}
}
func TestDecayedLogPersistentGarbageCollector(t *testing.T) {
t.Parallel()
dbPath := tempDecayedLogPath(t)
d, _, hashedSecret, err := startup(dbPath, true)
if err != nil {
t.Fatalf("Unable to start up DecayedLog: %v", err)
}
defer shutdown(dbPath, d)
if err = d.Put(hashedSecret, cltv); err != nil {
t.Fatalf("Unable to store in channeldb: %v", err)
}
_, err = d.Get(hashedSecret)
if err != nil {
t.Fatalf("Get failed - received unexpected error upon Get: %v", err)
}
d.Stop()
d2, notifier2, _, err := startup(dbPath, true)
if err != nil {
t.Fatalf("Unable to restart DecayedLog: %v", err)
}
defer shutdown(dbPath, d2)
_, err = d2.Get(hashedSecret)
if err != nil {
t.Fatalf("Get failed - received unexpected error upon Get: %v", err)
}
notifier2.epochChan <- &chainntnfs.BlockEpoch{
Height: int32(100001),
}
time.Sleep(500 * time.Millisecond)
_, err = d2.Get(hashedSecret)
if err != sphinx.ErrLogEntryNotFound {
t.Fatalf("Get failed - received unexpected error upon Get: %v", err)
}
}
func TestDecayedLogInsertionAndDeletion(t *testing.T) {
t.Parallel()
dbPath := tempDecayedLogPath(t)
d, _, hashedSecret, err := startup(dbPath, false)
if err != nil {
t.Fatalf("Unable to start up DecayedLog: %v", err)
}
defer shutdown(dbPath, d)
err = d.Put(hashedSecret, cltv)
if err != nil {
t.Fatalf("Unable to store in channeldb: %v", err)
}
err = d.Delete(hashedSecret)
if err != nil {
t.Fatalf("Unable to delete from channeldb: %v", err)
}
_, 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)
}
}
func TestDecayedLogStartAndStop(t *testing.T) {
t.Parallel()
dbPath := tempDecayedLogPath(t)
d, _, hashedSecret, err := startup(dbPath, false)
if err != nil {
t.Fatalf("Unable to start up DecayedLog: %v", err)
}
defer shutdown(dbPath, d)
err = d.Put(hashedSecret, cltv)
if err != nil {
t.Fatalf("Unable to store in channeldb: %v", err)
}
d.Stop()
d2, _, hashedSecret2, err := startup(dbPath, false)
if err != nil {
t.Fatalf("Unable to restart DecayedLog: %v", err)
}
defer shutdown(dbPath, d2)
value, err := d2.Get(hashedSecret)
if err != nil {
t.Fatalf("Unable to retrieve from channeldb: %v", err)
}
if cltv != value {
t.Fatalf("Value retrieved doesn't match value stored")
}
err = d2.Delete(hashedSecret2)
if err != nil {
t.Fatalf("Unable to delete from channeldb: %v", err)
}
d2.Stop()
d3, _, hashedSecret3, err := startup(dbPath, false)
if err != nil {
t.Fatalf("Unable to restart DecayedLog: %v", err)
}
defer shutdown(dbPath, d3)
_, 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)
}
}
func TestDecayedLogStorageAndRetrieval(t *testing.T) {
t.Parallel()
dbPath := tempDecayedLogPath(t)
d, _, hashedSecret, err := startup(dbPath, false)
if err != nil {
t.Fatalf("Unable to start up DecayedLog: %v", err)
}
defer shutdown(dbPath, d)
err = d.Put(hashedSecret, cltv)
if err != nil {
t.Fatalf("Unable to store in channeldb: %v", err)
}
value, err := d.Get(hashedSecret)
if err != nil {
t.Fatalf("Unable to retrieve from channeldb: %v", err)
}
if cltv != value {
t.Fatalf("Value retrieved doesn't match value stored")
}
}