nanook 2.1.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +79 -1
- data/README.md +47 -19
- data/lib/nanook.rb +5 -5
- data/lib/nanook/account.rb +176 -118
- data/lib/nanook/block.rb +62 -11
- data/lib/nanook/error.rb +1 -0
- data/lib/nanook/node.rb +261 -3
- data/lib/nanook/rpc.rb +23 -2
- data/lib/nanook/util.rb +21 -2
- data/lib/nanook/version.rb +1 -1
- data/lib/nanook/wallet.rb +228 -86
- data/lib/nanook/wallet_account.rb +47 -40
- metadata +14 -15
data/lib/nanook/block.rb
CHANGED
@@ -45,8 +45,11 @@ class Nanook
|
|
45
45
|
rpc(:work_cancel, :hash).empty?
|
46
46
|
end
|
47
47
|
|
48
|
-
# Returns
|
49
|
-
#
|
48
|
+
# Returns a consecutive list of block hashes in the account chain
|
49
|
+
# starting at block back to count (direction from frontier back to
|
50
|
+
# open block, from newer blocks to older). Will list all blocks back
|
51
|
+
# to the open block of this chain when count is set to "-1".
|
52
|
+
# The requested block hash is included in the answer.
|
50
53
|
#
|
51
54
|
# See also #successors.
|
52
55
|
#
|
@@ -60,20 +63,66 @@ class Nanook
|
|
60
63
|
# "36A0FB717368BA8CF8D255B63DC207771EABC6C6FFC22A7F455EC2209464897E",
|
61
64
|
# "FBF8B0E6623A31AB528EBD839EEAA91CAFD25C12294C46754E45FD017F7939EB"
|
62
65
|
# ]
|
66
|
+
#
|
63
67
|
# @param limit [Integer] maximum number of block hashes to return (default is 1000)
|
64
|
-
|
65
|
-
|
68
|
+
# @param offset [Integer] return the account chain block hashes offset by the specified number of blocks (default is 0)
|
69
|
+
def chain(limit: 1000, offset: 0)
|
70
|
+
response = rpc(:chain, :block, count: limit, offset: offset)[:blocks]
|
66
71
|
Nanook::Util.coerce_empty_string_to_type(response, Array)
|
67
72
|
end
|
73
|
+
alias_method :ancestors, :chain
|
74
|
+
|
75
|
+
# Request confirmation for a block from online representative nodes.
|
76
|
+
# Will return immediately with a boolean to indicate if the request for
|
77
|
+
# confirmation was successful. Note that this boolean does not indicate
|
78
|
+
# the confirmation status of the block. If confirmed, your block should
|
79
|
+
# appear in {Nanook::Node#confirmation_history} within a short amount of
|
80
|
+
# time, or you can use the convenience method {Nanook::Block#confirmed_recently?}
|
81
|
+
#
|
82
|
+
# ==== Example:
|
83
|
+
# block.confirm # => true
|
84
|
+
#
|
85
|
+
# @return [Boolean] if the confirmation request was sent successful
|
86
|
+
def confirm
|
87
|
+
rpc(:block_confirm, :hash)[:started] == 1
|
88
|
+
end
|
89
|
+
|
90
|
+
# This call is for internal diagnostics/debug purposes only. Do not
|
91
|
+
# rely on this interface being stable and do not use in a production system.
|
92
|
+
#
|
93
|
+
# Check if the block appears in the list of recently confirmed blocks by
|
94
|
+
# online representatives. The full list of blocks can be queried for with {Nanook::Node#confirmation_history}.
|
95
|
+
#
|
96
|
+
# This method can work in conjunction with {Nanook::Block#confirm},
|
97
|
+
# whereby you can send any block (old or new) out to online representatives to
|
98
|
+
# confirm. The confirmation process can take up to a couple of minutes.
|
99
|
+
#
|
100
|
+
# The method returning +false+ can indicate that the block is still in the process of being
|
101
|
+
# confirmed and that you should call the method again soon, or that it
|
102
|
+
# was confirmed earlier than the list available in {Nanook::Node#confirmation_history},
|
103
|
+
# or that it was not confirmed.
|
104
|
+
#
|
105
|
+
# ==== Example:
|
106
|
+
# block.confirmed_recently? # => true
|
107
|
+
#
|
108
|
+
# @return [Boolean] +true+ if the block has been recently confirmed by
|
109
|
+
# online representatives.
|
110
|
+
def confirmed_recently?
|
111
|
+
@rpc.call(:confirmation_history)[:confirmations].map{|h| h[:hash]}.include?(@block)
|
112
|
+
end
|
113
|
+
alias_method :recently_confirmed?, :confirmed_recently?
|
68
114
|
|
69
115
|
# Generate work for a block.
|
70
116
|
#
|
71
117
|
# ==== Example:
|
72
118
|
# block.generate_work # => "2bf29ef00786a6bc"
|
73
119
|
#
|
120
|
+
# @param use_peers [Boolean] if set to +true+, then the node will query
|
121
|
+
# its work peers (if it has any, see {Nanook::WorkPeer#list}).
|
122
|
+
# When +false+, the node will only generate work locally (default is +false+)
|
74
123
|
# @return [String] the work id of the work completed.
|
75
|
-
def generate_work
|
76
|
-
rpc(:work_generate, :hash)[:work]
|
124
|
+
def generate_work(use_peers: false)
|
125
|
+
rpc(:work_generate, :hash, use_peers: use_peers)[:work]
|
77
126
|
end
|
78
127
|
|
79
128
|
# Returns Array of Hashes containing information about a chain of
|
@@ -87,7 +136,7 @@ class Nanook
|
|
87
136
|
#
|
88
137
|
# [
|
89
138
|
# {
|
90
|
-
# :account=>"
|
139
|
+
# :account=>"nano_3x7cjioqahgs5ppheys6prpqtb4rdknked83chf97bot1unrbdkaux37t31b",
|
91
140
|
# :amount=>539834279601145558517940224,
|
92
141
|
# :hash=>"36A0FB717368BA8CF8D255B63DC207771EABC6C6FFC22A7F455EC2209464897E",
|
93
142
|
# :type=>"send"
|
@@ -124,7 +173,7 @@ class Nanook
|
|
124
173
|
# :id=>"36A0FB717368BA8CF8D255B63DC207771EABC6C6FFC22A7F455EC2209464897E",
|
125
174
|
# :type=>"send",
|
126
175
|
# :previous=>"FBF8B0E6623A31AB528EBD839EEAA91CAFD25C12294C46754E45FD017F7939EB",
|
127
|
-
# :destination=>"
|
176
|
+
# :destination=>"nano_3x7cjioqahgs5ppheys6prpqtb4rdknked83chf97bot1unrbdkaux37t31b",
|
128
177
|
# :balance=>"00000000000000000000000000000000",
|
129
178
|
# :work=>"44cc24b60705083a",
|
130
179
|
# :signature=>"42ADFEFE7C3FFF188AE92A202F8A5734DE91779C454613E446EEC93D001D6C953E9FD16730AF32C891791BA8EDAECEB059A213E2FE1EEB7ADF9D5D0815464D06"
|
@@ -220,10 +269,12 @@ class Nanook
|
|
220
269
|
# ["36A0FB717368BA8CF8D255B63DC207771EABC6C6FFC22A7F455EC2209464897E"]
|
221
270
|
#
|
222
271
|
# @param limit [Integer] maximum number of send/receive block hashes
|
223
|
-
#
|
272
|
+
# to return in the chain (default is 1000)
|
273
|
+
# @param offset [Integer] return the account chain block hashes offset
|
274
|
+
# by the specified number of blocks (default is 0)
|
224
275
|
# @return [Array<String>] block hashes in the account chain ending at this block
|
225
|
-
def successors(limit: 1000)
|
226
|
-
response = rpc(:successors, :block, count: limit)[:blocks]
|
276
|
+
def successors(limit: 1000, offset: 0)
|
277
|
+
response = rpc(:successors, :block, count: limit, offset: offset)[:blocks]
|
227
278
|
Nanook::Util.coerce_empty_string_to_type(response, Array)
|
228
279
|
end
|
229
280
|
|
data/lib/nanook/error.rb
CHANGED
data/lib/nanook/node.rb
CHANGED
@@ -1,31 +1,220 @@
|
|
1
1
|
class Nanook
|
2
|
+
|
3
|
+
# The <tt>Nanook::Node</tt> class contains methods to manage your nano
|
4
|
+
# node and query its data of the nano network.
|
5
|
+
#
|
6
|
+
# Your node is constantly syncing data with other nodes on the network. When
|
7
|
+
# your node first starts up after being built, its database will be empty
|
8
|
+
# and it will begin synchronizing and downloading data of the nano ledger
|
9
|
+
# to its local database. The ledger is the central record of all accounts
|
10
|
+
# and transactions. Some of the methods in this class query your node's
|
11
|
+
# database formed from the nano ledger, and so the responses are determined
|
12
|
+
# by the completeness of your node's database.
|
13
|
+
#
|
14
|
+
# You can determine how synchronized your node is with the nano ledger
|
15
|
+
# with the {#sync_progress} method.
|
16
|
+
#
|
17
|
+
# === Initializing
|
18
|
+
#
|
19
|
+
# Initialize this class through the convenient {Nanook#node} method:
|
20
|
+
#
|
21
|
+
# node = Nanook.new.node
|
22
|
+
#
|
23
|
+
# Or compose the longhand way like this:
|
24
|
+
#
|
25
|
+
# rpc_conn = Nanook::Rpc.new
|
26
|
+
# node = Nanook::Node.new(rpc_conn)
|
2
27
|
class Node
|
3
28
|
|
4
29
|
def initialize(rpc)
|
5
30
|
@rpc = rpc
|
6
31
|
end
|
7
32
|
|
33
|
+
# The number of accounts in the nano ledger--essentially all
|
34
|
+
# accounts with _open_ blocks. An _open_ block
|
35
|
+
# is the type of block written to the nano ledger when an account
|
36
|
+
# receives its first payment (see {Nanook::WalletAccount#receive}). All accounts
|
37
|
+
# that respond +true+ to {Nanook::Account#exists?} have open blocks in the ledger.
|
38
|
+
#
|
39
|
+
# @return [Integer] number of accounts with _open_ blocks.
|
8
40
|
def account_count
|
9
41
|
rpc(:frontier_count)[:count]
|
10
42
|
end
|
11
43
|
alias_method :frontier_count, :account_count
|
12
44
|
|
45
|
+
# The count of all blocks downloaded to the node, and
|
46
|
+
# blocks still to be synchronized by the node.
|
47
|
+
#
|
48
|
+
# ==== Example:
|
49
|
+
#
|
50
|
+
#
|
51
|
+
#
|
52
|
+
# @return [Hash{Symbol=>Integer}] number of blocks and unchecked
|
53
|
+
# synchronizing blocks
|
13
54
|
def block_count
|
14
55
|
rpc(:block_count)
|
15
56
|
end
|
16
57
|
|
17
|
-
|
58
|
+
# The count of all known blocks by their type.
|
59
|
+
#
|
60
|
+
# ==== Example:
|
61
|
+
#
|
62
|
+
# node.block_count_by_type
|
63
|
+
#
|
64
|
+
# Example response:
|
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
|
18
75
|
rpc(:block_count_type)
|
19
76
|
end
|
77
|
+
alias_method :block_count_type, :block_count_by_type
|
20
78
|
|
79
|
+
# Initialize bootstrap to a specific IP address and port.
|
80
|
+
#
|
81
|
+
# @return [Boolean] indicating if the action was successful
|
21
82
|
def bootstrap(address:, port:)
|
22
83
|
rpc(:bootstrap, address: address, port: port).has_key?(:success)
|
23
84
|
end
|
24
85
|
|
86
|
+
# Initialize multi-connection bootstrap to random peers
|
87
|
+
#
|
88
|
+
# @return [Boolean] indicating if the action was successful
|
25
89
|
def bootstrap_any
|
26
90
|
rpc(:bootstrap_any).has_key?(:success)
|
27
91
|
end
|
28
92
|
|
93
|
+
# Initialize lazy bootstrap with given block hash
|
94
|
+
#
|
95
|
+
# @param hash [String]
|
96
|
+
# @param force [Boolean] False by default. Manually force closing
|
97
|
+
# of all current bootstraps
|
98
|
+
# @return [Boolean] indicating if the action was successful
|
99
|
+
def bootstrap_lazy(hash, force: false)
|
100
|
+
rpc(:bootstrap_lazy, hash: hash, force: force)[:started] == 1
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returning status of current bootstrap attempt for debug purposes only.
|
104
|
+
# This call is for internal diagnostics/debug purposes only.
|
105
|
+
# Do not rely on this interface being stable and do not use in a
|
106
|
+
# production system.
|
107
|
+
#
|
108
|
+
# ==== Example:
|
109
|
+
#
|
110
|
+
# node.bootstrap_status
|
111
|
+
#
|
112
|
+
# Example response:
|
113
|
+
#
|
114
|
+
# {
|
115
|
+
# clients: 5790,
|
116
|
+
# pulls: 141065,
|
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"
|
130
|
+
# }
|
131
|
+
#
|
132
|
+
# @return [Hash{Symbol=>String|Integer|Boolean}]
|
133
|
+
def bootstrap_status
|
134
|
+
rpc(:bootstrap_status)
|
135
|
+
end
|
136
|
+
|
137
|
+
# This call is for internal diagnostics/debug purposes only.
|
138
|
+
# Do not rely on this interface being stable and do not use in a
|
139
|
+
# production system.
|
140
|
+
#
|
141
|
+
# Returns block and tally weight (in raw) election duration (in
|
142
|
+
# milliseconds), election confirmation timestamp for recent elections
|
143
|
+
# winners.
|
144
|
+
#
|
145
|
+
# ==== Example:
|
146
|
+
#
|
147
|
+
# node.confirmation_history
|
148
|
+
#
|
149
|
+
# Example response:
|
150
|
+
#
|
151
|
+
# [
|
152
|
+
# {
|
153
|
+
# block: "EA70B32C55C193345D625F766EEA2FCA52D3F2CCE0B3A30838CC543026BB0FEA",
|
154
|
+
# tally: 80394786589602980996311817874549318248,
|
155
|
+
# duration: 4000,
|
156
|
+
# time: 1544819986,
|
157
|
+
# },
|
158
|
+
# {
|
159
|
+
# block: "F2F8DA6D2CA0A4D78EB043A7A29E12BDE5B4CE7DE1B99A93A5210428EE5B8667",
|
160
|
+
# tally: 68921714529890443063672782079965877749,
|
161
|
+
# duration: 6000,
|
162
|
+
# time: 1544819988,
|
163
|
+
# }
|
164
|
+
# ]
|
165
|
+
#
|
166
|
+
# @return [Hash{Symbol=>String|Integer}]
|
167
|
+
def confirmation_history
|
168
|
+
rpc(:confirmation_history)[:confirmations].map do |history|
|
169
|
+
# Rename hash key to block
|
170
|
+
block = history.delete(:hash)
|
171
|
+
{block: block}.merge(history)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns the difficulty values (16 hexadecimal digits string, 64 bit)
|
176
|
+
# for the minimum required on the network (network_minimum) as well
|
177
|
+
# as the current active difficulty seen on the network (network_current,
|
178
|
+
# 5 minute trended average of adjusted difficulty seen on confirmed
|
179
|
+
# transactions) which can be used to perform rework for better
|
180
|
+
# prioritization of transaction processing. A multiplier of the
|
181
|
+
# network_current from the base difficulty of network_minimum is also
|
182
|
+
# provided for comparison.
|
183
|
+
#
|
184
|
+
# ==== Example:
|
185
|
+
#
|
186
|
+
# node.difficulty(include_trend: true)
|
187
|
+
#
|
188
|
+
# Example response:
|
189
|
+
#
|
190
|
+
# {
|
191
|
+
# network_minimum: "ffffffc000000000",
|
192
|
+
# network_current: "ffffffc1816766f2",
|
193
|
+
# multiplier: 1.024089858417128,
|
194
|
+
# difficulty_trend: [
|
195
|
+
# 1.156096135149775,
|
196
|
+
# 1.190133894573061,
|
197
|
+
# 1.135567138563921,
|
198
|
+
# 1.000000000000000,
|
199
|
+
# ]
|
200
|
+
# }
|
201
|
+
#
|
202
|
+
# @param include_trend [Boolean] false by default. Also returns the
|
203
|
+
# trend of difficulty seen on the network as a list of multipliers.
|
204
|
+
# Sampling occurs every 16 to 36 seconds. The list is ordered such
|
205
|
+
# that the first value is the most recent sample.
|
206
|
+
# @return [Hash{Symbol=>String|Float|Array}]
|
207
|
+
def difficulty(include_trend: false)
|
208
|
+
rpc(:active_difficulty, include_trend: include_trend).tap do |response|
|
209
|
+
response[:multiplier] = response[:multiplier].to_f
|
210
|
+
|
211
|
+
if response.key?(:difficulty_trend)
|
212
|
+
response[:difficulty_trend].map!(&:to_f)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# @return [String]
|
29
218
|
def inspect
|
30
219
|
"#{self.class.name}(object_id: \"#{"0x00%x" % (object_id << 1)}\")"
|
31
220
|
end
|
@@ -34,14 +223,60 @@ class Nanook
|
|
34
223
|
rpc(:peers)[:peers]
|
35
224
|
end
|
36
225
|
|
37
|
-
|
38
|
-
|
226
|
+
# All representatives and their voting weight.
|
227
|
+
#
|
228
|
+
# ==== Example:
|
229
|
+
#
|
230
|
+
# node.representatives
|
231
|
+
#
|
232
|
+
# Example response:
|
233
|
+
#
|
234
|
+
# {
|
235
|
+
# nano_1111111111111111111111111111111111111111111111111117353trpda: 3822372327060170000000000000000000000,
|
236
|
+
# nano_1111111111111111111111111111111111111111111111111awsq94gtecn: 30999999999999999999999999000000,
|
237
|
+
# nano_114nk4rwjctu6n6tr6g6ps61g1w3hdpjxfas4xj1tq6i8jyomc5d858xr1xi: 0
|
238
|
+
# }
|
239
|
+
#
|
240
|
+
# @return [Hash{Symbol=>Integer}] known representatives and their voting weight
|
241
|
+
def representatives(unit: Nanook.default_unit)
|
242
|
+
unless Nanook::UNITS.include?(unit)
|
243
|
+
raise ArgumentError.new("Unsupported unit: #{unit}")
|
244
|
+
end
|
245
|
+
|
246
|
+
response = rpc(:representatives)[:representatives]
|
247
|
+
return response if unit == :raw
|
248
|
+
|
249
|
+
r = response.map do |account_id, balance|
|
250
|
+
balance = Nanook::Util.raw_to_NANO(balance)
|
251
|
+
|
252
|
+
[account_id, balance]
|
253
|
+
end
|
254
|
+
|
255
|
+
Hash[r].to_symbolized_hash
|
256
|
+
end
|
257
|
+
|
258
|
+
# All online representatives that have voted recently. Note, due to the
|
259
|
+
# design of the nano RPC, this method cannot return the voting weight
|
260
|
+
# of the representatives.
|
261
|
+
#
|
262
|
+
# ==== Example:
|
263
|
+
#
|
264
|
+
# node.representatives_online # => ["nano_111...", "nano_222"]
|
265
|
+
#
|
266
|
+
# @return [Array<String>] array of representative account ids
|
267
|
+
def representatives_online
|
268
|
+
rpc(:representatives_online)[:representatives].keys.map(&:to_s)
|
39
269
|
end
|
40
270
|
|
271
|
+
# Safely shuts down the node.
|
272
|
+
#
|
273
|
+
# @return [Boolean] indicating if action was successful
|
41
274
|
def stop
|
42
275
|
rpc(:stop).has_key?(:success)
|
43
276
|
end
|
44
277
|
|
278
|
+
# @param limit [Integer] number of synchronizing blocks to return
|
279
|
+
# @return [Hash{Symbol=>String}] information about the synchronizing blocks for this node
|
45
280
|
def synchronizing_blocks(limit: 1000)
|
46
281
|
response = rpc(:unchecked, count: limit)[:blocks]
|
47
282
|
response = response.map do |block, info|
|
@@ -49,7 +284,16 @@ class Nanook
|
|
49
284
|
end
|
50
285
|
Hash[response.sort].to_symbolized_hash
|
51
286
|
end
|
287
|
+
alias_method :unchecked, :synchronizing_blocks
|
52
288
|
|
289
|
+
# The percentage completeness of the synchronization process for
|
290
|
+
# your node as it downloads the nano ledger. Note, it's normal for
|
291
|
+
# your progress to not ever reach 100. The closer to 100, the more
|
292
|
+
# complete your node's data is, and so the query methods in this class
|
293
|
+
# become more reliable.
|
294
|
+
#
|
295
|
+
# @return [Float] the percentage completeness of the synchronization
|
296
|
+
# process for your node
|
53
297
|
def sync_progress
|
54
298
|
response = rpc(:block_count)
|
55
299
|
|
@@ -60,13 +304,27 @@ class Nanook
|
|
60
304
|
count.to_f * 100 / total.to_f
|
61
305
|
end
|
62
306
|
|
307
|
+
# Returns node uptime in seconds
|
308
|
+
#
|
309
|
+
# @return [Integer] seconds of uptime
|
310
|
+
def uptime
|
311
|
+
rpc(:uptime)['seconds']
|
312
|
+
end
|
313
|
+
|
314
|
+
# This method is deprecated and will be removed in 3.0, as a node never
|
315
|
+
# reaches 100% synchronization.
|
316
|
+
#
|
317
|
+
# @return [Boolean] signalling if this node ever reaches 100% synchronized
|
63
318
|
def synced?
|
319
|
+
warn "[DEPRECATION] `synced?` is deprecated and will be removed in 3.0"
|
64
320
|
rpc(:block_count)[:unchecked] == 0
|
65
321
|
end
|
66
322
|
|
323
|
+
# @return [Hash{Symbol=>Integer|String}] version information for this node
|
67
324
|
def version
|
68
325
|
rpc(:version)
|
69
326
|
end
|
327
|
+
alias_method :info, :version
|
70
328
|
|
71
329
|
private
|
72
330
|
|