Skip to content

Commit

Permalink
Merge pull request #9 from bigmarh/sim-ln-js
Browse files Browse the repository at this point in the history
Added the ability to generate executables so that users will not need to have nodejs to run.  But it also provides flexibility to build from source.
  • Loading branch information
bigmarh authored Aug 17, 2023
2 parents 4086f94 + 56eee08 commit 1c7f3c7
Show file tree
Hide file tree
Showing 215 changed files with 183,475 additions and 1 deletion.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/target
*/config.json
node_modules
config.json
package-lock.json
activity-generator/releases/*
.DS_Store
Binary file added activity-generator/.DS_Store
Binary file not shown.
14 changes: 14 additions & 0 deletions activity-generator/bin/default_activities_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const DefaultConfig = {
"HIGH_VOLUME_TRANSACTIONS": {
name: "High Volume Keysend Payments",
frequency: 5,
amount: 100000,
action: "keysend"
},
"LOW_VOLUME_TRANSACTIONS": {
name: "Low Volume Keysend Payments",
frequency: 5,
amount: 100,
action: "keysend"
},
}
189 changes: 189 additions & 0 deletions activity-generator/bin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#! /usr/bin/env node


import LndGrpc from 'lnd-grpc';
import fs from 'fs';
import path from 'path';
import { program } from 'commander';
import { select, input, confirm } from '@inquirer/prompts';
import { v4 } from 'uuid';
import { parse } from 'json2csv';
import { getFrequency, getAmountInSats, verifyPubKey } from './validation/inputGetters.js';
import { DefaultConfig } from './default_activities_config.js';
program.requiredOption('--config <file>');
program.option('--csv');
program.parse();

const options = program.opts();
const configFile = options.config;

// Blocking example with fs.readFileSync
const fileName = configFile;
const config = JSON.parse(fs.readFileSync(fileName, 'utf-8'));
let nodeObj = {};
const controlNodes = config.nodes.map(node => node);



console.log(`Setting up ${config.nodes.length} Controlled Nodes...`)
async function buildControlNodes() {
if (!controlNodes.length) return promptForActivities();

let node = controlNodes.shift()
const grpc = new LndGrpc({
host: node.ip,
cert: node.cert,
macaroon: node.macaroon,
protoDir:path.join(__dirname,"proto")
})

grpc.connect();
(async function() {

const { WalletUnlocker, Lightning } = grpc.services
// Do something cool if we detect that the wallet is locked.
grpc.on(`connected`, () => console.log('wallet connected!'))
// Do something cool if we detect that the wallet is locked.
grpc.on(`locked`, async () => {
await grpc.activateLightning()
})

// Do something cool when the wallet gets unlocked.
grpc.on(`active`, async () => {
const current_node = await Lightning.getInfo();
const nodeGraph = await Lightning.describeGraph();

if (nodeGraph.nodes < 1) {
console.log(`Node: ${node.alias} has no graph`)
return console.error("Please check that controlled nodes have open channels to other nodes")
}

//dump graph information
nodeObj[current_node.identity_pubkey] = current_node;
nodeObj[current_node.identity_pubkey].graph = nodeGraph;
node.id = current_node.identity_pubkey;

//create array of possible destintations for node
nodeObj[current_node.identity_pubkey].possible_dests = nodeGraph.nodes.filter((n) => {
return n.pub_key != current_node.identity_pubkey
})
grpc.disconnect()
})
// Do something cool when the connection gets disconnected.
grpc.on(`disconnected`, () => {

if (Object.keys(nodeObj).length == config.nodes.length) promptForActivities();
else buildControlNodes();
})


})()

}

buildControlNodes();

let activities = [];
async function promptForActivities() {

console.log("\nCreate a New Activity");
console.log("_________________________________\n");
let activity = {};
activity.uuid = v4();

const predefinedActivity = await select({
message: " \n",
choices: [
{name: "Select a predefined activity", value: true},
{name: "Manually create an activity", value: false}
]
})

if (predefinedActivity) {
const selectedPredefinedActivity = await select({
message: " \n",
choices: Object.keys(DefaultConfig).map((config) => {
return {
name: DefaultConfig[config].name,
value: config
}
})
})
activity.freq = DefaultConfig[selectedPredefinedActivity].frequency
activity.amt = DefaultConfig[selectedPredefinedActivity].amount
activity.action = DefaultConfig[selectedPredefinedActivity].action
activity.src = Object.values(nodeObj)[Math.floor(Math.random() * Object.values(nodeObj).length)].identity_pubkey
activity.dest = nodeObj[activity.src].possible_dests[Math.floor(Math.random() * nodeObj[activity.src].possible_dests.length)].pub_key
} else {
let sourceArray = [{
name: '(choose random)',
value: Object.values(nodeObj)[Math.floor(Math.random() * Object.values(nodeObj).length)].identity_pubkey
}, {
name: '(input pubkey)',
value: false
}]

activity.src = await select({
message: "Choose a source? \n",
choices: sourceArray.concat(Object.keys(nodeObj).map(key => {
let node = nodeObj[key];
return {
name: `${node.alias}: (${node.identity_pubkey})`,
value: node.identity_pubkey
}
}))
})

if (!activity.src) {
activity.src = await input({ message: 'Enter pubkey:' });
}

let destArray = [{
name: `(choose random)`,
value: nodeObj[activity.src].possible_dests[Math.floor(Math.random() * nodeObj[activity.src].possible_dests.length)].pub_key
}, {
name: '(input pubkey)',
value: false
}]

activity.dest = await select({
message: "Choose a destination? \n",
choices: destArray.concat(nodeObj[activity.src].possible_dests.map(dest => {
return {
name: `${dest.alias}: (${dest.pub_key})`,
value: dest.pub_key
}
}))

})

if (!activity.dest) {
const singleNodeGraph = Object.values(nodeObj).find((node) => {
return node.graph.nodes
}).graph

const allPossibleNodes = singleNodeGraph.nodes.map((node) => node.pub_key)
activity.dest = await verifyPubKey(allPossibleNodes)
}

activity.action = await input({ message: 'What action?', default: "keysend" });

activity.frequency = await getFrequency()
activity.amount = await getAmountInSats()
}

activities.push(activity);

console.log(
`\n------------------------\nCreated: ${activity.uuid}\nTotal activities: ${activities.length}\n------------------------\n`
)

const anotherOne = await confirm({ message: 'Create another one?', default: false });
if (anotherOne) {
promptForActivities();
} else {
if (options.csv) activities = parse(activities, { header: true });
config.activity = activities;
console.log(config);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
syntax = "proto3";

package autopilotrpc;

option go_package = "github.com/lightningnetwork/lnd/lnrpc/autopilotrpc";

// Autopilot is a service that can be used to get information about the current
// state of the daemon's autopilot agent, and also supply it with information
// that can be used when deciding where to open channels.
service Autopilot {
/**
Status returns whether the daemon's autopilot agent is active.
*/
rpc Status (StatusRequest) returns (StatusResponse);

