From b79af426f7b0dd64743c2c77151022e1efad3422 Mon Sep 17 00:00:00 2001 From: ShahanaFarooqui Date: Thu, 13 Feb 2025 21:44:28 -0800 Subject: [PATCH] Updated javascript example Fixed bug due to encodeDelimited function. --- examples/javascript/README.md | 20 ++--- examples/javascript/grpc-web-proxy-client.js | 78 +++++++++++++------- 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/examples/javascript/README.md b/examples/javascript/README.md index e20e6a0f9..4a8a00d0a 100644 --- a/examples/javascript/README.md +++ b/examples/javascript/README.md @@ -45,32 +45,24 @@ lightning-hsmtool getnodeid $HOME/greenlight/.gltestserver/gl-testserver/hsm_sec ``` Sample Output: 034c46b632a9ff3975fb7cd4e764a36ec476b522be2555e83a3183ab1ee3e36e93 -### 5.3: Encode Node ID to Base64 -```python -import binascii -import base64 -print(base64.b64encode(binascii.unhexlify("")).decode('utf-8')) -``` -Sample Output: A0xGtjKp/zl1+3zU52SjbsR2tSK+JVXoOjGDqx7j426T - -### 5.4: Modify Default Values +### 5.3: Modify Default Values - Open the file `./examples/javascript/grpc-web-proxy-client.js`. -- Locate the line defining `AUTH_PUBKEY` and replace its value with the Base64-encoded public key output from Step 5.3: +- Locate the line defining `NODE_PUBKEY` and replace its value with your node's public key from Step 5.2: ```javascript - const AUTH_PUBKEY = 'replace+this+with+your+base64+encoded+pubkey'; + const NODE_PUBKEY = 'yournodepubkeyhexvalue00000000000000000000000000000000000000000000'; ``` -- Replace the default PORT value `1111` with the port number from `grpc_web_proxy_uri` obtained in Step 1: +- If `getPortFromMetadata` fails to retrieve the gRPC port value, replace the default `PORT` value (`1111`) with the port number extracted from the `grpc_web_proxy_uri` obtained in Step 1. ```javascript - const PORT = process.argv[2] || '1111'; + const PORT = process.argv[2] || getPortFromMetadata() || '1111'; ``` Alternatively, the port number can be passed as a command-line argument when running the nodejs script in the next step. - Save the changes to the file. -### 5.5: Run the Example +### 5.4: Run the Example ```bash node grpc-web-proxy-client.js ``` diff --git a/examples/javascript/grpc-web-proxy-client.js b/examples/javascript/grpc-web-proxy-client.js index 10acde6ff..ed605ee43 100644 --- a/examples/javascript/grpc-web-proxy-client.js +++ b/examples/javascript/grpc-web-proxy-client.js @@ -1,15 +1,36 @@ +const fs = require('fs'); const path = require('path'); const axios = require('axios'); const protobuf = require('protobufjs'); -const PORT = process.argv[2] || '1111'; -const AUTH_PUBKEY = 'replace+this+with+your+base64+encoded+pubkey'; +const PORT = process.argv[2] || getPortFromMetadata() || '1111'; +const NODE_PUBKEY = 'yournodepubkeyhexvalue00000000000000000000000000000000000000000000'; +const AUTH_PUBKEY = Buffer.from(NODE_PUBKEY, 'hex').toString('base64'); const AUTH_SIGNATURE = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; const PROTO_PATHS = [ path.join(process.cwd(), '../../libs/gl-client/.resources/proto/node.proto'), path.join(process.cwd(), '../../libs/gl-client/.resources/proto/primitives.proto') ]; +function getPortFromMetadata() { + try { + const grpcWebProxyUri = JSON.parse(fs.readFileSync('../../metadata.json')).grpc_web_proxy_uri; + if (!grpcWebProxyUri) { + console.error('grpc_web_proxy_uri not found in metadata.json'); + return null; + } + const grpc_port = new URL(grpcWebProxyUri).port; + if (!grpc_port) { + console.error('Port not found in grpc_web_proxy_uri'); + return null; + } + return grpc_port; + } catch (error) { + console.error('Error reading metadata.json: ', error.message); + return null; + } +} + function getGrpcErrorMessage(grpcStatusCode) { const grpcStatusMessages = { 0: 'OK: The operation completed successfully.', @@ -37,11 +58,13 @@ async function encodePayload(clnNode, method, payload) { const methodRequest = clnNode.lookupType(`cln.${method}Request`); const errMsg = methodRequest.verify(payload); if (errMsg) throw new Error(errMsg); - const header = Buffer.alloc(4); - header.writeUInt8(0, 0); const requestPayload = methodRequest.create(payload); - const encodedPayload = methodRequest.encodeDelimited(requestPayload).finish(); - return Buffer.concat([header, encodedPayload]); + const encodedPayload = methodRequest.encode(requestPayload).finish(); + const flags = Buffer.alloc(1); + flags.writeUInt8(0, 0); + const header = Buffer.alloc(4); + header.writeUInt32BE(encodedPayload.length, 0); + return Buffer.concat([flags, header, encodedPayload]); } async function sendRequest(methodUrl, encodedPayload) { @@ -72,31 +95,34 @@ function transformValue(key, value) { } function decodeResponse(clnNode, method, response) { - const methodResponse = clnNode.lookupType(`cln.${method}Response`) - const offset = 5; - const responseData = new Uint8Array(response.data).slice(offset); + const methodResponse = clnNode.lookupType(`cln.${method}Response`); + const dataBuffer = Buffer.from(response.data); + const resFlag = dataBuffer.subarray(0, 1); + const resDataLength = dataBuffer.subarray(1, 5); + const responseData = dataBuffer.subarray(5); const grpcStatus = +response.headers['grpc-status']; if (grpcStatus !== 0) { - let errorDecoded = new TextDecoder("utf-8").decode(responseData); - if (errorDecoded !== 'None') { - errorDecoded = JSON.parse(errorDecoded.replace(/([a-zA-Z0-9_]+):/g, '"$1":')); - } else { - errorDecoded = {code: grpcStatus, message: getGrpcErrorMessage(grpcStatus)}; - } - return { grpc_code: grpcStatus, grpc_error: getGrpcErrorMessage(grpcStatus), error: errorDecoded}; - } else { - // FIXME: Use decodeDelimited - const decodedRes = methodResponse.decode(responseData); - const decodedResObject = methodResponse.toObject(decodedRes, { + let errorMessage = 'None'; + try { + errorMessage = decodeURIComponent(new TextDecoder('utf-8').decode(responseData)).trim(); + if (errorMessage == 'None') { + errorMessage = getGrpcErrorMessage(grpcStatus); + } + } catch (decodeError) { + errorMessage = decodeError; + } + throw new Error(errorMessage); + } + const decodedRes = methodResponse.decode(responseData); + const decodedResObject = methodResponse.toObject(decodedRes, { longs: String, enums: String, bytes: Buffer, defaults: true, arrays: true, objects: true, - }); - return JSON.parse(JSON.stringify(decodedResObject, transformValue)); - } + }); + return JSON.parse(JSON.stringify(decodedResObject, transformValue)); } async function fetchNodeData() { @@ -120,14 +146,14 @@ async function fetchNodeData() { console.log('\nResponse Decoded:'); console.dir(responseJSON, { depth: null, color: true }); } catch (error) { - console.error('\nResponse Error:\n', error.response.status, ' - ', error.response.statusText); + console.error('\nResponse Error:\n', error.response?.status || error.code, ' - ', error.response?.statusText || error.response?.data || error.message || ''); } } } catch (error) { console.error('Error:', error.message); if (error.response) { - console.error('Error status:', error.response.status); - console.error('Error data:', error.response.data); + console.error('Error status:', error.response?.status || error.code); + console.error('Error data:', error.response?.statusText || error.response?.data || error.message || ''); } } }