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