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.

474 lines
12 KiB

  1. package loopdb
  2. import (
  3. "crypto/sha256"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "reflect"
  8. "testing"
  9. "time"
  10. "github.com/btcsuite/btcd/chaincfg"
  11. "github.com/btcsuite/btcd/chaincfg/chainhash"
  12. "github.com/coreos/bbolt"
  13. "github.com/lightninglabs/loop/test"
  14. "github.com/lightningnetwork/lnd/lntypes"
  15. "github.com/lightningnetwork/lnd/routing/route"
  16. "github.com/stretchr/testify/require"
  17. )
  18. var (
  19. senderKey = [33]byte{
  20. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  21. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
  22. }
  23. receiverKey = [33]byte{
  24. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  25. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
  26. }
  27. testPreimage = lntypes.Preimage([32]byte{
  28. 1, 1, 1, 1, 2, 2, 2, 2,
  29. 3, 3, 3, 3, 4, 4, 4, 4,
  30. 1, 1, 1, 1, 2, 2, 2, 2,
  31. 3, 3, 3, 3, 4, 4, 4, 4,
  32. })
  33. testTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
  34. )
  35. // TestLoopOutStore tests all the basic functionality of the current bbolt
  36. // swap store.
  37. func TestLoopOutStore(t *testing.T) {
  38. destAddr := test.GetDestAddr(t, 0)
  39. initiationTime := time.Date(2018, 11, 1, 0, 0, 0, 0, time.UTC)
  40. // Next, we'll make a new pending swap that we'll insert into the
  41. // database shortly.
  42. unrestrictedSwap := LoopOutContract{
  43. SwapContract: SwapContract{
  44. AmountRequested: 100,
  45. Preimage: testPreimage,
  46. CltvExpiry: 144,
  47. SenderKey: senderKey,
  48. ReceiverKey: receiverKey,
  49. MaxMinerFee: 10,
  50. MaxSwapFee: 20,
  51. InitiationHeight: 99,
  52. // Convert to/from unix to remove timezone, so that it
  53. // doesn't interfere with DeepEqual.
  54. InitiationTime: time.Unix(0, initiationTime.UnixNano()),
  55. },
  56. MaxPrepayRoutingFee: 40,
  57. PrepayInvoice: "prepayinvoice",
  58. DestAddr: destAddr,
  59. SwapInvoice: "swapinvoice",
  60. MaxSwapRoutingFee: 30,
  61. SweepConfTarget: 2,
  62. HtlcConfirmations: 2,
  63. SwapPublicationDeadline: time.Unix(0, initiationTime.UnixNano()),
  64. }
  65. t.Run("no outgoing set", func(t *testing.T) {
  66. testLoopOutStore(t, &unrestrictedSwap)
  67. })
  68. restrictedSwap := unrestrictedSwap
  69. restrictedSwap.OutgoingChanSet = ChannelSet{1, 2}
  70. t.Run("two channel outgoing set", func(t *testing.T) {
  71. testLoopOutStore(t, &restrictedSwap)
  72. })
  73. labelledSwap := unrestrictedSwap
  74. labelledSwap.Label = "test label"
  75. t.Run("labelled swap", func(t *testing.T) {
  76. testLoopOutStore(t, &labelledSwap)
  77. })
  78. }
  79. // testLoopOutStore tests the basic functionality of the current bbolt
  80. // swap store for specific swap parameters.
  81. func testLoopOutStore(t *testing.T, pendingSwap *LoopOutContract) {
  82. tempDirName, err := ioutil.TempDir("", "clientstore")
  83. if err != nil {
  84. t.Fatal(err)
  85. }
  86. defer os.RemoveAll(tempDirName)
  87. store, err := NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
  88. if err != nil {
  89. t.Fatal(err)
  90. }
  91. // First, verify that an empty database has no active swaps.
  92. swaps, err := store.FetchLoopOutSwaps()
  93. if err != nil {
  94. t.Fatal(err)
  95. }
  96. if len(swaps) != 0 {
  97. t.Fatal("expected empty store")
  98. }
  99. // checkSwap is a test helper function that'll assert the state of a
  100. // swap.
  101. checkSwap := func(expectedState SwapState) {
  102. t.Helper()
  103. swaps, err := store.FetchLoopOutSwaps()
  104. if err != nil {
  105. t.Fatal(err)
  106. }
  107. if len(swaps) != 1 {
  108. t.Fatal("expected pending swap in store")
  109. }
  110. swap := swaps[0].Contract
  111. if !reflect.DeepEqual(swap, pendingSwap) {
  112. t.Fatal("invalid pending swap data")
  113. }
  114. if swaps[0].State().State != expectedState {
  115. t.Fatalf("expected state %v, but got %v",
  116. expectedState, swaps[0].State(),
  117. )
  118. }
  119. if expectedState == StatePreimageRevealed {
  120. require.NotNil(t, swaps[0].State().HtlcTxHash)
  121. }
  122. }
  123. hash := pendingSwap.Preimage.Hash()
  124. // If we create a new swap, then it should show up as being initialized
  125. // right after.
  126. if err := store.CreateLoopOut(hash, pendingSwap); err != nil {
  127. t.Fatal(err)
  128. }
  129. checkSwap(StateInitiated)
  130. // Trying to make the same swap again should result in an error.
  131. if err := store.CreateLoopOut(hash, pendingSwap); err == nil {
  132. t.Fatal("expected error on storing duplicate")
  133. }
  134. checkSwap(StateInitiated)
  135. // Next, we'll update to the next state of the pre-image being
  136. // revealed. The state should be reflected here again.
  137. err = store.UpdateLoopOut(
  138. hash, testTime,
  139. SwapStateData{
  140. State: StatePreimageRevealed,
  141. HtlcTxHash: &chainhash.Hash{1, 6, 2},
  142. },
  143. )
  144. if err != nil {
  145. t.Fatal(err)
  146. }
  147. checkSwap(StatePreimageRevealed)
  148. // Next, we'll update to the final state to ensure that the state is
  149. // properly updated.
  150. err = store.UpdateLoopOut(
  151. hash, testTime,
  152. SwapStateData{
  153. State: StateFailInsufficientValue,
  154. },
  155. )
  156. if err != nil {
  157. t.Fatal(err)
  158. }
  159. checkSwap(StateFailInsufficientValue)
  160. if err := store.Close(); err != nil {
  161. t.Fatal(err)
  162. }
  163. // If we re-open the same store, then the state of the current swap
  164. // should be the same.
  165. store, err = NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
  166. if err != nil {
  167. t.Fatal(err)
  168. }
  169. checkSwap(StateFailInsufficientValue)
  170. }
  171. // TestLoopInStore tests all the basic functionality of the current bbolt
  172. // swap store.
  173. func TestLoopInStore(t *testing.T) {
  174. initiationTime := time.Date(2018, 11, 1, 0, 0, 0, 0, time.UTC)
  175. // Next, we'll make a new pending swap that we'll insert into the
  176. // database shortly.
  177. lastHop := route.Vertex{1, 2, 3}
  178. pendingSwap := LoopInContract{
  179. SwapContract: SwapContract{
  180. AmountRequested: 100,
  181. Preimage: testPreimage,
  182. CltvExpiry: 144,
  183. SenderKey: senderKey,
  184. ReceiverKey: receiverKey,
  185. MaxMinerFee: 10,
  186. MaxSwapFee: 20,
  187. InitiationHeight: 99,
  188. // Convert to/from unix to remove timezone, so that it
  189. // doesn't interfere with DeepEqual.
  190. InitiationTime: time.Unix(0, initiationTime.UnixNano()),
  191. },
  192. HtlcConfTarget: 2,
  193. LastHop: &lastHop,
  194. ExternalHtlc: true,
  195. }
  196. t.Run("loop in", func(t *testing.T) {
  197. testLoopInStore(t, pendingSwap)
  198. })
  199. labelledSwap := pendingSwap
  200. labelledSwap.Label = "test label"
  201. t.Run("loop in with label", func(t *testing.T) {
  202. testLoopInStore(t, labelledSwap)
  203. })
  204. }
  205. func testLoopInStore(t *testing.T, pendingSwap LoopInContract) {
  206. tempDirName, err := ioutil.TempDir("", "clientstore")
  207. if err != nil {
  208. t.Fatal(err)
  209. }
  210. defer os.RemoveAll(tempDirName)
  211. store, err := NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
  212. if err != nil {
  213. t.Fatal(err)
  214. }
  215. // First, verify that an empty database has no active swaps.
  216. swaps, err := store.FetchLoopInSwaps()
  217. if err != nil {
  218. t.Fatal(err)
  219. }
  220. if len(swaps) != 0 {
  221. t.Fatal("expected empty store")
  222. }
  223. // checkSwap is a test helper function that'll assert the state of a
  224. // swap.
  225. checkSwap := func(expectedState SwapState) {
  226. t.Helper()
  227. swaps, err := store.FetchLoopInSwaps()
  228. if err != nil {
  229. t.Fatal(err)
  230. }
  231. if len(swaps) != 1 {
  232. t.Fatal("expected pending swap in store")
  233. }
  234. swap := swaps[0].Contract
  235. if !reflect.DeepEqual(swap, &pendingSwap) {
  236. t.Fatal("invalid pending swap data")
  237. }
  238. if swaps[0].State().State != expectedState {
  239. t.Fatalf("expected state %v, but got %v",
  240. expectedState, swaps[0].State(),
  241. )
  242. }
  243. }
  244. hash := sha256.Sum256(testPreimage[:])
  245. // If we create a new swap, then it should show up as being initialized
  246. // right after.
  247. if err := store.CreateLoopIn(hash, &pendingSwap); err != nil {
  248. t.Fatal(err)
  249. }
  250. checkSwap(StateInitiated)
  251. // Trying to make the same swap again should result in an error.
  252. if err := store.CreateLoopIn(hash, &pendingSwap); err == nil {
  253. t.Fatal("expected error on storing duplicate")
  254. }
  255. checkSwap(StateInitiated)
  256. // Next, we'll update to the next state of the pre-image being
  257. // revealed. The state should be reflected here again.
  258. err = store.UpdateLoopIn(
  259. hash, testTime,
  260. SwapStateData{
  261. State: StatePreimageRevealed,
  262. },
  263. )
  264. if err != nil {
  265. t.Fatal(err)
  266. }
  267. checkSwap(StatePreimageRevealed)
  268. // Next, we'll update to the final state to ensure that the state is
  269. // properly updated.
  270. err = store.UpdateLoopIn(
  271. hash, testTime,
  272. SwapStateData{
  273. State: StateFailInsufficientValue,
  274. },
  275. )
  276. if err != nil {
  277. t.Fatal(err)
  278. }
  279. checkSwap(StateFailInsufficientValue)
  280. if err := store.Close(); err != nil {
  281. t.Fatal(err)
  282. }
  283. // If we re-open the same store, then the state of the current swap
  284. // should be the same.
  285. store, err = NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
  286. if err != nil {
  287. t.Fatal(err)
  288. }
  289. checkSwap(StateFailInsufficientValue)
  290. }
  291. // TestVersionNew tests that a new database is initialized with the current
  292. // version.
  293. func TestVersionNew(t *testing.T) {
  294. tempDirName, err := ioutil.TempDir("", "clientstore")
  295. if err != nil {
  296. t.Fatal(err)
  297. }
  298. defer os.RemoveAll(tempDirName)
  299. store, err := NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
  300. if err != nil {
  301. t.Fatal(err)
  302. }
  303. ver, err := getDBVersion(store.db)
  304. if err != nil {
  305. t.Fatal(err)
  306. }
  307. if ver != latestDBVersion {
  308. t.Fatal("db not at latest version")
  309. }
  310. }
  311. // TestVersionNew tests that an existing version zero database is migrated to
  312. // the latest version.
  313. func TestVersionMigrated(t *testing.T) {
  314. tempDirName, err := ioutil.TempDir("", "clientstore")
  315. if err != nil {
  316. t.Fatal(err)
  317. }
  318. defer os.RemoveAll(tempDirName)
  319. createVersionZeroDb(t, tempDirName)
  320. store, err := NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
  321. if err != nil {
  322. t.Fatal(err)
  323. }
  324. ver, err := getDBVersion(store.db)
  325. if err != nil {
  326. t.Fatal(err)
  327. }
  328. if ver != latestDBVersion {
  329. t.Fatal("db not at latest version")
  330. }
  331. }
  332. // createVersionZeroDb creates a database with an empty meta bucket. In version
  333. // zero, there was no version key specified yet.
  334. func createVersionZeroDb(t *testing.T, dbPath string) {
  335. path := filepath.Join(dbPath, dbFileName)
  336. bdb, err := bbolt.Open(path, 0600, nil)
  337. if err != nil {
  338. t.Fatal(err)
  339. }
  340. defer bdb.Close()
  341. err = bdb.Update(func(tx *bbolt.Tx) error {
  342. _, err := tx.CreateBucket(metaBucketKey)
  343. return err
  344. })
  345. if err != nil {
  346. t.Fatal(err)
  347. }
  348. }
  349. // TestLegacyOutgoingChannel asserts that a legacy channel restriction is
  350. // properly mapped onto the newer channel set.
  351. func TestLegacyOutgoingChannel(t *testing.T) {
  352. var (
  353. legacyDbVersion = Hex("00000003")
  354. legacyOutgoingChannel = Hex("0000000000000005")
  355. )
  356. legacyDb := map[string]interface{}{
  357. "loop-in": map[string]interface{}{},
  358. "metadata": map[string]interface{}{
  359. "dbp": legacyDbVersion,
  360. },
  361. "uncharge-swaps": map[string]interface{}{
  362. Hex("2a595d79a55168970532805ae20c9b5fac98f04db79ba4c6ae9b9ac0f206359e"): map[string]interface{}{
  363. "contract": Hex("1562d6fbec140000010101010202020203030303040404040101010102020202030303030404040400000000000000640d707265706179696e766f69636501010101010101010101010101010101010101010101010101010101010101010201010101010101010101010101010101010101010101010101010101010101010300000090000000000000000a0000000000000014000000000000002800000063223347454e556d6e4552745766516374344e65676f6d557171745a757a5947507742530b73776170696e766f69636500000002000000000000001e") + legacyOutgoingChannel + Hex("1562d6fbec140000"),
  364. "updates": map[string]interface{}{
  365. Hex("0000000000000001"): Hex("1508290a92d4c00001000000000000000000000000000000000000000000000000"),
  366. Hex("0000000000000002"): Hex("1508290a92d4c00006000000000000000000000000000000000000000000000000"),
  367. },
  368. },
  369. },
  370. }
  371. // Restore a legacy database.
  372. tempDirName, err := ioutil.TempDir("", "clientstore")
  373. if err != nil {
  374. t.Fatal(err)
  375. }
  376. defer os.RemoveAll(tempDirName)
  377. tempPath := filepath.Join(tempDirName, dbFileName)
  378. db, err := bbolt.Open(tempPath, 0600, nil)
  379. if err != nil {
  380. t.Fatal(err)
  381. }
  382. err = db.Update(func(tx *bbolt.Tx) error {
  383. return RestoreDB(tx, legacyDb)
  384. })
  385. if err != nil {
  386. t.Fatal(err)
  387. }
  388. db.Close()
  389. // Fetch the legacy swap.
  390. store, err := NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
  391. if err != nil {
  392. t.Fatal(err)
  393. }
  394. swaps, err := store.FetchLoopOutSwaps()
  395. if err != nil {
  396. t.Fatal(err)
  397. }
  398. // Assert that the outgoing channel is read properly.
  399. expectedChannelSet := ChannelSet{5}
  400. if !reflect.DeepEqual(swaps[0].Contract.OutgoingChanSet, expectedChannelSet) {
  401. t.Fatal("invalid outgoing channel")
  402. }
  403. }