-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
example: Add simple Javascript Getinfo Example
- Loading branch information
1 parent
81a18de
commit 86378d6
Showing
4 changed files
with
232 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# How to run javascript examples with gltestserver | ||
|
||
## Step 1 (Terminal 1): Start the Server | ||
```bash | ||
make gltestserver | ||
``` | ||
|
||
## Step 2 (Terminal 2): Register Node | ||
```bash | ||
GL_CA_CRT=$HOME/.gltestserver/gl-testserver/certs/ca.crt \ | ||
GL_NOBODY_CRT=$HOME/.gltestserver/gl-testserver/certs/users/nobody.crt \ | ||
GL_NOBODY_KEY=$HOME/.gltestserver/gl-testserver/certs/users/nobody-key.pem \ | ||
GL_SCHEDULER_GRPC_URI=https://localhost:38067 \ | ||
cargo run --bin glcli scheduler register --network=regtest --data-dir=$HOME/.gltestserver/gl-testserver | ||
``` | ||
|
||
## Step 3 (Terminal 2): Schedule Node | ||
```bash | ||
GL_CA_CRT=$HOME/.gltestserver/gl-testserver/certs/ca.crt \ | ||
GL_NOBODY_CRT=$HOME/.gltestserver/gl-testserver/certs/users/nobody.crt \ | ||
GL_NOBODY_KEY=$HOME/.gltestserver/gl-testserver/certs/users/nobody-key.pem \ | ||
GL_SCHEDULER_GRPC_URI=https://localhost:38067 \ | ||
cargo run --bin glcli scheduler schedule --verbose --network=regtest --data-dir=$HOME/.gltestserver/gl-testserver | ||
``` | ||
|
||
## Step 4 (Terminal 3): Start the Signer | ||
```bash | ||
GL_CA_CRT=$HOME/.gltestserver/gl-testserver/certs/ca.crt \ | ||
GL_NOBODY_CRT=$HOME/.gltestserver/gl-testserver/certs/users/nobody.crt \ | ||
GL_NOBODY_KEY=$HOME/.gltestserver/gl-testserver/certs/users/nobody-key.pem \ | ||
GL_SCHEDULER_GRPC_URI=https://localhost:38067 \ | ||
cargo run --bin glcli signer run --verbose --network=regtest --data-dir=$HOME/.gltestserver/gl-testserver | ||
``` | ||
|
||
## Step 5 (Terminal 4): Run Example | ||
### 5.1: Navigate and Install Dependencies for the Example | ||
```bash | ||
cd ./examples/javascript | ||
npm install | ||
``` | ||
|
||
### 5.2: Get Node ID | ||
```bash | ||
lightning-hsmtool getnodeid $HOME/.gltestserver/gl-testserver/hsm_secret | ||
``` | ||
Sample Output: 034c46b632a9ff3975fb7cd4e764a36ec476b522be2555e83a3183ab1ee3e36e93 | ||
|
||
### 5.3: Encode Node ID to Base64 | ||
```python | ||
import binascii | ||
import base64 | ||
|
||
node_id_hex = "<node id from step 5.2>" | ||
node_id_base64 = base64.b64encode(binascii.unhexlify(node_id_hex)).decode('utf-8') | ||
print(node_id_base64) | ||
``` | ||
Sample Output: A0xGtjKp/zl1+3zU52SjbsR2tSK+JVXoOjGDqx7j426T | ||
|
||
### 5.4: Modify Default Values | ||
- Open ./examples/javascript/grpc-web-proxy-client.js. | ||
- Change the PORT value on line 5 to the grpc_web_proxy_uri port number from Step 1. | ||
- Replace the AUTH_PUBKEY value on line 6 with the node_id_base64 from Step 5.3. | ||
- Save the changes. | ||
|
||
### 5.5: Run the Example | ||
```bash | ||
node grpc-web-proxy-client.js | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
const path = require('path'); | ||
const axios = require('axios'); | ||
const protobuf = require('protobufjs'); | ||
|
||
const PORT = process.argv[2] || '1111'; | ||
const AUTH_PUBKEY = 'A0xGtjKp/zl1+3zU52SjbsR2tSK+JVXoOjGDqx7j426T'; | ||
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 getGrpcErrorMessage(grpcStatusCode) { | ||
const grpcStatusMessages = { | ||
0: 'OK: The operation completed successfully.', | ||
1: 'CANCELLED: The operation was cancelled (typically by the caller).', | ||
2: 'UNKNOWN: Unknown error. Usually means an internal error occurred.', | ||
3: 'INVALID_ARGUMENT: The client specified an invalid argument.', | ||
4: 'DEADLINE_EXCEEDED: The operation took too long and exceeded the time limit.', | ||
5: 'NOT_FOUND: A specified resource was not found.', | ||
6: 'ALREADY_EXISTS: The resource already exists.', | ||
7: 'PERMISSION_DENIED: The caller does not have permission to execute the operation.', | ||
8: 'RESOURCE_EXHAUSTED: A resource (such as quota) was exhausted.', | ||
9: 'FAILED_PRECONDITION: The operation was rejected due to a failed precondition.', | ||
10: 'ABORTED: The operation was aborted, typically due to a concurrency issue.', | ||
11: 'OUT_OF_RANGE: The operation attempted to access an out-of-range value.', | ||
12: 'UNIMPLEMENTED: The operation is not implemented or supported by the server.', | ||
13: 'INTERNAL: Internal server error.', | ||
14: 'UNAVAILABLE: The service is unavailable (e.g., network issues, server down).', | ||
15: 'DATA_LOSS: Unrecoverable data loss or corruption.', | ||
16: 'UNAUTHENTICATED: The request is missing or has invalid authentication credentials.' | ||
} | ||
return grpcStatusMessages[grpcStatusCode] || "UNKNOWN_STATUS_CODE: The status code returned by gRPC server is not in the list."; | ||
} | ||
|
||
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]); | ||
} | ||
|
||
async function sendRequest(methodUrl, encodedPayload) { | ||
const buffer = Buffer.alloc(8); | ||
buffer.writeUInt32BE(Math.floor(Date.now() / 1000), 4); | ||
const axiosConfig = { | ||
responseType: 'arraybuffer', | ||
headers: { | ||
'content-type': 'application/grpc', | ||
'accept': 'application/grpc', | ||
'glauthpubkey': AUTH_PUBKEY, | ||
'glauthsig': AUTH_SIGNATURE, | ||
'glts': buffer.toString('base64'), | ||
}, | ||
}; | ||
return await axios.post(`http://localhost:${PORT}/cln.Node/${methodUrl}`, encodedPayload, axiosConfig); | ||
} | ||
|
||
function transformValue(key, value) { | ||
if ((value.type && value.type === "Buffer") || value instanceof Buffer || value instanceof Uint8Array) { | ||
return Buffer.from(value).toString('hex'); | ||
} | ||
if (value.msat && !Number.isNaN(parseInt(value.msat))) { | ||
// FIXME: Amount.varify check will work with 0 NOT '0'. Amount default is '0'. | ||
return parseInt(value.msat); | ||
} | ||
return 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 grpcStatus = +response.headers['grpc-status']; | ||
if (grpcStatus !== 200) { | ||
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, { | ||
longs: String, | ||
enums: String, | ||
bytes: Buffer, | ||
defaults: true, | ||
arrays: true, | ||
objects: true, | ||
}); | ||
return JSON.parse(JSON.stringify(decodedResObject, transformValue)); | ||
} | ||
} | ||
|
||
async function fetchNodeData() { | ||
try { | ||
const clnNode = new protobuf.Root().loadSync(PROTO_PATHS, { keepCase: true }); | ||
const FeeratesStyle = clnNode.lookupEnum('cln.FeeratesStyle'); | ||
const NewaddrAddresstype = clnNode.lookupEnum('cln.NewaddrAddresstype'); | ||
const methods = ['Getinfo', 'Feerates', 'Newaddr', 'Invoice', 'Listinvoices']; | ||
const method_payloads = [{}, {style: FeeratesStyle.values.PERKW}, {addresstype: NewaddrAddresstype.values.ALL}, {amount_msat: {amount: {msat: 500000}}, description: 'My coffee', label: 'coffeeinvat' + Date.now()}, {}]; | ||
for (let i = 0; i < methods.length; i++) { | ||
console.log('--------------------------------------------\n', (i + 1), '-', methods[i], '\n--------------------------------------------'); | ||
console.log('Payload Raw:\n', method_payloads[i]); | ||
const encodedPayload = await encodePayload(clnNode, methods[i], method_payloads[i]); | ||
console.log('\nPayload Encoded:\n', encodedPayload); | ||
try { | ||
const response = await sendRequest(methods[i], encodedPayload); | ||
console.log('\nResponse Headers:\n', response.headers); | ||
console.log('\nResponse Data:\n', response.data); | ||
const responseJSON = decodeResponse(clnNode, methods[i], response); | ||
console.log('\nResponse Decoded:'); | ||
console.dir(responseJSON, { depth: null, color: true }); | ||
} catch (error) { | ||
console.error('\nResponse Error:\n', error.response.status, ' - ', error.response.statusText); | ||
} | ||
} | ||
} catch (error) { | ||
console.error('Error:', error.message); | ||
if (error.response) { | ||
console.error('Error status:', error.response.status); | ||
console.error('Error data:', error.response.data); | ||
} | ||
} | ||
} | ||
|
||
fetchNodeData(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"name": "grpc-web-proxy-client", | ||
"version": "1.0.0", | ||
"description": "Example for grpc web proxy client", | ||
"main": "grpc-web-proxy-client.js", | ||
"directories": { | ||
"doc": "doc", | ||
"test": "tests" | ||
}, | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"axios": "^1.7.9", | ||
"protobufjs": "^7.4.0" | ||
} | ||
} |