Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Activity Generator #9

Merged
merged 18 commits into from
Aug 17, 2023
Merged
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
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