Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ gltestserver-image: ${REPO_ROOT}/docker/gl-testserver/Dockerfile
.

gltestserver: gltestserver-image
rm -rf .gltestserver/gl-testserver && \
docker run \
--rm \
--user $(shell id -u):$(shell id -g) \
Expand Down
107 changes: 107 additions & 0 deletions examples/javascript/node-operations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
const path = require('path');
const axios = require('axios');
const protobuf = require('protobufjs');

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 requestPayload = methodRequest.create(payload);
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(port, authPubkey, 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': Buffer.from(authPubkey, 'hex').toString('base64'),
'glauthsig': 'A'.repeat(64),
'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))) {
return parseInt(value.msat);
}
return value;
}

function decodeResponse(clnNode, method, response) {
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 errorMessage = 'None';
try {
errorMessage = decodeURIComponent(new TextDecoder('utf-8').decode(responseData)).trim();
if (errorMessage == 'None') {
errorMessage = 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));
}

async function callMethod(port, auth_pubkey, clnNode, method, methodReq, methodRes, methodPayload) {
await new Promise(resolve => setTimeout(resolve, 1000));
const encodedPayload = await encodePayload(clnNode, methodReq, methodPayload);
try {
const response = await sendRequest(port, auth_pubkey, method, encodedPayload);
const responseJSON = decodeResponse(clnNode, methodRes, response);
console.log(JSON.stringify(responseJSON, null, 2));
} catch (error) {
throw error;
}
}

async function main() {
try {
const args = process.argv.slice(2);
const port = args[0];
const auth_pubkey = args[1];
const method = args[2];
const methodReq = args[3];
const methodRes = args[4];
const methodPayload = JSON.parse(args[5]);
const proto_path = [
path.join(process.cwd(), '../../libs/gl-client/.resources/proto/node.proto'),
path.join(process.cwd(), '../../libs/gl-client/.resources/proto/primitives.proto')
];
const clnNode = new protobuf.Root().loadSync(proto_path, { keepCase: true });
await callMethod(port, auth_pubkey, clnNode, method, methodReq, methodRes, methodPayload);
} catch (error) {
console.error(error);
}
}

main();
86 changes: 86 additions & 0 deletions examples/javascript/scheduler-operations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// node scheduler-operations.js "41725" "ListLsps" "{}"
// node scheduler-operations.js "41725" "GetLspInfo" '{"public_key": "02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}'
const path = require('path');
const http2 = require('http2');
const fs = require('fs');
const protobuf = require('protobufjs');

const PROTO_PATH = [
path.join(process.cwd(), '../../libs/proto/glclient/scheduler.proto'),
path.join(process.cwd(), '../../libs/proto/glclient/greenlight.proto')
];
const glScheduler = new protobuf.Root().loadSync(PROTO_PATH, { keepCase: true });

function encodePayload(method, payload) {
const methodRequest = glScheduler.lookupType(`scheduler.${method}Request`);
const requestMessage = methodRequest.encode(payload).finish();
const messageLength = requestMessage.length;
const encodedPayload = Buffer.alloc(5 + messageLength);
encodedPayload.writeUInt8(0, 0);
encodedPayload.writeUInt32BE(messageLength, 1);
encodedPayload.set(requestMessage, 5);
return encodedPayload;
}

function decodeResponse(method, responseData) {
const ListLspsResponse = glScheduler.lookupType(`scheduler.${method}Response`);
const responseMessage = responseData.slice(5);
const decodedResponse = ListLspsResponse.decode(responseMessage);
const decodedResObject = ListLspsResponse.toObject(decodedResponse, {
longs: String,
enums: String,
bytes: Buffer,
defaults: true,
arrays: true,
objects: true,
});
return decodedResObject;
}

function callMethod(glsClient, method, payload) {
const encodedPayload = encodePayload(method, payload);

const req = glsClient.request({
':path': `/scheduler.Lsps/${method}`,
':method': 'POST',
'content-type': 'application/grpc',
'te': 'trailers',
});
req.write(encodedPayload);

let responseData = Buffer.alloc(0);
req.on('data', (chunk) => {
responseData = Buffer.concat([responseData, chunk]);
});

req.on('end', () => {
const decodedResObject = decodeResponse(method, responseData);
console.log('Response:', JSON.stringify(decodedResObject, null, 2));
glsClient.close();
process.exit(0);
});

req.on('error', (error) => {
console.error('Request error:', error);
glsClient.close();
process.exit(1);
});

req.end();
}

function main() {
const args = process.argv.slice(2);
const port = args[0];
const method = args[1];
const payload = JSON.parse(args[2]);

const glsClient = http2.connect(`https://localhost:${port}`, {
ca: fs.readFileSync('../../.gltestserver/gl-testserver/certs/ca.crt'),
rejectUnauthorized: false,
});

callMethod(glsClient, method, payload);
}

main();
Loading
Loading