lighstorm 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,33 +6,48 @@ require_relative '../satoshis'
6
6
 
7
7
  require_relative '../connections/payment_channel'
8
8
  require_relative '../nodes/node'
9
- require_relative '../payment_request'
9
+ require_relative '../invoice'
10
+ require_relative '../secret'
10
11
 
11
12
  module Lighstorm
12
13
  module Models
13
14
  class Payment
14
- attr_reader :_key, :hash, :request, :status, :created_at, :settled_at, :purpose
15
+ attr_reader :_key, :at, :state, :secret, :purpose, :through, :message
15
16
 
16
17
  def initialize(data)
17
18
  @data = data
18
19
 
19
20
  @_key = data[:_key]
20
- @status = data[:status]
21
- @created_at = data[:created_at]
22
- @settled_at = data[:settled_at]
21
+ @at = data[:at]
22
+ @state = data[:state]
23
23
  @purpose = data[:purpose]
24
+ @through = data[:through]
25
+ @message = data[:message]
24
26
  end
25
27
 
26
- def request
27
- @request ||= PaymentRequest.new(@data[:request])
28
+ def how
29
+ @how ||= spontaneous? ? 'spontaneously' : 'with-invoice'
30
+ end
31
+
32
+ def invoice
33
+ @invoice ||= !spontaneous? && @data[:invoice] ? Invoice.new(@data[:invoice]) : nil
34
+ end
35
+
36
+ def amount
37
+ @amount ||= @data[:amount] ? Satoshis.new(millisatoshis: @data[:amount][:millisatoshis]) : nil
28
38
  end
29
39
 
30
40
  def fee
31
- @fee ||= Satoshis.new(millisatoshis: @data[:fee][:millisatoshis])
41
+ @fee ||= @data[:fee] ? Satoshis.new(millisatoshis: @data[:fee][:millisatoshis]) : nil
42
+ end
43
+
44
+ def secret
45
+ @secret ||= @data[:secret] ? Secret.new(@data[:secret]) : nil
32
46
  end
33
47
 
34
48
  def hops
35
49
  return @hops if @hops
50
+ return nil if @data[:hops].nil?
36
51
 
37
52
  @data[:hops].last[:is_last] = true
38
53
  @hops = @data[:hops].map do |hop|
@@ -41,29 +56,45 @@ module Lighstorm
41
56
  end
42
57
 
43
58
  def from
44
- @from ||= hops.first
59
+ @from ||= @data[:hops].nil? ? nil : hops.first
45
60
  end
46
61
 
47
62
  def to
48
- @to ||= hops.last
63
+ @to ||= @data[:hops].nil? ? nil : hops.last
49
64
  end
50
65
 
51
66
  def to_h
52
67
  response = {
53
68
  _key: _key,
54
- status: status,
55
- created_at: created_at,
56
- settled_at: settled_at,
69
+ at: at,
70
+ state: state,
71
+ through: through,
57
72
  purpose: purpose,
58
- fee: {
59
- millisatoshis: fee.millisatoshis,
60
- parts_per_million: fee.parts_per_million(request.amount.millisatoshis)
61
- },
62
- request: request.to_h,
73
+ how: how,
74
+ message: message,
75
+ invoice: invoice&.to_h,
63
76
  from: from.to_h,
64
- to: to.to_h,
65
- hops: hops.map(&:to_h)
77
+ to: to.to_h
66
78
  }
79
+
80
+ response[:secret] = secret.to_h if secret
81
+ response[:amount] = amount.to_h if amount
82
+ if fee
83
+ response[:fee] = {
84
+ millisatoshis: fee.millisatoshis,
85
+ parts_per_million: fee.parts_per_million(amount.millisatoshis)
86
+ }
87
+ end
88
+
89
+ response[:hops] = hops.map(&:to_h) unless hops.nil?
90
+
91
+ response
92
+ end
93
+
94
+ private
95
+
96
+ def spontaneous?
97
+ !@data[:invoice] || @data[:invoice][:code].nil?
67
98
  end
