github.com/decred/dcrlnd@v0.7.6/contractcourt/htlc_outgoing_contest_resolver_test.go (about) 1 package contractcourt 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/decred/dcrd/wire" 8 "github.com/decred/dcrlnd/chainntnfs" 9 "github.com/decred/dcrlnd/channeldb" 10 "github.com/decred/dcrlnd/input" 11 "github.com/decred/dcrlnd/kvdb" 12 "github.com/decred/dcrlnd/lntest/mock" 13 "github.com/decred/dcrlnd/lntypes" 14 "github.com/decred/dcrlnd/lnwallet" 15 "github.com/decred/dcrlnd/lnwire" 16 ) 17 18 const ( 19 outgoingContestHtlcExpiry = 110 20 ) 21 22 // TestHtlcOutgoingResolverTimeout tests resolution of an offered htlc that 23 // timed out. 24 func TestHtlcOutgoingResolverTimeout(t *testing.T) { 25 t.Parallel() 26 defer timeout(t)() 27 28 // Setup the resolver with our test resolution. 29 ctx := newOutgoingResolverTestContext(t) 30 31 // Start the resolution process in a goroutine. 32 ctx.resolve() 33 34 // Notify arrival of the block after which the timeout path of the htlc 35 // unlocks. 36 ctx.notifyEpoch(outgoingContestHtlcExpiry - 1) 37 38 // Assert that the resolver finishes without error and transforms in a 39 // timeout resolver. 40 ctx.waitForResult(true) 41 } 42 43 // TestHtlcOutgoingResolverRemoteClaim tests resolution of an offered htlc that 44 // is claimed by the remote party. 45 func TestHtlcOutgoingResolverRemoteClaim(t *testing.T) { 46 t.Parallel() 47 defer timeout(t)() 48 49 // Setup the resolver with our test resolution and start the resolution 50 // process. 51 ctx := newOutgoingResolverTestContext(t) 52 53 // Replace our mocked checkpoint function with one which will push 54 // reports into a channel for us to consume. We do so on the resolver 55 // level because our test context has already created the resolver. 56 reportChan := make(chan *channeldb.ResolverReport) 57 ctx.resolver.Checkpoint = func(_ ContractResolver, 58 reports ...*channeldb.ResolverReport) error { 59 60 // Send all of our reports into the channel. 61 for _, report := range reports { 62 reportChan <- report 63 } 64 65 return nil 66 } 67 68 ctx.resolve() 69 70 // The remote party sweeps the htlc. Notify our resolver of this event. 71 preimage := lntypes.Preimage{} 72 sigScript := [35]byte{ 73 0: 0x51, 74 1: 0x52, 75 2: 0x20, 76 } 77 copy(sigScript[3:], preimage[:]) 78 spendTx := &wire.MsgTx{ 79 TxIn: []*wire.TxIn{ 80 { 81 SignatureScript: sigScript[:], 82 }, 83 }, 84 } 85 86 spendHash := spendTx.TxHash() 87 88 ctx.notifier.SpendChan <- &chainntnfs.SpendDetail{ 89 SpendingTx: spendTx, 90 SpenderTxHash: &spendHash, 91 } 92 93 // We expect the extracted preimage to be added to the witness beacon. 94 <-ctx.preimageDB.newPreimages 95 96 // We also expect a resolution message to the incoming side of the 97 // circuit. 98 <-ctx.resolutionChan 99 100 // Finally, check that we have a report as expected. 101 expectedReport := &channeldb.ResolverReport{ 102 OutPoint: wire.OutPoint{}, 103 Amount: 0, 104 ResolverType: channeldb.ResolverTypeOutgoingHtlc, 105 ResolverOutcome: channeldb.ResolverOutcomeClaimed, 106 SpendTxID: &spendHash, 107 } 108 109 assertResolverReport(t, reportChan, expectedReport) 110 111 // Assert that the resolver finishes without error. 112 ctx.waitForResult(false) 113 } 114 115 type resolveResult struct { 116 err error 117 nextResolver ContractResolver 118 } 119 120 type outgoingResolverTestContext struct { 121 resolver *htlcOutgoingContestResolver 122 notifier *mock.ChainNotifier 123 preimageDB *mockWitnessBeacon 124 resolverResultChan chan resolveResult 125 resolutionChan chan ResolutionMsg 126 t *testing.T 127 } 128 129 func newOutgoingResolverTestContext(t *testing.T) *outgoingResolverTestContext { 130 notifier := &mock.ChainNotifier{ 131 EpochChan: make(chan *chainntnfs.BlockEpoch), 132 SpendChan: make(chan *chainntnfs.SpendDetail), 133 ConfChan: make(chan *chainntnfs.TxConfirmation), 134 } 135 136 checkPointChan := make(chan struct{}, 1) 137 resolutionChan := make(chan ResolutionMsg, 1) 138 139 preimageDB := newMockWitnessBeacon() 140 141 onionProcessor := &mockOnionProcessor{} 142 143 chainCfg := ChannelArbitratorConfig{ 144 ChainArbitratorConfig: ChainArbitratorConfig{ 145 Notifier: notifier, 146 PreimageDB: preimageDB, 147 DeliverResolutionMsg: func(msgs ...ResolutionMsg) error { 148 if len(msgs) != 1 { 149 return fmt.Errorf("expected 1 "+ 150 "resolution msg, instead got %v", 151 len(msgs)) 152 } 153 154 resolutionChan <- msgs[0] 155 return nil 156 }, 157 OnionProcessor: onionProcessor, 158 }, 159 PutResolverReport: func(_ kvdb.RwTx, 160 _ *channeldb.ResolverReport) error { 161 162 return nil 163 }, 164 } 165 166 outgoingRes := lnwallet.OutgoingHtlcResolution{ 167 Expiry: outgoingContestHtlcExpiry, 168 SweepSignDesc: input.SignDescriptor{ 169 Output: &wire.TxOut{}, 170 }, 171 } 172 173 cfg := ResolverConfig{ 174 ChannelArbitratorConfig: chainCfg, 175 Checkpoint: func(_ ContractResolver, 176 _ ...*channeldb.ResolverReport) error { 177 178 checkPointChan <- struct{}{} 179 return nil 180 }, 181 } 182 183 resolver := &htlcOutgoingContestResolver{ 184 htlcTimeoutResolver: &htlcTimeoutResolver{ 185 contractResolverKit: *newContractResolverKit(cfg), 186 htlcResolution: outgoingRes, 187 htlc: channeldb.HTLC{ 188 Amt: lnwire.MilliAtom(testHtlcAmount), 189 RHash: testResHash, 190 OnionBlob: testOnionBlob, 191 }, 192 }, 193 } 194 195 return &outgoingResolverTestContext{ 196 resolver: resolver, 197 notifier: notifier, 198 preimageDB: preimageDB, 199 resolutionChan: resolutionChan, 200 t: t, 201 } 202 } 203 204 func (i *outgoingResolverTestContext) resolve() { 205 // Start resolver. 206 i.resolverResultChan = make(chan resolveResult, 1) 207 go func() { 208 nextResolver, err := i.resolver.Resolve() 209 i.resolverResultChan <- resolveResult{ 210 nextResolver: nextResolver, 211 err: err, 212 } 213 }() 214 215 // Notify initial block height. 216 i.notifyEpoch(testInitialBlockHeight) 217 } 218 219 func (i *outgoingResolverTestContext) notifyEpoch(height int32) { 220 i.notifier.EpochChan <- &chainntnfs.BlockEpoch{ 221 Height: height, 222 } 223 } 224 225 func (i *outgoingResolverTestContext) waitForResult(expectTimeoutRes bool) { 226 i.t.Helper() 227 228 result := <-i.resolverResultChan 229 if result.err != nil { 230 i.t.Fatal(result.err) 231 } 232 233 if !expectTimeoutRes { 234 if result.nextResolver != nil { 235 i.t.Fatal("expected no next resolver") 236 } 237 return 238 } 239 240 _, ok := result.nextResolver.(*htlcTimeoutResolver) 241 if !ok { 242 i.t.Fatal("expected htlcTimeoutResolver") 243 } 244 }