utxoracle 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e06742e7448c182f3f2399e200c50e0563f3d8672433d942028cbc7c1827f60b
4
- data.tar.gz: 33f2c9d1d7e496664c84a682e3b0d09a0971f152f7f351b59d7cdeb0baa48bba
3
+ metadata.gz: 966c4c18a2b785a539acbfd09a9b491b24d5e83b6b606e5db05f4f3997c3f13c
4
+ data.tar.gz: 6089c1e572294e3b0a2a0c5f1e842f5b52449d78001522fe1f5c6c3db1c3382e
5
5
  SHA512:
6
- metadata.gz: b64d8799da84d9ee041e61548adcf889067bd0bcef7b5331d59e43aca4bec7a4075d6bdd0773939132d9b8dec4b428e60a05be9bf9b720ecfec74b40b805e74f
7
- data.tar.gz: caeb20a9d134903c29cccfd3bd03697a1bcf5b56452762ea383850b9423a4bc056d7ac77e4d18ed6f455d7765e21935421172ccd2ab3ca733298545792a03628
6
+ metadata.gz: 9cbde112559f59680e49d3fbef85dee93660b2ca03f3c8c73c45d07565ec6f90871e00de939ff530ce0a63f53e8fad3ef0c0b8e338e60d05aae9628fb72af971
7
+ data.tar.gz: 592eaf022a3bdfd81856cb9dfe1a6ee822daaf71d43bd824f7dd39e88d6b192cf17bb6d7d42f1a27368de5417ff8bbbbaa33c0a1e908e5b492427ae94a91a709
data/README.md CHANGED
@@ -1,7 +1,17 @@
1
- # UTXOracle.rb
2
- Ruby implementation of https://utxo.live/oracle/.
1
+ [![Gem Version](https://badge.fury.io/rb/utxoracle.svg)](https://badge.fury.io/rb/utxoracle)
2
+
3
+ # Utxoracle
4
+ Offline price oracle for Bitcoin written in Ruby.
5
+
6
+ In September 2023 [SteveSimple](https://twitter.com/SteveSimple) & [DanielLHinton](https://twitter.com/DanielLHinton) [dropped](https://twitter.com/SteveSimple/status/1704864674431332503) https://utxo.live/oracle/, the first method to derive historical USD price of bitcoin based purely on UTXO set.
7
+
8
+ Needless to say, a lot of bitcoiners found this VERY cool. We're in a unique phase of history; transitioning from the Dollar Network to a Bitcoin Standard. Pulling out these patterns sparks joy.
9
+
10
+ The purpose of releasing Utxoracle as a Gem is multi-fold:
11
+ 1. Make the tool available as a [Gem](https://rubygems.org/gems/utxoracle) to the Ruby community.
12
+ 2. Provide a flexible API, where folks can extend this for other use cases (currencies, language, etc).
13
+ 3. Provide a _modular_ provider model, so folks can pull data from mempool.space or other RPCs.
3
14
 
4
- (Original implementation by [SteveSimple](https://twitter.com/SteveSimple) & [DanielLHinton](https://twitter.com/DanielLHinton) who discovered and built the first offline bitcoin price oracle.)
5
15
 
6
16
  ## Table of contents
7
17
 
@@ -37,14 +47,14 @@ require 'utxoracle'
37
47
 
38
48
  ### Fetching price
39
49
 
40
- #### Using a raw bitcoin node
50
+ #### Using a specific bitcoin node
41
51
  ```ruby
42
- provider = Utxoracle::RawBitcoinNode.new("aUser", "aPassword", "127.0.0.1", 8332)
52
+ provider = Utxoracle::Node.new("aUser", "aPassword", "127.0.0.1", 8332)
43
53
  ```
44
54
 
45
55
  #### Using mempool.space node
46
56
  ```ruby
47
- provider = Utxoracle::MempoolDotSpace.new
57
+ provider = Utxoracle::Mempool.new
48
58
  ```
49
59
 
50
60
  ####
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
2
  require 'rubocop/rake_task'
3
3
  require 'rspec/core/rake_task'
4
4
 
data/bin/run ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'utxoracle'
6
+
7
+ user = ARGV[0]
8
+ password = ARGV[1]
9
+ ip = ARGV[2]
10
+ port = ARGV[3]
11
+ date = ARGV[4]
12
+
13
+ provider = Utxoracle::Node.new(user, password, ip, port)
14
+ oracle = Utxoracle::Oracle.new(provider, true)
15
+ oracle.price(date)
@@ -1,6 +1,5 @@
1
1
  require 'time'
2
2
  require_relative 'rpc'
3
- require_relative 'providers/raw_bitcoin_node'
4
3
 
5
4
  module Utxoracle
6
5
  class Oracle
@@ -19,12 +18,14 @@ module Utxoracle
19
18
 
20
19
  def price(requested_date)
21
20
  unless validate_date(requested_date)
22
- puts "Invalid date.\nEarliest available: 2020-07-26.\nLatest available #{Date.today}.\nFormat: YYYY-MM-DD."
23
- return
21
+ if @log
22
+ puts "Invalid date.\nEarliest available: 2020-07-26.\nLatest available #{Date.today}.\nFormat: YYYY-MM-DD."
23
+ end
24
+ return -1
24
25
  end
25
26
 
26
27
  if price_estimate = @cache[requested_date]
27
- puts "price_estimate is #{price_estimate}"
28
+ puts "price_estimate is #{price_estimate}" if @log
28
29
  return price_estimate
29
30
  end
30
31
 
@@ -124,7 +125,7 @@ module Utxoracle
124
125
 
125
126
  puts("Reading all blocks on #{price_day_date_utc}...") if @log
126
127
  puts('This will take a few minutes (~144 blocks)...') if @log
127
- puts("Height\tTime(utc)\t Completion %") if @log
128
+ puts("Height\tTime(utc)\tCompletion %") if @log
128
129
 
129
130
  block_height = price_day_block_estimate
130
131
  block_hash_b = @provider.getblockhash(block_height)
@@ -143,8 +144,8 @@ module Utxoracle
143
144
  while target == day
144
145
  blocks_on_this_day += 1
145
146
 
146
- progress_estimate = 100.0 * (hour + minute / 60) / 24.0
147
- puts("#{block_height}\t#{time.strftime('%H:%M:%S')}\t#{progress_estimate}") if @log
147
+ progress_estimate = 100.0 * (hour + minute / 60.0) / 24.0
148
+ puts("#{block_height}\t#{time.strftime('%H:%M:%S')}\t#{progress_estimate.round(2)}") if @log
148
149
 
149
150
  for tx in block_b['tx']
150
151
  outputs = tx['vout']
@@ -347,6 +348,8 @@ module Utxoracle
347
348
  (Date.parse(date) < Date.today)
348
349
 
349
350
  valid_format && valid_range
351
+ rescue Date::Error
352
+ false
350
353
  end
351
354
  end
352
355
  end
@@ -5,7 +5,7 @@ require_relative '../request'
5
5
  # and throttles the number of requests the oracle takes to compute.
6
6
  # Running this requires an Enterprise license.
7
7
  module Utxoracle
8
- class MempoolDotSpace < Provider
8
+ class Mempool < Provider
9
9
  def initialize; end
10
10
 
11
11
  def getblockcount
@@ -2,7 +2,7 @@ require_relative '../provider'
2
2
  require_relative '../rpc'
3
3
 
4
4
  module Utxoracle
5
- class RawBitcoinNode < Provider
5
+ class Node < Provider
6
6
  def initialize(rpcuser, rpcpassword, ip, port)
7
7
  @rpc = Rpc.new("http://#{rpcuser}:#{rpcpassword}@#{ip}:#{port}")
8
8
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Utxoracle
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.2'
5
5
  end
data/lib/utxoracle.rb CHANGED
@@ -3,8 +3,8 @@
3
3
  require_relative 'utxoracle/rpc'
4
4
  require_relative 'utxoracle/version'
5
5
  require_relative 'utxoracle/oracle'
6
- require_relative 'utxoracle/providers/mempool_dot_space'
7
- require_relative 'utxoracle/providers/raw_bitcoin_node'
6
+ require_relative 'utxoracle/providers/mempool'
7
+ require_relative 'utxoracle/providers/node'
8
8
 
9
9
  module Utxoracle
10
10
  end
@@ -2,20 +2,38 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- # RSpec.describe Utxoracle::Oracle do
6
- # let(:oracle) do
7
- # described_class.new('aUser', 'aPassword', '127.0.0.1', '8332')
8
- # end
9
- #
10
- # describe '.new' do
11
- # it 'initialized an Rpc instance' do
12
- # expect(Utxoracle::Rpc).to receive(:new)
13
- # described_class.new('aUser', 'aPassword', '127.0.0.1', '8332')
14
- # expect(oracle.class).to eq Utxoracle::Oracle
15
- # end
16
- # end
17
- #
18
- # # TODO: - test cache
19
- # # TODO - test provider interface
20
- # # TODO - test `price` return type
21
- # end
5
+ RSpec.describe Utxoracle::Oracle do
6
+ describe '.new' do
7
+ it 'requires provider and takes optional log config' do
8
+ provider = Utxoracle::Mempool
9
+ Utxoracle::Oracle.new(provider, log = true)
10
+ end
11
+ end
12
+
13
+ describe 'date validation' do
14
+ it 'returns -1 for garbage date inputs' do
15
+ provider = Utxoracle::Mempool
16
+ oracle = Utxoracle::Oracle.new(provider)
17
+ price = oracle.price('garbage')
18
+ expect(price).to be(-1)
19
+ end
20
+
21
+ it 'returns -1 for dates before 2020-07-26' do
22
+ provider = Utxoracle::Mempool
23
+ oracle = Utxoracle::Oracle.new(provider)
24
+ price = oracle.price('2015-01-01')
25
+ expect(price).to be(-1)
26
+ end
27
+
28
+ it 'returns -1 for dates in the future' do
29
+ provider = Utxoracle::Mempool
30
+ oracle = Utxoracle::Oracle.new(provider)
31
+ price = oracle.price('3000-01-01')
32
+ expect(price).to be(-1)
33
+ end
34
+ end
35
+
36
+ # TODO: - test cache
37
+ # TODO - test provider interface
38
+ # TODO - test `price` return type
39
+ end
@@ -6,6 +6,7 @@ RSpec.describe Utxoracle::Rpc do
6
6
  let(:rpc) do
7
7
  described_class.new('http://foo:bar@127.0.0.1:8332')
8
8
  end
9
+
9
10
  describe '.new' do
10
11
  it 'creates an instance of Rpc with given uri' do
11
12
  expect(rpc.class).to eq(Utxoracle::Rpc)
@@ -18,12 +19,16 @@ RSpec.describe Utxoracle::Rpc do
18
19
  end
19
20
 
20
21
  it 'forwards methods over http' do
21
- allow(rpc).to receive(:http_post_request).and_return("{\"result\":814521,\"error\":null,\"id\":\"jsonrpc\"}\n")
22
+ allow(rpc).to receive(:http_post_request).and_return(
23
+ "{\"result\":814521,\"error\":null,\"id\":\"jsonrpc\"}\n"
24
+ )
22
25
  expect(rpc.getblockcount).to eq 814_521
23
26
  end
24
27
 
25
28
  it 'returns error from http endpoint when indicated' do
26
- expect(rpc).to receive(:http_post_request).and_return("{\"result\":814521,\"error\":\"test error\",\"id\":\"jsonrpc\"}\n")
29
+ expect(rpc).to receive(:http_post_request).and_return(
30
+ "{\"result\":814521,\"error\":\"test error\",\"id\":\"jsonrpc\"}\n"
31
+ )
27
32
  expect { rpc.test_rpc_call }.to raise_error(Utxoracle::Rpc::JSONRPCError)
28
33
  end
29
34
  end
data/utxoracle.gemspec CHANGED
@@ -11,17 +11,17 @@ Gem::Specification.new do |spec|
11
11
  spec.platform = Gem::Platform::RUBY
12
12
  spec.authors = ['Keith Gardner']
13
13
 
14
- spec.summary = 'Interface for UTXOracle.'
15
- spec.description = 'Object oriented design for interacting with UTXOracle.'
14
+ spec.summary = 'Offline price oracle for Bitcoin.'
15
+ spec.description = 'Offline price oracle for Bitcoin. Offers programmable data providers (Bitcoin node, mempool, etc). Can be used from commandline, or integrated into existing ruby stacks.'
16
16
  spec.homepage = 'https://github.com/Carolina-Bitcoin-Project/UTXOracle'
17
17
  spec.license = 'MIT'
18
18
  spec.required_ruby_version = '>= 3.1.0'
19
19
 
20
20
  spec.files = `git ls-files`.split("\n")
21
- spec.bindir = 'exe'
21
+ spec.bindir = 'bin'
22
22
  spec.require_path = 'lib'
23
23
 
24
- spec.add_dependency 'typhoeus'
24
+ spec.add_dependency 'typhoeus', '~> 1.4.0'
25
25
 
26
26
  spec.add_development_dependency 'pry'
27
27
  spec.add_development_dependency 'rake'
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: utxoracle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keith Gardner
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-01 00:00:00.000000000 Z
11
+ date: 2023-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 1.4.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 1.4.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pry
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -122,26 +122,27 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
- description: Object oriented design for interacting with UTXOracle.
125
+ description: Offline price oracle for Bitcoin. Offers programmable data providers
126
+ (Bitcoin node, mempool, etc). Can be used from commandline, or integrated into existing
127
+ ruby stacks.
126
128
  email:
127
129
  executables: []
128
130
  extensions: []
129
131
  extra_rdoc_files: []
130
132
  files:
131
133
  - ".gitignore"
132
- - ".rspec"
133
- - ".rspec_status"
134
134
  - ".ruby-version"
135
135
  - Gemfile
136
136
  - LICENSE
137
137
  - README.md
138
138
  - Rakefile
139
139
  - bin/console
140
+ - bin/run
140
141
  - lib/utxoracle.rb
141
142
  - lib/utxoracle/oracle.rb
142
143
  - lib/utxoracle/provider.rb
143
- - lib/utxoracle/providers/mempool_dot_space.rb
144
- - lib/utxoracle/providers/raw_bitcoin_node.rb
144
+ - lib/utxoracle/providers/mempool.rb
145
+ - lib/utxoracle/providers/node.rb
145
146
  - lib/utxoracle/request.rb
146
147
  - lib/utxoracle/rpc.rb
147
148
  - lib/utxoracle/version.rb
@@ -172,5 +173,5 @@ requirements: []
172
173
  rubygems_version: 3.4.10
173
174
  signing_key:
174
175
  specification_version: 4
175
- summary: Interface for UTXOracle.
176
+ summary: Offline price oracle for Bitcoin.
176
177
  test_files: []
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/.rspec_status DELETED
@@ -1,8 +0,0 @@
1
- example_id | status | run_time |
2
- -------------------------------------- | ------ | --------------- |
3
- ./spec/utxoracle/oracle_spec.rb[1:1:1] | failed | 0.00966 seconds |
4
- ./spec/utxoracle/rpc_spec.rb[1:1:1] | passed | 0.00025 seconds |
5
- ./spec/utxoracle/rpc_spec.rb[1:2] | passed | 0.01109 seconds |
6
- ./spec/utxoracle/rpc_spec.rb[1:3] | passed | 0.00028 seconds |
7
- ./spec/utxoracle/rpc_spec.rb[1:4] | passed | 0.00214 seconds |
8
- ./spec/utxoracle_spec.rb[1:1] | passed | 0.00127 seconds |