68
99
  end
69
100
  end
data/models/errors.rb CHANGED
@@ -6,11 +6,11 @@ module Lighstorm
6
6
 
7
7
  class ToDoError < LighstormError; end
8
8
 
9
+ class ArgumentError < LighstormError; end
9
10
  class TooManyArgumentsError < LighstormError; end
10
11
  class IncoherentGossipError < LighstormError; end
11
12
  class MissingGossipHandlerError < LighstormError; end
12
13
  class MissingCredentialsError < LighstormError; end
13
- class MissingMillisatoshisError < LighstormError; end
14
14
  class MissingPartsPerMillionError < LighstormError; end
15
15
  class MissingTTLError < LighstormError; end
16
16
  class NegativeNotAllowedError < LighstormError; end
@@ -20,6 +20,34 @@ module Lighstorm
20
20
  class UnexpectedNumberOfHTLCsError < LighstormError; end
21
21
  class UnknownChannelError < LighstormError; end
22
22
 
23
+ class InvoiceMayHaveMultiplePaymentsError < LighstormError; end
24
+
25
+ class PaymentError < LighstormError
26
+ attr_reader :response, :result, :grpc
27
+
28
+ def initialize(message, response: nil, result: nil, grpc: nil)
29
+ super(message)
30
+ @response = response
31
+ @result = result
32
+ @grpc = grpc
33
+ end
34
+
35
+ def to_h
36
+ output = { message: message }
37
+
38
+ output[:response] = response unless response.nil?
39
+ output[:result] = result.to_h unless result.nil?
40
+ output[:grpc] = grpc.message unless grpc.nil?
41
+
42
+ output
43
+ end
44
+ end
45
+
46
+ class NoRouteFoundError < PaymentError; end
47
+ class AlreadyPaidError < PaymentError; end
48
+ class AmountForNonZeroError < PaymentError; end
49
+ class MissingMillisatoshisError < PaymentError; end
50
+
23
51
  class UpdateChannelPolicyError < LighstormError
24
52
  attr_reader :response
25
53
 
data/models/invoice.rb CHANGED
@@ -3,8 +3,6 @@
3
3
  require_relative 'satoshis'
4
4
  require 'securerandom'
5
5
 
6
- require_relative 'payment_request'
7
-
8
6
  require_relative '../controllers/invoice/actions/create'
9
7
  require_relative '../controllers/invoice/actions/pay'
10
8
  require_relative '../controllers/invoice/actions/pay_through_route'
@@ -12,36 +10,94 @@ require_relative '../controllers/invoice/actions/pay_through_route'
12
10
  module Lighstorm
13
11
  module Models
14
12
  class Invoice
15
- attr_reader :_key, :created_at, :settle_at, :state
13
+ attr_reader :_key, :created_at, :expires_at, :settled_at, :state, :payable, :code
16
14
 
17
15
  def initialize(data)
18
16
  @data = data
19
17
 
20
18
  @_key = data[:_key]
21
19
  @created_at = data[:created_at]
22
- @settle_at = data[:settle_at]
20
+ @expires_at = data[:expires_at]
21
+ @settled_at = data[:settled_at]
23
22
  @state = data[:state]
23
+
24
+ @payable = data[:payable]
25
+
26
+ @code = data[:code]
27
+ end
28
+
29
+ def payment
30
+ if payable != 'once' || @data[:payments].size > 1
31
+ raise InvoiceMayHaveMultiplePaymentsError, "payable: #{payable}, payments: #{@data[:payments].size.size}"
32
+ end
33
+
34
+ @payment ||= payments.first
35
+ end
36
+
37
+ def payments
38
+ @payments ||= @data[:payments]&.map { |data| Payment.new(data) }
24
39
  end
25
40
 