/**
ModifyStatus is used to modify the status of the autopilot agent, like
enabling or disabling it.
*/
rpc ModifyStatus (ModifyStatusRequest) returns (ModifyStatusResponse);

/**
QueryScores queries all available autopilot heuristics, in addition to any
active combination of these heruristics, for the scores they would give to
the given nodes.
*/
rpc QueryScores (QueryScoresRequest) returns (QueryScoresResponse);

/**
SetScores attempts to set the scores used by the running autopilot agent,
if the external scoring heuristic is enabled.
*/
rpc SetScores (SetScoresRequest) returns (SetScoresResponse);
}

message StatusRequest {
}

message StatusResponse {
/// Indicates whether the autopilot is active or not.
bool active = 1;
}

message ModifyStatusRequest {
/// Whether the autopilot agent should be enabled or not.
bool enable = 1;
}

message ModifyStatusResponse {
}

message QueryScoresRequest {
repeated string pubkeys = 1;

/// If set, we will ignore the local channel state when calculating scores.
bool ignore_local_state = 2;
}

message QueryScoresResponse {
message HeuristicResult {
string heuristic = 1;
map<string, double> scores = 2;
}

repeated HeuristicResult results = 1;
}

message SetScoresRequest {
/// The name of the heuristic to provide scores to.
string heuristic = 1;

/**
A map from hex-encoded public keys to scores. Scores must be in the range
[0.0, 1.0].
*/
map<string, double> scores = 2;
}

message SetScoresResponse {
}
Loading

0 comments on commit 1c7f3c7

Please sign in to comment.