pickynode 0.0.3 → 0.1.0

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
  SHA1:
3
- metadata.gz: a9ed335fbc05dfe5122819dab044c3f087f6855d
4
- data.tar.gz: ae580a59583a6c5b355883ee71e76cf8b30f8665
3
+ metadata.gz: dfe782c9a3c9232314fb733ca5e9a0cc31f8bdd5
4
+ data.tar.gz: 3044d3f752bdca822b5f0e547f9d478febdad2da
5
5
  SHA512:
6
- metadata.gz: 1051e7d02b39ee86c2a2cc031d484c5f00ac9ee21c9634822395a10ff03702560ff9d5c76d24a4e8fad5374594920b8b42969dfca2159b5161a28c22a86be952
7
- data.tar.gz: 7d215790b39f5b36b0f05cb0e4f4e47bdaaa552e4b975d81a6452e558740f0bdf84cf3818879be03fdc44b2366a3e0e8709df101f1a7faf48161e122f1281ec8
6
+ metadata.gz: c509c44a20db271c1735296f946e17562f968ce4d52d15cb3720c323b1cbfa90de08acef6bfbf01fc4c7a4b2bc79a356d489d34e991670ecdbcfbd2edd8f8e47
7
+ data.tar.gz: db50a3333c22a40e18bc7a08f1c2b34d24b92cd5b7d0035a42c6418c794c0cc5e97ffda1d609990a0f304461df8993cc59d2333d93d58de416d0212ee2e49246
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/.rubocop.yml ADDED
@@ -0,0 +1,4 @@
1
+ Metrics/BlockLength:
2
+ ExcludedMethods: ['describe', 'context']
3
+ Style/IndentHeredoc:
4
+ Enabled: false
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ pickynode
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.4
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.2.7"
4
+ - "2.3.4"
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+ ruby '>= 2.0'
5
+
6
+ gem 'awesome_print', '~> 1.7'
7
+ gem 'rake', '~> 12.0'
8
+ gem 'rspec', '~> 3.6'
9
+ gem 'rubocop', '~> 0.48'
10
+ gem 'trollop', '~> 2.1'
data/Gemfile.lock ADDED
@@ -0,0 +1,50 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ ast (2.3.0)
5
+ awesome_print (1.7.0)
6
+ diff-lcs (1.3)
7
+ parser (2.4.0.0)
8
+ ast (~> 2.2)
9
+ powerpack (0.1.1)
10
+ rainbow (2.2.2)
11
+ rake
12
+ rake (12.0.0)
13
+ rspec (3.6.0)
14
+ rspec-core (~> 3.6.0)
15
+ rspec-expectations (~> 3.6.0)
16
+ rspec-mocks (~> 3.6.0)
17
+ rspec-core (3.6.0)
18
+ rspec-support (~> 3.6.0)
19
+ rspec-expectations (3.6.0)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.6.0)
22
+ rspec-mocks (3.6.0)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.6.0)
25
+ rspec-support (3.6.0)
26
+ rubocop (0.48.1)
27
+ parser (>= 2.3.3.1, < 3.0)
28
+ powerpack (~> 0.1)
29
+ rainbow (>= 1.99.1, < 3.0)
30
+ ruby-progressbar (~> 1.7)
31
+ unicode-display_width (~> 1.0, >= 1.0.1)
32
+ ruby-progressbar (1.8.1)
33
+ trollop (2.1.2)
34
+ unicode-display_width (1.2.1)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ awesome_print (~> 1.7)
41
+ rake (~> 12.0)
42
+ rspec (~> 3.6)
43
+ rubocop (~> 0.48)
44
+ trollop (~> 2.1)
45
+
46
+ RUBY VERSION
47
+ ruby 2.3.4p301
48
+
49
+ BUNDLED WITH
50
+ 1.14.6
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Josh Ellithorpe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ [![Gem Version](https://badge.fury.io/rb/pickynode.svg)](https://badge.fury.io/rb/pickynode) [![Build Status](https://travis-ci.org/zquestz/pickynode.svg)](https://travis-ci.org/zquestz/pickynode) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
2
+ # pickynode
3
+
4
+ Some people are picky about the bitcoin nodes they connect to.
5
+
6
+ ### Requirements:
7
+
8
+ You need a working full node on your machine. The `bitcoin-cli` command should be functional.
9
+
10
+ ### Installation:
11
+
12
+ ```
13
+ gem install pickynode
14
+ ```
15
+
16
+ ### Usage:
17
+
18
+ Display list of currently connected nodes:
19
+
20
+ ```
21
+ pickynode
22
+ ```
23
+
24
+ Add node type:
25
+ ```
26
+ pickynode --add=USER_AGENT_FILTER
27
+ ```
28
+
29
+ Ban node type:
30
+ ```
31
+ pickynode --ban=USER_AGENT_FILTER
32
+ ```
33
+
34
+ Connect to node type:
35
+ ```
36
+ pickynode --connect=USER_AGENT_FILTER
37
+ ```
38
+
39
+ Disconnect from node type:
40
+
41
+ ```
42
+ pickynode --disconnect=USER_AGENT_FILTER
43
+ ```
44
+
45
+ ### Help:
46
+
47
+ ```
48
+ pickynode v0.1.0
49
+ Options:
50
+ -a, --add=<s> Add node type
51
+ -c, --connect=<s> Connect to node type
52
+ -b, --ban=<s> Ban node type
53
+ -d, --debug Debug mode
54
+ -o, --output Output commands
55
+ -i, --disconnect=<s> Disconnect from node type
56
+ -v, --version Print version and exit
57
+ -h, --help Show this message
58
+
59
+ ```
60
+
61
+ The --add and --connect commands pull data from Bitnodes.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ # frozen_string_literal: true
3
+
4
+ require File.join('bundler', 'gem_tasks')
5
+ require File.join('rspec', 'core', 'rake_task')
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task default: :spec
data/bin/pickynode CHANGED
@@ -1,86 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
- require 'awesome_print'
3
- require 'json'
4
- require 'net/http'
5
- require 'pp'
6
- require 'trollop'
7
- require 'uri'
8
-
9
- VERSION = "0.0.3"
10
-
11
- opts = Trollop::options do
12
- version "pickynode v#{VERSION}"
13
- opt :add, "Add node type", :type => :string
14
- opt :connect, "Connect to node type", :type => :string
15
- opt :ban, "Ban node type", :type => :string
16
- opt :debug, "Debug mode"
17
- opt :output, "Output commands"
18
- opt :disconnect, "Disconnect from node type", :type => :string
19
- end
20
-
21
- def run(cmd, opts={})
22
- puts "Running #{cmd}" if opts[:output] || opts[:debug]
23
- `#{cmd}` unless opts[:debug]
24
- end
25
-
26
- def get_addr_types
27
- nodes = `bitcoin-cli getpeerinfo`
28
- parsed_nodes = JSON.parse(nodes)
29
- parsed_nodes.map { |n| [n["addr"], n["subver"]] }.to_h
30
- end
31
-
32
- def get_bitnode_addr_types
33
- u = URI.parse("https://bitnodes.21.co/api/v1/snapshots/latest/")
34
- nodelist = Net::HTTP.get(u)
35
- parsed_nodelist = JSON.parse(nodelist)
36
- parsed_nodelist['nodes'].map { |k,v| [k, v[1]] }.to_h
37
- end
38
-
39
- if opts[:add] || opts[:connect]
40
- bitnode_addr_types = get_bitnode_addr_types
41
-
42
- if filter = opts[:add]
43
- bitnode_addr_types.each do |k,v|
44
- if v.include?(filter)
45
- u = URI.parse("https://#{k}")
46
- run(%^bitcoin-cli addnode "#{u.host}" "add"^, opts)
47
- end
48
- end
49
- end
50
-
51
- if filter = opts[:connect]
52
- bitnode_addr_types.each do |k,v|
53
- if v.include?(filter)
54
- u = URI.parse("https://#{k}")
55
- run(%^bitcoin-cli addnode "#{u.host}" "onetry"^, opts)
56
- end
57
- end
58
- end
59
- end
60
-
61
- if opts[:ban] || opts[:disconnect]
62
- addr_types = get_addr_types
63
-
64
- if filter = opts[:ban]
65
- addr_types.each do |k,v|
66
- if v.include?(filter)
67
- u = URI.parse("https://#{k}")
68
- run(%^bitcoin-cli setban "#{u.host}" "add"^, opts)
69
- end
70
- end
71
- end
72
-
73
- if filter = opts[:disconnect]
74
- addr_types.each do |k,v|
75
- if v.include?(filter)
76
- run(%^bitcoin-cli disconnectnode "#{k}"^, opts)
77
- end
78
- end
79
- end
80
- end
81
-
82
- unless opts.values.select { |v| v }.empty?
83
- exit(0)
2
+ # frozen_string_literal: true
3
+
4
+ require 'pickynode'
5
+
6
+ opts = Trollop.options do
7
+ version "pickynode v#{Pickynode::VERSION}"
8
+ opt :add, 'Add node type', type: :string
9
+ opt :connect, 'Connect to node type', type: :string
10
+ opt :ban, 'Ban node type', type: :string
11
+ opt :debug, 'Debug mode'
12
+ opt :output, 'Output commands'
13
+ opt :disconnect, 'Disconnect from node type', type: :string
84
14
  end
85
15
 
86
- ap get_addr_types
16
+ Pickynode.new(opts).run
data/lib/pickynode.rb ADDED
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'awesome_print'
4
+ require 'json'
5
+ require 'net/http'
6
+ require 'trollop'
7
+ require 'uri'
8
+
9
+ # Allows you to easily add/ban/connect/disconnect nodes
10
+ # based on User Agent.
11
+ class Pickynode
12
+ VERSION = '0.1.0'
13
+
14
+ def initialize(opts = {})
15
+ @opts = opts
16
+ end
17
+
18
+ def add(filter)
19
+ return unless filter
20
+ bitnode_addr_types.each do |k, v|
21
+ run_cmd(%(bitcoin-cli addnode "#{k}" "add")) if v.include?(filter)
22
+ end
23
+ end
24
+
25
+ def ban(filter)
26
+ return unless filter
27
+ addr_types.each do |k, v|
28
+ if v.include?(filter)
29
+ u = URI.parse("https://#{k}")
30
+ run_cmd(%(bitcoin-cli setban "#{u.host}" "add"))
31
+ end
32
+ end
33
+ end
34
+
35
+ def connect(filter)
36
+ return unless filter
37
+ bitnode_addr_types.each do |k, v|
38
+ run_cmd(%(bitcoin-cli addnode "#{k}" "onetry")) if v.include?(filter)
39
+ end
40
+ end
41
+
42
+ def disconnect(filter)
43
+ return unless filter
44
+ addr_types.each do |k, v|
45
+ run_cmd(%(bitcoin-cli disconnectnode "#{k}")) if v.include?(filter)
46
+ end
47
+ end
48
+
49
+ def display
50
+ ap addr_types
51
+ end
52
+
53
+ def run
54
+ add(@opts[:add])
55
+ connect(@opts[:connect])
56
+
57
+ ban(@opts[:ban])
58
+ disconnect(@opts[:disconnect])
59
+
60
+ return unless @opts.values.select { |v| v }.empty?
61
+
62
+ display
63
+ end
64
+
65
+ def clear_cache
66
+ @addr_types = nil
67
+ @bitnode_addr_types = nil
68
+ end
69
+
70
+ private
71
+
72
+ def run_cmd(cmd)
73
+ puts "Running #{cmd}" if @opts[:output] || @opts[:debug]
74
+ `#{cmd}` unless @opts[:debug]
75
+ end
76
+
77
+ def addr_types
78
+ return @addr_types if @addr_types
79
+ nodes = getpeerinfo
80
+ parsed_nodes = JSON.parse(nodes)
81
+ @addr_types = parsed_nodes.map do |n|
82
+ [n['addr'], n['subver']]
83
+ end.to_h
84
+ rescue JSON::ParserError
85
+ {}
86
+ end
87
+
88
+ def bitnode_addr_types
89
+ return @bitnode_addr_types if @bitnode_addr_types
90
+ parsed_nodelist = JSON.parse(bitnodes_snapshot)
91
+ @bitnode_addr_types = parsed_nodelist['nodes'].map do |k, v|
92
+ [k, v[1]]
93
+ end.to_h
94
+ rescue JSON::ParserError
95
+ {}
96
+ end
97
+
98
+ def bitnodes_snapshot
99
+ Net::HTTP.get(URI.parse('https://bitnodes.21.co/api/v1/snapshots/latest/'))
100
+ end
101
+
102
+ def getpeerinfo
103
+ `bitcoin-cli getpeerinfo`
104
+ end
105
+ end
data/pickynode.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path(File.join('..', 'lib', 'pickynode'), __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'pickynode'
7
+ s.version = Pickynode::VERSION
8
+ s.date = '2017-05-19'
9
+ s.summary = 'Manage connections to your bitcoin node'
10
+ s.description = 'Some people are picky about the \
11
+ bitcoin nodes they connect to.'
12
+ s.authors = ['Josh Ellithorpe']
13
+ s.email = 'quest@mac.com'
14
+ s.homepage = 'http://github.com/zquestz/pickynode'
15
+ s.license = 'MIT'
16
+ s.executables << 'pickynode'
17
+ s.files = `git ls-files`.split("\n")
18
+ s.require_paths = ['lib']
19
+ s.required_ruby_version = '>= 2.0'
20
+
21
+ s.add_dependency 'awesome_print', '~> 1.7'
22
+ s.add_dependency 'trollop', '~> 2.1'
23
+
24
+ s.add_development_dependency 'rspec', '~> 3.6'
25
+ s.add_development_dependency 'rake', '~> 12.0'
26
+ s.add_development_dependency 'rubocop', '~> 0.48'
27
+ end
data/spec/mocks.rb ADDED
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ BITNODES_SNAPSHOT = <<-HEREDOC
4
+ {
5
+ "timestamp": 1495414383,
6
+ "total_nodes": 7055,
7
+ "latest_height": 467520,
8
+ "nodes": {
9
+ "88.99.199.87:8333": [80002, "/BitcoinUnlimited:1.0.2(EB16; AD12)/", 1494608364, 5, 467520, "static.87.199.99.88.clients.your-server.de", null, "DE", 51.2993, 9.491, "Europe/Berlin", "AS24940", "Hetzner Online GmbH"],
10
+ "[2a01:e34:ee3a:5730:21f:5bff:fec5:e356]:8333": [70015, "/Satoshi:0.14.0/", 1495378733, 13, 467520, "2a01:e34:ee3a:5730:21f:5bff:fec5:e356", null, "FR", 46.0, 2.0, "Europe/Paris", "AS12322", "Free SAS"]
11
+ }
12
+ }
13
+ HEREDOC
14
+
15
+ PEER_INFO = <<-HEREDOC
16
+ [
17
+ {
18
+ "id": 2,
19
+ "addr": "131.114.88.218:33422",
20
+ "addrlocal": "67.188.11.253:8333",
21
+ "services": "0000000000000000",
22
+ "relaytxes": true,
23
+ "lastsend": 1495412829,
24
+ "lastrecv": 1495412825,
25
+ "bytessent": 5200917,
26
+ "bytesrecv": 2044140,
27
+ "conntime": 1495393261,
28
+ "timeoffset": -24,
29
+ "pingtime": 0.369602,
30
+ "minping": 0.210565,
31
+ "version": 70002,
32
+ "subver": "/FirstClient/",
33
+ "inbound": true,
34
+ "startingheight": 435862,
35
+ "banscore": 0,
36
+ "synced_headers": 467515,
37
+ "synced_blocks": -1,
38
+ "inflight": [
39
+ ],
40
+ "whitelisted": false
41
+ },
42
+ {
43
+ "id": 3,
44
+ "addr": "[2a01:e34:ee3a:5730:21f:5bff:fec5:e356]:8333",
45
+ "addrlocal": "[2a01:e34:ee3a:5730:21f:5bff:fec5:e356]:8333",
46
+ "services": "0000000000000005",
47
+ "relaytxes": true,
48
+ "lastsend": 1495412829,
49
+ "lastrecv": 1495412830,
50
+ "bytessent": 10235040,
51
+ "bytesrecv": 2729345,
52
+ "conntime": 1495393262,
53
+ "timeoffset": -11,
54
+ "pingtime": 0.275245,
55
+ "minping": 0.07081899999999999,
56
+ "version": 80002,
57
+ "subver": "/SecondClient/",
58
+ "inbound": false,
59
+ "startingheight": 467483,
60
+ "banscore": 0,
61
+ "synced_headers": 467515,
62
+ "synced_blocks": 467515,
63
+ "inflight": [
64
+ ],
65
+ "whitelisted": false
66
+ }
67
+ ]
68
+ HEREDOC
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'spec_helper'
4
+ require_relative 'mocks'
5
+
6
+ describe Pickynode do
7
+ # Debug mode makes sure we don't execute real commands.
8
+ let(:opts) do
9
+ { debug: true }
10
+ end
11
+
12
+ # IPv6 addresses are long, easier to refer by let.
13
+ let(:ipv6_ip) { '[2a01:e34:ee3a:5730:21f:5bff:fec5:e356]:8333' }
14
+ let(:ipv6_ip_no_port) { '[2a01:e34:ee3a:5730:21f:5bff:fec5:e356]' }
15
+
16
+ # The currently connected nodes for the specs.
17
+ let(:node_hash) do
18
+ { '131.114.88.218:33422' => '/FirstClient/',
19
+ ipv6_ip => '/SecondClient/' }
20
+ end
21
+
22
+ subject { Pickynode.new(opts) }
23
+
24
+ describe '.add' do
25
+ it 'should add nodes based on user agent' do
26
+ expect(subject).to receive(:bitnodes_snapshot).once
27
+ .and_return(BITNODES_SNAPSHOT)
28
+ expect(subject).to receive(:run_cmd)
29
+ .with(%(bitcoin-cli addnode "#{ipv6_ip}" "add"))
30
+ subject.add('Satoshi')
31
+ expect(subject).to receive(:run_cmd)
32
+ .with('bitcoin-cli addnode "88.99.199.87:8333" "add"')
33
+ subject.add('Unlimited')
34
+ end
35
+
36
+ it 'should return if the filter is falsy' do
37
+ expect(subject).to_not receive(:bitnodes_snapshot)
38
+ expect(subject).to_not receive(:run_cmd)
39
+ subject.add(false)
40
+ subject.add(nil)
41
+ end
42
+ end
43
+
44
+ describe '.ban' do
45
+ it 'should ban nodes based on user agent' do
46
+ expect(subject).to receive(:getpeerinfo).once
47
+ .and_return(PEER_INFO)
48
+ expect(URI).to receive(:parse).with('https://131.114.88.218:33422')
49
+ .and_call_original
50
+ expect(subject).to receive(:run_cmd)
51
+ .with('bitcoin-cli setban "131.114.88.218" "add"')
52
+ subject.ban('FirstClient')
53
+ expect(URI).to receive(:parse).with("https://#{ipv6_ip}")
54
+ .and_call_original
55
+ expect(subject).to receive(:run_cmd)
56
+ .with(%(bitcoin-cli setban "#{ipv6_ip_no_port}" "add"))
57
+ subject.ban('SecondClient')
58
+ end
59
+
60
+ it 'should return if the filter is falsy' do
61
+ expect(subject).to_not receive(:getpeerinfo)
62
+ expect(URI).to_not receive(:parse)
63
+ expect(subject).to_not receive(:run_cmd)
64
+ subject.ban(false)
65
+ subject.ban(nil)
66
+ end
67
+ end
68
+
69
+ describe '.connect' do
70
+ it 'should connect to nodes based on user agent' do
71
+ expect(subject).to receive(:bitnodes_snapshot).once
72
+ .and_return(BITNODES_SNAPSHOT)
73
+ expect(subject).to receive(:run_cmd)
74
+ .with('bitcoin-cli addnode "88.99.199.87:8333" "onetry"')
75
+ subject.connect('Unlimited')
76
+ expect(subject).to receive(:run_cmd)
77
+ .with(%(bitcoin-cli addnode "#{ipv6_ip}" "onetry"))
78
+ subject.connect('Satoshi')
79
+ end
80
+
81
+ it 'should return if the filter is falsy' do
82
+ expect(subject).to_not receive(:bitnodes_snapshot)
83
+ expect(subject).to_not receive(:run_cmd)
84
+ subject.connect(false)
85
+ subject.connect(nil)
86
+ end
87
+ end
88
+
89
+ describe '.disconnect' do
90
+ it 'should disconnect nodes based on user agent' do
91
+ expect(subject).to receive(:getpeerinfo).once
92
+ .and_return(PEER_INFO)
93
+ expect(subject).to receive(:run_cmd)
94
+ .with(%(bitcoin-cli disconnectnode "#{ipv6_ip}"))
95
+ subject.disconnect('SecondClient')
96
+ expect(subject).to receive(:run_cmd)
97
+ .with('bitcoin-cli disconnectnode "131.114.88.218:33422"')
98
+ subject.disconnect('FirstClient')
99
+ end
100
+
101
+ it 'should return if the filter is falsy' do
102
+ expect(subject).to_not receive(:getpeerinfo)
103
+ expect(subject).to_not receive(:run_cmd)
104
+ subject.disconnect(false)
105
+ subject.disconnect(nil)
106
+ end
107
+ end
108
+
109
+ describe '.display' do
110
+ it 'should display connected nodes' do
111
+ expect(subject).to receive(:getpeerinfo).and_return(PEER_INFO)
112
+ expect(subject).to receive(:ap).with(node_hash).and_return(node_hash)
113
+ expect(subject.display).to eq(node_hash)
114
+ end
115
+ end
116
+
117
+ describe '.run' do
118
+ context 'with opts' do
119
+ let(:opts) do
120
+ {
121
+ debug: true,
122
+ add: 'Wanted',
123
+ connect: 'Now',
124
+ ban: 'Nefarious',
125
+ disconnect: 'Fools'
126
+ }
127
+ end
128
+
129
+ it 'should call add, connect, ban and disconnect' do
130
+ expect(subject).to receive(:bitnodes_snapshot).once
131
+ .and_return(BITNODES_SNAPSHOT)
132
+ expect(subject).to receive(:getpeerinfo).and_return(PEER_INFO)
133
+ expect(subject).to receive(:add).with(opts[:add])
134
+ .and_call_original
135
+ expect(subject).to receive(:connect).with(opts[:connect])
136
+ .and_call_original
137
+ expect(subject).to receive(:ban).with(opts[:ban])
138
+ .and_call_original
139
+ expect(subject).to receive(:disconnect).with(opts[:disconnect])
140
+ .and_call_original
141
+ expect(subject).to_not receive(:display)
142
+ subject.run
143
+ end
144
+ end
145
+
146
+ context 'without opts' do
147
+ let(:opts) do
148
+ {}
149
+ end
150
+
151
+ it 'should call display' do
152
+ expect(subject).to receive(:getpeerinfo).once
153
+ .and_return(PEER_INFO)
154
+ expect(subject).to receive(:ap).with(node_hash).and_return(node_hash)
155
+ expect(subject).to receive(:display).and_call_original
156
+ subject.run
157
+ end
158
+ end
159
+ end
160
+
161
+ describe 'clear_cache' do
162
+ it 'should clear the bitnodes cache' do
163
+ expect(subject).to receive(:bitnodes_snapshot).once
164
+ .and_return(BITNODES_SNAPSHOT)
165
+ expect(subject).to receive(:run_cmd)
166
+ .with(%(bitcoin-cli addnode "#{ipv6_ip}" "add"))
167
+ expect(subject).to receive(:run_cmd)
168
+ .with('bitcoin-cli addnode "88.99.199.87:8333" "add"')
169
+ subject.add('Satoshi')
170
+ subject.add('Unlimited')
171
+ subject.clear_cache
172
+ expect(subject).to receive(:bitnodes_snapshot).once
173
+ .and_return(BITNODES_SNAPSHOT)
174
+ expect(subject).to receive(:run_cmd)
175
+ .with(%(bitcoin-cli addnode "#{ipv6_ip}" "add"))
176
+ subject.add('Satoshi')
177
+ end
178
+
179
+ it 'should clear the getpeerinfo cache' do
180
+ expect(subject).to receive(:getpeerinfo).once
181
+ .and_return(PEER_INFO)
182
+ expect(URI).to receive(:parse).with('https://131.114.88.218:33422')
183
+ .and_call_original
184
+ expect(URI).to receive(:parse).with(%(https://#{ipv6_ip}))
185
+ .and_call_original
186
+ expect(subject).to receive(:run_cmd)
187
+ .with('bitcoin-cli setban "131.114.88.218" "add"')
188
+ expect(subject).to receive(:run_cmd)
189
+ .with(%(bitcoin-cli setban "#{ipv6_ip_no_port}" "add"))
190
+ subject.ban('FirstClient')
191
+ subject.ban('SecondClient')
192
+ subject.clear_cache
193
+ expect(subject).to receive(:getpeerinfo).once
194
+ .and_return(PEER_INFO)
195
+ expect(URI).to receive(:parse).with('https://131.114.88.218:33422')
196
+ .and_call_original
197
+ expect(subject).to receive(:run_cmd)
198
+ .with('bitcoin-cli setban "131.114.88.218" "add"')
199
+ subject.ban('FirstClient')
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'spec_helper'
4
+
5
+ describe 'Rubocop' do
6
+ it 'should pass with no offenses detected' do
7
+ expect(`rubocop`).to include('no offenses detected')
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.join('bundler', 'setup')
4
+ require 'rspec'
5
+ require 'pickynode'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pickynode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Ellithorpe
@@ -38,14 +38,74 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.1'
41
- description: Some people are picky about the bitcoin nodes they connect to.
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.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '12.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '12.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.48'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.48'
83
+ description: |-
84
+ Some people are picky about the \
85
+ bitcoin nodes they connect to.
42
86
  email: quest@mac.com
43
87
  executables:
44
88
  - pickynode
45
89
  extensions: []
46
90
  extra_rdoc_files: []
47
91
  files:
92
+ - ".gitignore"
93
+ - ".rubocop.yml"
94
+ - ".ruby-gemset"
95
+ - ".ruby-version"
96
+ - ".travis.yml"
97
+ - Gemfile
98
+ - Gemfile.lock
99
+ - LICENSE
100
+ - README.md
101
+ - Rakefile
48
102
  - bin/pickynode
103
+ - lib/pickynode.rb
104
+ - pickynode.gemspec
105
+ - spec/mocks.rb
106
+ - spec/pickynode_spec.rb
107
+ - spec/rubocop_spec.rb
108
+ - spec/spec_helper.rb
49
109
  homepage: http://github.com/zquestz/pickynode
50
110
  licenses:
51
111
  - MIT
@@ -58,7 +118,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
58
118
  requirements:
59
119
  - - ">="
60
120
  - !ruby/object:Gem::Version
61
- version: '0'
121
+ version: '2.0'
62
122
  required_rubygems_version: !ruby/object:Gem::Requirement
63
123
  requirements:
64
124
  - - ">="