26
- def request
27
- @request ||= PaymentRequest.new(@data[:request])
41
+ def amount
42
+ @amount ||= @data[:amount] ? Satoshis.new(millisatoshis: @data[:amount][:millisatoshis]) : nil
43
+ end
44
+
45
+ def received
46
+ @received ||= @data[:received] ? Satoshis.new(millisatoshis: @data[:received][:millisatoshis]) : nil
47
+ end
48
+
49
+ def secret
50
+ @secret ||= Secret.new(@data[:secret])
51
+ end
52
+
53
+ def description
54
+ @description ||= Struct.new(:data) do
55
+ def memo
56
+ data[:memo]
57
+ end
58
+
59
+ def hash
60
+ data[:hash]
61
+ end
62
+
63
+ def to_h
64
+ { memo: memo, hash: hash }
65
+ end
66
+ end.new(@data[:description] || {})
28
67
  end
29
68
 
30
69
  def to_h
31
- {
70
+ result = {
32
71
  _key: _key,
33
72
  created_at: created_at,
34
- settle_at: settle_at,
73
+ expires_at: expires_at,
74
+ settled_at: settled_at,
75
+ payable: payable,
35
76
  state: state,
36
- request: request.to_h
77
+ code: code,
78
+ amount: amount&.to_h,
79
+ received: received&.to_h,
80
+ description: description.to_h,
81
+ secret: secret.to_h,
82
+ payments: payments&.map(&:to_h)
37
83
  }
38
84
  end
39
85
 
40
- def pay!(route: nil, preview: false)
86
+ def pay(
87
+ millisatoshis: nil, message: nil, route: nil,
88
+ times_out_in: { seconds: 5 },
89
+ preview: false
90
+ )
41
91
  if route
42
92
  Controllers::Invoice::PayThroughRoute.perform(self, route: route, preview: preview)
43
93
  else
44
- Controllers::Invoice::Pay.perform(self, preview: preview)
94
+ Controllers::Invoice::Pay.perform(
95
+ request_code: code,
96
+ millisatoshis: millisatoshis,
97
+ message: message,
98
+ times_out_in: times_out_in,
99
+ preview: preview
100
+ )
45
101
  end
46
102
  end
47
103
  end
data/models/nodes/node.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../controllers/node/actions/apply_gossip'
4
+ require_relative '../../controllers/node/actions/pay'
4
5
  require_relative '../../controllers/channel'
5
6
  require_relative '../../adapters/nodes/node'
6
7
  require_relative '../concerns/protectable'
@@ -97,6 +98,37 @@ module Lighstorm
97
98
  self, gossip
98
99
  )
99
100
  end
101
+
102
+ def send_message(
103
+ message, millisatoshis:, secret: nil,
104
+ times_out_in: { seconds: 5 }, through: 'amp',
105
+ preview: false
106
+ )
107
+ pay(
108
+ message: message,
109
+ millisatoshis: millisatoshis,
110
+ secret: secret,
111
+ times_out_in: times_out_in,
112
+ through: through,
113
+ preview: preview
114
+ )
115
+ end
116
+
117
+ def pay(
118
+ millisatoshis:, message: nil, secret: nil,
119
+ times_out_in: { seconds: 5 }, through: 'amp',
120
+ preview: false
121
+ )
122
+ Controllers::Node::Pay.perform(
123
+ public_key: public_key,
124
+ millisatoshis: millisatoshis,
125
+ through: through,
126
+ secret: secret,
127
+ message: message,
128
+ times_out_in: times_out_in,
129
+ preview: preview
130
+ )
131
+ end
100
132
  end
101
133
  end
102
134
  end
data/models/satoshis.rb CHANGED
@@ -5,10 +5,18 @@ require_relative '../ports/dsl/lighstorm/errors'
5
5
  module Lighstorm
6
6
  module Models
7
7
  class Satoshis
