Skip to content
Open
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
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ GEM
grpc (~> 1.53)
method_source (1.0.0)
parallel (1.22.1)
parser (3.2.1.1)
parser (3.2.2.0)
ast (~> 2.4.1)
pry (0.14.2)
coderay (~> 1.1)
Expand All @@ -47,7 +47,7 @@ GEM
rspec-expectations (3.12.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.4)
rspec-mocks (3.12.5)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-support (3.12.0)
Expand Down
33 changes: 33 additions & 0 deletions adapters/bitcoin/request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

require 'bigdecimal'

require 'digest'

module Lighstorm
module Adapter
module Bitcoin
class Request
def self.decode(uri)
result = {
_source: :decode,
_key: Digest::SHA256.hexdigest([uri[:host]].concat(uri[:params].values).join('/')),
address: { code: uri[:host] }
}

if uri[:params]['amount']
result[:amount] = {
millisatoshis: BigDecimal(uri[:params]['amount']) * BigDecimal('100000000000.0')
}
end

result[:description] = uri[:params]['label'] if uri[:params]['label'] && uri[:params]['label'] != ''

result[:message] = uri[:params]['message'] if uri[:params]['message'] && uri[:params]['message'] != ''

result
end
end
end
end
end
4 changes: 2 additions & 2 deletions controllers/bitcoin/address.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ def new(code:)
Model::Bitcoin::Address.new({ code: code }, components)
end

def create(preview: false, &vcr)
Create.perform(components, preview: preview, &vcr)
def create(format: 'taproot', preview: false, &vcr)
Create.perform(components, format: format, preview: preview, &vcr)
end
end
end
Expand Down
50 changes: 24 additions & 26 deletions controllers/bitcoin/address/actions/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,43 @@ module Controller
module Bitcoin
module Address
module Create
def self.call(components, grpc_request)
{
at: Time.now,
new_address: components[:grpc].send(grpc_request[:service]).send(
grpc_request[:method], grpc_request[:params]
).to_h
}
end
# https://bitcoin.design/guide/glossary/address/
SPECIFICATIONS = {
# Taproot: pay-to-taproot (P2TR)
# TAPROOT_PUBKEY (4): A new type of Bitcoin address that comes with the Taproot upgrade.
# It improves privacy and supports more complex transactions (like smart contracts).
# Addresses usually start with "bc1p" (mainnet) or "tb1p" (testnet).
'taproot' => :TAPROOT_PUBKEY,

def self.prepare
# SegWit: pay-to-witness-public-key-hash (P2WPKH)
# WITNESS_PUBKEY_HASH (0)
# A modern and efficient Bitcoin address type. It uses less space in transactions,
# so it has lower fees. Addresses usually start with "bc1" (mainnet) or "tb1" (testnet).
'segwit' => :WITNESS_PUBKEY_HASH,

# Script: pay-to-script-hash (P2SH)
# NESTED_PUBKEY_HASH (1)
# \A backward-compatible version of the modern address. It works with older wallets and
# services but has slightly higher fees. Addresses usually start with "3" (mainnet)
# or "2" (testnet).
'script' => :NESTED_PUBKEY_HASH
}.freeze

# UNUSED_WITNESS_PUBKEY_HASH (2)
# An unused version of the modern address. It's like having a reserved spot for a future
# address, but it's not being used right now.

# UNUSED_NESTED_PUBKEY_HASH (3)
# An unused version of the backward-compatible address. Similar to the unused modern
# address, it's a reserved spot for a future address but not in use currently.

# TAPROOT_PUBKEY (4): A new type of Bitcoin address that comes with the Taproot upgrade.
# It improves privacy and supports more complex transactions (like smart contracts).
# Addresses usually start with "bc1p" (mainnet) or "tb1p" (testnet).
def self.call(components, grpc_request)
{
at: Time.now,
new_address: components[:grpc].send(grpc_request[:service]).send(
grpc_request[:method], grpc_request[:params]
).to_h
}
end

# UNUSED_TAPROOT_PUBKEY (5)
# An unused version of the Taproot address. Like the other unused addresses, it's a
# reserved spot for a future address but not in use at the moment.
def self.prepare(format:)
{
service: :lightning,
method: :new_address,
params: {
type: :WITNESS_PUBKEY_HASH
type: SPECIFICATIONS[format]
}
}
end
Expand All @@ -71,8 +69,8 @@ def self.model(data, components)
Model::Bitcoin::Address.new(data, components)
end

def self.perform(components, preview: false, &vcr)
grpc_request = prepare
def self.perform(components, format: 'taproot', preview: false, &vcr)
grpc_request = prepare(format: format)

return grpc_request if preview

Expand Down
2 changes: 1 addition & 1 deletion controllers/bitcoin/address/actions/pay.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def self.prepare(address_code:, amount:, fee:, description:, required_confirmati
params: {
addr: address_code,
amount: (amount[:millisatoshis].to_f / 1000.0).to_i,
sat_per_vbyte: fee[:satoshis_per_vitual_byte],
sat_per_vbyte: fee[:maximum][:satoshis_per_vitual_byte],
min_confs: required_confirmations
}
}
Expand Down
55 changes: 55 additions & 0 deletions controllers/bitcoin/request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

