c-lightningrb 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 217391984ad129f43d5a9c94faf0768d26bf1b58ea3693bb4c63699c74e08c1c
4
- data.tar.gz: 3d535c1c49a4952e7f202ba4d6e67c2a90c873e2c2b0c5aa2bd8d1ab8c731a38
3
+ metadata.gz: 2c2fd714b383235136db352d6b4a907a2b418d595de24f900102497887629d4e
4
+ data.tar.gz: 6046070fc4a9ba437cd4688a423b4acc8ade9f07953d77a36827e92d8ea7d38b
5
5
  SHA512:
6
- metadata.gz: 0bfcbdefe33df305f684fefd8f6c636a9ba709742b409b3aa57a2eabb9f9f1493b70ffabba6fc60e3983eedca3cb44e2ec112a35bf5991e7cc4663648745db6f
7
- data.tar.gz: 92440120dc13251e3beba89a0c10dd9c96593d26481f23626711c6de0d9059a8584e0d7db1934468ee3ac160c97f4f779a2e0c1ab8e5f63253330df5d55527b3
6
+ metadata.gz: 96e06e96542e9851634321af9be64d09a774a94f8835eb7d7b49b61058397ecbb7b189bcc91a9eb36597b459b23d7e28931875301a118720ae7555ce4fb61a22
7
+ data.tar.gz: d7d2b4015a2abbb689fbba7832459bc1c8cc045cf1e4f12a3596d3c4a0ec21580206dad730cde2e855797dab95576c640062c5ace3e6e16a6f44ab21d70ee7a9
data/README.md CHANGED
@@ -21,6 +21,56 @@ Or install it yourself as:
21
21
 
22
22
  ## Examples
23
23
 
24
+ ### Using the JSON-RPC client
25
+
26
+ ```ruby
27
+ require 'lightning'
28
+
29
+ # initialize RPC interface using unix socket file.
30
+ rpc = Lightning::RPC.new('/home/azuchi/.lightning/lightning-rpc')
31
+
32
+ puts rpc.getinfo
33
+
34
+ => {
35
+ "id": "02a7581f5aafd3ed01a6664ad5108ce1601435d9e9e47c57f1c40cff152cd59307",
36
+ "alias": "GREENPHOTO",
37
+ "color": "02a758",
38
+ "num_peers": 0,
39
+ "num_pending_channels": 0,
40
+ "num_active_channels": 0,
41
+ "num_inactive_channels": 0,
42
+ "address": [
43
+
44
+ ],
45
+ "binding": [
46
+ {
47
+ "type": "ipv6",
48
+ "address": "::",
49
+ "port": 9735
50
+ },
51
+ {
52
+ "type": "ipv4",
53
+ "address": "0.0.0.0",
54
+ "port": 9735
55
+ }
56
+ ],
57
+ "version": "v0.7.0",
58
+ "blockheight": 1518441,
59
+ "network": "testnet",
60
+ "msatoshi_fees_collected": 0,
61
+ "fees_collected_msat": "0msat"
62
+ }
63
+
64
+ puts rpc.invoice(1000, 'example', 'test payment')
65
+
66
+ => {
67
+ "payment_hash": "76b2f5d6791a2e0be44071543c71d27238e2153fd832ac23d8c027b33e024fb8",
68
+ "expires_at": 1558856940,
69
+ "bolt11": "lntb10n1pww5dkupp5w6e0t4nerghqhezqw92rcuwjwguwy9flmqe2cg7ccqnmx0szf7uqdq5w3jhxapqwpshjmt9de6qcqp2phn9mgplxj2mxg59zjrlhwh2p66h2r3p4f7kyk8w4s3zcma5htn807r8lgfmg75hwcvhse8sqtgcyakgezdzjc0zyd87uahe3wsz3qcp4nv6f0",
70
+ "warning_capacity": "No channels have sufficient incoming capacity"
71
+ }
72
+ ```
73
+
24
74
  ### Writing a plugin
25
75
 
26
76
  You can write your own Plugin by inheriting `Lightning::Plugin`.
@@ -30,6 +80,9 @@ You can write your own Plugin by inheriting `Lightning::Plugin`.
30
80
  require 'lightning'
31
81
 
32
82
  class HelloPlugin < Lightning::Plugin
83
+
84
+ # Command line option pass-through
85
+ option 'greeting', 'World', "What name should I call you?"
33
86
 
34
87
  # define new rpc. Usage and description are required only for the definition of RPC.
35
88
  desc '[name]', 'Returns a personalized greeting for {greeting} (set via options).'
@@ -61,6 +114,7 @@ p.run
61
114
 
62
115
  Write all RPC, notification, and hook handlers in Lambda.
63
116
  These Lambdas are implemented as methods, so you can access any of the fields and methods of the Plugin.
