You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

491 lines
11 KiB

  1. package loop
  2. import (
  3. "context"
  4. "testing"
  5. "github.com/lightninglabs/lndclient"
  6. "github.com/lightninglabs/loop/loopdb"
  7. "github.com/lightninglabs/loop/swap"
  8. "github.com/lightninglabs/loop/test"
  9. "github.com/lightningnetwork/lnd/chainntnfs"
  10. "github.com/lightningnetwork/lnd/channeldb"
  11. "github.com/stretchr/testify/require"
  12. "github.com/btcsuite/btcd/wire"
  13. "github.com/btcsuite/btcutil"
  14. )
  15. var (
  16. testLoopInRequest = LoopInRequest{
  17. Amount: btcutil.Amount(50000),
  18. MaxSwapFee: btcutil.Amount(1000),
  19. HtlcConfTarget: 2,
  20. Initiator: "test",
  21. }
  22. )
  23. // TestLoopInSuccess tests the success scenario where the swap completes the
  24. // happy flow.
  25. func TestLoopInSuccess(t *testing.T) {
  26. defer test.Guard(t)()
  27. ctx := newLoopInTestContext(t)
  28. height := int32(600)
  29. cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
  30. initResult, err := newLoopInSwap(
  31. context.Background(), cfg,
  32. height, &testLoopInRequest,
  33. )
  34. if err != nil {
  35. t.Fatal(err)
  36. }
  37. swap := initResult.swap
  38. ctx.store.assertLoopInStored()
  39. errChan := make(chan error)
  40. go func() {
  41. err := swap.execute(context.Background(), ctx.cfg, height)
  42. if err != nil {
  43. log.Error(err)
  44. }
  45. errChan <- err
  46. }()
  47. ctx.assertState(loopdb.StateInitiated)
  48. ctx.assertState(loopdb.StateHtlcPublished)
  49. ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
  50. // Expect htlc to be published.
  51. htlcTx := <-ctx.lnd.SendOutputsChannel
  52. // Expect the same state to be written again with the htlc tx hash.
  53. state := ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
  54. require.NotNil(t, state.HtlcTxHash)
  55. // Expect register for htlc conf.
  56. <-ctx.lnd.RegisterConfChannel
  57. <-ctx.lnd.RegisterConfChannel
  58. // Confirm htlc.
  59. ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{
  60. Tx: &htlcTx,
  61. }
  62. // Client starts listening for spend of htlc.
  63. <-ctx.lnd.RegisterSpendChannel
  64. // Client starts listening for swap invoice updates.
  65. subscription := <-ctx.lnd.SingleInvoiceSubcribeChannel
  66. if subscription.Hash != ctx.server.swapHash {
  67. t.Fatal("client subscribing to wrong invoice")
  68. }
  69. // Server has already paid invoice before spending the htlc. Signal
  70. // settled.
  71. subscription.Update <- lndclient.InvoiceUpdate{
  72. State: channeldb.ContractSettled,
  73. AmtPaid: 49000,
  74. }
  75. // Swap is expected to move to the state InvoiceSettled
  76. ctx.assertState(loopdb.StateInvoiceSettled)
  77. ctx.store.assertLoopInState(loopdb.StateInvoiceSettled)
  78. // Server spends htlc.
  79. successTx := wire.MsgTx{}
  80. successTx.AddTxIn(&wire.TxIn{
  81. Witness: [][]byte{{}, {}, {}},
  82. })
  83. ctx.lnd.SpendChannel <- &chainntnfs.SpendDetail{
  84. SpendingTx: &successTx,
  85. SpenderInputIndex: 0,
  86. }
  87. ctx.assertState(loopdb.StateSuccess)
  88. ctx.store.assertLoopInState(loopdb.StateSuccess)
  89. err = <-errChan
  90. if err != nil {
  91. t.Fatal(err)
  92. }
  93. }
  94. // TestLoopInTimeout tests scenarios where the server doesn't sweep the htlc
  95. // and the client is forced to reclaim the funds using the timeout tx.
  96. func TestLoopInTimeout(t *testing.T) {
  97. testAmt := int64(testLoopInRequest.Amount)
  98. t.Run("internal htlc", func(t *testing.T) {
  99. testLoopInTimeout(t, swap.HtlcP2WSH, 0)
  100. })
  101. outputTypes := []swap.HtlcOutputType{swap.HtlcP2WSH, swap.HtlcNP2WSH}
  102. for _, outputType := range outputTypes {
  103. outputType := outputType
  104. t.Run(outputType.String(), func(t *testing.T) {
  105. t.Run("external htlc", func(t *testing.T) {
  106. testLoopInTimeout(t, outputType, testAmt)
  107. })
  108. t.Run("external amount too high", func(t *testing.T) {
  109. testLoopInTimeout(t, outputType, testAmt+1)
  110. })
  111. t.Run("external amount too low", func(t *testing.T) {
  112. testLoopInTimeout(t, outputType, testAmt-1)
  113. })
  114. })
  115. }
  116. }
  117. func testLoopInTimeout(t *testing.T,
  118. outputType swap.HtlcOutputType, externalValue int64) {
  119. defer test.Guard(t)()
  120. ctx := newLoopInTestContext(t)
  121. height := int32(600)
  122. cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
  123. req := testLoopInRequest
  124. if externalValue != 0 {
  125. req.ExternalHtlc = true
  126. }
  127. initResult, err := newLoopInSwap(
  128. context.Background(), cfg,
  129. height, &req,
  130. )
  131. if err != nil {
  132. t.Fatal(err)
  133. }
  134. s := initResult.swap
  135. ctx.store.assertLoopInStored()
  136. errChan := make(chan error)
  137. go func() {
  138. err := s.execute(context.Background(), ctx.cfg, height)
  139. if err != nil {
  140. log.Error(err)
  141. }
  142. errChan <- err
  143. }()
  144. ctx.assertState(loopdb.StateInitiated)
  145. ctx.assertState(loopdb.StateHtlcPublished)
  146. ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
  147. var htlcTx wire.MsgTx
  148. if externalValue == 0 {
  149. // Expect htlc to be published.
  150. htlcTx = <-ctx.lnd.SendOutputsChannel
  151. // Expect the same state to be written again with the htlc tx hash.
  152. state := ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
  153. require.NotNil(t, state.HtlcTxHash)
  154. } else {
  155. // Create an external htlc publish tx.
  156. var pkScript []byte
  157. if outputType == swap.HtlcNP2WSH {
  158. pkScript = s.htlcNP2WSH.PkScript
  159. } else {
  160. pkScript = s.htlcP2WSH.PkScript
  161. }
  162. htlcTx = wire.MsgTx{
  163. TxOut: []*wire.TxOut{
  164. {
  165. PkScript: pkScript,
  166. Value: externalValue,
  167. },
  168. },
  169. }
  170. }
  171. // Expect register for htlc conf.
  172. <-ctx.lnd.RegisterConfChannel
  173. <-ctx.lnd.RegisterConfChannel
  174. // Confirm htlc.
  175. ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{
  176. Tx: &htlcTx,
  177. }
  178. // Assert that the swap is failed in case of an invalid amount.
  179. invalidAmt := externalValue != 0 && externalValue != int64(req.Amount)
  180. if invalidAmt {
  181. ctx.assertState(loopdb.StateFailIncorrectHtlcAmt)
  182. ctx.store.assertLoopInState(loopdb.StateFailIncorrectHtlcAmt)
  183. err = <-errChan
  184. if err != nil {
  185. t.Fatal(err)
  186. }
  187. return
  188. }
  189. // Client starts listening for spend of htlc.
  190. <-ctx.lnd.RegisterSpendChannel
  191. // Client starts listening for swap invoice updates.
  192. subscription := <-ctx.lnd.SingleInvoiceSubcribeChannel
  193. if subscription.Hash != ctx.server.swapHash {
  194. t.Fatal("client subscribing to wrong invoice")
  195. }
  196. // Let htlc expire.
  197. ctx.blockEpochChan <- s.LoopInContract.CltvExpiry
  198. // Expect a signing request for the htlc tx output value.
  199. signReq := <-ctx.lnd.SignOutputRawChannel
  200. if signReq.SignDescriptors[0].Output.Value != htlcTx.TxOut[0].Value {
  201. t.Fatal("invalid signing amount")
  202. }
  203. // Expect timeout tx to be published.
  204. timeoutTx := <-ctx.lnd.TxPublishChannel
  205. // Confirm timeout tx.
  206. ctx.lnd.SpendChannel <- &chainntnfs.SpendDetail{
  207. SpendingTx: timeoutTx,
  208. SpenderInputIndex: 0,
  209. }
  210. // Now that timeout tx has confirmed, the client should be able to
  211. // safely cancel the swap invoice.
  212. <-ctx.lnd.FailInvoiceChannel
  213. // Signal the the invoice was canceled.
  214. subscription.Update <- lndclient.InvoiceUpdate{
  215. State: channeldb.ContractCanceled,
  216. }
  217. ctx.assertState(loopdb.StateFailTimeout)
  218. ctx.store.assertLoopInState(loopdb.StateFailTimeout)
  219. err = <-errChan
  220. if err != nil {
  221. t.Fatal(err)
  222. }
  223. }
  224. // TestLoopInResume tests resuming swaps in various states.
  225. func TestLoopInResume(t *testing.T) {
  226. storedVersion := []loopdb.ProtocolVersion{
  227. loopdb.ProtocolVersionUnrecorded,
  228. loopdb.ProtocolVersionHtlcV2,
  229. }
  230. htlcVersion := []swap.ScriptVersion{
  231. swap.HtlcV1,
  232. swap.HtlcV2,
  233. }
  234. for i, version := range storedVersion {
  235. version := version
  236. scriptVersion := htlcVersion[i]
  237. t.Run(version.String(), func(t *testing.T) {
  238. t.Run("initiated", func(t *testing.T) {
  239. testLoopInResume(
  240. t, loopdb.StateInitiated, false,
  241. version, scriptVersion,
  242. )
  243. })
  244. t.Run("initiated expired", func(t *testing.T) {
  245. testLoopInResume(
  246. t, loopdb.StateInitiated, true,
  247. version, scriptVersion,
  248. )
  249. })
  250. t.Run("htlc published", func(t *testing.T) {
  251. testLoopInResume(
  252. t, loopdb.StateHtlcPublished, false,
  253. version, scriptVersion,
  254. )
  255. })
  256. })
  257. }
  258. }
  259. func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
  260. storedVersion loopdb.ProtocolVersion, scriptVersion swap.ScriptVersion) {
  261. defer test.Guard(t)()
  262. ctx := newLoopInTestContext(t)
  263. cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server)
  264. senderKey := [33]byte{4}
  265. receiverKey := [33]byte{5}
  266. contract := &loopdb.LoopInContract{
  267. HtlcConfTarget: 2,
  268. SwapContract: loopdb.SwapContract{
  269. Preimage: testPreimage,
  270. AmountRequested: 100000,
  271. CltvExpiry: 744,
  272. ReceiverKey: receiverKey,
  273. SenderKey: senderKey,
  274. MaxSwapFee: 60000,
  275. MaxMinerFee: 50000,
  276. ProtocolVersion: storedVersion,
  277. },
  278. }
  279. pendSwap := &loopdb.LoopIn{
  280. Contract: contract,
  281. Loop: loopdb.Loop{
  282. Events: []*loopdb.LoopEvent{
  283. {
  284. SwapStateData: loopdb.SwapStateData{
  285. State: state,
  286. },
  287. },
  288. },
  289. Hash: testPreimage.Hash(),
  290. },
  291. }
  292. htlc, err := swap.NewHtlc(
  293. scriptVersion, contract.CltvExpiry, contract.SenderKey,
  294. contract.ReceiverKey, testPreimage.Hash(), swap.HtlcNP2WSH,
  295. cfg.lnd.ChainParams,
  296. )
  297. if err != nil {
  298. t.Fatal(err)
  299. }
  300. err = ctx.store.CreateLoopIn(testPreimage.Hash(), contract)
  301. if err != nil {
  302. t.Fatal(err)
  303. }
  304. swap, err := resumeLoopInSwap(
  305. context.Background(), cfg,
  306. pendSwap,
  307. )
  308. if err != nil {
  309. t.Fatal(err)
  310. }
  311. var height int32
  312. if expired {
  313. height = 740
  314. } else {
  315. height = 600
  316. }
  317. errChan := make(chan error)
  318. go func() {
  319. err := swap.execute(context.Background(), ctx.cfg, height)
  320. if err != nil {
  321. log.Error(err)
  322. }
  323. errChan <- err
  324. }()
  325. defer func() {
  326. err = <-errChan
  327. if err != nil {
  328. t.Fatal(err)
  329. }
  330. select {
  331. case <-ctx.lnd.SendPaymentChannel:
  332. t.Fatal("unexpected payment sent")
  333. default:
  334. }
  335. select {
  336. case <-ctx.lnd.SendOutputsChannel:
  337. t.Fatal("unexpected tx published")
  338. default:
  339. }
  340. }()
  341. var htlcTx wire.MsgTx
  342. if state == loopdb.StateInitiated {
  343. ctx.assertState(loopdb.StateInitiated)
  344. if expired {
  345. ctx.assertState(loopdb.StateFailTimeout)
  346. return
  347. }
  348. ctx.assertState(loopdb.StateHtlcPublished)
  349. ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
  350. // Expect htlc to be published.
  351. htlcTx = <-ctx.lnd.SendOutputsChannel
  352. // Expect the same state to be written again with the htlc tx
  353. // hash.
  354. state := ctx.store.assertLoopInState(loopdb.StateHtlcPublished)
  355. require.NotNil(t, state.HtlcTxHash)
  356. } else {
  357. ctx.assertState(loopdb.StateHtlcPublished)
  358. htlcTx.AddTxOut(&wire.TxOut{
  359. PkScript: htlc.PkScript,
  360. Value: int64(contract.AmountRequested),
  361. })
  362. }
  363. // Expect register for htlc conf.
  364. <-ctx.lnd.RegisterConfChannel
  365. <-ctx.lnd.RegisterConfChannel
  366. // Confirm htlc.
  367. ctx.lnd.ConfChannel <- &chainntnfs.TxConfirmation{
  368. Tx: &htlcTx,
  369. }
  370. // Client starts listening for spend of htlc.
  371. <-ctx.lnd.RegisterSpendChannel
  372. // Client starts listening for swap invoice updates.
  373. subscription := <-ctx.lnd.SingleInvoiceSubcribeChannel
  374. if subscription.Hash != testPreimage.Hash() {
  375. t.Fatal("client subscribing to wrong invoice")
  376. }
  377. // Server has already paid invoice before spending the htlc. Signal
  378. // settled.
  379. subscription.Update <- lndclient.InvoiceUpdate{
  380. State: channeldb.ContractSettled,
  381. AmtPaid: 49000,
  382. }
  383. // Swap is expected to move to the state InvoiceSettled
  384. ctx.assertState(loopdb.StateInvoiceSettled)
  385. ctx.store.assertLoopInState(loopdb.StateInvoiceSettled)
  386. // Server spends htlc.
  387. successTx := wire.MsgTx{}
  388. successTx.AddTxIn(&wire.TxIn{
  389. Witness: [][]byte{{}, {}, {}},
  390. })
  391. successTxHash := successTx.TxHash()
  392. ctx.lnd.SpendChannel <- &chainntnfs.SpendDetail{
  393. SpendingTx: &successTx,
  394. SpenderTxHash: &successTxHash,
  395. SpenderInputIndex: 0,
  396. }
  397. ctx.assertState(loopdb.StateSuccess)
  398. }