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.

373 lines
9.1 KiB

  1. package loop
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/sha256"
  6. "errors"
  7. "testing"
  8. "github.com/btcsuite/btcd/chaincfg"
  9. "github.com/btcsuite/btcd/chaincfg/chainhash"
  10. "github.com/btcsuite/btcutil"
  11. "github.com/lightninglabs/lndclient"
  12. "github.com/lightninglabs/loop/loopdb"
  13. "github.com/lightninglabs/loop/swap"
  14. "github.com/lightninglabs/loop/test"
  15. "github.com/lightningnetwork/lnd/lnrpc"
  16. "github.com/lightningnetwork/lnd/lntypes"
  17. "github.com/stretchr/testify/require"
  18. )
  19. var (
  20. testAddr, _ = btcutil.NewAddressScriptHash(
  21. []byte{123}, &chaincfg.TestNet3Params,
  22. )
  23. testRequest = &OutRequest{
  24. Amount: btcutil.Amount(50000),
  25. DestAddr: testAddr,
  26. MaxMinerFee: 50000,
  27. HtlcConfirmations: defaultConfirmations,
  28. SweepConfTarget: 2,
  29. MaxSwapFee: 1050,
  30. MaxPrepayAmount: 100,
  31. MaxPrepayRoutingFee: 75000,
  32. MaxSwapRoutingFee: 70000,
  33. Initiator: "test",
  34. }
  35. swapInvoiceDesc = "swap"
  36. prepayInvoiceDesc = "prepay"
  37. defaultConfirmations = int32(loopdb.DefaultLoopOutHtlcConfirmations)
  38. )
  39. // TestSuccess tests the loop out happy flow, using a custom htlc confirmation
  40. // target.
  41. func TestSuccess(t *testing.T) {
  42. defer test.Guard(t)()
  43. ctx := createClientTestContext(t, nil)
  44. req := *testRequest
  45. req.HtlcConfirmations = 2
  46. // Initiate loop out.
  47. info, err := ctx.swapClient.LoopOut(context.Background(), &req)
  48. if err != nil {
  49. t.Fatal(err)
  50. }
  51. ctx.assertStored()
  52. ctx.assertStatus(loopdb.StateInitiated)
  53. signalSwapPaymentResult := ctx.AssertPaid(swapInvoiceDesc)
  54. signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
  55. // Expect client to register for conf.
  56. confIntent := ctx.AssertRegisterConf(false, req.HtlcConfirmations)
  57. testSuccess(ctx, testRequest.Amount, info.SwapHash,
  58. signalPrepaymentResult, signalSwapPaymentResult, false,
  59. confIntent, swap.HtlcV2,
  60. )
  61. }
  62. // TestFailOffchain tests the handling of swap for which the server failed the
  63. // payments.
  64. func TestFailOffchain(t *testing.T) {
  65. defer test.Guard(t)()
  66. ctx := createClientTestContext(t, nil)
  67. _, err := ctx.swapClient.LoopOut(context.Background(), testRequest)
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. ctx.assertStored()
  72. ctx.assertStatus(loopdb.StateInitiated)
  73. signalSwapPaymentResult := ctx.AssertPaid(swapInvoiceDesc)
  74. signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
  75. ctx.AssertRegisterConf(false, defaultConfirmations)
  76. signalSwapPaymentResult(
  77. errors.New(lndclient.PaymentResultUnknownPaymentHash),
  78. )
  79. signalPrepaymentResult(
  80. errors.New(lndclient.PaymentResultUnknownPaymentHash),
  81. )
  82. ctx.assertStatus(loopdb.StateFailOffchainPayments)
  83. ctx.assertStoreFinished(loopdb.StateFailOffchainPayments)
  84. ctx.finish()
  85. }
  86. // TestWrongAmount asserts that the client checks the server invoice amounts.
  87. func TestFailWrongAmount(t *testing.T) {
  88. defer test.Guard(t)()
  89. test := func(t *testing.T, modifier func(*serverMock),
  90. expectedErr error) {
  91. ctx := createClientTestContext(t, nil)
  92. // Modify mock for this subtest.
  93. modifier(ctx.serverMock)
  94. _, err := ctx.swapClient.LoopOut(
  95. context.Background(), testRequest,
  96. )
  97. if err != expectedErr {
  98. t.Fatalf("Expected %v, but got %v", expectedErr, err)
  99. }
  100. ctx.finish()
  101. }
  102. t.Run("swap fee too high", func(t *testing.T) {
  103. test(t, func(m *serverMock) {
  104. m.swapInvoiceAmt += 10
  105. }, ErrSwapFeeTooHigh)
  106. })
  107. t.Run("prepay amount too high", func(t *testing.T) {
  108. test(t, func(m *serverMock) {
  109. // Keep total swap fee unchanged, but increase prepaid
  110. // portion.
  111. m.swapInvoiceAmt -= 10
  112. m.prepayInvoiceAmt += 10
  113. }, ErrPrepayAmountTooHigh)
  114. })
  115. }
  116. // TestResume tests that swaps in various states are properly resumed after a
  117. // restart.
  118. func TestResume(t *testing.T) {
  119. defer test.Guard(t)()
  120. defaultConfs := loopdb.DefaultLoopOutHtlcConfirmations
  121. storedVersion := []loopdb.ProtocolVersion{
  122. loopdb.ProtocolVersionUnrecorded,
  123. loopdb.ProtocolVersionHtlcV2,
  124. }
  125. for _, version := range storedVersion {
  126. version := version
  127. t.Run(version.String(), func(t *testing.T) {
  128. t.Run("not expired", func(t *testing.T) {
  129. testResume(
  130. t, defaultConfs, false, false, true,
  131. version,
  132. )
  133. })
  134. t.Run("not expired, custom confirmations",
  135. func(t *testing.T) {
  136. testResume(
  137. t, 3, false, false, true,
  138. version,
  139. )
  140. })
  141. t.Run("expired not revealed", func(t *testing.T) {
  142. testResume(
  143. t, defaultConfs, true, false, false,
  144. version,
  145. )
  146. })
  147. t.Run("expired revealed", func(t *testing.T) {
  148. testResume(
  149. t, defaultConfs, true, true, true,
  150. version,
  151. )
  152. })
  153. })
  154. }
  155. }
  156. func testResume(t *testing.T, confs uint32, expired, preimageRevealed,
  157. expectSuccess bool, protocolVersion loopdb.ProtocolVersion) {
  158. defer test.Guard(t)()
  159. preimage := testPreimage
  160. hash := sha256.Sum256(preimage[:])
  161. dest := test.GetDestAddr(t, 0)
  162. amt := btcutil.Amount(50000)
  163. swapPayReq, err := getInvoice(hash, amt, swapInvoiceDesc)
  164. if err != nil {
  165. t.Fatal(err)
  166. }
  167. prePayReq, err := getInvoice(hash, 100, prepayInvoiceDesc)
  168. if err != nil {
  169. t.Fatal(err)
  170. }
  171. _, senderPubKey := test.CreateKey(1)
  172. var senderKey [33]byte
  173. copy(senderKey[:], senderPubKey.SerializeCompressed())
  174. _, receiverPubKey := test.CreateKey(2)
  175. var receiverKey [33]byte
  176. copy(receiverKey[:], receiverPubKey.SerializeCompressed())
  177. update := loopdb.LoopEvent{
  178. SwapStateData: loopdb.SwapStateData{
  179. State: loopdb.StateInitiated,
  180. },
  181. }
  182. if preimageRevealed {
  183. update.State = loopdb.StatePreimageRevealed
  184. update.HtlcTxHash = &chainhash.Hash{1, 2, 6}
  185. }
  186. // Create a pending swap with our custom number of confirmations.
  187. pendingSwap := &loopdb.LoopOut{
  188. Contract: &loopdb.LoopOutContract{
  189. DestAddr: dest,
  190. SwapInvoice: swapPayReq,
  191. SweepConfTarget: 2,
  192. HtlcConfirmations: confs,
  193. MaxSwapRoutingFee: 70000,
  194. PrepayInvoice: prePayReq,
  195. SwapContract: loopdb.SwapContract{
  196. Preimage: preimage,
  197. AmountRequested: amt,
  198. CltvExpiry: 744,
  199. ReceiverKey: receiverKey,
  200. SenderKey: senderKey,
  201. MaxSwapFee: 60000,
  202. MaxMinerFee: 50000,
  203. ProtocolVersion: protocolVersion,
  204. },
  205. },
  206. Loop: loopdb.Loop{
  207. Events: []*loopdb.LoopEvent{&update},
  208. Hash: hash,
  209. },
  210. }
  211. if expired {
  212. // Set cltv expiry so that it has already expired at the test
  213. // block height.
  214. pendingSwap.Contract.CltvExpiry = 610
  215. }
  216. ctx := createClientTestContext(t, []*loopdb.LoopOut{pendingSwap})
  217. if preimageRevealed {
  218. ctx.assertStatus(loopdb.StatePreimageRevealed)
  219. } else {
  220. ctx.assertStatus(loopdb.StateInitiated)
  221. }
  222. signalSwapPaymentResult := ctx.AssertPaid(swapInvoiceDesc)
  223. signalPrepaymentResult := ctx.AssertPaid(prepayInvoiceDesc)
  224. // Expect client to register for our expected number of confirmations.
  225. confIntent := ctx.AssertRegisterConf(preimageRevealed, int32(confs))
  226. // Assert that the loopout htlc equals to the expected one.
  227. scriptVersion := GetHtlcScriptVersion(protocolVersion)
  228. htlc, err := swap.NewHtlc(
  229. scriptVersion, pendingSwap.Contract.CltvExpiry, senderKey,
  230. receiverKey, hash, swap.HtlcP2WSH, &chaincfg.TestNet3Params,
  231. )
  232. require.NoError(t, err)
  233. require.Equal(t, htlc.PkScript, confIntent.PkScript)
  234. signalSwapPaymentResult(nil)
  235. signalPrepaymentResult(nil)
  236. if !expectSuccess {
  237. ctx.assertStatus(loopdb.StateFailTimeout)
  238. ctx.assertStoreFinished(loopdb.StateFailTimeout)
  239. ctx.finish()
  240. return
  241. }
  242. // Because there is no reliable payment yet, an invoice is assumed to be
  243. // paid after resume.
  244. testSuccess(ctx, amt, hash,
  245. func(r error) {},
  246. func(r error) {},
  247. preimageRevealed,
  248. confIntent, scriptVersion,
  249. )
  250. }
  251. func testSuccess(ctx *testContext, amt btcutil.Amount, hash lntypes.Hash,
  252. signalPrepaymentResult, signalSwapPaymentResult func(error),
  253. preimageRevealed bool, confIntent *test.ConfRegistration,
  254. scriptVersion swap.ScriptVersion) {
  255. htlcOutpoint := ctx.publishHtlc(confIntent.PkScript, amt)
  256. signalPrepaymentResult(nil)
  257. ctx.AssertRegisterSpendNtfn(confIntent.PkScript)
  258. // Assert that a call to track payment was sent, and respond with status
  259. // in flight so that our swap will push its preimage to the server.
  260. ctx.trackPayment(lnrpc.Payment_IN_FLIGHT)
  261. // Publish tick.
  262. ctx.expiryChan <- testTime
  263. // Expect a signing request.
  264. <-ctx.Lnd.SignOutputRawChannel
  265. if !preimageRevealed {
  266. ctx.assertStatus(loopdb.StatePreimageRevealed)
  267. ctx.assertStorePreimageReveal()
  268. }
  269. // Expect client on-chain sweep of HTLC.
  270. sweepTx := ctx.ReceiveTx()
  271. if !bytes.Equal(sweepTx.TxIn[0].PreviousOutPoint.Hash[:],
  272. htlcOutpoint.Hash[:]) {
  273. ctx.T.Fatalf("client not sweeping from htlc tx")
  274. }
  275. preImageIndex := 1
  276. if scriptVersion == swap.HtlcV2 {
  277. preImageIndex = 0
  278. }
  279. // Check preimage.
  280. clientPreImage := sweepTx.TxIn[0].Witness[preImageIndex]
  281. clientPreImageHash := sha256.Sum256(clientPreImage)
  282. if clientPreImageHash != hash {
  283. ctx.T.Fatalf("incorrect preimage")
  284. }
  285. // Since we successfully published our sweep, we expect the preimage to
  286. // have been pushed to our mock server.
  287. preimage, err := lntypes.MakePreimage(clientPreImage)
  288. require.NoError(ctx.T, err)
  289. ctx.assertPreimagePush(preimage)
  290. // Simulate server pulling payment.
  291. signalSwapPaymentResult(nil)
  292. ctx.NotifySpend(sweepTx, 0)
  293. ctx.assertStatus(loopdb.StateSuccess)
  294. ctx.assertStoreFinished(loopdb.StateSuccess)
  295. ctx.finish()
  296. }