Skip to content

Commit

Permalink
Automaticcaly roll between multiple signing keys.
Browse files Browse the repository at this point in the history
- Handle a list of signing keys.
- Detect current key used, and switch to the next one if needed/requested.
- Avoid to input a new key after a key switch;
- Update documentation and commands accordingly.
  • Loading branch information
Zapata committed Jul 12, 2018
1 parent beca17c commit 08eb281
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 21 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Open config-sample.json in your favourite text editor and edit with your own set
"missed_block_threshold": 3,
"checking_interval": 10,
"reset_period": 300,
"backup_key": "BTSXXXXXXXXXXXXXXXXXX",
"witness_signing_keys": [ "BTSXXXXXXXXXXXXXXXXXX", "BTSYYYYYYYYYYYYYYY"],
"recap_time": 60,
"debug_level": 3,
"telegram_token": "<telegram_access_token>",
Expand All @@ -40,10 +40,12 @@ and then save as config.json

| Key | Description |
| --- | --- |
| `witness_id` | The id of the witness to monitor. |
| `api_node` | Bitshares Websocket url to use to retrieve blockchain information. |
| `private_key` | The active key of your normal witness-owning account used to sign the witness_update operation. |
| `missed_block_threshold` | How many blocks must be missed within a `reset_period` sec window before the script switches your signing key. Recommend to set at 2 or higher since 1 will possibly trigger updates on maintenance intervals (see [bitshares-core#504](https://github.com/bitshares/bitshares-core/issues/504)). |
| `checking_interval` | How often should the script check for new missed blocks in seconds. |
| `backup_key` | The public signing key of your backup witness to be used when switching. |
| `witness_signing_keys` | All the public keys of your witness, to switch key if too many blocks are missed. |
| `recap_time` | The interval in minutes on which bot will auto-notify telegram user of latest stats (if authenticated). |
| `reset_period` | The time after which the missed blocks counter is reset for the session in seconds. |
| `debug_level` | Logging level. Can be: _0_ (Minimum - Explicit logging & Errors, _1_ (Info - 0 + Basic logging), _2_ (Verbose - 1 + Verbose logging), _3_. Transient - 2 + Transient messages. Not currently used. |
Expand Down Expand Up @@ -99,8 +101,8 @@ Open a chat to your bot and use the following:
- `/start`: Introduction message.
- `/help`: Get the list of available commands.
- `/stats`: Return the current configuration and statistics of the monitoring session.
- `/switch`: IMMEDIATELY update your signing key to the currently configured backup key.
- `/new_key <BTS_public_signing_key>`: Set a new backup key in place of the configured one.
- `/switch`: IMMEDIATELY update your signing key to the new available signing key.
- `/signing_keys <BTS_public_signing_key1> <BTS_public_signing_key2>`: Set a new list of public keys.
- `/new_node wss://<api_node_url>`: Set a new API node to connect to.
- `/threshold X`: Set the missed block threshold before updating signing key to X blocks.
- `/interval Y`: Set the checking interval to every Y seconds.
Expand All @@ -122,7 +124,7 @@ start - Introduction
help - List all commands
stats - Gather statistics
switch - Update signing key to backup
new_key - Set a new backup key
signing_keys - Set signing keys
new_node - Set a new API node to connect to
threshold - Set the missed block threshold
interval - Set the checking interval
Expand Down
2 changes: 1 addition & 1 deletion config-sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"missed_block_threshold": 3,
"checking_interval": 10,
"reset_period": 300,
"backup_key": "",
"witness_signing_keys": [],
"recap_time": 60,
"debug_level": 3,
"telegram_token": "",
Expand Down
18 changes: 10 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ function send_stats(recipient_id) {
const current_stats = witness_monitor.current_statistics();
let stats = [
`Total missed blocks: \`${current_stats.total_missed}\``,
`Missed blocks in current time window: \`${current_stats.current_missed}\``,
`Missed blocks in current time window: \`${current_stats.window_missed}\``,
`Current signing key: \`${current_stats.signing_key}\``,
`Feed publications: `
]
current_stats.feed_publications.forEach(feed_stat => {
Expand All @@ -37,7 +38,7 @@ function send_settings(recipient_id) {
`Node failed connection attempt notification threshold: \`${config.retries_threshold}\``,
`Missed block threshold: \`${config.missed_block_threshold}\``,
`Missed block reset time window: \`${config.reset_period} sec\``,
`Backup signing key: \`${config.backup_key}\``,
`Public signing keys: ${config.witness_signing_keys.map(k => '`' + k + '`').join(', ')}`,
`Recap time period: \`${config.recap_time} min\``,
`Feeds to check: \`${config.feeds_to_check}\``,
`Feed publication treshold: \`${config.feed_publication_threshold} min\``,
Expand Down Expand Up @@ -65,8 +66,8 @@ bot.onText(/\/start/, (msg) => {
bot.onText(/\/help/, (msg) => {
const help = [
`\`/stats\`: Return the current configuration and statistics of the monitoring session.`,
`\`/switch\`: IMMEDIATELY update your signing key to the currently configured backup key.`,
`\`/new_key <BTS_public_signing_key>\`: Set a new backup key in place of the configured one.`,
`\`/switch\`: IMMEDIATELY update your signing key to the next available signing key.`,
`\`/signing_keys <BTS_public_signing_key1> <BTS_public_signing_key2>\`: Set a new list of public keys.`,
`\`/new_node wss://<api_node_url>\`: Set a new API node to connect to.`,
`\`/threshold X\`: Set the missed block threshold before updating signing key to X blocks.`,
`\`/interval Y\`: Set the checking interval to every Y seconds.`,
Expand Down Expand Up @@ -95,14 +96,15 @@ bot.onText(/\/reset/, (msg, match) => {

});

bot.onText(/\/new_key (.+)/, (msg, match) => {
bot.onText(/\/signing_keys (.+)/, (msg, match) => {

const chatId = msg.chat.id;
const key = match[1];
const keys = match[1].split(' ');

if (checkAuthorization(chatId)) {
config.backup_key = key;
bot.sendMessage(chatId, `Backup signing key set to: ${config.backup_key}`);
config.witness_signing_keys = keys;
bot.sendMessage(chatId, `Signing keys set to: ${config.witness_signing_keys.map(k => '`' + k + '`').join(', ')}`,
{ parse_mode: 'Markdown' });
}

});
Expand Down
24 changes: 17 additions & 7 deletions lib/WitnessMonitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const moment = require('moment');
const FeedStat = require('./FeedStat.js');

class WitnessMonitor extends EventEmitter {


constructor(config, logger) {
super();
Expand All @@ -19,16 +18,17 @@ class WitnessMonitor extends EventEmitter {
this._checking = false;
this._witness_account = null;
this._witness_url = null;
this._witness_current_signing_key = null;
this._feed_stats = null;
this._last_feed_check = null;
this._node_retries = 0;
}


current_statistics() {
return {
total_missed : this._total_missed,
current_missed : this._total_missed - this._start_missed,
window_missed : this._total_missed - this._start_missed,
signing_key : this._witness_current_signing_key,
feed_publications : this._feed_stats
}
}
Expand All @@ -53,11 +53,18 @@ class WitnessMonitor extends EventEmitter {
return Apis.close();
}
});
}

find_next_signing_key() {
const i = this._config.witness_signing_keys.findIndex(k => k == this._witness_current_signing_key);
if (i == -1) {
return this._config.witness_signing_keys[0];
}
return this._config.witness_signing_keys[(i + 1) % this._config.witness_signing_keys.length];
}

update_signing_key() {
const private_key = PrivateKey.fromWif(this._config.private_key);
const backup_signing_key = this.find_next_signing_key();
const tr = new TransactionBuilder();
tr.add_type_operation('witness_update', {
fee: {
Expand All @@ -67,21 +74,23 @@ class WitnessMonitor extends EventEmitter {
witness: this._config.witness_id,
witness_account: this._witness_account,
new_url: this._witness_url,
new_signing_key: this._config.backup_key
new_signing_key: backup_signing_key
});

return tr.set_required_fees().then(() => {
const private_key = PrivateKey.fromWif(this._config.private_key);
tr.add_signer(private_key, private_key.toPublicKey().toPublicKeyString());
return tr.broadcast();
})
.then(() => {
this.reset_missed_block_window();
this.notify('Signing key updated');
this.notify(`Signing key updated to: ${backup_signing_key}`);
}).catch(() => {
this.notify('Could not broadcast update_witness tx.');
});

}

notify(msg) {
this._logger.log(msg);
this.emit('notify', msg)
Expand Down Expand Up @@ -176,7 +185,8 @@ class WitnessMonitor extends EventEmitter {
this._logger.log('Connected to API node: ' + this._config.api_node);
return Apis.instance().db_api().exec('get_objects', [[this._config.witness_id]]).then((witness) => {
this._witness_account = witness[0].witness_account;
this._witness_url = witness[0].url
this._witness_url = witness[0].url;
this._witness_current_signing_key = witness[0].signing_key;
this._total_missed = witness[0].total_missed;

const should_reset_window = moment().diff(this._window_start, 'seconds') >= this._config.reset_period
Expand Down

0 comments on commit 08eb281

Please sign in to comment.