blockchain-node 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 84fc82f7e1b3fc091280581c9fbf718f96993914d0bd2e8d81812ade1cf9a5f9
4
+ data.tar.gz: 51b0caecb2abe1986ec3967d56c6f7287563bf1a719d5e27eeb87e8c74faf857
5
+ SHA512:
6
+ metadata.gz: e8a31a95fdbaf5631d74cbcaab974f6c09fcf98cce1dccc9c15ad08fadfce9e1181e8a190440005c58841406edea14afc121cd01cf5c17a851488a5695b98fbc
7
+ data.tar.gz: 48104e3e93f3d8d631f9569f233f772eb8582014ad93f3099cdaebb145e7c7ee464a208fc54effaae994e389f3fa6e2eaa2d263c5fd7d200f127b06f86bc1e58
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README-RPC-BTC.md ADDED
@@ -0,0 +1,55 @@
1
+ # Bitcoin RPC Methods Calls
2
+
3
+
4
+ ```
5
+ backupwallet "destination"
6
+ bumpfee "txid" ( options )
7
+ createwallet "wallet_name" ( disable_private_keys )
8
+ dumpprivkey "address"
9
+ dumpwallet "filename"
10
+ encryptwallet "passphrase"
11
+ getaddressesbylabel "label"
12
+ getaddressinfo "address"
13
+ getbalance ( "dummy" minconf include_watchonly )
14
+ getnewaddress ( "label" "address_type" )
15
+ getrawchangeaddress ( "address_type" )
16
+ getreceivedbyaddress "address" ( minconf )
17
+ getreceivedbylabel "label" ( minconf )
18
+ gettransaction "txid" ( include_watchonly )
19
+ getunconfirmedbalance
20
+ getwalletinfo
21
+ importaddress "address" ( "label" rescan p2sh )
22
+ importmulti "requests" ( "options" )
23
+ importprivkey "privkey" ( "label" rescan )
24
+ importprunedfunds "rawtransaction" "txoutproof"
25
+ importpubkey "pubkey" ( "label" rescan )
26
+ importwallet "filename"
27
+ keypoolrefill ( newsize )
28
+ listaddressgroupings
29
+ listlabels ( "purpose" )
30
+ listlockunspent
31
+ listreceivedbyaddress ( minconf include_empty include_watchonly "address_filter" )
32
+ listreceivedbylabel ( minconf include_empty include_watchonly )
33
+ listsinceblock ( "blockhash" target_confirmations include_watchonly include_removed )
34
+ listtransactions ( "label" count skip include_watchonly )
35
+ listunspent ( minconf maxconf ["address",...] include_unsafe query_options )
36
+ listwalletdir
37
+ listwallets
38
+ loadwallet "filename"
39
+ lockunspent unlock ( [{"txid":"hex","vout":n},...] )
40
+ removeprunedfunds "txid"
41
+ rescanblockchain ( start_height stop_height )
42
+ sendmany "" {"address":amount} ( minconf "comment" ["address",...] replaceable conf_target "estimate_mode" )
43
+ sendtoaddress "address" amount ( "comment" "comment_to" subtractfeefromamount replaceable conf_target "estimate_mode" )
44
+ sethdseed ( newkeypool "seed" )
45
+ setlabel "address" "label"
46
+ settxfee amount
47
+ signmessage "address" "message"
48
+ signrawtransactionwithwallet "hexstring" ( [{"txid":"hex","vout":n,"scriptPubKey":"hex","redeemScript":"hex","amount":amount},...] "sighashtype" )
49
+ unloadwallet ( "wallet_name" )
50
+ walletcreatefundedpsbt [{"txid":"hex","vout":n,"sequence":n},...] [{"address":amount},{"data":"hex"},...] ( locktime options bip32derivs )
51
+ walletlock
52
+ walletpassphrase "passphrase" timeout
53
+ walletpassphrasechange "oldpassphrase" "newpassphrase"
54
+ walletprocesspsbt "psbt" ( sign "sighashtype" bip32derivs )
55
+ ```
data/README-RPC-ETH.md ADDED
@@ -0,0 +1,61 @@
1
+ # Ethereum RPC Methods Calls
2
+
3
+
4
+ ```
5
+
6
+ web3_clientVersion
7
+ web3_sha3
8
+ net_version
9
+ net_peerCount
10
+ net_listening
11
+ eth_protocolVersion
12
+ eth_syncing
13
+ eth_coinbase
14
+ eth_mining
15
+ eth_hashrate
16
+ eth_gasPrice
17
+ eth_accounts
18
+ eth_blockNumber
19
+ eth_getBalance
20
+ eth_getStorageAt
21
+ eth_getTransactionCount
22
+ eth_getBlockTransactionCountByHash
23
+ eth_getBlockTransactionCountByNumber
24
+ eth_getUncleCountByBlockHash
25
+ eth_getUncleCountByBlockNumber
26
+ eth_getCode
27
+ eth_sign
28
+ eth_sendTransaction
29
+ eth_sendRawTransaction
30
+ eth_call
31
+ eth_estimateGas
32
+ eth_getBlockByHash
33
+ eth_getBlockByNumber
34
+ eth_getTransactionByHash
35
+ eth_getTransactionByBlockHashAndIndex
36
+ eth_getTransactionByBlockNumberAndIndex
37
+ eth_getTransactionReceipt
38
+ eth_getUncleByBlockHashAndIndex
39
+ eth_getUncleByBlockNumberAndIndex
40
+ eth_getCompilers
41
+ eth_compileLLL
42
+ eth_compileSolidity
43
+ eth_compileSerpent
44
+ eth_newFilter
45
+ eth_newBlockFilter
46
+ eth_newPendingTransactionFilter
47
+ eth_uninstallFilter
48
+ eth_getFilterChanges
49
+ eth_getFilterLogs
50
+ eth_getLogs
51
+ eth_getWork
52
+ eth_submitWork
53
+ eth_submitHashrate
54
+ eth_getProof
55
+ db_putString
56
+ db_getString
57
+ db_putHex
58
+ db_getHex
59
+
60
+
61
+ ```
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # BlockchainNode (BCN) Ruby Client
2
+
3
+ This gem is a secure RPC wrapper to connect to nodes launched by
4
+ [https://blockchainnode.io](https://blockchainnode.io).
5
+
6
+ Provides a Ruby library to the complete Bitcoin JSON-RPC API. Implements all methods listed
7
+ at {https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list}[https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_Calls_list].
8
+ Also supports customizing the host and port number to connect to.
9
+
10
+ ## Installation and Configuration
11
+
12
+ ```bash
13
+ gem install blockchain-node
14
+ ```
15
+
16
+ The client is secured using a client-credentials OAUTH flow. You can generate API keys in your BCN account.
17
+
18
+ If you are using a Rails application, add the following initializer to `config/initializers/blockchain_node.rb`
19
+
20
+ ```ruby
21
+ BlockchainNode.configure do |config|
22
+ config.client_id = "CLIENT_ID"
23
+ config.client_secret = "CLIENT_SECRET"
24
+ end
25
+ ```
26
+
27
+ **Security Note:**
28
+ It is recommended that you secure your `CLIENT_ID` and `CLIENT_SECRET` and do not check that into your code repo.
29
+ If your `CLIENT_ID` and `CLIENT_SECRET` are comprimised, your wallet will be secure as long as the it is
30
+ encrypted and not left unlocked.
31
+
32
+
33
+ ## Usage
34
+
35
+ ### Initialize the library
36
+
37
+ ```ruby
38
+ # the node ID from your BCN console
39
+ node_id = "123ABC"
40
+ client = BlockchainNode::Client.new(node_id)
41
+ ```
42
+
43
+ ### Bitcoin Example
44
+
45
+
46
+ [Link to RPC Calls](README-RPC-BTC.md)
47
+
48
+ ### Ethereum Example
49
+
50
+ Make any RPC method call directly. Pass in parameters as args to the method call.
51
+
52
+ Notice, for geth, responses are returned in hex so they have to be converted to an integer.
53
+
54
+ Balances are stored as integers with 18 decimals of spacing (wei). Convert to ether.
55
+ [Helpful Calculator](https://etherconverter.online/)
56
+
57
+ ```ruby
58
+ resp = client.eth_blockNumber
59
+ Integer(resp[:response])
60
+
61
+ client.personal_listAccounts
62
+
63
+ resp = client.eth_getBalance("0xf4c2a25fcbaad4e568fb74d6644b164e999d3132", "latest")
64
+ Integer(resp[:response]) / 1000000000000000000.0
65
+ ```
66
+
67
+ [Link to RPC Calls](README-ETH-BTC.md)
68
+
69
+ ## Contact
70
+
71
+ Comments and feedback are welcome. Send an email to matt at blockchainnode.io.
72
+
73
+ ## License
74
+
75
+ This code is free to use under the terms of the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Default: run specs.'
5
+ task :default => :spec
6
+
7
+ desc "Run specs"
8
+ RSpec::Core::RakeTask.new
@@ -0,0 +1,68 @@
1
+ module BlockchainNode
2
+ class Client
3
+
4
+ @@_auth_token
5
+ AuthToken = Struct.new(:token, :expires_at)
6
+
7
+ attr_reader :node_id
8
+ attr_accessor :configuration
9
+
10
+ def initialize(node_id)
11
+ @node_id = node_id
12
+ # allow a different configuration per client instance
13
+ @configuration = BlockchainNode::Configuration.new
14
+ end
15
+
16
+ # convenience method to get nodes index
17
+ def nodes
18
+ request.get(path: node_index_path, auth_token: auth_token)
19
+ end
20
+
21
+ def auth_token
22
+ @@_auth_token ||= get_new_auth_token
23
+ @@_auth_token = get_new_auth_token if auth_token_expired?
24
+ @@_auth_token.token
25
+ end
26
+
27
+ private
28
+
29
+ # catch all other method calls and assume its the RPC method call
30
+ def method_missing(method, *args, &block)
31
+ data = { method: method, parameters: args }
32
+ request.post(path: nodes_path, data: data, auth_token: auth_token)
33
+ end
34
+
35
+ def auth_token_expired?
36
+ @@_auth_token.nil? || @@_auth_token.expires_at < Time.now.utc - 30
37
+ end
38
+
39
+ def get_new_auth_token
40
+ data = {
41
+ grant_type: "client_credentials",
42
+ client_id: configuration.client_id,
43
+ client_secret: configuration.client_secret,
44
+ }
45
+ response = request.post(path: oauth_token_path, data: data)
46
+ token = response["access_token"]
47
+ expires_at = Time.at(response["created_at"] + response["expires_in"]).utc
48
+ AuthToken.new(token, expires_at)
49
+ end
50
+
51
+ def request
52
+ @request ||= BlockchainNode::Request.new(configuration.request_options)
53
+ end
54
+
55
+ def oauth_token_path
56
+ "/oauth/token"
57
+ end
58
+
59
+ def node_index_path
60
+ "/api/nodes"
61
+ end
62
+
63
+ def nodes_path
64
+ "/api/nodes/#{@node_id}"
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,24 @@
1
+ module BlockchainNode
2
+ class Configuration
3
+ ATTRIBUTES = [:client_id, :client_secret, :request_options]
4
+
5
+ # class attributes
6
+ class << self
7
+ ATTRIBUTES.each do |attr|
8
+ attr_accessor attr
9
+ end
10
+ end
11
+
12
+ # instance attributes
13
+ ATTRIBUTES.each do |attr|
14
+ attr_accessor attr
15
+ end
16
+
17
+ def initialize
18
+ @client_id = self.class.client_id
19
+ @client_secret = self.class.client_secret
20
+ @request_options = self.class.request_options || {}
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ module BlockchainNode
2
+ module Errors
3
+ class BadRequest < StandardError; end
4
+ class UnAuthorized < StandardError; end
5
+ class Unknown < StandardError; end
6
+ end
7
+ end
@@ -0,0 +1,64 @@
1
+ require 'json'
2
+ require 'net/http'
3
+
4
+ module BlockchainNode
5
+ class Request
6
+
7
+ DEFAULT_BASE_URL = "https://api.blockchainnode.io"
8
+
9
+ def initialize(options)
10
+ @host = options[:host] || DEFAULT_BASE_URL
11
+ @read_timeout = options[:read_timeout] || 45
12
+ @open_timeout = options[:open_timeout] || 3
13
+ end
14
+
15
+ def get(path:, auth_token:)
16
+ uri = URI(@host + path)
17
+
18
+ request = Net::HTTP::Get.new(uri)
19
+ request['Content-Type'] = "application/json"
20
+ request['Authorization'] = "Bearer #{auth_token}" if auth_token
21
+
22
+ process_request(uri, request)
23
+ end
24
+
25
+ def post(path:, data: {}, auth_token: nil)
26
+ uri = URI(@host + path)
27
+
28
+ request = Net::HTTP::Post.new(uri)
29
+ request['Content-Type'] = "application/json"
30
+ request['Authorization'] = "Bearer #{auth_token}" if auth_token
31
+ request.body = data.to_json
32
+
33
+ process_request(uri, request)
34
+ end
35
+
36
+ private
37
+
38
+ def process_request(uri, request)
39
+ response = Net::HTTP.start(uri.hostname, uri.port,
40
+ use_ssl: uri.scheme == 'https',
41
+ read_timeout: @read_timeout,
42
+ open_timeout: @open_timeout
43
+ ) do |http|
44
+ http.request(request)
45
+ end
46
+
47
+ if response.code == "200"
48
+ begin
49
+ JSON.parse(response.body)
50
+ rescue JSON::ParserError
51
+ { response: response.body }
52
+ end
53
+ elsif response.code == "400"
54
+ raise BlockchainNode::Errors::BadRequest.new(response.body)
55
+ elsif response.code == "401"
56
+ raise BlockchainNode::Errors::UnAuthorized.new(response.body)
57
+ else
58
+ raise BlockchainNode::Errors::Unknown.new("#{response.code} #{response.body}")
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ module BlockchainNode
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ PATCH = 1
6
+
7
+ VERSION = [MAJOR, MINOR, PATCH].join('.').freeze
8
+ end
@@ -0,0 +1,20 @@
1
+ module BlockchainNode
2
+ autoload :Client, 'blockchain-node/client'
3
+ autoload :Configuration, 'blockchain-node/configuration'
4
+ autoload :Errors, 'blockchain-node/errors'
5
+ autoload :Request,'blockchain-node/request'
6
+ autoload :VERSION,'blockchain-node/version'
7
+
8
+ def self.configure
9
+ yield Configuration if block_given?
10
+ end
11
+
12
+ def self.config
13
+ config = {}
14
+ Configuration::ATTRIBUTES.each do |attribute|
15
+ config[attribute] = BlockchainNode::Configuration.send(attribute)
16
+ end
17
+ config
18
+ end
19
+
20
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe BlockchainNode::Configuration do
4
+
5
+ before :each do
6
+ BlockchainNode.configure do |config|
7
+ config.client_id = CLIENT_ID
8
+ config.client_secret = CLIENT_SECRET
9
+ config.request_options = { }
10
+ end
11
+ end
12
+
13
+ describe "configuration" do
14
+ it "should be accessible as a class property" do
15
+ expect(BlockchainNode.config[:client_id]).to eq CLIENT_ID
16
+ expect(BlockchainNode.config[:client_secret]).to eq CLIENT_SECRET
17
+ end
18
+
19
+ it "should configure properly" do
20
+ client = BlockchainNode::Client.new(NODE_ID)
21
+ expect(client.configuration.client_id).to eq CLIENT_ID
22
+ expect(client.configuration.client_secret).to eq CLIENT_SECRET
23
+ end
24
+
25
+ it "should allow api host override" do
26
+ host = "http://localhost:3000"
27
+ BlockchainNode.configure do |config|
28
+ config.request_options = { host: host }
29
+ end
30
+ expect(BlockchainNode.config[:request_options][:host]).to eq host
31
+ end
32
+ end
33
+
34
+ describe "oauth tokens" do
35
+ it "requests a new token if 1 is expired" do
36
+ expect_any_instance_of(BlockchainNode::Request).to receive(:process_request).exactly(3).times.and_return(
37
+ {
38
+ "access_token" => "a9b29c6810ba513f08f87fafadaa6154690f9246aa663b1b708c1c94a5887386",
39
+ "expires_in" => 7200, "created_at" => Time.now.to_i
40
+ }
41
+ )
42
+ client = BlockchainNode::Client.new(NODE_ID)
43
+ client.auth_token
44
+ Timecop.travel(Time.now + 7300)
45
+ client.auth_token
46
+ Timecop.travel(Time.now + 7200)
47
+ client.auth_token
48
+ end
49
+ end
50
+
51
+ describe "api calls" do
52
+ it "should make a successful API call" do
53
+ stub_oauth
54
+ stub_basic_method
55
+
56
+ client = BlockchainNode::Client.new(NODE_ID)
57
+ response = client.eth_blockNumber
58
+ expect(Integer(response[:response])).to eq 4666
59
+ end
60
+ end
61
+
62
+ end
@@ -0,0 +1,7 @@
1
+ require 'pry'
2
+ require 'timecop'
3
+ require 'webmock/rspec'
4
+
5
+ require File.expand_path('../lib/blockchain-node', File.dirname(__FILE__))
6
+
7
+ Dir[File.expand_path("support/**/*.rb", File.dirname(__FILE__))].each { |f| require f }
@@ -0,0 +1,33 @@
1
+ require 'json'
2
+
3
+ CLIENT_ID = "12345ABC"
4
+ CLIENT_SECRET = "1234567890ABCDEFG"
5
+ NODE_ID = "ABC123"
6
+
7
+ OAUTH_REQUEST = { grant_type: "client_credentials", client_id: CLIENT_ID, client_secret: CLIENT_SECRET }
8
+ OAUTH_RESPONSE = {
9
+ access_token: "a9b29c6810ba513f08f87fafadaa6154690f9246aa663b1b708c1c94a5887386",
10
+ token_type: "Bearer",
11
+ expires_in: 7200,
12
+ created_at: Time.now.to_i
13
+ }
14
+ def stub_oauth
15
+ stub_request(:post, "#{BlockchainNode::Request::DEFAULT_BASE_URL}/oauth/token").
16
+ with(body: OAUTH_REQUEST ).
17
+ to_return(status: 200, body: OAUTH_RESPONSE.to_json)
18
+ end
19
+
20
+ def stub_basic_method
21
+ stub_request(:post, "#{BlockchainNode::Request::DEFAULT_BASE_URL}/api/nodes/#{NODE_ID}").
22
+ with(body: api_method_body('eth_blockNumber')).
23
+ to_return(status: 200, body: '0x123A ')
24
+ end
25
+
26
+
27
+ def api_method_body(method, *params)
28
+ {
29
+ method: method,
30
+ parameters: params
31
+ }.to_json
32
+ end
33
+
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blockchain-node
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Matt Pestritto
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-01-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: timecop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3'
83
+ description: Client SDK for accessing BlockchainNode API.
84
+ email:
85
+ - matt@blockchainnode.io
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - Gemfile
91
+ - README-RPC-BTC.md
92
+ - README-RPC-ETH.md
93
+ - README.md
94
+ - Rakefile
95
+ - lib/blockchain-node.rb
96
+ - lib/blockchain-node/client.rb
97
+ - lib/blockchain-node/configuration.rb
98
+ - lib/blockchain-node/errors.rb
99
+ - lib/blockchain-node/request.rb
100
+ - lib/blockchain-node/version.rb
101
+ - spec/lib/blockchain-node_spec.rb
102
+ - spec/spec_helper.rb
103
+ - spec/support/webmock.rb
104
+ homepage: https://blockchainnode.io
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.7.6
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: BlockchainNode Ruby Client
128
+ test_files: []