nanook 2.5.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|