117
+ For example, `Lightning::Plugin` instance has `rpc` field which can access `lightningd` via `Lightning::RPC`.
64
118
 
65
119
  And it works if you specify Plugin as the parameter when c-lightning launches.
66
120
 
@@ -0,0 +1,9 @@
1
+ # Examples
2
+
3
+ ### [hello Plugin](hello.rb)
4
+
5
+ Add hello rpc to lightning-cli.
6
+
7
+ $ lightning-cli hello c-lightning
8
+ hello c-lightning
9
+
data/examples/hello.rb ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ require 'lightning'
3
+
4
+ class HelloPlugin < Lightning::Plugin
5
+
6
+ desc '[name]', 'Returns a personalized greeting for {greeting} (set via options).'
7
+ define_rpc :hello, -> (name) do
8
+ log.info "log = #{log}"
9
+ "hello #{name}"
10
+ end
11
+
12
+ option 'greeting', 'World', "What name should I call you?"
13
+
14
+ end
15
+
16
+ p = HelloPlugin.new
17
+ p.run
@@ -53,6 +53,20 @@ module Lightning
53
53
  @subscriptions ||= []
54
54
  end
55
55
 
56
+ # get options
57
+ def options
58
+ @options ||= []
59
+ end
60
+
61
+ # Define option for lightningd command line.
62
+ # @param [String] name option name.
63
+ # @param [String] default default value.
64
+ # @param [String] description description.
65
+ def option(name, default, description)
66
+ # currently only string options are supported.
67
+ options << {name: name, default: default, description: description, type: 'string'}
68
+ end
69
+
56
70
  # Define the definition of RPC method
57
71
  # @param [String] usage the usage of RPC method.
58
72
  # @param [String] desc the description of RPC method.
@@ -100,7 +114,6 @@ module Lightning
100
114
 
101
115
  end
102
116
 
103
- attr_reader :options
104
117
  attr_reader :stdout
105
118
  attr_reader :stdin
106
119
  attr_reader :log
@@ -110,20 +123,21 @@ module Lightning
110
123
 
111
124
  def initialize
112
125
  methods[:init] = Method.new(:init)
113
- @options = {}
114
126
  @stdout = STDOUT
115
127
  @stdin = STDIN
116
128
  methods[:getmanifest] = Method.new(:getmanifest)
117
129
  @log = Lightning::Logger.create(:plugin)
118
130
  end
119
131
 
120
- def init(options, configuration)
132
+ def init(opts, configuration)
121
133
  log.info("init")
122
134
  @lightning_dir = configuration['lightning-dir']
123
135
  @rpc_filename = configuration['rpc-file']
124
136
  socket_path = (Pathname.new(lightning_dir) + rpc_filename).to_path
125
- @rpc = Lightning::RPC.new(socket_path, log)
126
- @options.merge!(options)
137
+ @rpc = Lightning::RPC.new(socket_path)
138
+ opts.each do |key, value|
139
+ options.find{|o|o[:name] == key}[:value] = value
140
+ end
127
141
  nil
128
142
  end
129
143
 
@@ -132,7 +146,7 @@ module Lightning
132
146
  def getmanifest
133
147
  log.info("getmanifest")
