From db7a23d82780f8c7615716d17f6f456d204431ee Mon Sep 17 00:00:00 2001 From: Michael Bumann Date: Fri, 8 Feb 2019 21:13:57 +0100 Subject: [PATCH] hello space --- .gitignore | 13 ++++ .rspec | 3 + Gemfile | 6 ++ Gemfile.lock | 64 ++++++++++++++++++++ LICENSE.txt | 21 +++++++ README.md | 85 +++++++++++++++++++++++++++ Rakefile | 6 ++ bin/console | 14 +++++ bin/setup | 8 +++ blockstream_satellite.gemspec | 44 ++++++++++++++ lib/blockstream_satellite.rb | 45 ++++++++++++++ lib/blockstream_satellite/client.rb | 40 +++++++++++++ lib/blockstream_satellite/order.rb | 78 ++++++++++++++++++++++++ lib/blockstream_satellite/response.rb | 23 ++++++++ lib/blockstream_satellite/version.rb | 3 + spec/blockstream_satellite_spec.rb | 9 +++ spec/spec_helper.rb | 14 +++++ 17 files changed, 476 insertions(+) create mode 100644 .gitignore create mode 100644 .rspec create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 Rakefile create mode 100755 bin/console create mode 100755 bin/setup create mode 100644 blockstream_satellite.gemspec create mode 100644 lib/blockstream_satellite.rb create mode 100644 lib/blockstream_satellite/client.rb create mode 100644 lib/blockstream_satellite/order.rb create mode 100644 lib/blockstream_satellite/response.rb create mode 100644 lib/blockstream_satellite/version.rb create mode 100644 spec/blockstream_satellite_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a6f5df --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +/.bundle/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ + +# rspec failure tracking +.rspec_status + +.ruby-version diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..34c5164 --- /dev/null +++ b/.rspec @@ -0,0 +1,3 @@ +--format documentation +--color +--require spec_helper diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..1b870be --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + +# Specify your gem's dependencies in blockstream_satellite.gemspec +gemspec diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..e052d42 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,64 @@ +PATH + remote: . + specs: + blockstream_satellite (0.1.0) + activesupport (>= 5.0.1) + faraday (> 0.8) + faraday_middleware (> 0.12) + lnrpc (>= 0.1.0) + +GEM + remote: https://rubygems.org/ + specs: + activesupport (5.2.2) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + concurrent-ruby (1.1.4) + diff-lcs (1.3) + faraday (0.15.4) + multipart-post (>= 1.2, < 3) + faraday_middleware (0.13.1) + faraday (>= 0.7.4, < 1.0) + google-protobuf (3.6.1) + googleapis-common-protos-types (1.0.3) + google-protobuf (~> 3.0) + grpc (1.18.0) + google-protobuf (~> 3.1) + googleapis-common-protos-types (~> 1.0.0) + i18n (1.5.3) + concurrent-ruby (~> 1.0) + lnrpc (0.1.0) + grpc (>= 1.16.0) + minitest (5.11.3) + multipart-post (2.0.0) + rake (10.5.0) + rspec (3.8.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-core (3.8.0) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-mocks (3.8.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-support (3.8.0) + thread_safe (0.3.6) + tzinfo (1.2.5) + thread_safe (~> 0.1) + +PLATFORMS + ruby + +DEPENDENCIES + blockstream_satellite! + bundler (~> 1.17) + rake (~> 10.0) + rspec (~> 3.0) + +BUNDLED WITH + 1.17.2 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..6229669 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Michael Bumann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4b9fd17 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# BlockstreamSatellite + +Ruby gem to interact with the [Blockstream Satellite](https://blockstream.com/satellite/) API. + +To learn more about the Blockstream Satellite check their [website](https://blockstream.com/satellite/) and [API documentation](https://blockstream.com/satellite-api/). + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'blockstream_satellite' +``` + +Or install it yourself as: + + $ gem install blockstream_satellite + + +## Usage + +### Quick example + +```ruby +order = BlockstreamSatellite::Order.create(path: '/path/to/file') +puts order.status # pending +order.pay # sends the lightning payment using the configured lnd client; request is synchronous + +puts order.status # transmitting +order.refresh +puts order.status # sent +``` + +### Configuration + +In order to be able to pay for the lightninig invoice you need to connect to you lightning node. +(By default the testnet cert and macaroon files are loaded from `~/.lnd` and `localhost:10009` is used) + +```ruby +lnd_client = Lnrpc::Client.new({ + credentials_path: '/path/to/tls.cert', + macaroon_path: '/path/to/admin.macaroon', + address: 'localhost:10009' +}) +BlockstreamSatellite.lnd_client = lnd_client +``` + +### Creating an order + +`BlockstreamSatellite::Order.create` accepts the following parameters: + +* `path`: path to the file to transmit +* `data`: data to transmit; can be used as an alternative `path` +* `bid`: bid for the order. defaults to file size * 51 + +```ruby +order = BlockstreamSatellite::Order.create(path: '/path/to/file') +``` + +### Loading an order by UUID and auth_token + +```ruby +order = BlockstreamSatellite::Order.get(uuid: 'uuid', auth_token: 'auth_token') +``` + +### Bump the bid for an order + +```ruby +order = BlockstreamSatellite::Order.get(uuid: 'uuid', auth_token: 'auth_token') +order.bump(1000) +``` + + +## Satus + +Currently I consider this gem experimental and it is missing tests current. + + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/blockstream_satellite. + +## License + +The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..b7e9ed5 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +task :default => :spec diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..b4c7e9a --- /dev/null +++ b/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require "bundler/setup" +require "blockstream_satellite" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require "irb" +IRB.start(__FILE__) diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/blockstream_satellite.gemspec b/blockstream_satellite.gemspec new file mode 100644 index 0000000..1075aaf --- /dev/null +++ b/blockstream_satellite.gemspec @@ -0,0 +1,44 @@ + +lib = File.expand_path("../lib", __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require "blockstream_satellite/version" + +Gem::Specification.new do |spec| + spec.name = "blockstream_satellite" + spec.version = BlockstreamSatellite::VERSION + spec.authors = ["Michael Bumann"] + spec.email = ["hello@michaelbumann.com"] + + spec.summary = %q{API client for the Blockstream satellite} + spec.description = %q{API clent to interact with the Blockstream satellite} + spec.homepage = "http://github.com/bumi/blockstream_satellite" + spec.license = "MIT" + + # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' + # to allow pushing to a single host or delete this section to allow pushing to any host. + if spec.respond_to?(:metadata) + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = spec.homepage + spec.metadata["changelog_uri"] = "http://github.com/bumi/blockstream_satellite/" + else + raise "RubyGems 2.0 or newer is required to protect against " \ + "public gem pushes." + end + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do + `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler", "~> 1.17" + spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "rspec", "~> 3.0" + spec.add_dependency "faraday", "> 0.8" + spec.add_dependency "faraday_middleware", ">0.12" + spec.add_dependency "lnrpc", ">= 0.1.0" + spec.add_dependency "activesupport", ">= 5.0.1" +end diff --git a/lib/blockstream_satellite.rb b/lib/blockstream_satellite.rb new file mode 100644 index 0000000..598692e --- /dev/null +++ b/lib/blockstream_satellite.rb @@ -0,0 +1,45 @@ +require "blockstream_satellite/version" + +require 'lnrpc' + +module BlockstreamSatellite + class Error < StandardError; end + autoload :Order, 'blockstream_satellite/order' + autoload :Client, 'blockstream_satellite/client' + autoload :Configuration, 'blockstream_satellite/configuration' + autoload :Response, 'blockstream_satellite/response' + + API_HOST = 'https://satellite.blockstream.com' + + def self.lnd_client=(value) + @lnd_client = value + end + + def self.lnd_client + @lnd_client ||= Lnrpc::Client.new({}) + end + + def http_client=(value) + @http_client = value + end + + def self.http_client + @http_client ||= Faraday.new(url: API_HOST) do |faraday| + faraday.request :multipart + faraday.request :url_encoded + faraday.response :json, :content_type => /\bjson$/ + + faraday.adapter Faraday.default_adapter + end + end + + def self.client + @client ||= Client.new(lnd_client: self.lnd_client, http_client: self.http_client) + end + + def self.info + self.client.get('/info') + end + +end +BSat = BlockstreamSatellite diff --git a/lib/blockstream_satellite/client.rb b/lib/blockstream_satellite/client.rb new file mode 100644 index 0000000..16a8500 --- /dev/null +++ b/lib/blockstream_satellite/client.rb @@ -0,0 +1,40 @@ +require "faraday" +require 'faraday_middleware' + +module BlockstreamSatellite + class Client + attr_accessor :http_client, :lnd_client + + def initialize(options) + self.lnd_client = options[:lnd_client] + self.http_client = options[:http_client] + end + + def get(path, params = nil, &block) + request(:get, path, params, &block) + end + + def post(path, body = nil, &block) + puts body.inspect + request(:post, path, nil, body, &block) + end + + def pay(payreq) + self.lnd_client.send_payment_sync(Lnrpc::SendRequest.new(payment_request: payreq)) + end + + def request(http_method, path, params=nil, body=nil) + response = http_client.send(http_method) do |req| + req.url "/api/#{path}" + req.params = params if params + req.body = body if body + yield req if block_given? + end + Response.new(response) + rescue Faraday::ClientError => error + return Response.new(error) + end + + end +end + diff --git a/lib/blockstream_satellite/order.rb b/lib/blockstream_satellite/order.rb new file mode 100644 index 0000000..fe7f4d2 --- /dev/null +++ b/lib/blockstream_satellite/order.rb @@ -0,0 +1,78 @@ +require 'faraday' +require 'faraday_middleware' +require "active_support/core_ext/hash/indifferent_access" +require 'tempfile' +require 'securerandom' + +module BlockstreamSatellite + class Order + + attr_accessor :attributes + + def initialize(attributes) + self.attributes = attributes.with_indifferent_access + end + + # defining accessors + [:auth_token, :uuid, :lightning_invoice, :sha256_message_digest, :message_digest, :status, :bid, :unpaid_bid].each do |attr| + define_method(attr) do + self.attributes[attr] + end + end + + def payreq + lightning_invoice['payreq'] + end + + def self.create(options) + if data = options.delete(:data) + file = Tempfile.new(SecureRandom.hex(5)) + file.write(data) + file.rewind + options[:file] = file + elsif path = options.delete(:path) + options[:file] = File.open(path) + end + options[:bid] ||= options[:file].size * 51 # default price + options[:file] = UploadIO.new(options[:file], 'text/plain') + response = BlockstreamSatellite.client.post('order', options) + if response.success? + Order.new(response.body) + else + response + end + end + + def self.get(order) + Order.new(order).tap do |o| + o.refresh + end + end + + def refresh + response = BlockstreamSatellite.client.get("order/#{self.uuid}") do |req| + req.headers['X-Auth-Token'] = self.auth_token + end + if response.success? + self.attributes.merge!(response.body) + end + end + + def pay + BlockstreamSatellite.client.pay(self.payreq) + self.refresh + end + + def bump(bid_increase) + response = BlockstreamSatellite.client.post("order/#{self.uuid}/bump", bid_increase: bid_increase) do |req| + req.headers['X-Auth-Token'] = self.auth_token + end + if response.success? + self.attributes['lightning_invoice'] = response['lightning_invoice'] + else + response + end + end + + end +end diff --git a/lib/blockstream_satellite/response.rb b/lib/blockstream_satellite/response.rb new file mode 100644 index 0000000..ec98b60 --- /dev/null +++ b/lib/blockstream_satellite/response.rb @@ -0,0 +1,23 @@ +require "faraday" +module BlockstreamSatellite + class Response + attr_accessor :response, :body, :error + + def initialize(response) + if response.is_a?(Faraday::ClientError) + @error = response + else + @response = response + @body = response.body + end + end + + def success? + self.error.nil? && !self.response.nil? && self.response.success? + end + + def [](key) + self.body[key.to_s] + end + end +end diff --git a/lib/blockstream_satellite/version.rb b/lib/blockstream_satellite/version.rb new file mode 100644 index 0000000..0805875 --- /dev/null +++ b/lib/blockstream_satellite/version.rb @@ -0,0 +1,3 @@ +module BlockstreamSatellite + VERSION = "0.1.0" +end diff --git a/spec/blockstream_satellite_spec.rb b/spec/blockstream_satellite_spec.rb new file mode 100644 index 0000000..3a270ce --- /dev/null +++ b/spec/blockstream_satellite_spec.rb @@ -0,0 +1,9 @@ +RSpec.describe BlockstreamSatellite do + it "has a version number" do + expect(BlockstreamSatellite::VERSION).not_to be nil + end + + it "does something useful" do + expect(false).to eq(true) + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..7324fed --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,14 @@ +require "bundler/setup" +require "blockstream_satellite" + +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end