require 'digest'

require_relative '../concerns/impersonatable'
require_relative './request/decode'
require_relative './address/actions/create'
require_relative '../action'

module Lighstorm
module Controller
module Bitcoin
module Request
extend Impersonatable

class DSL < Impersonatable::DSL
def create(address: nil, format: 'taproot', amount: nil, description: nil, message: nil, preview: false, &vcr)
if address.nil?
address_action = Address::Create.perform(components, format: format, preview: preview, &vcr)
return address_action if preview

address = address_action.result.to_h
else
address_action = nil
end

model = Model::Bitcoin::Request.new(
{
_key: Digest::SHA256.hexdigest(
[
address ? address[:code] : nil,
amount ? amount[:millisatoshis] : nil,
description, message
].join('/')
),
address: address, amount: amount, description: description, message: message
},
components
)

Action::Output.new({
request: address_action&.request,
response: address_action&.response,
result: model
})
end

def decode(uri)
Decode.model(Decode.data(uri: uri), components)
end
end
end
end
end
end
55 changes: 55 additions & 0 deletions controllers/bitcoin/request/decode.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

require 'uri'

require_relative '../../../adapters/bitcoin/request'
require_relative '../../../models/bitcoin/request'

module Lighstorm
module Controller
module Bitcoin
module Request
module Decode
def self.parse(uri)
# BIP21: https://bips.xyz/21
parsed = if uri =~ /^\w+:/
URI.parse(uri.sub('bitcoin:', 'bitcoin://'))
else
URI.parse("unknown://#{uri}")
end

uri_hash = {
scheme: parsed.scheme,
host: parsed.host,
port: parsed.port,
path: parsed.path,
query: parsed.query,
fragment: parsed.fragment
}

params = parsed.query ? URI.decode_www_form(parsed.query).to_h : {}

{
raw: uri,
parsed: uri_hash.merge({ params: params })
}
end

def self.adapt(raw)
Lighstorm::Adapter::Bitcoin::Request.decode(raw[:parsed])
end

def self.data(uri:)
raw = parse(uri)

adapt(raw)
end

def self.model(data, components)
Lighstorm::Model::Bitcoin::Request.new(data, components)
end
end
end
end
end
end
4 changes: 2 additions & 2 deletions controllers/bitcoin/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ module Transaction
extend Impersonatable

class DSL < Impersonatable::DSL
def all(limit: nil)
All.model(All.data(components, limit: limit))
def all(direction: nil, limit: nil)
All.model(All.data(components, direction: direction, limit: limit))
end

def find_by_hash(hash, &vcr)
Expand Down
15 changes: 10 additions & 5 deletions controllers/bitcoin/transaction/all.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,20 @@ module Controller
module Bitcoin
module Transaction
module All
def self.fetch(components, hash: nil, limit: nil)
def self.fetch(components, direction: nil, hash: nil, limit: nil)
at = Time.now

transactions = []

response = components[:grpc].lightning.get_transactions

response.transactions.each do |transaction|
next unless hash.nil? || transaction.to_h[:tx_hash] == hash
next unless hash.nil? || transaction.tx_hash == hash

next unless
direction.nil? ||
(direction == 'in' && transaction.amount.positive?) ||
(direction == 'out' && transaction.amount.negative?)

transactions << transaction.to_h
end
Expand All @@ -41,11 +46,11 @@ def self.transform(adapted)
adapted[:get_transactions]
end

def self.data(components, hash: nil, limit: nil, &vcr)
def self.data(components, hash: nil, direction: nil, limit: nil, &vcr)
raw = if vcr.nil?
fetch(components, hash: hash, limit: limit)
fetch(components, hash: hash, direction: direction, limit: limit)
else
vcr.call(-> { fetch(components, hash: hash, limit: limit) })
vcr.call(-> { fetch(components, hash: hash, direction: direction, limit: limit) })
end

adapted = adapt(raw)
Expand Down
10 changes: 6 additions & 4 deletions controllers/lightning/invoice/decode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ module Invoice
module Decode
def self.fetch(components, code)
{
_code: code,
decode_pay_req: components[:grpc].lightning.decode_pay_req(pay_req: code).to_h
_code: code.sub('lightning:', ''),
decode_pay_req: components[:grpc].lightning.decode_pay_req(
pay_req: code.sub('lightning:', '')
).to_h
}
end

Expand All @@ -30,9 +32,9 @@ def self.transform(adapted)

def self.data(components, code, &vcr)
raw = if vcr.nil?
fetch(components, code.sub('lightning:', ''))
fetch(components, code)
else
vcr.call(-> { fetch(components, code.sub('lightning:', '')) })
vcr.call(-> { fetch(components, code) })
end

adapted = adapt(raw)
Expand Down
Loading