8
- def initialize(millisatoshis: nil)
9
- raise MissingMillisatoshisError, 'missing millisatoshis' if millisatoshis.nil?
8
+ def initialize(millisatoshis: nil, bitcoins: nil)
9
+ if [millisatoshis, bitcoins].compact.empty?
10
+ raise MissingMillisatoshisError, 'missing millisatoshis'
11
+ elsif [millisatoshis, bitcoins].compact.size > 1
12
+ raise Errors::TooManyArgumentsError, 'you need to pass millisatoshis: or bitcoins:, not both'
13
+ end
10
14
 
11
- @amount_in_millisatoshis = millisatoshis
15
+ if !millisatoshis.nil?
16
+ @amount_in_millisatoshis = millisatoshis
17
+ elsif !bitcoins.nil?
18
+ @amount_in_millisatoshis = (bitcoins.to_f * 100_000_000_000.0).to_i
19
+ end
12
20
  end
13
21
 
14
22
  def parts_per_million(reference_millisatoshis)
data/models/secret.rb ADDED
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module Lighstorm
6
+ module Models
7
+ class Secret
8
+ attr_reader :preimage, :hash
9
+
10
+ def self.create
11
+ data = { preimage: SecureRandom.hex(32) }
12
+ data[:hash] = Digest::SHA256.hexdigest([data[:preimage]].pack('H*'))
13
+ Secret.new(data)
14
+ end
15
+
16
+ def initialize(data)
17
+ @data = data
18
+
19
+ @preimage = data[:preimage]
20
+ @hash = data[:hash]
21
+ end
22
+
23
+ def to_h
24
+ {
25
+ preimage: preimage,
26
+ hash: hash
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'invoice'
4
+
5
+ module Lighstorm
6
+ module Models
7
+ class Transaction
8
+ attr_reader :direction, :at, :message, :_key
9
+
10
+ def initialize(data)
11
+ @data = data
12
+
13
+ @_key = @data[:_key]
14
+ @at = @data[:at]
15
+ @direction = @data[:direction]
16
+ @message = @data[:message]
17
+ end
18
+
19
+ def amount
20
+ @amount ||= Satoshis.new(millisatoshis: @data[:amount][:millisatoshis])
21
+ end
22
+
23
+ def invoice
24
+ @invoice ||= @data[:kind] == 'invoice' ? Invoice.new(@data[:data]) : nil
25
+ end
26
+
27
+ def to_h
28
+ output = {
29
+ _key: _key,
30
+ at: at,
31
+ direction: direction,
32
+ amount: amount.to_h,
33
+ message: message
34
+ }
35
+
36
+ output[:invoice] = invoice.to_h unless invoice.nil?
37
+
38
+ output
39
+ end
40
+ end
41
+ end
42
+ end
@@ -11,6 +11,7 @@ require_relative '../../controllers/channel'
11
11
  require_relative '../../controllers/payment'
12
12
  require_relative '../../controllers/forward'
13
13
  require_relative '../../controllers/invoice'
14
+ require_relative '../../controllers/transaction'
14
15
 
15
16
  module Lighstorm
16
17
  Node = Controllers::Node
@@ -18,6 +19,7 @@ module Lighstorm
18
19
  Payment = Controllers::Payment
19
20
  Forward = Controllers::Forward
20
21
  Invoice = Controllers::Invoice
22
+ Transaction = Controllers::Transaction
21
23
 
22
24
  Satoshis = Models::Satoshis
23
25
 
data/static/cache.rb CHANGED
@@ -3,27 +3,28 @@
3
3
  module Lighstorm
4
4
  module Static
5
5
  CACHE = {
6
- update_channel_policy: false,
7
- add_invoice: false,
8
- fee_report: {
6
+ lightning_update_channel_policy: false,
7
+ lightning_add_invoice: false,
8
+ router_send_payment_v2: false,
9
+ lightning_fee_report: {
9
10
  ttl: 0.01,
10
11
  properties: %i[
11
12
  base_fee_msat fee_per_mil
12
13
  ]
13
14
  },
14
- decode_pay_req: {
15
+ lightning_decode_pay_req: {
15
16
  ttl: 5 * 60
16
17
  },
17
- describe_graph: {
18
+ lightning_describe_graph: {
18
19
  ttl: 0
19
20
  },
20
- lookup_invoice: {
21
+ lightning_lookup_invoice: {
21
22
  ttl: 1
22
23
  },
23
- list_invoices: {
24
+ lightning_list_invoices: {
24
25
  ttl: 1
25
26
  },
26
- forwarding_history: {
27
+ lightning_forwarding_history: {
27
28
  ttl: 1,
28
29
  properties: %i[
29
30
  amt_in_msat
@@ -34,7 +35,7 @@ module Lighstorm
34
35
  timestamp_ns
35
36
  ]
36
37
  },
37
- get_chan_info: {
38
+ lightning_get_chan_info: {
38
39
  ttl: 5 * 60,
39
40
  properties: %i[
40
41
  channel_id
@@ -46,15 +47,15 @@ module Lighstorm
46
47
  capacity
47
48
  ]
48
49
  },
49
- get_info: {
50
+ lightning_get_info: {
50
51
  ttl: 5 * 60,
51
52
  properties: %i[identity_pubkey version chains chain network]
52
53
  },
53
- get_node_info: {
54
+ lightning_get_node_info: {
54
55
  ttl: 5 * 60,
55
56
  properties: %i[alias pub_key color]
56
57
  },
57
- list_channels: {
58
+ lightning_list_channels: {
58
59
  ttl: 1,
59
60
  properties: %i[
60
61
  local_balance remote_balance unsettled_balance
@@ -64,7 +65,7 @@ module Lighstorm
64
65
  total_satoshis_sent total_satoshis_received
65
66
  ]
66
67
  },
67
- list_payments: {
68
+ lightning_list_payments: {
68
69
  ttl: 1,
69
70
  properties: %i[
70
71
  creation_date payment_hash status
data/static/spec.rb CHANGED
@@ -4,7 +4,7 @@ module Lighstorm
4
4
  module Static
5
5
  SPEC = {
6
6
  name: 'lighstorm',
7
- version: '0.0.8',
7
+ version: '0.0.9',
8
8
  author: 'icebaker',
9
9
  summary: 'API for interacting with a Lightning Node.',
10
10
  description: 'Lighstorm is an opinionated abstraction layer on top of the lnd-client for interacting with a Lightning Node.',
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lighstorm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - icebaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-06 00:00:00.000000000 Z
11
+ date: 2023-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -74,6 +74,7 @@ files:
74
74
  - Gemfile.lock
75
75
  - LICENSE
76
76
  - README.md
77
+ - Rakefile
77
78
  - adapters/connections/channel_node.rb
78
79
  - adapters/connections/channel_node/fee.rb
79
80
  - adapters/connections/channel_node/policy.rb
@@ -84,7 +85,6 @@ files:
84
85
  - adapters/edges/payment/purpose.rb
85
86
  - adapters/invoice.rb
86
87
  - adapters/nodes/node.rb
87
- - adapters/payment_request.rb
88
88
  - components/cache.rb
89
89
  - components/lnd.rb
90
90
  - controllers/action.rb
@@ -106,11 +106,16 @@ files:
106
106
  - controllers/invoice/find_by_secret_hash.rb
107
107
  - controllers/node.rb
108
108
  - controllers/node/actions/apply_gossip.rb
109
+ - controllers/node/actions/pay.rb
109
110
  - controllers/node/all.rb
110
111
  - controllers/node/find_by_public_key.rb
111
112
  - controllers/node/myself.rb
112
113
  - controllers/payment.rb
114
+ - controllers/payment/actions/pay.rb
113
115
  - controllers/payment/all.rb
116
+ - controllers/transaction.rb
117
+ - controllers/transaction/all.rb
118
+ - deleted.sh
114
119
  - docs/.nojekyll
115
120
  - docs/README.md
116
121
  - docs/_coverpage.md
@@ -121,6 +126,7 @@ files:
121
126
  - docs/vendor/prismjs/prism-bash.min.js
122
127
  - docs/vendor/prismjs/prism-ruby.min.js
123
128
  - docs/vendor/prismjs/prism-tomorrow.min.css
129
+ - helpers/time_expression.rb
124
130
  - lighstorm.gemspec
125
131
  - models/concerns/protectable.rb
126
132
  - models/connections/channel_node.rb
@@ -143,9 +149,10 @@ files:
143
149
  - models/nodes/node.rb
144
150
  - models/nodes/node/lightning.rb
145
151
  - models/nodes/node/platform.rb
146
- - models/payment_request.rb
147
152
  - models/rate.rb
148
153
  - models/satoshis.rb
154
+ - models/secret.rb
155
+ - models/transaction.rb
149
156
  - ports/dsl/lighstorm.rb
150
157
  - ports/dsl/lighstorm/errors.rb
151
158
  - ports/grpc.rb
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'digest'
4
-
5
- require_relative '../ports/dsl/lighstorm/errors'
6
-
7
- module Lighstorm
8
- module Adapter
9
- class PaymentRequest
10
- def self.add_invoice(grpc)
11
- {
12
- _source: :add_invoice,
13
- code: grpc[:payment_request],
14
- address: grpc[:payment_addr].unpack1('H*'),
15
- secret: {
16
- hash: grpc[:r_hash].unpack1('H*')
17
- }
18
- }
19
- end
20
-
21
- def self.decode_pay_req(grpc)
22
- {
23
- _source: :decode_pay_req,
24
- amount: { millisatoshis: grpc[:num_msat] },
25
- description: {
26
- memo: grpc[:description],
27
- hash: grpc[:description_hash] == '' ? nil : grpc[:description_hash]
28
- },
29
- address: grpc[:payment_addr].unpack1('H*'),
30
- secret: {
31
- hash: grpc[:payment_hash]
32
- }
33
- }
34
- end
35
-
36
- def self.lookup_invoice(grpc)
37
- adapted = list_or_lookup_invoice(grpc)
38
- adapted[:_source] = :lookup_invoice
39
- adapted
40
- end
41
-
42
- def self.list_invoices(grpc)
43
- adapted = list_or_lookup_invoice(grpc)
44
- adapted[:_source] = :list_invoices
45
- adapted
46
- end
47
-
48
- def self.list_or_lookup_invoice(grpc)
49
- {
50
- code: grpc[:payment_request],
51
- amount: { millisatoshis: grpc[:value_msat] },
52
- description: {
53
- memo: grpc[:memo],
54
- hash: grpc[:description_hash] == '' ? nil : grpc[:description_hash]
55
- },
56
- address: grpc[:payment_addr].unpack1('H*'),
57
- secret: {
58
- preimage: grpc[:r_preimage].unpack1('H*'),
59
- hash: grpc[:r_hash].unpack1('H*')
60
- }
61
- }
62
- end
63
-
64
- def self.list_payments(grpc)
65
- raise UnexpectedNumberOfHTLCsError, "htlcs: #{grpc[:htlcs].size}" if grpc[:htlcs].size > 1
66
-
67
- data = {
68
- _source: :list_payments,
69
- code: grpc[:payment_request],
70
- amount: { millisatoshis: grpc[:value_msat] },
71
- secret: {
72
- preimage: grpc[:payment_preimage],
73
- hash: grpc[:payment_hash]
74
- }
75
- }
76
-
77
- grpc[:htlcs].first[:route][:hops].map do |raw_hop|
78
- if raw_hop[:mpp_record] && raw_hop[:mpp_record][:payment_addr]
79
- data[:address] = raw_hop[:mpp_record][:payment_addr].unpack1('H*')
80
- end
81
- end
82
-
83
- data
84
- end
85
- end
86
- end
87
- end