From 97e632895646d228efe112be1ce633804f123ee3 Mon Sep 17 00:00:00 2001 From: michael1011 Date: Sat, 10 Feb 2024 20:52:09 +0100 Subject: [PATCH] refactor: Reverse Swap BIP-21 query by invoice (#483) --- lib/api/v2/routers/SwapRouter.ts | 109 ++++++++++--------- lib/service/Service.ts | 11 +- swagger-spec.json | 114 ++++++++++---------- test/unit/api/v2/routers/SwapRouter.spec.ts | 14 +-- test/unit/service/Service.spec.ts | 16 ++- 5 files changed, 143 insertions(+), 121 deletions(-) diff --git a/lib/api/v2/routers/SwapRouter.ts b/lib/api/v2/routers/SwapRouter.ts index 74639406..e849887a 100644 --- a/lib/api/v2/routers/SwapRouter.ts +++ b/lib/api/v2/routers/SwapRouter.ts @@ -731,56 +731,6 @@ class SwapRouter extends RouterBase { */ router.post('/reverse', this.handleError(this.createReverse)); - /** - * @openapi - * components: - * schemas: - * ReverseBip21: - * type: object - * properties: - * bip21: - * type: string - * description: BIP-21 for the Reverse Swap - * signature: - * type: string - * description: Signature of the address in the BIP-21 of the public key in the routing hint - */ - - /** - * @openapi - * /swap/reverse/{id}/bip21: - * get: - * tags: [Reverse] - * description: Get the BIP-21 of a Reverse Swap for a direct payment - * parameters: - * - in: path - * name: id - * required: true - * schema: - * type: string - * description: ID of the Reverse Swap - * responses: - * '200': - * description: BIP-21 and signature to prove the authenticity of the BIP-21 - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/ReverseBip21' - * '404': - * description: When no BIP-21 was set for the Reverse Swap - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/ErrorResponse' - * '400': - * description: Error that caused the request to fail - * content: - * application/json: - * schema: - * $ref: '#/components/schemas/ErrorResponse' - */ - router.get('/reverse/:id/bip21', this.handleError(this.getReverseBip21)); - /** * @openapi * components: @@ -888,6 +838,59 @@ class SwapRouter extends RouterBase { */ router.post('/reverse/claim', this.handleError(this.claimReverse)); + /** + * @openapi + * components: + * schemas: + * ReverseBip21: + * type: object + * properties: + * bip21: + * type: string + * description: BIP-21 for the Reverse Swap + * signature: + * type: string + * description: Signature of the address in the BIP-21 of the public key in the routing hint + */ + + /** + * @openapi + * /swap/reverse/{invoice}/bip21: + * get: + * tags: [Reverse] + * description: Get the BIP-21 of a Reverse Swap for a direct payment + * parameters: + * - in: path + * name: invoice + * required: true + * schema: + * type: string + * description: Invoice of the Reverse Swap + * responses: + * '200': + * description: BIP-21 and signature to prove the authenticity of the BIP-21 + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ReverseBip21' + * '404': + * description: When no BIP-21 was set for the Reverse Swap + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + * '400': + * description: Error that caused the request to fail + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ErrorResponse' + */ + router.get( + '/reverse/:invoice/bip21', + this.handleError(this.getReverseBip21), + ); + /** * @openapi * tags: @@ -1198,11 +1201,11 @@ class SwapRouter extends RouterBase { }; private getReverseBip21 = async (req: Request, res: Response) => { - const { id } = validateRequest(req.params, [ - { name: 'id', type: 'string' }, + const { invoice } = validateRequest(req.params, [ + { name: 'invoice', type: 'string' }, ]); - const hint = await this.service.getReverseBip21(id); + const hint = await this.service.getReverseBip21(invoice.toLowerCase()); if (hint === undefined) { errorResponse(this.logger, req, res, 'no BIP-21 for swap', 404); return; diff --git a/lib/service/Service.ts b/lib/service/Service.ts index 35e913d9..9cd03b1c 100644 --- a/lib/service/Service.ts +++ b/lib/service/Service.ts @@ -587,8 +587,15 @@ class Service { return response; }; - public getReverseBip21 = async (id: string) => { - const hint = await ReverseRoutingHintRepository.getHint(id); + public getReverseBip21 = async (invoice: string) => { + const reverseSwap = await ReverseSwapRepository.getReverseSwap({ + invoice, + }); + if (!reverseSwap) { + return undefined; + } + + const hint = await ReverseRoutingHintRepository.getHint(reverseSwap.id); if (!hint) { return undefined; } diff --git a/swagger-spec.json b/swagger-spec.json index 731a0017..23cb03d8 100644 --- a/swagger-spec.json +++ b/swagger-spec.json @@ -1224,12 +1224,12 @@ } } }, - "/swap/reverse/{id}/bip21": { + "/swap/reverse/{id}/transaction": { "get": { "tags": [ "Reverse" ], - "description": "Get the BIP-21 of a Reverse Swap for a direct payment", + "description": "Get the lockup transaction of a Reverse Swap", "parameters": [ { "in": "path", @@ -1243,11 +1243,11 @@ ], "responses": { "200": { - "description": "BIP-21 and signature to prove the authenticity of the BIP-21", + "description": "The lockup transaction of the Reverse Swap and accompanying information", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReverseBip21" + "$ref": "#/components/schemas/ReverseTransaction" } } } @@ -1261,9 +1261,39 @@ } } } + } + } + } + }, + "/swap/reverse/claim": { + "post": { + "description": "Requests a partial signature for a cooperative Reverse Swap claim transaction", + "tags": [ + "Reverse" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReverseClaimRequest" + } + } + } + }, + "responses": { + "200": { + "description": "A partial signature", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PartialSignature" + } + } + } }, - "404": { - "description": "When no BIP-21 was set for the Reverse Swap", + "400": { + "description": "Error that caused signature request to fail", "content": { "application/json": { "schema": { @@ -1275,30 +1305,30 @@ } } }, - "/swap/reverse/{id}/transaction": { + "/swap/reverse/{invoice}/bip21": { "get": { "tags": [ "Reverse" ], - "description": "Get the lockup transaction of a Reverse Swap", + "description": "Get the BIP-21 of a Reverse Swap for a direct payment", "parameters": [ { "in": "path", - "name": "id", + "name": "invoice", "required": true, "schema": { "type": "string" }, - "description": "ID of the Reverse Swap" + "description": "Invoice of the Reverse Swap" } ], "responses": { "200": { - "description": "The lockup transaction of the Reverse Swap and accompanying information", + "description": "BIP-21 and signature to prove the authenticity of the BIP-21", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReverseTransaction" + "$ref": "#/components/schemas/ReverseBip21" } } } @@ -1312,39 +1342,9 @@ } } } - } - } - } - }, - "/swap/reverse/claim": { - "post": { - "description": "Requests a partial signature for a cooperative Reverse Swap claim transaction", - "tags": [ - "Reverse" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReverseClaimRequest" - } - } - } - }, - "responses": { - "200": { - "description": "A partial signature", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PartialSignature" - } - } - } }, - "400": { - "description": "Error that caused signature request to fail", + "404": { + "description": "When no BIP-21 was set for the Reverse Swap", "content": { "application/json": { "schema": { @@ -1836,19 +1836,6 @@ } } }, - "ReverseBip21": { - "type": "object", - "properties": { - "bip21": { - "type": "string", - "description": "BIP-21 for the Reverse Swap" - }, - "signature": { - "type": "string", - "description": "Signature of the address in the BIP-21 of the public key in the routing hint" - } - } - }, "ReverseTransaction": { "type": "object", "properties": { @@ -1896,6 +1883,19 @@ } } }, + "ReverseBip21": { + "type": "object", + "properties": { + "bip21": { + "type": "string", + "description": "BIP-21 for the Reverse Swap" + }, + "signature": { + "type": "string", + "description": "Signature of the address in the BIP-21 of the public key in the routing hint" + } + } + }, "SwapStatus": { "type": "object", "properties": { diff --git a/test/unit/api/v2/routers/SwapRouter.spec.ts b/test/unit/api/v2/routers/SwapRouter.spec.ts index 1fe666d3..d7adce4a 100644 --- a/test/unit/api/v2/routers/SwapRouter.spec.ts +++ b/test/unit/api/v2/routers/SwapRouter.spec.ts @@ -167,7 +167,7 @@ describe('SwapRouter', () => { expect.anything(), ); expect(mockedRouter.get).toHaveBeenCalledWith( - '/reverse/:id/bip21', + '/reverse/:invoice/bip21', expect.anything(), ); expect(mockedRouter.get).toHaveBeenCalledWith( @@ -890,16 +890,16 @@ describe('SwapRouter', () => { }); test('should get BIP-21 of reverse swaps', async () => { - const id = 'bip21Swap'; + const invoice = 'bip21Swap'; const res = mockResponse(); await swapRouter['getReverseBip21']( - mockRequest(undefined, undefined, { id }), + mockRequest(undefined, undefined, { invoice }), res, ); expect(service.getReverseBip21).toHaveBeenCalledTimes(1); - expect(service.getReverseBip21).toHaveBeenCalledWith(id); + expect(service.getReverseBip21).toHaveBeenCalledWith(invoice.toLowerCase()); expect(res.status).toHaveBeenCalledWith(200); expect(res.json).toHaveBeenCalledWith({ @@ -909,18 +909,18 @@ describe('SwapRouter', () => { }); test('should write 404 when no BIP-21 of reverse swap was set', async () => { - const id = 'noBip21Swap'; + const invoice = 'noBip21Swap'; service.getReverseBip21 = jest.fn().mockResolvedValue(undefined); const res = mockResponse(); await swapRouter['getReverseBip21']( - mockRequest(undefined, undefined, { id }), + mockRequest(undefined, undefined, { invoice }), res, ); expect(service.getReverseBip21).toHaveBeenCalledTimes(1); - expect(service.getReverseBip21).toHaveBeenCalledWith(id); + expect(service.getReverseBip21).toHaveBeenCalledWith(invoice.toLowerCase()); expect(res.status).toHaveBeenCalledWith(404); expect(res.json).toHaveBeenCalledWith({ diff --git a/test/unit/service/Service.spec.ts b/test/unit/service/Service.spec.ts index 3523ce13..4d3d9853 100644 --- a/test/unit/service/Service.spec.ts +++ b/test/unit/service/Service.spec.ts @@ -1082,8 +1082,18 @@ describe('Service', () => { signature: 'some valid sig', }); - const id = 'reverseId'; - const res = await service.getReverseBip21(id); + const id = 'bip21Reverse'; + mockGetReverseSwapResult = { + id, + }; + + const invoice = 'someInvoice'; + const res = await service.getReverseBip21(invoice); + + expect(ReverseSwapRepository.getReverseSwap).toHaveBeenCalledTimes(1); + expect(ReverseSwapRepository.getReverseSwap).toHaveBeenCalledWith({ + invoice, + }); expect(ReverseRoutingHintRepository.getHint).toHaveBeenCalledTimes(1); expect(ReverseRoutingHintRepository.getHint).toHaveBeenCalledWith(id); @@ -1655,6 +1665,8 @@ describe('Service', () => { }); test('should create reverse swaps', async () => { + mockGetReverseSwapResult = null; + service.allowReverseSwaps = true; let pair = 'BTC/BTC';