#!/usr/bin/env ruby require 'lnd/tool' require 'thor' require 'yaml' require 'terminal-table' # CLI class of lnd-tool class CLI < Thor class << self def exit_on_failure? true end end desc 'capture_htlc --config "Path to configuration file"', 'Capture the HTLC events of LND.' option :config, required: true, type: :string def capture_htlc raise Thor::Error, "config file #{options['config']} does not exist." unless File.exist?(options['config']) raise Thor::Error, 'Capture process already running.' if LND::Tool::Daemon.running? config = YAML.load_file(options['config']) LND::Tool::Daemon.start do capture = LND::Tool::HTLCEventCapture.new(config['lnd']) capture.start end end desc 'stop_capture', 'Stop capture process.' def stop_capture raise Thor::Error, 'Capture process not running.' unless LND::Tool::Daemon.running? LND::Tool::Daemon.stop end desc 'query_htlc', 'Query the captured data in SQLite DB.' option :event, type: :string, desc: 'Targets the specified type of event. Valid type are "send", "receive", "forward".' option :limit, type: :numeric, desc: 'Maximum number of data' def query_htlc if options['event'] && !%w[send receive forward].include?(options['event']) raise Thor::Error, 'event must be specified as send, receive, or forward.' end headers = [ 'incoming channel', 'incoming htlc', 'outgoing channel', 'outgoing htlc', 'timestamp', 'event type', 'result', 'detail', 'incoming msat', 'outgoing msat' ] store = LND::Tool::Store::HTLCEvent.new rows = store.query(event_type: options['event']&.upcase, limit: options['limit']).map do |r| result, detail, htlc = if r.forward_event ['FORWARD', nil, r.forward_event.info] elsif r.forward_fail_event ['FORWARD FAIL'] elsif r.settle_event ['SETTLE'] elsif r.link_fail_event ['LINK_FAIL', r.link_fail_event.wire_failure, r.link_fail_event.info] end [ { value: r.incoming_channel_id, alignment: :right }, { value: r.incoming_htlc_id, alignment: :right }, { value: r.outgoing_channel_id, alignment: :right }, { value: r.outgoing_htlc_id, alignment: :right }, Time.at(r.timestamp_ns / 1000000000), r.event_type, result, detail, { value: htlc&.incoming_amt_msat, alignment: :right }, { value: htlc&.outgoing_amt_msat, alignment: :right } ] end table = Terminal::Table.new(title: 'HTLC Events', headings: headers, rows: rows) puts table end desc 'prune_htlc', 'Pruning the data for the HTLC event in DB. Run with either the max option or the date option.' option :max, type: :numeric, desc: 'This is the maximum number of data to be kept. Data exceeding this limit will be deleted in order of oldest to newest.' option :date, type: :string, desc: 'Date in 2021-09-04 format. Delete data prior to this date.' def prune_htlc begin store = LND::Tool::Store::HTLCEvent.new if options['max'] store.prune_up_to(options['max']) elsif options['date'] time = Date.parse(options['date']).to_time store.prune_prior_to(time) else raise Thor::Error, 'Either the max or date option should be specified.' end puts "The current number of data is #{store.count}." rescue Date::Error raise Thor::Error, 'The format of the date option is invalid.' end end end CLI.start(ARGV)