134
148
  {
135
- options: options.values,
149
+ options: options,
136
150
  rpcmethods: rpc_methods.map(&:to_h),
137
151
  subscriptions: subscriptions,
138
152
  hooks: hook_methods.map(&:name),
@@ -148,20 +162,16 @@ module Lightning
148
162
  def run
149
163
  log.info("Plugin run.")
150
164
  begin
151
- partial = ''
152
- stdin.each_line do |l|
153
- partial << l
154
- msgs = partial.split("\n\n", -1)
155
- next if msgs.size < 2
156
- partial = multi_dispatch(msgs)
157
- end
158
- rescue Exception => e
159
- if e.is_a?(Interrupt)
160
- shutdown
161
- else
162
- log.error e
163
- throw e
165
+ partial = ''
166
+ stdin.each_line do |l|
167
+ partial << l
168
+ msgs = partial.split("\n\n", -1)
169
+ next if msgs.size < 2
170
+ partial = multi_dispatch(msgs)
164
171
  end
172
+ rescue Interrupt
173
+ log.info "Interrupt occur."
174
+ shutdown
165
175
  end
166
176
  log.info("Plugin end.")
167
177
  end
@@ -179,6 +189,11 @@ module Lightning
179
189
  self.class.subscriptions # delegate to class instance
180
190
  end
181
191
 
192
+ # get options
193
+ def options
194
+ self.class.options # delegate to class instance
195
+ end
196
+
182
197
  def multi_dispatch(msgs)
183
198
  msgs[0...-1].each do |payload|
184
199
  json = JSON.parse(payload)
@@ -194,16 +209,25 @@ module Lightning
194
209
  end
195
210
 
196
211
  def dispatch_request(request)
197
- method = methods[request.method]
198
- raise ArgumentError, "No method #{request.method} found." unless method
199
- result = request.method_args.empty? ? send(method.name) : send(method.name, *request.method_args)
200
- request.apply_result(result) if result
212
+ begin
213
+ method = methods[request.method]
214
+ raise ArgumentError, "No method #{request.method} found." unless method
215
+ result = request.method_args.empty? ? send(method.name) : send(method.name, *request.method_args)
216
+ request.apply_result(result) if result
217
+ rescue Exception => e
218
+ log.error e
219
+ request.write_error(e)
220
+ end
201
221
  end
202
222
 
203
223
  def dispatch_notification(request)
204
- name = request.method
205
- raise ArgumentError, "No handler #{request.method} found." unless subscriptions.include?(request.method)
206
- request.method_args.empty? ? send(name) : send(name, *request.method_args)
224
+ begin
225
+ name = request.method
226
+ raise ArgumentError, "No handler #{request.method} found." unless subscriptions.include?(request.method)
227
+ request.method_args.empty? ? send(name) : send(name, *request.method_args)
228
+ rescue Exception => e
229
+ log.error e
230
+ end
207
231
  end
208
232
 
209
233
  def rpc_methods
@@ -32,6 +32,8 @@ module Lightning
32
32
  end
33
33
  end
34
34
 
35
+ # write response
36
+ # @param [Hash] result the content of response
35
37
  def apply_result(result)
36
38
  @result = result
37
39
  json = {
@@ -39,8 +41,27 @@ module Lightning
39
41
  id: id,
40
42
  result: result
41
43
  }.to_json
42
- log.info "write response: #{json.to_s}"
43
- plugin.stdout.write(json.to_s + "\n\n")
44
+ write(json.to_s)
45
+ end
46
+
47
+ # write error
48
+ # @param [Exception] e an error.
49
+ def write_error(e)
50
+ error = {message: e.message}
51
+ error[:code] = e.code if e.is_a?(Lightning::RPCError)
52
+ json = {
53
+ jsonrpc: '2.0',
54
+ id: id,
55
+ error: error
56
+ }.to_json
57
+ write(json.to_s)
58
+ end
59
+
60
+ private
61
+
62
+ def write(content)
63
+ log.info "write response: #{content}"
64
+ plugin.stdout.write(content + "\n\n")
44
65
  plugin.stdout.flush
45
66
  end
46
67
 
data/lib/lightning/rpc.rb CHANGED
@@ -1,14 +1,59 @@
1
+ require 'socket'
2
+ require 'json'
3
+
1
4
  module Lightning
2
5
 
6
+ class RPCError < StandardError
7
+
8
+ INVALID_REQUEST = -32600
9
+ METHOD_NOT_FOUND = -32601
10
+ INVALID_PARAMS = -32602
11
+
12
+ attr_reader :code
13
+ attr_reader :message
14
+
15
+ def initialize(code, message)
16
+ @code = code
17
+ @message = message
18
+ end
19
+
20
+ end
21
+
3
22
  # RPC client for the `lightningd` daemon.
4
23
  class RPC
5
24
 
6
25
  attr_reader :socket_path
7
- attr_reader :log
26
+ attr_accessor :next_id
8
27
 
9
- def initialize(socket_path, log)
28
+ def initialize(socket_path)
10
29
  @socket_path = socket_path
11
- @log = log
30
+ @next_id = 0
31
+ end
32
+
33
+ private
34
+
35
+ def call(method, *kargs)
36
+ UNIXSocket.open(socket_path) do |socket|
37
+ msg = {
38
+ method: method.to_s,
39
+ params: kargs,
40
+ id: next_id
41
+ }
42
+ socket.write(msg.to_json)
43
+ self.next_id += 1
44
+ response = ''
45
+ loop do
46
+ response << socket.gets
47
+ break if response.include?("\n\n")
48
+ end
49
+ json = JSON.parse(response)
50
+ raise RPCError.new(json['error']['code'], json['error']['message']) if json['error']
51
+ json['result']
52
+ end
53
+ end
54
+
55
+ def method_missing(method, *args)
56
+ call(method, *args)
12
57
  end
13
58
 
14
59
  end
@@ -1,3 +1,3 @@
1
1
  module Lightning
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: c-lightningrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-05-25 00:00:00.000000000 Z
11
+ date: 2019-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -71,6 +71,8 @@ files:
71
71
  - Rakefile
72
72
  - bin/console
73
73
  - bin/setup
74
+ - examples/README.md
75
+ - examples/hello.rb
74
76
  - lib/lightning.rb
75
77
  - lib/lightning/logger.rb
76
78
  - lib/lightning/plugin.rb