nanook 2.5.0 → 4.0.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 +4 -4
- data/CHANGELOG.md +137 -0
- data/README.md +156 -99
- data/bin/console +4 -3
- data/lib/nanook.rb +76 -20
- data/lib/nanook/account.rb +271 -170
- data/lib/nanook/block.rb +384 -156
- data/lib/nanook/errors.rb +10 -0
- data/lib/nanook/node.rb +188 -131
- data/lib/nanook/private_key.rb +115 -0
- data/lib/nanook/public_key.rb +55 -0
- data/lib/nanook/rpc.rb +104 -44
- data/lib/nanook/util.rb +72 -19
- data/lib/nanook/version.rb +3 -1
- data/lib/nanook/wallet.rb +355 -164
- data/lib/nanook/wallet_account.rb +152 -92
- data/lib/nanook/work_peer.rb +14 -7
- metadata +30 -29
- data/lib/nanook/error.rb +0 -5
- data/lib/nanook/key.rb +0 -46
data/lib/nanook/node.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'util'
|
2
4
|
|
5
|
+
class Nanook
|
3
6
|
# The <tt>Nanook::Node</tt> class contains methods to manage your nano
|
4
7
|
# node and query its data of the nano network.
|
5
8
|
#
|
@@ -25,6 +28,7 @@ class Nanook
|
|
25
28
|
# rpc_conn = Nanook::Rpc.new
|
26
29
|
# node = Nanook::Node.new(rpc_conn)
|
27
30
|
class Node
|
31
|
+
include Nanook::Util
|
28
32
|
|
29
33
|
def initialize(rpc)
|
30
34
|
@rpc = rpc
|
@@ -38,140 +42,125 @@ class Nanook
|
|
38
42
|
#
|
39
43
|
# @return [Integer] number of accounts with _open_ blocks.
|
40
44
|
def account_count
|
41
|
-
rpc(:frontier_count
|
45
|
+
rpc(:frontier_count, _access: :count)
|
42
46
|
end
|
43
|
-
|
47
|
+
alias frontier_count account_count
|
44
48
|
|
45
49
|
# The count of all blocks downloaded to the node, and
|
46
50
|
# blocks still to be synchronized by the node.
|
47
51
|
#
|
48
52
|
# ==== Example:
|
49
53
|
#
|
50
|
-
#
|
54
|
+
# {
|
55
|
+
# count: 100,
|
56
|
+
# unchecked: 10,
|
57
|
+
# cemented: 25
|
58
|
+
# }
|
51
59
|
#
|
52
60
|
# @return [Hash{Symbol=>Integer}] number of blocks and unchecked
|
53
61
|
# synchronizing blocks
|
54
62
|
def block_count
|
55
|
-
rpc(:block_count)
|
63
|
+
rpc(:block_count, _coerce: Hash)
|
56
64
|
end
|
57
65
|
|
58
|
-
#
|
59
|
-
#
|
60
|
-
# ==== Example:
|
61
|
-
#
|
62
|
-
# node.block_count_by_type
|
66
|
+
# Tells the node to send a keepalive packet to a specific IP address and port.
|
63
67
|
#
|
64
|
-
#
|
65
|
-
|
66
|
-
|
67
|
-
# send: 1000,
|
68
|
-
# receive: 900,
|
69
|
-
# open: 900,
|
70
|
-
# change: 50
|
71
|
-
# }
|
72
|
-
#
|
73
|
-
# @return [Hash{Symbol=>Integer}] number of blocks by type
|
74
|
-
def block_count_by_type
|
75
|
-
rpc(:block_count_type)
|
68
|
+
# @return [Boolean] indicating if the action was successful
|
69
|
+
def keepalive(address:, port:)
|
70
|
+
rpc(:keepalive, address: address, port: port).key?(:started)
|
76
71
|
end
|
77
|
-
alias_method :block_count_type, :block_count_by_type
|
78
72
|
|
79
73
|
# Initialize bootstrap to a specific IP address and port.
|
80
74
|
#
|
81
75
|
# @return [Boolean] indicating if the action was successful
|
82
76
|
def bootstrap(address:, port:)
|
83
|
-
rpc(:bootstrap, address: address, port: port).
|
77
|
+
rpc(:bootstrap, address: address, port: port).key?(:success)
|
84
78
|
end
|
85
79
|
|
86
80
|
# Initialize multi-connection bootstrap to random peers
|
87
81
|
#
|
88
|
-
# @
|
89
|
-
def bootstrap_any
|
90
|
-
rpc(:bootstrap_any).has_key?(:success)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Initialize lazy bootstrap with given block hash
|
94
|
-
#
|
95
|
-
# @param hash [String]
|
96
|
-
# @param force [Boolean] False by default. Manually force closing
|
82
|
+
# @param account [Nanook::Account] False by default. Manually force closing
|
97
83
|
# of all current bootstraps
|
98
84
|
# @return [Boolean] indicating if the action was successful
|
99
|
-
def
|
100
|
-
|
85
|
+
def bootstrap_any(account: nil)
|
86
|
+
params = {
|
87
|
+
account: account
|
88
|
+
}.compact
|
89
|
+
|
90
|
+
rpc(:bootstrap_any, params).key?(:success)
|
101
91
|
end
|
102
92
|
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
# production system.
|
93
|
+
# Initialize lazy bootstrap with given block hash.
|
94
|
+
# Response includes whether new election was started and whether a
|
95
|
+
# new lazy key_inserted was successful.
|
107
96
|
#
|
108
97
|
# ==== Example:
|
109
98
|
#
|
110
|
-
# node.
|
99
|
+
# node.bootstrap_lazy
|
111
100
|
#
|
112
101
|
# Example response:
|
113
102
|
#
|
114
103
|
# {
|
115
|
-
#
|
116
|
-
#
|
117
|
-
# pulling: 3,
|
118
|
-
# connections: 16,
|
119
|
-
# idle: 0,
|
120
|
-
# target_connections: 64,
|
121
|
-
# total_blocks: 536820,
|
122
|
-
# lazy_mode: true,
|
123
|
-
# lazy_blocks: 423388,
|
124
|
-
# lazy_state_unknown: 2,
|
125
|
-
# lazy_balances: 0,
|
126
|
-
# lazy_pulls: 0,
|
127
|
-
# lazy_stopped: 644,
|
128
|
-
# lazy_keys: 449,
|
129
|
-
# lazy_key_1: "A86EB2B479AAF3CD531C8356A1FBE3CB500DFBF5BF292E5E6B8D1048DE199C32"
|
104
|
+
# "started": true,
|
105
|
+
# "key_inserted": false
|
130
106
|
# }
|
131
107
|
#
|
132
|
-
# @
|
133
|
-
|
134
|
-
|
108
|
+
# @param hash [String]
|
109
|
+
# @param force [Boolean] False by default. Manually force closing
|
110
|
+
# of all current bootstraps
|
111
|
+
# @return [Hash{Symbol=>Boolean}] indicating if the action was successful
|
112
|
+
def bootstrap_lazy(hash, force: false)
|
113
|
+
response = rpc(:bootstrap_lazy, hash: hash, force: force)
|
114
|
+
values = response.map { |k, v| [k, v == 1] }
|
115
|
+
|
116
|
+
Hash[values]
|
135
117
|
end
|
136
118
|
|
137
|
-
#
|
138
|
-
# Do not rely on this interface being stable and do not use in a
|
139
|
-
# production system.
|
119
|
+
# Returns information about node elections settings and observed network state:
|
140
120
|
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
#
|
121
|
+
# - `quorum_delta`: delta tally required to rollback block
|
122
|
+
# - `online_weight_quorum_percent`: percentage of online weight for delta
|
123
|
+
# - `online_weight_minimum`: minimum online weight to confirm block
|
124
|
+
# - `online_stake_total`: currently observed online total weight
|
125
|
+
# - `peers_stake_total`: known peers total weight
|
126
|
+
# - `peers_stake_required`: effective stake needed from directly connected peers for quorum
|
144
127
|
#
|
145
128
|
# ==== Example:
|
146
129
|
#
|
147
|
-
# node.
|
130
|
+
# node.confirmation_quorum
|
148
131
|
#
|
149
132
|
# Example response:
|
150
133
|
#
|
151
|
-
#
|
152
|
-
#
|
153
|
-
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
159
|
-
# block: "F2F8DA6D2CA0A4D78EB043A7A29E12BDE5B4CE7DE1B99A93A5210428EE5B8667",
|
160
|
-
# tally: 68921714529890443063672782079965877749,
|
161
|
-
# duration: 6000,
|
162
|
-
# time: 1544819988,
|
163
|
-
# }
|
164
|
-
# ]
|
134
|
+
# {
|
135
|
+
# quorum_delta: 43216377.43025059,
|
136
|
+
# online_weight_quorum_percent: 50,
|
137
|
+
# online_weight_minimum: 60000000.0,
|
138
|
+
# online_stake_total: 86432754.86050119,
|
139
|
+
# peers_stake_total: 84672338.52479072,
|
140
|
+
# peers_stake_required: 60000000.0
|
141
|
+
# }
|
165
142
|
#
|
166
143
|
# @return [Hash{Symbol=>String|Integer}]
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
144
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
145
|
+
def confirmation_quorum(unit: Nanook.default_unit)
|
146
|
+
validate_unit!(unit)
|
147
|
+
|
148
|
+
response = rpc(:confirmation_quorum, _coerce: Hash)
|
149
|
+
|
150
|
+
return response unless unit == :nano
|
151
|
+
|
152
|
+
response[:quorum_delta] = raw_to_NANO(response[:quorum_delta])
|
153
|
+
response[:online_weight_minimum] = raw_to_NANO(response[:online_weight_minimum])
|
154
|
+
response[:online_stake_total] = raw_to_NANO(response[:online_stake_total])
|
155
|
+
response[:peers_stake_total] = raw_to_NANO(response[:peers_stake_total])
|
156
|
+
response[:peers_stake_required] = raw_to_NANO(response[:peers_stake_required])
|
157
|
+
|
158
|
+
response.compact
|
173
159
|
end
|
174
160
|
|
161
|
+
# Note: This RPC call is deprecated as of v22 of the node software.
|
162
|
+
# https://docs.nano.org/releases/release-v22-0/
|
163
|
+
#
|
175
164
|
# Returns the difficulty values (16 hexadecimal digits string, 64 bit)
|
176
165
|
# for the minimum required on the network (network_minimum) as well
|
177
166
|
# as the current active difficulty seen on the network (network_current,
|
@@ -205,22 +194,35 @@ class Nanook
|
|
205
194
|
# that the first value is the most recent sample.
|
206
195
|
# @return [Hash{Symbol=>String|Float|Array}]
|
207
196
|
def difficulty(include_trend: false)
|
208
|
-
rpc(:active_difficulty, include_trend: include_trend).tap do |response|
|
197
|
+
rpc(:active_difficulty, include_trend: include_trend, _coerce: Hash).tap do |response|
|
209
198
|
response[:multiplier] = response[:multiplier].to_f
|
210
199
|
|
211
|
-
if response.key?(:difficulty_trend)
|
212
|
-
response[:difficulty_trend].map!(&:to_f)
|
213
|
-
end
|
200
|
+
response[:difficulty_trend].map!(&:to_f) if response.key?(:difficulty_trend)
|
214
201
|
end
|
215
202
|
end
|
216
203
|
|
217
204
|
# @return [String]
|
218
|
-
def
|
219
|
-
|
205
|
+
def to_s
|
206
|
+
self.class.name
|
220
207
|
end
|
208
|
+
alias inspect to_s
|
221
209
|
|
210
|
+
# Returns peers information.
|
211
|
+
#
|
212
|
+
# Example response:
|
213
|
+
#
|
214
|
+
# {
|
215
|
+
# :"[::ffff:104.131.102.132]:7075" => {
|
216
|
+
# protocol_version: 20,
|
217
|
+
# node_id: "node_1y7j5rdqhg99uyab1145gu3yur1ax35a3b6qr417yt8cd6n86uiw3d4whty3",
|
218
|
+
# type: "udp"
|
219
|
+
# },
|
220
|
+
# :"[::ffff:104.131.114.102]:7075" => { ... }
|
221
|
+
# }
|
222
|
+
#
|
223
|
+
# @return [Hash{Symbol=>Hash{Symbol=>Integer|String}}]
|
222
224
|
def peers
|
223
|
-
rpc(:peers
|
225
|
+
rpc(:peers, peer_details: true, _access: :peers, _coerce: Hash)
|
224
226
|
end
|
225
227
|
|
226
228
|
# All representatives and their voting weight.
|
@@ -232,59 +234,86 @@ class Nanook
|
|
232
234
|
# Example response:
|
233
235
|
#
|
234
236
|
# {
|
235
|
-
#
|
236
|
-
#
|
237
|
-
#
|
237
|
+
# Nanook::Account: 3822372327060170000000000000000000000,
|
238
|
+
# Nanook::Account: 30999999999999999999999999000000,
|
239
|
+
# Nanook::Account: 0
|
238
240
|
# }
|
239
241
|
#
|
240
|
-
# @return [Hash{
|
242
|
+
# @return [Hash{Nanook::Account=>Float|Integer}] known representatives and their voting weight
|
243
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
241
244
|
def representatives(unit: Nanook.default_unit)
|
242
|
-
|
243
|
-
raise ArgumentError.new("Unsupported unit: #{unit}")
|
244
|
-
end
|
245
|
+
validate_unit!(unit)
|
245
246
|
|
246
|
-
response = rpc(:representatives
|
247
|
-
return response if unit == :raw
|
247
|
+
response = rpc(:representatives, _access: :representatives, _coerce: Hash)
|
248
248
|
|
249
|
-
r = response.map do |account_id,
|
250
|
-
|
249
|
+
r = response.map do |account_id, weight|
|
250
|
+
weight = raw_to_NANO(weight) if unit == :nano
|
251
251
|
|
252
|
-
[account_id,
|
252
|
+
[as_account(account_id), weight]
|
253
253
|
end
|
254
254
|
|
255
|
-
Hash[r]
|
255
|
+
Hash[r]
|
256
256
|
end
|
257
257
|
|
258
|
-
# All online representatives that have voted recently
|
259
|
-
# design of the nano RPC, this method cannot return the voting weight
|
260
|
-
# of the representatives.
|
258
|
+
# All online representatives that have voted recently and their weight.
|
261
259
|
#
|
262
260
|
# ==== Example:
|
263
261
|
#
|
264
|
-
# node.representatives_online # => [
|
262
|
+
# node.representatives_online # => [Nanook::Account, ...]
|
265
263
|
#
|
266
|
-
# @return [
|
264
|
+
# @return [Nanook::Account] array of representative accounts
|
267
265
|
def representatives_online
|
268
|
-
rpc(:representatives_online
|
266
|
+
rpc(:representatives_online, _access: :representatives, _coerce: Array).map do |representative|
|
267
|
+
as_account(representative)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Tells the node to look for any account in all available wallets.
|
272
|
+
#
|
273
|
+
# ==== Example:
|
274
|
+
#
|
275
|
+
# node.search_pending #=> true
|
276
|
+
# @return [Boolean] indicates if the action was successful
|
277
|
+
def search_pending
|
278
|
+
rpc(:search_pending_all).key?(:success)
|
269
279
|
end
|
270
280
|
|
271
281
|
# Safely shuts down the node.
|
272
282
|
#
|
273
283
|
# @return [Boolean] indicating if action was successful
|
274
284
|
def stop
|
275
|
-
rpc(:stop).
|
285
|
+
rpc(:stop).key?(:success)
|
276
286
|
end
|
277
287
|
|
278
288
|
# @param limit [Integer] number of synchronizing blocks to return
|
289
|
+
# @param unit (see Nanook::Account#balance)
|
290
|
+
#
|
279
291
|
# @return [Hash{Symbol=>String}] information about the synchronizing blocks for this node
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
292
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
293
|
+
def synchronizing_blocks(limit: 1000, unit: Nanook.default_unit)
|
294
|
+
validate_unit!(unit)
|
295
|
+
|
296
|
+
params = {
|
297
|
+
count: limit,
|
298
|
+
json_block: true,
|
299
|
+
_access: :blocks,
|
300
|
+
_coerce: Hash
|
301
|
+
}
|
302
|
+
|
303
|
+
response = rpc(:unchecked, params).map do |block, info|
|
304
|
+
info[:account] = as_account(info[:account])
|
305
|
+
info[:link_as_account] = as_account(info[:link_as_account])
|
306
|
+
info[:representative] = as_account(info[:representative])
|
307
|
+
info[:previous] = as_block(info[:previous])
|
308
|
+
info[:link] = as_block(info[:link])
|
309
|
+
info[:balance] = raw_to_NANO(info[:balance]) if unit == :nano
|
310
|
+
|
311
|
+
[as_block(block), info]
|
284
312
|
end
|
285
|
-
|
313
|
+
|
314
|
+
Hash[response]
|
286
315
|
end
|
287
|
-
|
316
|
+
alias unchecked synchronizing_blocks
|
288
317
|
|
289
318
|
# The percentage completeness of the synchronization process for
|
290
319
|
# your node as it downloads the nano ledger. Note, it's normal for
|
@@ -295,11 +324,11 @@ class Nanook
|
|
295
324
|
# @return [Float] the percentage completeness of the synchronization
|
296
325
|
# process for your node
|
297
326
|
def sync_progress
|
298
|
-
response = rpc(:block_count)
|
327
|
+
response = rpc(:block_count, _coerce: Hash)
|
299
328
|
|
300
329
|
count = response[:count]
|
301
330
|
unchecked = response[:unchecked]
|
302
|
-
total =
|
331
|
+
total = count + unchecked
|
303
332
|
|
304
333
|
count.to_f * 100 / total.to_f
|
305
334
|
end
|
@@ -308,29 +337,57 @@ class Nanook
|
|
308
337
|
#
|
309
338
|
# @return [Integer] seconds of uptime
|
310
339
|
def uptime
|
311
|
-
rpc(:uptime)
|
340
|
+
rpc(:uptime, _access: :seconds, _coerce: Hash)
|
312
341
|
end
|
313
342
|
|
314
|
-
#
|
315
|
-
#
|
343
|
+
# Sets the receive minimum for wallets on the node. The value is in +Nano+ by default.
|
344
|
+
# To specify an amount in +raw+, pass the argument +unit: :raw+.
|
345
|
+
#
|
346
|
+
# ==== Example:
|
347
|
+
#
|
348
|
+
# account.change_receive_minimum(0.01) # true
|
316
349
|
#
|
317
|
-
# @return [Boolean]
|
318
|
-
|
319
|
-
|
320
|
-
|
350
|
+
# @return [Boolean] true if the action was successful
|
351
|
+
# @param minimum Amount to set as the receive minimum
|
352
|
+
# @param unit optional. Specify +raw+ if you want to set the amount in +raw+. (See Nanook::Account#balance)
|
353
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
354
|
+
def change_receive_minimum(minimum, unit: Nanook.default_unit)
|
355
|
+
validate_unit!(unit)
|
356
|
+
|
357
|
+
minimum = NANO_to_raw(minimum) if unit == :nano
|
358
|
+
|
359
|
+
rpc(:receive_minimum_set, amount: minimum).key?(:success)
|
360
|
+
end
|
361
|
+
|
362
|
+
# Returns receive minimum for wallets on the node.
|
363
|
+
#
|
364
|
+
# ==== Example:
|
365
|
+
#
|
366
|
+
# account.receive_minimum # => 0.01
|
367
|
+
#
|
368
|
+
# @return [Integer|Float] the receive minimum
|
369
|
+
# @param unit (see Nanook::Account#balance)
|
370
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
371
|
+
def receive_minimum(unit: Nanook.default_unit)
|
372
|
+
validate_unit!(unit)
|
373
|
+
|
374
|
+
amount = rpc(:receive_minimum, _access: :amount)
|
375
|
+
|
376
|
+
return amount unless unit == :nano
|
377
|
+
|
378
|
+
raw_to_NANO(amount)
|
321
379
|
end
|
322
380
|
|
323
381
|
# @return [Hash{Symbol=>Integer|String}] version information for this node
|
324
382
|
def version
|
325
|
-
rpc(:version)
|
383
|
+
rpc(:version, _coerce: Hash)
|
326
384
|
end
|
327
|
-
|
385
|
+
alias info version
|
328
386
|
|
329
387
|
private
|
330
388
|
|
331
|
-
def rpc(action, params={})
|
389
|
+
def rpc(action, params = {})
|
332
390
|
@rpc.call(action, params)
|
333
391
|
end
|
334
|
-
|
335
392
|
end
|
336
393
|
end
|