contractcourt: add new chainWatcherTestHarness
We'll use this for all the upcoming tests.
This commit is contained in:
1 parent
0f59756b40
commit
ce779a9707
1 file changed
+656
@@ -0,0 +1,656 @@
|
||||
package contractcourt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
"github.com/lightningnetwork/lnd/channeldb"
|
||||
"github.com/lightningnetwork/lnd/fn/v2"
|
||||
lnmock "github.com/lightningnetwork/lnd/lntest/mock"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
)
|
||||
|
||||
// testReporter is a minimal interface for test reporting that is satisfied
|
||||
// by both *testing.T and *rapid.T, allowing the harness to work with
|
||||
// property-based tests.
|
||||
type testReporter interface {
|
||||
Helper()
|
||||
Fatalf(format string, args ...any)
|
||||
}
|
||||
|
||||
// chainWatcherTestHarness provides a test harness for chain watcher tests
|
||||
// with utilities for simulating spends, confirmations, and reorganizations.
|
||||
type chainWatcherTestHarness struct {
|
||||
t testReporter
|
||||
|
||||
// aliceChannel and bobChannel are the test channels.
|
||||
aliceChannel *lnwallet.LightningChannel
|
||||
bobChannel *lnwallet.LightningChannel
|
||||
|
||||
// chainWatcher is the chain watcher under test.
|
||||
chainWatcher *chainWatcher
|
||||
|
||||
// notifier is the mock chain notifier.
|
||||
notifier *mockChainNotifier
|
||||
|
||||
// chanEvents is the channel event subscription.
|
||||
chanEvents *ChainEventSubscription
|
||||
|
||||
// currentHeight tracks the current block height.
|
||||
currentHeight int32
|
||||
|
||||
// blockbeatProcessed is a channel that signals when a blockbeat has
|
||||
// been processed.
|
||||
blockbeatProcessed chan struct{}
|
||||
}
|
||||
|
||||
// mockChainNotifier extends the standard mock with additional channels for
|
||||
// testing cooperative close reorgs.
|
||||
type mockChainNotifier struct {
|
||||
*lnmock.ChainNotifier
|
||||
|
||||
// confEvents tracks active confirmation event subscriptions.
|
||||
confEvents []*mockConfirmationEvent
|
||||
|
||||
// confRegistered is a channel that signals when a new confirmation
|
||||
// event has been registered.
|
||||
confRegistered chan struct{}
|
||||
|
||||
// spendEvents tracks active spend event subscriptions.
|
||||
spendEvents []*chainntnfs.SpendEvent
|
||||
|
||||
// spendRegistered is a channel that signals when a new spend
|
||||
// event has been registered.
|
||||
spendRegistered chan struct{}
|
||||
}
|
||||
|
||||
// mockConfirmationEvent represents a mock confirmation event subscription.
|
||||
type mockConfirmationEvent struct {
|
||||
txid chainhash.Hash
|
||||
numConfs uint32
|
||||
confirmedChan chan *chainntnfs.TxConfirmation
|
||||
negConfChan chan int32
|
||||
cancelled bool
|
||||
}
|
||||
|
||||
// RegisterSpendNtfn creates a new mock spend event.
|
||||
func (m *mockChainNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
|
||||
pkScript []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) {
|
||||
|
||||
// The base mock already has SpendChan, use that.
|
||||
spendEvent := &chainntnfs.SpendEvent{
|
||||
Spend: m.SpendChan,
|
||||
Cancel: func() {
|
||||
// No-op for now.
|
||||
},
|
||||
}
|
||||
|
||||
m.spendEvents = append(m.spendEvents, spendEvent)
|
||||
|
||||
// Signal that a new spend event has been registered.
|
||||
select {
|
||||
case m.spendRegistered <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
|
||||
return spendEvent, nil
|
||||
}
|
||||
|
||||
// RegisterConfirmationsNtfn creates a new mock confirmation event.
|
||||
func (m *mockChainNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash,
|
||||
pkScript []byte, numConfs, heightHint uint32,
|
||||
opts ...chainntnfs.NotifierOption,
|
||||
) (*chainntnfs.ConfirmationEvent, error) {
|
||||
|
||||
mockEvent := &mockConfirmationEvent{
|
||||
txid: *txid,
|
||||
numConfs: numConfs,
|
||||
confirmedChan: make(chan *chainntnfs.TxConfirmation, 1),
|
||||
negConfChan: make(chan int32, 1),
|
||||
}
|
||||
|
||||
m.confEvents = append(m.confEvents, mockEvent)
|
||||
|
||||
// Signal that a new confirmation event has been registered.
|
||||
select {
|
||||
case m.confRegistered <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
|
||||
return &chainntnfs.ConfirmationEvent{
|
||||
Confirmed: mockEvent.confirmedChan,
|
||||
NegativeConf: mockEvent.negConfChan,
|
||||
Cancel: func() {
|
||||
mockEvent.cancelled = true
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// harnessOpt is a functional option for configuring the test harness.
|
||||
type harnessOpt func(*harnessConfig)
|
||||
|
||||
// harnessConfig holds configuration for the test harness.
|
||||
type harnessConfig struct {
|
||||
requiredConfs fn.Option[uint32]
|
||||
}
|
||||
|
||||
// withRequiredConfs sets the number of confirmations required for channel
|
||||
// closes.
|
||||
func withRequiredConfs(confs uint32) harnessOpt {
|
||||
return func(cfg *harnessConfig) {
|
||||
cfg.requiredConfs = fn.Some(confs)
|
||||
}
|
||||
}
|
||||
|
||||
// newChainWatcherTestHarness creates a new test harness for chain watcher
|
||||
// tests.
|
||||
func newChainWatcherTestHarness(t *testing.T,
|
||||
opts ...harnessOpt) *chainWatcherTestHarness {
|
||||
|
||||
return newChainWatcherTestHarnessFromReporter(t, t, opts...)
|
||||
}
|
||||
|
||||
// newChainWatcherTestHarnessFromReporter creates a test harness that works
|
||||
// with both *testing.T and *rapid.T. The t parameter is used for
|
||||
// operations that specifically require *testing.T (like CreateTestChannels),
|
||||
// while reporter is used for all test reporting (Helper, Fatalf).
|
||||
func newChainWatcherTestHarnessFromReporter(t *testing.T,
|
||||
reporter testReporter, opts ...harnessOpt) *chainWatcherTestHarness {
|
||||
|
||||
reporter.Helper()
|
||||
|
||||
// Apply options.
|
||||
cfg := &harnessConfig{
|
||||
requiredConfs: fn.None[uint32](),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(cfg)
|
||||
}
|
||||
|
||||
// Create test channels.
|
||||
aliceChannel, bobChannel, err := lnwallet.CreateTestChannels(
|
||||
t, channeldb.SingleFunderTweaklessBit,
|
||||
)
|
||||
if err != nil {
|
||||
reporter.Fatalf("unable to create test channels: %v", err)
|
||||
}
|
||||
|
||||
// Create mock notifier.
|
||||
baseNotifier := &lnmock.ChainNotifier{
|
||||
SpendChan: make(chan *chainntnfs.SpendDetail, 1),
|
||||
EpochChan: make(chan *chainntnfs.BlockEpoch),
|
||||
ConfChan: make(chan *chainntnfs.TxConfirmation, 1),
|
||||
}
|
||||
|
||||
notifier := &mockChainNotifier{
|
||||
ChainNotifier: baseNotifier,
|
||||
confEvents: make([]*mockConfirmationEvent, 0),
|
||||
confRegistered: make(chan struct{}, 10),
|
||||
spendEvents: make([]*chainntnfs.SpendEvent, 0),
|
||||
spendRegistered: make(chan struct{}, 10),
|
||||
}
|
||||
|
||||
// Create chain watcher.
|
||||
chainWatcher, err := newChainWatcher(chainWatcherConfig{
|
||||
chanState: aliceChannel.State(),
|
||||
notifier: notifier,
|
||||
signer: aliceChannel.Signer,
|
||||
extractStateNumHint: lnwallet.GetStateNumHint,
|
||||
chanCloseConfs: cfg.requiredConfs,
|
||||
contractBreach: func(
|
||||
retInfo *lnwallet.BreachRetribution,
|
||||
) error {
|
||||
// In tests, we just need to accept the breach
|
||||
// notification.
|
||||
return nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
reporter.Fatalf("unable to create chain watcher: %v", err)
|
||||
}
|
||||
|
||||
// Start chain watcher (this will register for spend notification).
|
||||
err = chainWatcher.Start()
|
||||
if err != nil {
|
||||
reporter.Fatalf("unable to start chain watcher: %v", err)
|
||||
}
|
||||
|
||||
// Subscribe to channel events.
|
||||
chanEvents := chainWatcher.SubscribeChannelEvents()
|
||||
|
||||
harness := &chainWatcherTestHarness{
|
||||
t: reporter,
|
||||
aliceChannel: aliceChannel,
|
||||
bobChannel: bobChannel,
|
||||
chainWatcher: chainWatcher,
|
||||
notifier: notifier,
|
||||
chanEvents: chanEvents,
|
||||
currentHeight: 100,
|
||||
blockbeatProcessed: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Wait for the initial spend registration that happens in Start().
|
||||
harness.waitForSpendRegistration()
|
||||
|
||||
// Verify BlockbeatChan is initialized.
|
||||
if chainWatcher.BlockbeatChan == nil {
|
||||
reporter.Fatalf("BlockbeatChan is nil after initialization")
|
||||
}
|
||||
|
||||
// Register cleanup. We use the t for Cleanup since rapid.T
|
||||
// may not have this method in the same way.
|
||||
t.Cleanup(func() {
|
||||
_ = chainWatcher.Stop()
|
||||
})
|
||||
|
||||
return harness
|
||||
}
|
||||
|
||||
// createCoopCloseTx creates a cooperative close transaction with the given
|
||||
// output value. The transaction will have the proper sequence number to
|
||||
// indicate it's a cooperative close.
|
||||
func (h *chainWatcherTestHarness) createCoopCloseTx(
|
||||
outputValue int64) *wire.MsgTx {
|
||||
|
||||
fundingOutpoint := h.aliceChannel.State().FundingOutpoint
|
||||
|
||||
return &wire.MsgTx{
|
||||
TxIn: []*wire.TxIn{{
|
||||
PreviousOutPoint: fundingOutpoint,
|
||||
Sequence: wire.MaxTxInSequenceNum,
|
||||
}},
|
||||
TxOut: []*wire.TxOut{{
|
||||
Value: outputValue,
|
||||
// Unique script.
|
||||
PkScript: []byte{byte(outputValue % 255)},
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
// createRemoteForceCloseTx creates a remote force close transaction.
|
||||
// From Alice's perspective, this is Bob's local commitment transaction.
|
||||
func (h *chainWatcherTestHarness) createRemoteForceCloseTx() *wire.MsgTx {
|
||||
return h.bobChannel.State().LocalCommitment.CommitTx
|
||||
}
|
||||
|
||||
// createLocalForceCloseTx creates a local force close transaction.
|
||||
// This is Alice's local commitment transaction.
|
||||
func (h *chainWatcherTestHarness) createLocalForceCloseTx() *wire.MsgTx {
|
||||
return h.aliceChannel.State().LocalCommitment.CommitTx
|
||||
}
|
||||
|
||||
// createBreachCloseTx creates a breach (revoked commitment) transaction.
|
||||
// We advance the channel state, save the commitment, then advance again
|
||||
// to revoke it. Returns the revoked commitment tx.
|
||||
func (h *chainWatcherTestHarness) createBreachCloseTx() *wire.MsgTx {
|
||||
h.t.Helper()
|
||||
|
||||
// To create a revoked commitment, we need to advance the channel state
|
||||
// at least once. We'll use the test utils helper to add an HTLC and
|
||||
// force a state transition.
|
||||
|
||||
// Get the current commitment before we advance (this will be revoked).
|
||||
revokedCommit := h.bobChannel.State().LocalCommitment.CommitTx
|
||||
|
||||
// Add a fake HTLC to advance state.
|
||||
htlcAmount := lnwire.NewMSatFromSatoshis(10000)
|
||||
paymentHash := [32]byte{4, 5, 6}
|
||||
htlc := &lnwire.UpdateAddHTLC{
|
||||
ID: 0,
|
||||
Amount: htlcAmount,
|
||||
Expiry: uint32(h.currentHeight + 100),
|
||||
PaymentHash: paymentHash,
|
||||
}
|
||||
|
||||
// Add HTLC to both channels.
|
||||
if _, err := h.aliceChannel.AddHTLC(htlc, nil); err != nil {
|
||||
h.t.Fatalf("unable to add HTLC to alice: %v", err)
|
||||
}
|
||||
if _, err := h.bobChannel.ReceiveHTLC(htlc); err != nil {
|
||||
h.t.Fatalf("unable to add HTLC to bob: %v", err)
|
||||
}
|
||||
|
||||
// Force state transition using the helper.
|
||||
err := lnwallet.ForceStateTransition(h.aliceChannel, h.bobChannel)
|
||||
if err != nil {
|
||||
h.t.Fatalf("unable to force state transition: %v", err)
|
||||
}
|
||||
|
||||
// Return the revoked commitment (Bob's previous local commitment).
|
||||
return revokedCommit
|
||||
}
|
||||
|
||||
// sendSpend sends a spend notification for the given transaction.
|
||||
func (h *chainWatcherTestHarness) sendSpend(tx *wire.MsgTx) {
|
||||
h.t.Helper()
|
||||
|
||||
txHash := tx.TxHash()
|
||||
spend := &chainntnfs.SpendDetail{
|
||||
SpenderTxHash: &txHash,
|
||||
SpendingTx: tx,
|
||||
SpendingHeight: h.currentHeight,
|
||||
}
|
||||
|
||||
select {
|
||||
case h.notifier.SpendChan <- spend:
|
||||
case <-time.After(time.Second):
|
||||
h.t.Fatalf("unable to send spend notification")
|
||||
}
|
||||
}
|
||||
|
||||
// confirmTx sends a confirmation notification for the given transaction.
|
||||
func (h *chainWatcherTestHarness) confirmTx(tx *wire.MsgTx, height int32) {
|
||||
h.t.Helper()
|
||||
|
||||
// Find the confirmation event for this transaction.
|
||||
txHash := tx.TxHash()
|
||||
var confEvent *mockConfirmationEvent
|
||||
for _, event := range h.notifier.confEvents {
|
||||
if event.txid == txHash && !event.cancelled {
|
||||
confEvent = event
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if confEvent == nil {
|
||||
h.t.Fatalf("no confirmation event registered for tx %v", txHash)
|
||||
}
|
||||
|
||||
// Send confirmation.
|
||||
select {
|
||||
case confEvent.confirmedChan <- &chainntnfs.TxConfirmation{
|
||||
Tx: tx,
|
||||
BlockHeight: uint32(height),
|
||||
}:
|
||||
case <-time.After(time.Second):
|
||||
h.t.Fatalf("unable to send confirmation")
|
||||
}
|
||||
}
|
||||
|
||||
// triggerReorg sends a negative confirmation (reorg) notification for the
|
||||
// given transaction with the specified reorg depth.
|
||||
func (h *chainWatcherTestHarness) triggerReorg(tx *wire.MsgTx,
|
||||
reorgDepth int32) {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
// Find the confirmation event for this transaction.
|
||||
txHash := tx.TxHash()
|
||||
var confEvent *mockConfirmationEvent
|
||||
for _, event := range h.notifier.confEvents {
|
||||
if event.txid == txHash && !event.cancelled {
|
||||
confEvent = event
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if confEvent == nil {
|
||||
// The chain watcher might not have registered for
|
||||
// confirmations yet.
|
||||
return
|
||||
}
|
||||
|
||||
// Send negative confirmation.
|
||||
select {
|
||||
case confEvent.negConfChan <- reorgDepth:
|
||||
case <-time.After(time.Second):
|
||||
h.t.Fatalf("unable to send negative confirmation")
|
||||
}
|
||||
}
|
||||
|
||||
// mineBlocks advances the current block height.
|
||||
func (h *chainWatcherTestHarness) mineBlocks(n int32) {
|
||||
h.currentHeight += n
|
||||
}
|
||||
|
||||
// waitForCoopClose waits for a cooperative close event and returns it.
|
||||
func (h *chainWatcherTestHarness) waitForCoopClose(
|
||||
timeout time.Duration) *CooperativeCloseInfo {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
select {
|
||||
case coopClose := <-h.chanEvents.CooperativeClosure:
|
||||
return coopClose
|
||||
case <-time.After(timeout):
|
||||
h.t.Fatalf("didn't receive cooperative close event")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// waitForConfRegistration waits for the chain watcher to register for
|
||||
// confirmation notifications.
|
||||
func (h *chainWatcherTestHarness) waitForConfRegistration() {
|
||||
h.t.Helper()
|
||||
|
||||
select {
|
||||
case <-h.notifier.confRegistered:
|
||||
// Registration complete.
|
||||
case <-time.After(2 * time.Second):
|
||||
// Not necessarily a failure - some tests don't register.
|
||||
}
|
||||
}
|
||||
|
||||
// waitForSpendRegistration waits for the chain watcher to register for
|
||||
// spend notifications.
|
||||
func (h *chainWatcherTestHarness) waitForSpendRegistration() {
|
||||
h.t.Helper()
|
||||
|
||||
select {
|
||||
case <-h.notifier.spendRegistered:
|
||||
// Registration complete.
|
||||
case <-time.After(2 * time.Second):
|
||||
// Not necessarily a failure - some tests don't register.
|
||||
}
|
||||
}
|
||||
|
||||
// assertCoopCloseTx asserts that the given cooperative close info matches
|
||||
// the expected transaction.
|
||||
func (h *chainWatcherTestHarness) assertCoopCloseTx(
|
||||
closeInfo *CooperativeCloseInfo, expectedTx *wire.MsgTx) {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
expectedHash := expectedTx.TxHash()
|
||||
if closeInfo.ClosingTXID != expectedHash {
|
||||
h.t.Fatalf("wrong tx confirmed: expected %v, got %v",
|
||||
expectedHash, closeInfo.ClosingTXID)
|
||||
}
|
||||
}
|
||||
|
||||
// assertNoCoopClose asserts that no cooperative close event is received
|
||||
// within the given timeout.
|
||||
func (h *chainWatcherTestHarness) assertNoCoopClose(timeout time.Duration) {
|
||||
h.t.Helper()
|
||||
|
||||
select {
|
||||
case <-h.chanEvents.CooperativeClosure:
|
||||
h.t.Fatalf("unexpected cooperative close event")
|
||||
case <-time.After(timeout):
|
||||
// Expected timeout.
|
||||
}
|
||||
}
|
||||
|
||||
// runCoopCloseFlow runs a complete cooperative close flow including spend,
|
||||
// optional reorg, and confirmation. This helper coordinates the timing
|
||||
// between the different events.
|
||||
func (h *chainWatcherTestHarness) runCoopCloseFlow(
|
||||
tx *wire.MsgTx, shouldReorg bool, reorgDepth int32,
|
||||
altTx *wire.MsgTx) *CooperativeCloseInfo {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
// Send initial spend notification. The closeObserver's state machine
|
||||
// will detect this and register for confirmations.
|
||||
h.sendSpend(tx)
|
||||
|
||||
// Wait for the chain watcher to register for confirmations.
|
||||
h.waitForConfRegistration()
|
||||
|
||||
if shouldReorg {
|
||||
// Trigger reorg which resets the state machine.
|
||||
h.triggerReorg(tx, reorgDepth)
|
||||
|
||||
// If we have an alternative transaction, send it.
|
||||
if altTx != nil {
|
||||
// After reorg, the chain watcher should re-register for
|
||||
// ANY spend of the funding output.
|
||||
h.waitForSpendRegistration()
|
||||
|
||||
// Send alternative spend.
|
||||
h.sendSpend(altTx)
|
||||
|
||||
// Wait for it to register for confirmations.
|
||||
h.waitForConfRegistration()
|
||||
|
||||
// Confirm alternative transaction to unblock.
|
||||
h.mineBlocks(1)
|
||||
h.confirmTx(altTx, h.currentHeight)
|
||||
}
|
||||
} else {
|
||||
// Normal confirmation flow - confirm to unblock
|
||||
// waitForCoopCloseConfirmation.
|
||||
h.mineBlocks(1)
|
||||
h.confirmTx(tx, h.currentHeight)
|
||||
}
|
||||
|
||||
// Wait for cooperative close event.
|
||||
return h.waitForCoopClose(5 * time.Second)
|
||||
}
|
||||
|
||||
// runMultipleReorgFlow simulates multiple consecutive reorganizations with
|
||||
// different transactions confirming after each reorg.
|
||||
func (h *chainWatcherTestHarness) runMultipleReorgFlow(txs []*wire.MsgTx,
|
||||
reorgDepths []int32) *CooperativeCloseInfo {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
if len(txs) < 2 {
|
||||
h.t.Fatalf("need at least 2 transactions for reorg flow")
|
||||
}
|
||||
if len(reorgDepths) != len(txs)-1 {
|
||||
h.t.Fatalf("reorg depths must be one less than transactions")
|
||||
}
|
||||
|
||||
// Send initial spend.
|
||||
h.sendSpend(txs[0])
|
||||
|
||||
// Process each reorg.
|
||||
for i, depth := range reorgDepths {
|
||||
// Wait for confirmation registration.
|
||||
h.waitForConfRegistration()
|
||||
|
||||
// Trigger reorg for current transaction.
|
||||
h.triggerReorg(txs[i], depth)
|
||||
|
||||
// Wait for re-registration for spend.
|
||||
h.waitForSpendRegistration()
|
||||
|
||||
// Send next transaction.
|
||||
h.sendSpend(txs[i+1])
|
||||
}
|
||||
|
||||
// Wait for final confirmation registration.
|
||||
h.waitForConfRegistration()
|
||||
|
||||
// Confirm the final transaction.
|
||||
finalTx := txs[len(txs)-1]
|
||||
h.mineBlocks(1)
|
||||
h.confirmTx(finalTx, h.currentHeight)
|
||||
|
||||
// Wait for cooperative close event.
|
||||
return h.waitForCoopClose(10 * time.Second)
|
||||
}
|
||||
|
||||
// waitForRemoteUnilateralClose waits for a remote unilateral close event.
|
||||
func (h *chainWatcherTestHarness) waitForRemoteUnilateralClose(
|
||||
timeout time.Duration) *RemoteUnilateralCloseInfo {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
select {
|
||||
case remoteClose := <-h.chanEvents.RemoteUnilateralClosure:
|
||||
return remoteClose
|
||||
case <-time.After(timeout):
|
||||
h.t.Fatalf("didn't receive remote unilateral close event")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// waitForLocalUnilateralClose waits for a local unilateral close event.
|
||||
func (h *chainWatcherTestHarness) waitForLocalUnilateralClose(
|
||||
timeout time.Duration) *LocalUnilateralCloseInfo {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
select {
|
||||
case localClose := <-h.chanEvents.LocalUnilateralClosure:
|
||||
return localClose
|
||||
case <-time.After(timeout):
|
||||
h.t.Fatalf("didn't receive local unilateral close event")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// waitForBreach waits for a breach (contract breach) event.
|
||||
func (h *chainWatcherTestHarness) waitForBreach(
|
||||
timeout time.Duration) *BreachCloseInfo {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
select {
|
||||
case breach := <-h.chanEvents.ContractBreach:
|
||||
return breach
|
||||
case <-time.After(timeout):
|
||||
h.t.Fatalf("didn't receive contract breach event")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// assertRemoteUnilateralCloseTx asserts that the given remote unilateral close
|
||||
// info matches the expected transaction.
|
||||
func (h *chainWatcherTestHarness) assertRemoteUnilateralCloseTx(
|
||||
closeInfo *RemoteUnilateralCloseInfo, expectedTx *wire.MsgTx) {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
expectedHash := expectedTx.TxHash()
|
||||
actualHash := closeInfo.UnilateralCloseSummary.SpendDetail.SpenderTxHash
|
||||
if *actualHash != expectedHash {
|
||||
h.t.Fatalf("wrong tx confirmed: expected %v, got %v",
|
||||
expectedHash, *actualHash)
|
||||
}
|
||||
}
|
||||
|
||||
// assertLocalUnilateralCloseTx asserts that the given local unilateral close
|
||||
// info matches the expected transaction.
|
||||
func (h *chainWatcherTestHarness) assertLocalUnilateralCloseTx(
|
||||
closeInfo *LocalUnilateralCloseInfo, expectedTx *wire.MsgTx) {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
expectedHash := expectedTx.TxHash()
|
||||
actualHash := closeInfo.LocalForceCloseSummary.CloseTx.TxHash()
|
||||
if actualHash != expectedHash {
|
||||
h.t.Fatalf("wrong tx confirmed: expected %v, got %v",
|
||||
expectedHash, actualHash)
|
||||
}
|
||||
}
|
||||
|
||||
// assertBreachTx asserts that the given breach info matches the expected
|
||||
// transaction.
|
||||
func (h *chainWatcherTestHarness) assertBreachTx(
|
||||
breachInfo *BreachCloseInfo, expectedTx *wire.MsgTx) {
|
||||
|
||||
h.t.Helper()
|
||||
|
||||
expectedHash := expectedTx.TxHash()
|
||||
if breachInfo.CommitHash != expectedHash {
|
||||
h.t.Fatalf("wrong tx confirmed: expected %v, got %v",
|
||||
expectedHash, breachInfo.CommitHash)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user