pickynode-bchd 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 +7 -0
- data/.gitignore +50 -0
- data/.rubocop.yml +8 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +58 -0
- data/LICENSE +21 -0
- data/README.md +62 -0
- data/Rakefile +9 -0
- data/bin/pickynode-bchd +19 -0
- data/lib/pickynode_bchd.rb +144 -0
- data/pickynode_bchd.gemspec +27 -0
- data/spec/mocks.rb +90 -0
- data/spec/pickynode_spec.rb +331 -0
- data/spec/rubocop_spec.rb +9 -0
- data/spec/spec_helper.rb +11 -0
- metadata +146 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 377298cc35a7243d785a916969de201cbfbf471b
|
|
4
|
+
data.tar.gz: 7ecacb4c91391adac2c04736a2c714ecee9bc3b0
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: d42292b1ff9c695261aa76a01342914519df5e44a59ece7ae9f4e5ae0c0ac2579fe55b749338407de3f51bb2938ca4cbd08f83785f12fa9e85dd0ab07d72a642
|
|
7
|
+
data.tar.gz: 72fa31e5363fe323744b172980b4cb517eb3e3acd621f19b3c2624cb7f6678476fa01f1ced4168309cc6ceb3e128042b836c348c560e0548a5086bc661e91770
|
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
data/.ruby-gemset
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pickynode-bchd
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.3.4
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
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 'simplecov', '~> 0.14', require: false, group: :test
|
|
11
|
+
gem 'trollop', '~> 2.1'
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
docile (1.1.5)
|
|
8
|
+
json (2.1.0)
|
|
9
|
+
parser (2.4.0.0)
|
|
10
|
+
ast (~> 2.2)
|
|
11
|
+
powerpack (0.1.1)
|
|
12
|
+
rainbow (2.2.2)
|
|
13
|
+
rake
|
|
14
|
+
rake (12.0.0)
|
|
15
|
+
rspec (3.6.0)
|
|
16
|
+
rspec-core (~> 3.6.0)
|
|
17
|
+
rspec-expectations (~> 3.6.0)
|
|
18
|
+
rspec-mocks (~> 3.6.0)
|
|
19
|
+
rspec-core (3.6.0)
|
|
20
|
+
rspec-support (~> 3.6.0)
|
|
21
|
+
rspec-expectations (3.6.0)
|
|
22
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
23
|
+
rspec-support (~> 3.6.0)
|
|
24
|
+
rspec-mocks (3.6.0)
|
|
25
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
26
|
+
rspec-support (~> 3.6.0)
|
|
27
|
+
rspec-support (3.6.0)
|
|
28
|
+
rubocop (0.48.1)
|
|
29
|
+
parser (>= 2.3.3.1, < 3.0)
|
|
30
|
+
powerpack (~> 0.1)
|
|
31
|
+
rainbow (>= 1.99.1, < 3.0)
|
|
32
|
+
ruby-progressbar (~> 1.7)
|
|
33
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
|
34
|
+
ruby-progressbar (1.8.1)
|
|
35
|
+
simplecov (0.14.1)
|
|
36
|
+
docile (~> 1.1.0)
|
|
37
|
+
json (>= 1.8, < 3)
|
|
38
|
+
simplecov-html (~> 0.10.0)
|
|
39
|
+
simplecov-html (0.10.1)
|
|
40
|
+
trollop (2.1.2)
|
|
41
|
+
unicode-display_width (1.2.1)
|
|
42
|
+
|
|
43
|
+
PLATFORMS
|
|
44
|
+
ruby
|
|
45
|
+
|
|
46
|
+
DEPENDENCIES
|
|
47
|
+
awesome_print (~> 1.7)
|
|
48
|
+
rake (~> 12.0)
|
|
49
|
+
rspec (~> 3.6)
|
|
50
|
+
rubocop (~> 0.48)
|
|
51
|
+
simplecov (~> 0.14)
|
|
52
|
+
trollop (~> 2.1)
|
|
53
|
+
|
|
54
|
+
RUBY VERSION
|
|
55
|
+
ruby 2.3.4p301
|
|
56
|
+
|
|
57
|
+
BUNDLED WITH
|
|
58
|
+
1.14.6
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 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,62 @@
|
|
|
1
|
+
[](https://badge.fury.io/rb/pickynode-bchd) [](https://travis-ci.org/zquestz/pickynode-bchd) [](https://opensource.org/licenses/MIT)
|
|
2
|
+
# pickynode-bchd
|
|
3
|
+
|
|
4
|
+
Some people are picky about the bitcoin cash nodes they connect to with bchd.
|
|
5
|
+
|
|
6
|
+
### Requirements:
|
|
7
|
+
|
|
8
|
+
You need a working bchd node on your machine. The `bchctl` command should be functional.
|
|
9
|
+
|
|
10
|
+
### Installation:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
gem install pickynode-bchd
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Usage:
|
|
17
|
+
|
|
18
|
+
Display list of currently connected nodes:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
pickynode-bchd
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Add node type:
|
|
25
|
+
```
|
|
26
|
+
pickynode-bchd --add=USER_AGENT_FILTER
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Remove node type:
|
|
30
|
+
```
|
|
31
|
+
pickynode-bchd --remove=USER_AGENT_FILTER
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Connect to node type:
|
|
35
|
+
```
|
|
36
|
+
pickynode-bchd --connect=USER_AGENT_FILTER
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Disconnect from node type:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
pickynode-bchd --disconnect=USER_AGENT_FILTER
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Help:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
pickynode-bchd v0.1.0
|
|
49
|
+
Options:
|
|
50
|
+
-i, --info Local node info
|
|
51
|
+
-d, --debug Debug mode
|
|
52
|
+
-o, --output Output commands
|
|
53
|
+
-a, --add=<s> Add node type
|
|
54
|
+
-r, --remove=<s> Remove node type
|
|
55
|
+
-c, --connect=<s> Connect to node type
|
|
56
|
+
-s, --disconnect=<s> Disconnect from node type
|
|
57
|
+
-l, --limit=<i> Limit number of nodes to add/connect
|
|
58
|
+
-v, --version Print version and exit
|
|
59
|
+
-h, --help Show this message
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The --add and --connect commands pull data from blockchair.
|
data/Rakefile
ADDED
data/bin/pickynode-bchd
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'pickynode_bchd'
|
|
5
|
+
|
|
6
|
+
opts = Trollop.options do
|
|
7
|
+
version "pickynode-bchd v#{PickynodeBCHD::VERSION}"
|
|
8
|
+
opt :info, 'Local node info'
|
|
9
|
+
opt :debug, 'Debug mode'
|
|
10
|
+
opt :output, 'Output commands'
|
|
11
|
+
opt :add, 'Add node type', type: :string
|
|
12
|
+
opt :remove, 'Remove node type', type: :string
|
|
13
|
+
opt :connect, 'Connect to node type', type: :string
|
|
14
|
+
opt :disconnect, 'Disconnect from node type', type: :string
|
|
15
|
+
opt :limit, 'Limit number of nodes to add/connect', type: :integer
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Trollop.die :limit, 'must be positive' if opts[:limit] && opts[:limit] <= 0
|
|
19
|
+
PickynodeBCHD.new(opts).run
|
|
@@ -0,0 +1,144 @@
|
|
|
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/remove/connect/disconnect nodes
|
|
10
|
+
# based on User Agent.
|
|
11
|
+
class PickynodeBCHD
|
|
12
|
+
VERSION = '0.1.0'
|
|
13
|
+
|
|
14
|
+
def initialize(opts = {})
|
|
15
|
+
@opts = opts
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add(filter, limit = nil)
|
|
19
|
+
return unless filter
|
|
20
|
+
|
|
21
|
+
validate_limit(limit)
|
|
22
|
+
|
|
23
|
+
blockchair_addr_types
|
|
24
|
+
.select { |_, v| v.include?(filter) }
|
|
25
|
+
.each_with_index do |(k, _), i|
|
|
26
|
+
break if limit == i
|
|
27
|
+
run_cmd(%(bchctl addnode "#{k}" add))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def remove(filter, limit = nil)
|
|
32
|
+
return unless filter
|
|
33
|
+
|
|
34
|
+
validate_limit(limit)
|
|
35
|
+
|
|
36
|
+
addr_types
|
|
37
|
+
.select { |_, v| v.include?(filter) }
|
|
38
|
+
.each_with_index do |(k, _), i|
|
|
39
|
+
break if limit == i
|
|
40
|
+
run_cmd(%(bchctl node remove "#{k}"))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def connect(filter, limit = nil)
|
|
45
|
+
return unless filter
|
|
46
|
+
|
|
47
|
+
validate_limit(limit)
|
|
48
|
+
|
|
49
|
+
blockchair_addr_types
|
|
50
|
+
.select { |_, v| v.include?(filter) }
|
|
51
|
+
.each_with_index do |(k, _), i|
|
|
52
|
+
break if limit == i
|
|
53
|
+
run_cmd(%(bchctl node connect "#{k}"))
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def disconnect(filter, limit = nil)
|
|
58
|
+
return unless filter
|
|
59
|
+
|
|
60
|
+
validate_limit(limit)
|
|
61
|
+
|
|
62
|
+
addr_types
|
|
63
|
+
.select { |_, v| v.include?(filter) }
|
|
64
|
+
.each_with_index do |(k, _), i|
|
|
65
|
+
break if limit == i
|
|
66
|
+
run_cmd(%(bchctl node disconnect "#{k}"))
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def display
|
|
71
|
+
ap addr_types
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def info
|
|
75
|
+
ap getinfo
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def run
|
|
79
|
+
add(@opts[:add], @opts[:limit])
|
|
80
|
+
connect(@opts[:connect], @opts[:limit])
|
|
81
|
+
|
|
82
|
+
remove(@opts[:remove], @opts[:limit])
|
|
83
|
+
disconnect(@opts[:disconnect], @opts[:limit])
|
|
84
|
+
|
|
85
|
+
display_info
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def clear_cache
|
|
89
|
+
@addr_types = nil
|
|
90
|
+
@blockchair_addr_types = nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def display_info
|
|
96
|
+
info if @opts[:info]
|
|
97
|
+
display if @opts.values.select { |v| v }.empty?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def run_cmd(cmd)
|
|
101
|
+
puts "Running #{cmd}" if @opts[:output] || @opts[:debug]
|
|
102
|
+
`#{cmd}` unless @opts[:debug]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def addr_types
|
|
106
|
+
return @addr_types if @addr_types
|
|
107
|
+
nodes = getpeerinfo
|
|
108
|
+
parsed_nodes = JSON.parse(nodes)
|
|
109
|
+
@addr_types = parsed_nodes.map do |n|
|
|
110
|
+
[n['addr'], n['subver']]
|
|
111
|
+
end.to_h
|
|
112
|
+
rescue JSON::ParserError
|
|
113
|
+
{}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def blockchair_addr_types
|
|
117
|
+
return @blockchair_addr_types if @blockchair_addr_types
|
|
118
|
+
parsed_nodelist = JSON.parse(blockchair_snapshot)
|
|
119
|
+
@blockchair_addr_types = parsed_nodelist['data']['nodes'].map do |k, v|
|
|
120
|
+
[k, v['version']]
|
|
121
|
+
end.to_h
|
|
122
|
+
rescue JSON::ParserError
|
|
123
|
+
{}
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def blockchair_snapshot
|
|
127
|
+
Net::HTTP.get(URI.parse('https://api.blockchair.com/bitcoin-cash/nodes'))
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def getinfo
|
|
131
|
+
JSON.parse(`bchctl getinfo`)
|
|
132
|
+
rescue JSON::ParserError
|
|
133
|
+
{}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def getpeerinfo
|
|
137
|
+
`bchctl getpeerinfo`
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def validate_limit(limit)
|
|
141
|
+
return unless limit
|
|
142
|
+
raise 'Limit must be greater than 0' unless limit > 0
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.expand_path(File.join('..', 'lib', 'pickynode_bchd'), __FILE__)
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = 'pickynode-bchd'
|
|
7
|
+
s.version = PickynodeBCHD::VERSION
|
|
8
|
+
s.summary = 'Manage connections to your bchd node'
|
|
9
|
+
s.description = "Some people are picky about the \
|
|
10
|
+
bitcoin cash nodes they connect to with bchd."
|
|
11
|
+
s.authors = ['Josh Ellithorpe']
|
|
12
|
+
s.email = 'quest@mac.com'
|
|
13
|
+
s.homepage = 'http://github.com/zquestz/pickynode-bchd'
|
|
14
|
+
s.license = 'MIT'
|
|
15
|
+
s.executables << 'pickynode-bchd'
|
|
16
|
+
s.files = `git ls-files`.split("\n")
|
|
17
|
+
s.require_paths = ['lib']
|
|
18
|
+
s.required_ruby_version = '>= 2.0'
|
|
19
|
+
|
|
20
|
+
s.add_dependency 'awesome_print', '~> 1.7'
|
|
21
|
+
s.add_dependency 'trollop', '~> 2.1'
|
|
22
|
+
|
|
23
|
+
s.add_development_dependency 'rspec', '~> 3.6'
|
|
24
|
+
s.add_development_dependency 'rake', '~> 12.0'
|
|
25
|
+
s.add_development_dependency 'rubocop', '~> 0.48'
|
|
26
|
+
s.add_development_dependency 'simplecov', '~> 0.14'
|
|
27
|
+
end
|
data/spec/mocks.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
BLOCKCHAIR_SNAPSHOT = <<-HEREDOC
|
|
4
|
+
{
|
|
5
|
+
"data": {
|
|
6
|
+
"nodes": {
|
|
7
|
+
"88.99.199.87:8333": {
|
|
8
|
+
"version": "\/BitcoinUnlimited:1.0.2(EB16; AD12)\/",
|
|
9
|
+
"country": "US",
|
|
10
|
+
"height": 555417,
|
|
11
|
+
"flags": 37
|
|
12
|
+
},
|
|
13
|
+
"[2a01:e34:ee3a:5730:21f:5bff:fec5:e356]:8333": {
|
|
14
|
+
"version": "\/Bitcoin ABC:0.18.2(EB32.0)\/",
|
|
15
|
+
"country": "US",
|
|
16
|
+
"height": 555416,
|
|
17
|
+
"flags": 37
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
HEREDOC
|
|
23
|
+
|
|
24
|
+
NODE_INFO = <<-HEREDOC
|
|
25
|
+
{
|
|
26
|
+
"version": 120000,
|
|
27
|
+
"protocolversion": 70013,
|
|
28
|
+
"blocks": 310554,
|
|
29
|
+
"timeoffset": 0,
|
|
30
|
+
"connections": 41,
|
|
31
|
+
"proxy": "",
|
|
32
|
+
"difficulty": 17336316978.50783,
|
|
33
|
+
"testnet": false,
|
|
34
|
+
"relayfee": 0.00001,
|
|
35
|
+
"errors": ""
|
|
36
|
+
}
|
|
37
|
+
HEREDOC
|
|
38
|
+
|
|
39
|
+
PEER_INFO = <<-HEREDOC
|
|
40
|
+
[
|
|
41
|
+
{
|
|
42
|
+
"id": 10,
|
|
43
|
+
"addr": "131.114.88.218:33422",
|
|
44
|
+
"addrlocal": "67.188.11.253:8333",
|
|
45
|
+
"services": "00000001",
|
|
46
|
+
"servicesStr": "SFNodeNetwork",
|
|
47
|
+
"relaytxes": true,
|
|
48
|
+
"lastsend": 1541490162,
|
|
49
|
+
"lastrecv": 1541490162,
|
|
50
|
+
"bytessent": 1587,
|
|
51
|
+
"bytesrecv": 3258,
|
|
52
|
+
"conntime": 1541486322,
|
|
53
|
+
"timeoffset": 0,
|
|
54
|
+
"pingtime": 125312,
|
|
55
|
+
"version": 70013,
|
|
56
|
+
"subver": "/FirstClient/",
|
|
57
|
+
"inbound": false,
|
|
58
|
+
"startingheight": 555420,
|
|
59
|
+
"currentheight": 555420,
|
|
60
|
+
"banscore": 0,
|
|
61
|
+
"whitelisted": false,
|
|
62
|
+
"feefilter": 18,
|
|
63
|
+
"syncnode": false
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": 12,
|
|
67
|
+
"addr": "[2a01:e34:ee3a:5730:21f:5bff:fec5:e356]:8333",
|
|
68
|
+
"addrlocal": "[2a01:e34:ee3a:5730:21f:5bff:fec5:e356]:8333",
|
|
69
|
+
"services": "00000053",
|
|
70
|
+
"servicesStr": "SFNodeNetwork|SFNodeBloom|SFNodeXthin|SFNodeBitcoinCash",
|
|
71
|
+
"relaytxes": true,
|
|
72
|
+
"lastsend": 1541490073,
|
|
73
|
+
"lastrecv": 1541490140,
|
|
74
|
+
"bytessent": 2163,
|
|
75
|
+
"bytesrecv": 6515,
|
|
76
|
+
"conntime": 1541486352,
|
|
77
|
+
"timeoffset": 0,
|
|
78
|
+
"pingtime": 180911,
|
|
79
|
+
"version": 80003,
|
|
80
|
+
"subver": "/SecondClient/",
|
|
81
|
+
"inbound": false,
|
|
82
|
+
"startingheight": 555420,
|
|
83
|
+
"currentheight": 555420,
|
|
84
|
+
"banscore": 0,
|
|
85
|
+
"whitelisted": false,
|
|
86
|
+
"feefilter": 0,
|
|
87
|
+
"syncnode": false
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
HEREDOC
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'spec_helper'
|
|
4
|
+
require_relative 'mocks'
|
|
5
|
+
|
|
6
|
+
describe PickynodeBCHD 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
|
+
|
|
15
|
+
# The currently connected nodes for the specs.
|
|
16
|
+
let(:node_hash) do
|
|
17
|
+
{ '131.114.88.218:33422' => '/FirstClient/',
|
|
18
|
+
ipv6_ip => '/SecondClient/' }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# String to simulate a json error.
|
|
22
|
+
let(:json_error) { 'An error occurred.' }
|
|
23
|
+
|
|
24
|
+
# Parsed information from getinfo.
|
|
25
|
+
let(:parsed_node_info) { JSON.parse(NODE_INFO) }
|
|
26
|
+
|
|
27
|
+
subject { PickynodeBCHD.new(opts) }
|
|
28
|
+
|
|
29
|
+
describe '.add' do
|
|
30
|
+
it 'should add nodes based on user agent' do
|
|
31
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
32
|
+
.and_return(BLOCKCHAIR_SNAPSHOT)
|
|
33
|
+
expect(subject).to receive(:run_cmd)
|
|
34
|
+
.with(%(bchctl addnode "#{ipv6_ip}" add))
|
|
35
|
+
subject.add('ABC')
|
|
36
|
+
expect(subject).to receive(:run_cmd)
|
|
37
|
+
.with('bchctl addnode "88.99.199.87:8333" add')
|
|
38
|
+
subject.add('Unlimited')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should return if the filter is falsy' do
|
|
42
|
+
expect(subject).to_not receive(:blockchair_snapshot)
|
|
43
|
+
expect(subject).to_not receive(:run_cmd)
|
|
44
|
+
subject.add(false)
|
|
45
|
+
subject.add(nil)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'should recover gracefully if json is malformed' do
|
|
49
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
50
|
+
.and_return(json_error)
|
|
51
|
+
expect(subject).to_not receive(:run_cmd)
|
|
52
|
+
subject.add('Anything')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'should raise an error if the limit is <= 0' do
|
|
56
|
+
expect { subject.add('Anything', 0) }
|
|
57
|
+
.to raise_error('Limit must be greater than 0')
|
|
58
|
+
expect { subject.add('Anything', -5) }
|
|
59
|
+
.to raise_error('Limit must be greater than 0')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context 'with a limit' do
|
|
63
|
+
it 'should respect a limit parameter of 1' do
|
|
64
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
65
|
+
.and_return(BLOCKCHAIR_SNAPSHOT)
|
|
66
|
+
expect(subject).to receive(:run_cmd)
|
|
67
|
+
.with('bchctl addnode "88.99.199.87:8333" add')
|
|
68
|
+
subject.add('i', 1)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'should respect a limit parameter greater than 1' do
|
|
72
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
73
|
+
.and_return(BLOCKCHAIR_SNAPSHOT)
|
|
74
|
+
expect(subject).to receive(:run_cmd)
|
|
75
|
+
.with('bchctl addnode "88.99.199.87:8333" add')
|
|
76
|
+
expect(subject).to receive(:run_cmd)
|
|
77
|
+
.with(%(bchctl addnode "#{ipv6_ip}" add))
|
|
78
|
+
subject.add('i', 2)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe '.remove' do
|
|
84
|
+
it 'should remove nodes based on user agent' do
|
|
85
|
+
expect(subject).to receive(:`).once
|
|
86
|
+
.and_return(PEER_INFO)
|
|
87
|
+
expect(subject).to receive(:run_cmd)
|
|
88
|
+
.with(%(bchctl node remove "#{ipv6_ip}"))
|
|
89
|
+
subject.remove('SecondClient')
|
|
90
|
+
expect(subject).to receive(:run_cmd)
|
|
91
|
+
.with('bchctl node remove "131.114.88.218:33422"')
|
|
92
|
+
subject.remove('FirstClient')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'should return if the filter is falsy' do
|
|
96
|
+
expect(subject).to_not receive(:getpeerinfo)
|
|
97
|
+
expect(subject).to_not receive(:run_cmd)
|
|
98
|
+
subject.remove(false)
|
|
99
|
+
subject.remove(nil)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'should recover gracefully if json is malformed' do
|
|
103
|
+
expect(subject).to receive(:`).once
|
|
104
|
+
.and_return(json_error)
|
|
105
|
+
expect(subject).to_not receive(:run_cmd)
|
|
106
|
+
subject.remove('Anything')
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
describe '.connect' do
|
|
111
|
+
it 'should connect to nodes based on user agent' do
|
|
112
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
113
|
+
.and_return(BLOCKCHAIR_SNAPSHOT)
|
|
114
|
+
expect(subject).to receive(:run_cmd)
|
|
115
|
+
.with('bchctl node connect "88.99.199.87:8333"')
|
|
116
|
+
subject.connect('Unlimited')
|
|
117
|
+
expect(subject).to receive(:run_cmd)
|
|
118
|
+
.with(%(bchctl node connect "#{ipv6_ip}"))
|
|
119
|
+
subject.connect('ABC')
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'should return if the filter is falsy' do
|
|
123
|
+
expect(subject).to_not receive(:blockchair_snapshot)
|
|
124
|
+
expect(subject).to_not receive(:run_cmd)
|
|
125
|
+
subject.connect(false)
|
|
126
|
+
subject.connect(nil)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'should recover gracefully if json is malformed' do
|
|
130
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
131
|
+
.and_return(json_error)
|
|
132
|
+
expect(subject).to_not receive(:run_cmd)
|
|
133
|
+
subject.connect('Anything')
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'should raise an error if the limit is <= 0' do
|
|
137
|
+
expect { subject.connect('Anything', 0) }
|
|
138
|
+
.to raise_error('Limit must be greater than 0')
|
|
139
|
+
expect { subject.connect('Anything', -5) }
|
|
140
|
+
.to raise_error('Limit must be greater than 0')
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
context 'with a limit' do
|
|
144
|
+
it 'should respect a limit parameter of 1' do
|
|
145
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
146
|
+
.and_return(BLOCKCHAIR_SNAPSHOT)
|
|
147
|
+
expect(subject).to receive(:run_cmd)
|
|
148
|
+
.with('bchctl node connect "88.99.199.87:8333"')
|
|
149
|
+
subject.connect('i', 1)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it 'should respect a limit parameter greater than 1' do
|
|
153
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
154
|
+
.and_return(BLOCKCHAIR_SNAPSHOT)
|
|
155
|
+
expect(subject).to receive(:run_cmd)
|
|
156
|
+
.with('bchctl node connect "88.99.199.87:8333"')
|
|
157
|
+
expect(subject).to receive(:run_cmd)
|
|
158
|
+
.with(%(bchctl node connect "#{ipv6_ip}"))
|
|
159
|
+
subject.connect('i', 2)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe '.disconnect' do
|
|
165
|
+
it 'should disconnect nodes based on user agent' do
|
|
166
|
+
expect(subject).to receive(:`).once
|
|
167
|
+
.and_return(PEER_INFO)
|
|
168
|
+
expect(subject).to receive(:run_cmd)
|
|
169
|
+
.with(%(bchctl node disconnect "#{ipv6_ip}"))
|
|
170
|
+
subject.disconnect('SecondClient')
|
|
171
|
+
expect(subject).to receive(:run_cmd)
|
|
172
|
+
.with('bchctl node disconnect "131.114.88.218:33422"')
|
|
173
|
+
subject.disconnect('FirstClient')
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it 'should return if the filter is falsy' do
|
|
177
|
+
expect(subject).to_not receive(:getpeerinfo)
|
|
178
|
+
expect(subject).to_not receive(:run_cmd)
|
|
179
|
+
subject.disconnect(false)
|
|
180
|
+
subject.disconnect(nil)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'should recover gracefully if json is malformed' do
|
|
184
|
+
expect(subject).to receive(:`).once
|
|
185
|
+
.and_return(json_error)
|
|
186
|
+
expect(subject).to_not receive(:run_cmd)
|
|
187
|
+
subject.disconnect('Anything')
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
describe '.display' do
|
|
192
|
+
it 'should display connected nodes' do
|
|
193
|
+
expect(subject).to receive(:`).once
|
|
194
|
+
.and_return(PEER_INFO)
|
|
195
|
+
expect(subject).to receive(:ap).with(node_hash).and_return(node_hash)
|
|
196
|
+
expect(subject.display).to eq(node_hash)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'should recover gracefully if json is malformed' do
|
|
200
|
+
expect(subject).to receive(:`).once
|
|
201
|
+
.and_return(json_error)
|
|
202
|
+
expect(subject).to receive(:ap).with({}).and_return({})
|
|
203
|
+
expect(subject.display).to eq({})
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
describe '.info' do
|
|
208
|
+
it 'should display local node info' do
|
|
209
|
+
expect(subject).to receive(:`).once
|
|
210
|
+
.and_return(NODE_INFO)
|
|
211
|
+
expect(subject).to receive(:ap).with(parsed_node_info)
|
|
212
|
+
.and_return(parsed_node_info)
|
|
213
|
+
expect(subject.info).to eq(parsed_node_info)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
it 'should recover gracefully if json is malformed' do
|
|
217
|
+
expect(subject).to receive(:`).once
|
|
218
|
+
.and_return(json_error)
|
|
219
|
+
expect(subject).to receive(:ap).with({}).and_return({})
|
|
220
|
+
expect(subject.info).to eq({})
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
describe '.run' do
|
|
225
|
+
context 'with opts' do
|
|
226
|
+
let(:opts) do
|
|
227
|
+
{
|
|
228
|
+
debug: true,
|
|
229
|
+
info: true,
|
|
230
|
+
add: 'Wanted',
|
|
231
|
+
connect: 'Now',
|
|
232
|
+
remove: 'Nefarious',
|
|
233
|
+
disconnect: 'Fools',
|
|
234
|
+
limit: 1
|
|
235
|
+
}
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
it 'should call add, connect, remove, disconnect and info' do
|
|
239
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
240
|
+
.and_return(BLOCKCHAIR_SNAPSHOT)
|
|
241
|
+
expect(subject).to receive(:getpeerinfo).once
|
|
242
|
+
.and_return(PEER_INFO)
|
|
243
|
+
expect(subject).to receive(:add).with(opts[:add], opts[:limit])
|
|
244
|
+
.and_call_original
|
|
245
|
+
expect(subject).to receive(:connect).with(opts[:connect], opts[:limit])
|
|
246
|
+
.and_call_original
|
|
247
|
+
expect(subject).to receive(:remove).with(opts[:remove], opts[:limit])
|
|
248
|
+
.and_call_original
|
|
249
|
+
expect(subject).to receive(:disconnect)
|
|
250
|
+
.with(opts[:disconnect], opts[:limit])
|
|
251
|
+
.and_call_original
|
|
252
|
+
expect(subject).to receive(:info)
|
|
253
|
+
expect(subject).to_not receive(:display)
|
|
254
|
+
subject.run
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
it 'should recover gracefully if json is malformed' do
|
|
258
|
+
expect(subject).to receive(:blockchair_snapshot).at_least(1)
|
|
259
|
+
.and_return(json_error)
|
|
260
|
+
expect(subject).to receive(:getpeerinfo).at_least(1)
|
|
261
|
+
.and_return(json_error)
|
|
262
|
+
expect(subject).to receive(:`)
|
|
263
|
+
.and_return(json_error)
|
|
264
|
+
expect(subject).to receive(:info)
|
|
265
|
+
.and_call_original
|
|
266
|
+
expect(subject).to receive(:ap).with({})
|
|
267
|
+
expect(subject).to_not receive(:run_cmd)
|
|
268
|
+
subject.run
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
context 'without opts' do
|
|
273
|
+
let(:opts) do
|
|
274
|
+
{}
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
it 'should call display' do
|
|
278
|
+
expect(subject).to receive(:`).once
|
|
279
|
+
.and_return(PEER_INFO)
|
|
280
|
+
expect(subject).to receive(:ap).with(node_hash).and_return(node_hash)
|
|
281
|
+
expect(subject).to receive(:display).and_call_original
|
|
282
|
+
subject.run
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
it 'should recover gracefully if json is malformed' do
|
|
286
|
+
expect(subject).to receive(:`).once
|
|
287
|
+
.and_return(json_error)
|
|
288
|
+
expect(subject).to_not receive(:run_cmd)
|
|
289
|
+
expect(subject).to_not receive(:info)
|
|
290
|
+
expect(subject).to receive(:ap).with({}).and_return({})
|
|
291
|
+
subject.run
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
describe 'clear_cache' do
|
|
297
|
+
it 'should clear the bitnodes cache' do
|
|
298
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
299
|
+
.and_return(BLOCKCHAIR_SNAPSHOT)
|
|
300
|
+
expect(subject).to receive(:run_cmd)
|
|
301
|
+
.with(%(bchctl addnode "#{ipv6_ip}" add))
|
|
302
|
+
expect(subject).to receive(:run_cmd)
|
|
303
|
+
.with('bchctl addnode "88.99.199.87:8333" add')
|
|
304
|
+
subject.add('ABC')
|
|
305
|
+
subject.add('Unlimited')
|
|
306
|
+
subject.clear_cache
|
|
307
|
+
expect(subject).to receive(:blockchair_snapshot).once
|
|
308
|
+
.and_return(BLOCKCHAIR_SNAPSHOT)
|
|
309
|
+
expect(subject).to receive(:run_cmd)
|
|
310
|
+
.with(%(bchctl addnode "#{ipv6_ip}" add))
|
|
311
|
+
subject.add('ABC')
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
it 'should clear the getpeerinfo cache' do
|
|
315
|
+
expect(subject).to receive(:getpeerinfo).once
|
|
316
|
+
.and_return(PEER_INFO)
|
|
317
|
+
expect(subject).to receive(:run_cmd)
|
|
318
|
+
.with('bchctl node remove "131.114.88.218:33422"')
|
|
319
|
+
expect(subject).to receive(:run_cmd)
|
|
320
|
+
.with(%(bchctl node remove "#{ipv6_ip}"))
|
|
321
|
+
subject.remove('FirstClient')
|
|
322
|
+
subject.remove('SecondClient')
|
|
323
|
+
subject.clear_cache
|
|
324
|
+
expect(subject).to receive(:getpeerinfo).once
|
|
325
|
+
.and_return(PEER_INFO)
|
|
326
|
+
expect(subject).to receive(:run_cmd)
|
|
327
|
+
.with('bchctl node remove "131.114.88.218:33422"')
|
|
328
|
+
subject.remove('FirstClient')
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: pickynode-bchd
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Josh Ellithorpe
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2018-11-06 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: awesome_print
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.7'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.7'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: trollop
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '2.1'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '2.1'
|
|
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
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: simplecov
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0.14'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0.14'
|
|
97
|
+
description: Some people are picky about the bitcoin cash nodes they connect to with
|
|
98
|
+
bchd.
|
|
99
|
+
email: quest@mac.com
|
|
100
|
+
executables:
|
|
101
|
+
- pickynode-bchd
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- ".gitignore"
|
|
106
|
+
- ".rubocop.yml"
|
|
107
|
+
- ".ruby-gemset"
|
|
108
|
+
- ".ruby-version"
|
|
109
|
+
- ".travis.yml"
|
|
110
|
+
- Gemfile
|
|
111
|
+
- Gemfile.lock
|
|
112
|
+
- LICENSE
|
|
113
|
+
- README.md
|
|
114
|
+
- Rakefile
|
|
115
|
+
- bin/pickynode-bchd
|
|
116
|
+
- lib/pickynode_bchd.rb
|
|
117
|
+
- pickynode_bchd.gemspec
|
|
118
|
+
- spec/mocks.rb
|
|
119
|
+
- spec/pickynode_spec.rb
|
|
120
|
+
- spec/rubocop_spec.rb
|
|
121
|
+
- spec/spec_helper.rb
|
|
122
|
+
homepage: http://github.com/zquestz/pickynode-bchd
|
|
123
|
+
licenses:
|
|
124
|
+
- MIT
|
|
125
|
+
metadata: {}
|
|
126
|
+
post_install_message:
|
|
127
|
+
rdoc_options: []
|
|
128
|
+
require_paths:
|
|
129
|
+
- lib
|
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
131
|
+
requirements:
|
|
132
|
+
- - ">="
|
|
133
|
+
- !ruby/object:Gem::Version
|
|
134
|
+
version: '2.0'
|
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - ">="
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: '0'
|
|
140
|
+
requirements: []
|
|
141
|
+
rubyforge_project:
|
|
142
|
+
rubygems_version: 2.6.11
|
|
143
|
+
signing_key:
|
|
144
|
+
specification_version: 4
|
|
145
|
+
summary: Manage connections to your bchd node
|
|
146
|
+
test_files: []
|