breacharbiter_test: distinguish spending transactions from justice tx

inputs

Since we want to test more complex combinations of spends of the
breached outputs, we use two maps tracking

1. which transaction will spend the outpoint
2. which outpoints we expect the breacharbiter to include in the justice
   tx

This let us trigger spends of the individual outputs, and depending on
what we want to test check whether the breacharbiter sweeps the expected
outpoints.
This commit is contained in:
Johan T. Halseth 2021-04-20 14:54:31 +02:00
parent 3aa5e650fb
commit a192718807
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26

@ -1220,7 +1220,7 @@ func TestBreachHandoffFail(t *testing.T) {
assertArbiterBreach(t, brar, chanPoint) assertArbiterBreach(t, brar, chanPoint)
} }
type publAssertion func(*testing.T, map[wire.OutPoint]*wire.MsgTx, type publAssertion func(*testing.T, map[wire.OutPoint]struct{},
chan *wire.MsgTx) chan *wire.MsgTx)
type breachTest struct { type breachTest struct {
@ -1271,7 +1271,7 @@ var breachTests = []breachTest{
name: "all spends", name: "all spends",
spend2ndLevel: true, spend2ndLevel: true,
whenNonZeroInputs: func(t *testing.T, whenNonZeroInputs: func(t *testing.T,
inputs map[wire.OutPoint]*wire.MsgTx, inputs map[wire.OutPoint]struct{},
publTx chan *wire.MsgTx) { publTx chan *wire.MsgTx) {
var tx *wire.MsgTx var tx *wire.MsgTx
@ -1281,7 +1281,7 @@ var breachTests = []breachTest{
t.Fatalf("tx was not published") t.Fatalf("tx was not published")
} }
// The justice transaction should have thee same number // The justice transaction should have the same number
// of inputs as we are tracking in the test. // of inputs as we are tracking in the test.
if len(tx.TxIn) != len(inputs) { if len(tx.TxIn) != len(inputs) {
t.Fatalf("expected justice txn to have %d "+ t.Fatalf("expected justice txn to have %d "+
@ -1297,7 +1297,7 @@ var breachTests = []breachTest{
}, },
whenZeroInputs: func(t *testing.T, whenZeroInputs: func(t *testing.T,
inputs map[wire.OutPoint]*wire.MsgTx, inputs map[wire.OutPoint]struct{},
publTx chan *wire.MsgTx) { publTx chan *wire.MsgTx) {
// Sanity check to ensure the brar doesn't try to // Sanity check to ensure the brar doesn't try to
@ -1315,17 +1315,33 @@ var breachTests = []breachTest{
spend2ndLevel: false, spend2ndLevel: false,
sendFinalConf: true, sendFinalConf: true,
whenNonZeroInputs: func(t *testing.T, whenNonZeroInputs: func(t *testing.T,
inputs map[wire.OutPoint]*wire.MsgTx, inputs map[wire.OutPoint]struct{},
publTx chan *wire.MsgTx) { publTx chan *wire.MsgTx) {
var tx *wire.MsgTx
select { select {
case <-publTx: case tx = <-publTx:
case <-time.After(5 * time.Second): case <-time.After(5 * time.Second):
t.Fatalf("tx was not published") t.Fatalf("tx was not published")
} }
// The justice transaction should have the same number
// of inputs as we are tracking in the test.
if len(tx.TxIn) != len(inputs) {
t.Fatalf("expected justice txn to have %d "+
"inputs, found %d", len(inputs),
len(tx.TxIn))
}
// Ensure that each input exists on the justice
// transaction.
for in := range inputs {
findInputIndex(t, in, tx)
}
}, },
whenZeroInputs: func(t *testing.T, whenZeroInputs: func(t *testing.T,
inputs map[wire.OutPoint]*wire.MsgTx, inputs map[wire.OutPoint]struct{},
publTx chan *wire.MsgTx) { publTx chan *wire.MsgTx) {
// Now a transaction attempting to spend from the second // Now a transaction attempting to spend from the second
@ -1486,49 +1502,69 @@ func testBreachSpends(t *testing.T, test breachTest) {
// we want it to be spent by. As the test progresses, this map will be // we want it to be spent by. As the test progresses, this map will be
// updated to contain only the set of commitment or second level // updated to contain only the set of commitment or second level
// outpoints that remain to be spent. // outpoints that remain to be spent.
inputs := map[wire.OutPoint]*wire.MsgTx{ spentBy := map[wire.OutPoint]*wire.MsgTx{
htlcOutpoint: htlc2ndLevlTx, htlcOutpoint: htlc2ndLevlTx,
localOutpoint: commitSpendTx, localOutpoint: commitSpendTx,
remoteOutpoint: commitSpendTx, remoteOutpoint: commitSpendTx,
} }
// We also keep a map of those remaining outputs we expect the
// breacharbiter to try and sweep.
inputsToSweep := map[wire.OutPoint]struct{}{
htlcOutpoint: {},
localOutpoint: {},
remoteOutpoint: {},
}
// Until no more inputs to spend remain, deliver the spend events and // Until no more inputs to spend remain, deliver the spend events and
// process the assertions prescribed by the test case. // process the assertions prescribed by the test case.
for len(inputs) > 0 { for len(spentBy) > 0 {
var ( var (
op wire.OutPoint op wire.OutPoint
spendTx *wire.MsgTx spendTx *wire.MsgTx
) )
// Pick an outpoint at random from the set of inputs. // Pick an outpoint at random from the set of inputs.
for op, spendTx = range inputs { for op, spendTx = range spentBy {
delete(inputs, op) delete(spentBy, op)
break break
} }
// Deliver the spend notification for the chosen transaction. // Deliver the spend notification for the chosen transaction.
notifier.Spend(&op, 2, spendTx) notifier.Spend(&op, 2, spendTx)
// When the second layer transfer is detected, add back the // Since the remote just swept this input, we expect our next
// outpoint of the second layer tx so that we can spend it // justice transaction to not include them.
// again. Only do so if the test requests this behavior. delete(inputsToSweep, op)
// If this is the second-level spend, we must add the new
// outpoint to our expected sweeps.
spendTxID := spendTx.TxHash() spendTxID := spendTx.TxHash()
if test.spend2ndLevel && spendTxID == htlc2ndLevlTx.TxHash() { if spendTxID == htlc2ndLevlTx.TxHash() {
// Create the second level outpoint that will be spent, // Create the second level outpoint that will
// the index is always zero for these 1-in-1-out txns. // be spent, the index is always zero for these
// 1-in-1-out txns.
spendOp := wire.OutPoint{Hash: spendTxID} spendOp := wire.OutPoint{Hash: spendTxID}
inputs[spendOp] = htlcSpendTx inputsToSweep[spendOp] = struct{}{}
// When the second layer transfer is detected, add back
// the outpoint of the second layer tx so that we can
// spend it again. Only do so if the test requests this
// behavior.
if test.spend2ndLevel {
spentBy[spendOp] = htlcSpendTx
}
} }
if len(inputs) > 0 { if len(spentBy) > 0 {
test.whenNonZeroInputs(t, inputs, publTx) test.whenNonZeroInputs(t, inputsToSweep, publTx)
} else { } else {
// Reset the publishing error so that any publication, // Reset the publishing error so that any publication,
// made by the breach arbiter, if any, will succeed. // made by the breach arbiter, if any, will succeed.
publMtx.Lock() publMtx.Lock()
publErr = nil publErr = nil
publMtx.Unlock() publMtx.Unlock()
test.whenZeroInputs(t, inputs, publTx) test.whenZeroInputs(t, inputsToSweep, publTx)
} }
} }