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/version.rb
CHANGED
data/lib/nanook/wallet.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'util'
|
2
4
|
|
3
|
-
|
4
|
-
#
|
5
|
+
class Nanook
|
6
|
+
# The <tt>Nanook::Wallet</tt> class lets you manage your nano wallets.
|
7
|
+
# Your node will need the <tt>enable_control</tt> setting enabled.
|
5
8
|
#
|
6
9
|
# === Wallet seeds vs ids
|
7
10
|
#
|
@@ -33,8 +36,7 @@ class Nanook
|
|
33
36
|
# want to restore the wallet anywhere else on the nano network besides
|
34
37
|
# the node you originally created it on. The nano command line interface
|
35
38
|
# (CLI) is the only method for discovering a wallet's seed. See the
|
36
|
-
# {https://
|
37
|
-
# --wallet_decrypt_unsafe CLI command}.
|
39
|
+
# {https://docs.nano.org/commands/command-line-interface/#-wallet_decrypt_unsafe-walletwallet-passwordpassword}.
|
38
40
|
#
|
39
41
|
# === Initializing
|
40
42
|
#
|
@@ -48,10 +50,32 @@ class Nanook
|
|
48
50
|
# rpc_conn = Nanook::Rpc.new
|
49
51
|
# wallet = Nanook::Wallet.new(rpc_conn, wallet_id)
|
50
52
|
class Wallet
|
53
|
+
include Nanook::Util
|
51
54
|
|
52
|
-
def initialize(rpc, wallet)
|
55
|
+
def initialize(rpc, wallet = nil)
|
53
56
|
@rpc = rpc
|
54
|
-
@wallet = wallet
|
57
|
+
@wallet = wallet.to_s if wallet
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [String] the wallet id
|
61
|
+
def id
|
62
|
+
@wallet
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param other [Nanook::Wallet] wallet to compare
|
66
|
+
# @return [Boolean] true if wallets are equal
|
67
|
+
def ==(other)
|
68
|
+
other.class == self.class &&
|
69
|
+
other.id == id
|
70
|
+
end
|
71
|
+
alias eql? ==
|
72
|
+
|
73
|
+
# The hash value is used along with #eql? by the Hash class to determine if two objects
|
74
|
+
# reference the same hash key.
|
75
|
+
#
|
76
|
+
# @return [Integer]
|
77
|
+
def hash
|
78
|
+
id.hash
|
55
79
|
end
|
56
80
|
|
57
81
|
# Returns the given account in the wallet as a {Nanook::WalletAccount} instance
|
@@ -70,14 +94,17 @@ class Nanook
|
|
70
94
|
# wallet.account("nano_...") # => Nanook::WalletAccount
|
71
95
|
# wallet.account.create # => Nanook::WalletAccount
|
72
96
|
#
|
73
|
-
# @param [String]
|
97
|
+
# @param account [String] optional String of an account (starting with
|
74
98
|
# <tt>"xrb..."</tt>) to start working with. Must be an account within
|
75
99
|
# the wallet. When no account is given, the instance returned only
|
76
100
|
# allows you to call +create+ on it, to create a new account.
|
77
|
-
# @raise [ArgumentError] if the wallet does
|
101
|
+
# @raise [ArgumentError] if the wallet does not contain the account
|
78
102
|
# @return [Nanook::WalletAccount]
|
79
|
-
def account(account=nil)
|
80
|
-
|
103
|
+
def account(account = nil)
|
104
|
+
check_wallet_required!
|
105
|
+
|
106
|
+
# We `allow_blank` in order to support `WalletAccount#create`.
|
107
|
+
as_wallet_account(account, allow_blank: true)
|
81
108
|
end
|
82
109
|
|
83
110
|
# Array of {Nanook::WalletAccount} instances of accounts in the wallet.
|
@@ -91,13 +118,33 @@ class Nanook
|
|
91
118
|
#
|
92
119
|
# @return [Array<Nanook::WalletAccount>] all accounts in the wallet
|
93
120
|
def accounts
|
94
|
-
|
95
|
-
|
96
|
-
Nanook::Util.coerce_empty_string_to_type(response, Array).map do |account|
|
97
|
-
Nanook::WalletAccount.new(@rpc, @wallet, account)
|
121
|
+
rpc(:account_list, _access: :accounts, _coerce: Array).map do |account|
|
122
|
+
as_wallet_account(account)
|
98
123
|
end
|
99
124
|
end
|
100
125
|
|
126
|
+
# Move accounts from another {Nanook::Wallet} on the node to this {Nanook::Wallet}.
|
127
|
+
#
|
128
|
+
# ==== Example:
|
129
|
+
#
|
130
|
+
# wallet.move_accounts("0023200...", ["nano_3e3j5...", "nano_5f2a1..."]) # => true
|
131
|
+
#
|
132
|
+
# @return [Boolean] true when the move was successful
|
133
|
+
def move_accounts(wallet, accounts)
|
134
|
+
rpc(:account_move, source: wallet, accounts: accounts, _access: :moved) == 1
|
135
|
+
end
|
136
|
+
|
137
|
+
# Remove an {Nanook::Account} from this {Nanook::Wallet}.
|
138
|
+
#
|
139
|
+
# ==== Example:
|
140
|
+
#
|
141
|
+
# wallet.remove_account("nano_3e3j5...") # => true
|
142
|
+
#
|
143
|
+
# @return [Boolean] true when the remove was successful
|
144
|
+
def remove_account(account)
|
145
|
+
rpc(:account_remove, account: account, _access: :removed) == 1
|
146
|
+
end
|
147
|
+
|
101
148
|
# Balance of all accounts in the wallet, optionally breaking the balances down by account.
|
102
149
|
#
|
103
150
|
# ==== Examples:
|
@@ -138,51 +185,50 @@ class Nanook
|
|
138
185
|
# },
|
139
186
|
# }
|
140
187
|
#
|
141
|
-
# @param [Boolean]
|
188
|
+
# @param account_break_down [Boolean] (default is +false+). When +true+
|
142
189
|
# the response will contain balances per account.
|
143
190
|
# @param unit (see Nanook::Account#balance)
|
144
191
|
#
|
145
192
|
# @return [Hash{Symbol=>Integer|Float|Hash}]
|
193
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
146
194
|
def balance(account_break_down: false, unit: Nanook.default_unit)
|
147
|
-
|
148
|
-
|
149
|
-
unless Nanook::UNITS.include?(unit)
|
150
|
-
raise ArgumentError.new("Unsupported unit: #{unit}")
|
151
|
-
end
|
195
|
+
validate_unit!(unit)
|
152
196
|
|
153
197
|
if account_break_down
|
154
|
-
return
|
198
|
+
return rpc(:wallet_balances, _access: :balances, _coerce: Hash).tap do |r|
|
155
199
|
if unit == :nano
|
156
|
-
r.each do |account,
|
157
|
-
r[account][:balance] =
|
158
|
-
r[account][:pending] =
|
200
|
+
r.each do |account, _balances|
|
201
|
+
r[account][:balance] = raw_to_NANO(r[account][:balance])
|
202
|
+
r[account][:pending] = raw_to_NANO(r[account][:pending])
|
159
203
|
end
|
160
204
|
end
|
161
205
|
end
|
162
206
|
end
|
163
207
|
|
164
|
-
rpc(:
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
208
|
+
response = rpc(:wallet_info, _coerce: Hash).slice(:balance, :pending)
|
209
|
+
return response unless unit == :nano
|
210
|
+
|
211
|
+
{
|
212
|
+
balance: raw_to_NANO(response[:balance]),
|
213
|
+
pending: raw_to_NANO(response[:pending])
|
214
|
+
}
|
170
215
|
end
|
171
216
|
|
172
217
|
# Changes a wallet's seed.
|
173
218
|
#
|
174
219
|
# It's recommended to only change the seed of a wallet that contains
|
175
|
-
# no accounts.
|
220
|
+
# no accounts. This will clear all deterministic accounts in the wallet.
|
221
|
+
# To restore accounts after changing the seed, see Nanook::WalletAccount#create.
|
176
222
|
#
|
177
223
|
# ==== Example:
|
178
224
|
#
|
179
225
|
# wallet.change_seed("000D1BA...") # => true
|
226
|
+
# wallet.account.create(5) # Restores first 5 accounts for wallet with new seed
|
180
227
|
#
|
181
228
|
# @param seed [String] the seed to change to.
|
182
229
|
# @return [Boolean] indicating whether the change was successful.
|
183
230
|
def change_seed(seed)
|
184
|
-
|
185
|
-
rpc(:wallet_change_seed, seed: seed).has_key?(:success)
|
231
|
+
rpc(:wallet_change_seed, seed: seed).key?(:success)
|
186
232
|
end
|
187
233
|
|
188
234
|
# Creates a new wallet.
|
@@ -200,7 +246,8 @@ class Nanook
|
|
200
246
|
#
|
201
247
|
# @return [Nanook::Wallet]
|
202
248
|
def create
|
203
|
-
|
249
|
+
skip_wallet_required!
|
250
|
+
@wallet = rpc(:wallet_create, _access: :wallet)
|
204
251
|
self
|
205
252
|
end
|
206
253
|
|
@@ -212,19 +259,33 @@ class Nanook
|
|
212
259
|
#
|
213
260
|
# @return [Boolean] indicating success of the action
|
214
261
|
def destroy
|
215
|
-
|
216
|
-
rpc(:wallet_destroy)
|
217
|
-
true
|
262
|
+
rpc(:wallet_destroy, _access: :destroyed) == 1
|
218
263
|
end
|
219
264
|
|
220
265
|
# Generates a String containing a JSON representation of your wallet.
|
221
266
|
#
|
222
267
|
# ==== Example:
|
223
268
|
#
|
224
|
-
# wallet.export
|
269
|
+
# wallet.export
|
270
|
+
# # => "{\n \"0000000000000000000000000000000000000000000000000000000000000000\": \"0000000000000000000000000000000000000000000000000000000000000003\",\n \"0000000000000000000000000000000000000000000000000000000000000001\": \"C3A176FC3B90113277BFC91F55128FC9A1F1B6166A73E7446927CFFCA4C2C9D9\",\n \"0000000000000000000000000000000000000000000000000000000000000002\": \"3E58EC805B99C52B4715598BD332C234A1FBF1780577137E18F53B9B7F85F04B\",\n \"0000000000000000000000000000000000000000000000000000000000000003\": \"5FF8021122F3DEE0E4EC4241D35A3F41DEF63CCF6ADA66AF235DE857718498CD\",\n \"0000000000000000000000000000000000000000000000000000000000000004\": \"A30E0A32ED41C8607AA9212843392E853FCBCB4E7CB194E35C94F07F91DE59EF\",\n \"0000000000000000000000000000000000000000000000000000000000000005\": \"E707002E84143AA5F030A6DB8DD0C0480F2FFA75AB1FFD657EC22B5AA8E395D5\",\n \"0000000000000000000000000000000000000000000000000000000000000006\": \"0000000000000000000000000000000000000000000000000000000000000001\",\n \"8646C0423160DEAEAA64034F9C6858F7A5C8A329E73E825A5B16814F6CCAFFE3\": \"0000000000000000000000000000000000000000000000000000000100000000\"\n}\n"
|
271
|
+
#
|
272
|
+
# @return [String]
|
225
273
|
def export
|
226
|
-
|
227
|
-
|
274
|
+
rpc(:wallet_export, _access: :json)
|
275
|
+
end
|
276
|
+
|
277
|
+
# Returns true if wallet exists on the node.
|
278
|
+
#
|
279
|
+
# ==== Example:
|
280
|
+
#
|
281
|
+
# wallet.exists? # => true
|
282
|
+
#
|
283
|
+
# @return [Boolean] true if wallet exists on the node
|
284
|
+
def exists?
|
285
|
+
export
|
286
|
+
true
|
287
|
+
rescue Nanook::NodeRpcError
|
288
|
+
false
|
228
289
|
end
|
229
290
|
|
230
291
|
# Will return +true+ if the account exists in the wallet.
|
@@ -235,26 +296,20 @@ class Nanook
|
|
235
296
|
# @param account [String] id (will start with <tt>"nano_..."</tt>)
|
236
297
|
# @return [Boolean] indicating if the wallet contains the given account
|
237
298
|
def contains?(account)
|
238
|
-
|
239
|
-
response = rpc(:wallet_contains, account: account)
|
240
|
-
!response.empty? && response[:exists] == 1
|
241
|
-
end
|
242
|
-
|
243
|
-
# @return [String] the wallet id
|
244
|
-
def id
|
245
|
-
@wallet
|
299
|
+
rpc(:wallet_contains, account: account, _access: :exists) == 1
|
246
300
|
end
|
247
301
|
|
248
302
|
# @return [String]
|
249
|
-
def
|
250
|
-
"#{self.class.name}(id: \"#{
|
303
|
+
def to_s
|
304
|
+
"#{self.class.name}(id: \"#{short_id}\")"
|
251
305
|
end
|
306
|
+
alias inspect to_s
|
252
307
|
|
253
308
|
# Makes a payment from an account in your wallet to another account
|
254
309
|
# on the nano network.
|
255
310
|
#
|
256
311
|
# Note, there may be a delay in receiving a response due to Proof of
|
257
|
-
# Work being done. From the {Nano RPC}[https://
|
312
|
+
# Work being done. From the {Nano RPC}[https://docs.nano.org/commands/rpc-protocol/#send]:
|
258
313
|
#
|
259
314
|
# <i>Proof of Work is precomputed for one transaction in the
|
260
315
|
# background. If it has been a while since your last transaction it
|
@@ -264,7 +319,8 @@ class Nanook
|
|
264
319
|
# ==== Examples:
|
265
320
|
#
|
266
321
|
# wallet.pay(from: "nano_...", to: "nano_...", amount: 1.1, id: "myUniqueId123") # => "9AE2311..."
|
267
|
-
# wallet.pay(from: "nano_...", to: "nano_...", amount: 54000000000000, unit: :raw, id: "myUniqueId123")
|
322
|
+
# wallet.pay(from: "nano_...", to: "nano_...", amount: 54000000000000, unit: :raw, id: "myUniqueId123")
|
323
|
+
# # => "9AE2311..."
|
268
324
|
#
|
269
325
|
# @param from [String] account id of an account in your wallet
|
270
326
|
# @param to (see Nanook::WalletAccount#pay)
|
@@ -273,8 +329,7 @@ class Nanook
|
|
273
329
|
# @params id (see Nanook::WalletAccount#pay)
|
274
330
|
# @return (see Nanook::WalletAccount#pay)
|
275
331
|
# @raise [Nanook::Error] if unsuccessful
|
276
|
-
def pay(from:, to:, amount:, unit: Nanook.default_unit
|
277
|
-
wallet_required!
|
332
|
+
def pay(from:, to:, amount:, id:, unit: Nanook.default_unit)
|
278
333
|
validate_wallet_contains_account!(from)
|
279
334
|
account(from).pay(to: to, amount: amount, unit: unit, id: id)
|
280
335
|
end
|
@@ -285,6 +340,8 @@ class Nanook
|
|
285
340
|
# See also the {#receive} method of this class for how to receive a pending payment.
|
286
341
|
#
|
287
342
|
# @param limit [Integer] number of accounts with pending payments to return (default is 1000)
|
343
|
+
# @param allow_unconfirmed [Boolean] +false+ by default. When +false+ only returns block which
|
344
|
+
# have their confirmation height set or are undergoing confirmation height processing.
|
288
345
|
# @param detailed [Boolean]return a more complex Hash of pending block information (default is +false+)
|
289
346
|
# @param unit (see Nanook::Account#balance)
|
290
347
|
#
|
@@ -295,12 +352,12 @@ class Nanook
|
|
295
352
|
# Example response:
|
296
353
|
#
|
297
354
|
# {
|
298
|
-
#
|
299
|
-
#
|
300
|
-
# "
|
355
|
+
# Nanook::Account=>[
|
356
|
+
# Nanook::Block,
|
357
|
+
# Nanook::Block"
|
301
358
|
# ],
|
302
|
-
#
|
303
|
-
#
|
359
|
+
# Nanook::Account=>[
|
360
|
+
# Nanook::Block
|
304
361
|
# ]
|
305
362
|
# }
|
306
363
|
#
|
@@ -311,57 +368,70 @@ class Nanook
|
|
311
368
|
# Example response:
|
312
369
|
#
|
313
370
|
# {
|
314
|
-
#
|
371
|
+
# Nanook::Account=>[
|
315
372
|
# {
|
316
373
|
# :amount=>6.0,
|
317
|
-
# :source=>
|
318
|
-
# :block
|
374
|
+
# :source=>Nanook::Account,
|
375
|
+
# :block=>Nanook::Block
|
319
376
|
# },
|
320
377
|
# {
|
321
378
|
# :amount=>12.0,
|
322
|
-
# :source=>
|
323
|
-
# :block
|
379
|
+
# :source=>Nanook::Account,
|
380
|
+
# :block=>Nanook::Block
|
324
381
|
# }
|
325
382
|
# ],
|
326
|
-
#
|
383
|
+
# Nanook::Account=>[
|
327
384
|
# {
|
328
385
|
# :amount=>106.370018,
|
329
|
-
# :source=>
|
330
|
-
# :block
|
386
|
+
# :source=>Nanook::Account,
|
387
|
+
# :block=>Nanook::Block
|
331
388
|
# }
|
332
389
|
# ]
|
333
390
|
# }
|
334
|
-
|
335
|
-
|
391
|
+
#
|
392
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
393
|
+
def pending(limit: 1000, detailed: false, allow_unconfirmed: false, unit: Nanook.default_unit)
|
394
|
+
validate_unit!(unit)
|
336
395
|
|
337
|
-
|
338
|
-
|
339
|
-
|
396
|
+
params = {
|
397
|
+
count: limit,
|
398
|
+
include_only_confirmed: !allow_unconfirmed,
|
399
|
+
_access: :blocks,
|
400
|
+
_coerce: Hash
|
401
|
+
}
|
340
402
|
|
341
|
-
params = { count: limit }
|
342
403
|
params[:source] = true if detailed
|
343
404
|
|
344
|
-
response = rpc(:wallet_pending, params)
|
345
|
-
response = Nanook::Util.coerce_empty_string_to_type(response, Hash)
|
405
|
+
response = rpc(:wallet_pending, params)
|
346
406
|
|
347
|
-
|
407
|
+
unless detailed
|
408
|
+
|
409
|
+
x = response.map do |account, block_ids|
|
410
|
+
blocks = block_ids.map { |block_id| as_block(block_id) }
|
411
|
+
[as_account(account), blocks]
|
412
|
+
end
|
413
|
+
|
414
|
+
return Hash[x]
|
415
|
+
end
|
348
416
|
|
349
417
|
# Map the RPC response, which is:
|
350
418
|
# account=>block=>[amount|source] into
|
351
419
|
# account=>[block|amount|source]
|
352
420
|
x = response.map do |account, data|
|
353
421
|
new_data = data.map do |block, amount_and_source|
|
354
|
-
d =
|
355
|
-
|
356
|
-
|
357
|
-
|
422
|
+
d = {
|
423
|
+
block: as_block(block),
|
424
|
+
source: as_account(amount_and_source[:source]),
|
425
|
+
amount: amount_and_source[:amount]
|
426
|
+
}
|
427
|
+
d[:amount] = raw_to_NANO(d[:amount]) if unit == :nano
|
358
428
|
d
|
359
429
|
end
|
360
430
|
|
361
|
-
[account, new_data]
|
431
|
+
[as_account(account), new_data]
|
362
432
|
end
|
363
433
|
|
364
|
-
Hash[x]
|
434
|
+
Hash[x]
|
365
435
|
end
|
366
436
|
|
367
437
|
# Receives a pending payment into an account in the wallet.
|
@@ -369,7 +439,7 @@ class Nanook
|
|
369
439
|
# When called with no +block+ argument, the latest pending payment
|
370
440
|
# for the account will be received.
|
371
441
|
#
|
372
|
-
# Returns a <i>receive</i> block
|
442
|
+
# Returns a <i>receive</i> block if a receive was successful,
|
373
443
|
# or +false+ if there were no pending payments to receive.
|
374
444
|
#
|
375
445
|
# You can receive a specific pending block if you know it by
|
@@ -377,19 +447,33 @@ class Nanook
|
|
377
447
|
#
|
378
448
|
# ==== Examples:
|
379
449
|
#
|
380
|
-
# wallet.receive(into: "xrb...") # =>
|
381
|
-
# wallet.receive("718CC21...", into: "xrb...") # =>
|
450
|
+
# wallet.receive(into: "xrb...") # => Nanook::Block
|
451
|
+
# wallet.receive("718CC21...", into: "xrb...") # => Nanook::Block
|
382
452
|
#
|
383
453
|
# @param block (see Nanook::WalletAccount#receive)
|
384
454
|
# @param into [String] account id of account in your wallet to receive the
|
385
455
|
# payment into
|
386
456
|
# @return (see Nanook::WalletAccount#receive)
|
387
|
-
def receive(block=nil, into:)
|
388
|
-
wallet_required!
|
457
|
+
def receive(block = nil, into:)
|
389
458
|
validate_wallet_contains_account!(into)
|
390
459
|
account(into).receive(block)
|
391
460
|
end
|
392
461
|
|
462
|
+
# Rebroadcast blocks for accounts from wallet starting at frontier down to count to the network.
|
463
|
+
#
|
464
|
+
# ==== Examples:
|
465
|
+
#
|
466
|
+
# wallet.republish_blocks # => [Nanook::Block, ...]
|
467
|
+
# wallet.republish_blocks(limit: 10) # => [Nanook::Block, ...
|
468
|
+
#
|
469
|
+
# @param limit [Integer] limit of blocks to publish. Default is 1000.
|
470
|
+
# @return [Array<Nanook::Block>] republished blocks
|
471
|
+
def republish_blocks(limit: 1000)
|
472
|
+
rpc(:wallet_republish, count: limit, _access: :blocks, _coerce: Array).map do |block|
|
473
|
+
as_block(block)
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
393
477
|
# The default representative account id for the wallet. This is the
|
394
478
|
# representative that all new accounts created in this wallet will have.
|
395
479
|
#
|
@@ -400,11 +484,12 @@ class Nanook
|
|
400
484
|
#
|
401
485
|
# wallet.default_representative # => "nano_3pc..."
|
402
486
|
#
|
403
|
-
# @return [
|
487
|
+
# @return [Nanook::Account] Representative account. Can be nil.
|
404
488
|
def default_representative
|
405
|
-
rpc(:wallet_representative
|
489
|
+
representative = rpc(:wallet_representative, _access: :representative)
|
490
|
+
as_account(representative)
|
406
491
|
end
|
407
|
-
|
492
|
+
alias representative default_representative
|
408
493
|
|
409
494
|
# Sets the default representative for the wallet. A wallet's default
|
410
495
|
# representative is the representative all new accounts created in
|
@@ -416,23 +501,21 @@ class Nanook
|
|
416
501
|
#
|
417
502
|
# wallet.change_default_representative("nano_...") # => "nano_..."
|
418
503
|
#
|
419
|
-
# @param [String]
|
504
|
+
# @param representative [String] id of the representative account
|
420
505
|
# to set as this account's representative
|
421
|
-
# @return [
|
422
|
-
# @raise [ArgumentError] if the representative account does not exist
|
506
|
+
# @return [Nanook::Account] the representative account
|
423
507
|
# @raise [Nanook::Error] if setting the representative fails
|
424
508
|
def change_default_representative(representative)
|
425
|
-
unless
|
426
|
-
raise
|
509
|
+
unless as_account(representative).exists?
|
510
|
+
raise Nanook::Error, "Representative account does not exist: #{representative}"
|
427
511
|
end
|
428
512
|
|
429
|
-
|
430
|
-
representative
|
431
|
-
|
432
|
-
|
433
|
-
end
|
513
|
+
raise Nanook::Error, 'Setting the representative failed' \
|
514
|
+
unless rpc(:wallet_representative_set, representative: representative, _access: :set) == 1
|
515
|
+
|
516
|
+
as_account(representative)
|
434
517
|
end
|
435
|
-
|
518
|
+
alias change_representative change_default_representative
|
436
519
|
|
437
520
|
# Restores a previously created wallet by its seed.
|
438
521
|
# A new wallet will be created on your node (with a new wallet id)
|
@@ -447,21 +530,68 @@ class Nanook
|
|
447
530
|
#
|
448
531
|
# @return [Nanook::Wallet] a new wallet
|
449
532
|
# @raise [Nanook::Error] if unsuccessful
|
450
|
-
def restore(seed, accounts:0)
|
533
|
+
def restore(seed, accounts: 0)
|
534
|
+
skip_wallet_required!
|
535
|
+
|
451
536
|
create
|
452
537
|
|
453
|
-
unless change_seed(seed)
|
454
|
-
raise Nanook::Error.new("Unable to set seed for wallet")
|
455
|
-
end
|
538
|
+
raise Nanook::Error, 'Unable to set seed for wallet' unless change_seed(seed)
|
456
539
|
|
457
|
-
if accounts
|
458
|
-
account.create(accounts)
|
459
|
-
end
|
540
|
+
account.create(accounts) if accounts.positive?
|
460
541
|
|
461
542
|
self
|
462
543
|
end
|
463
544
|
|
464
|
-
# Information about this wallet
|
545
|
+
# Information ledger information about this wallet's accounts.
|
546
|
+
#
|
547
|
+
# This call may return results that include unconfirmed blocks, so it should not be
|
548
|
+
# used in any processes or integrations requiring only details from blocks confirmed
|
549
|
+
# by the network.
|
550
|
+
#
|
551
|
+
# ==== Examples:
|
552
|
+
#
|
553
|
+
# wallet.ledger
|
554
|
+
#
|
555
|
+
# Example response:
|
556
|
+
#
|
557
|
+
# {
|
558
|
+
# Nanook::Account => {
|
559
|
+
# frontier: "E71AF3E9DD86BBD8B4620EFA63E065B34D358CFC091ACB4E103B965F95783321",
|
560
|
+
# open_block: "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
|
561
|
+
# representative_block: "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
|
562
|
+
# balance: 1.45,
|
563
|
+
# modified_timestamp: 1511476234,
|
564
|
+
# block_count: 2
|
565
|
+
# },
|
566
|
+
# Nanook::Account => { ... }
|
567
|
+
# }
|
568
|
+
#
|
569
|
+
# @param unit (see Nanook::Account#balance)
|
570
|
+
# @return [Hash{Nanook::Account=>Hash{Symbol=>Nanook::Block|Integer|Float|Time}}] ledger.
|
571
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
572
|
+
def ledger(unit: Nanook.default_unit)
|
573
|
+
validate_unit!(unit)
|
574
|
+
|
575
|
+
response = rpc(:wallet_ledger, _access: :accounts, _coerce: Hash)
|
576
|
+
|
577
|
+
accounts = response.map do |account_id, data|
|
578
|
+
data[:frontier] = as_block(data[:frontier])
|
579
|
+
data[:open_block] = as_block(data[:open_block])
|
580
|
+
data[:representative_block] = as_block(data[:representative_block])
|
581
|
+
data[:balance] = raw_to_NANO(data[:balance]) if unit == :nano
|
582
|
+
data[:last_modified_at] = as_time(data.delete(:modified_timestamp))
|
583
|
+
|
584
|
+
[as_account(account_id), data]
|
585
|
+
end
|
586
|
+
|
587
|
+
Hash[accounts]
|
588
|
+
end
|
589
|
+
|
590
|
+
# Information about this wallet.
|
591
|
+
#
|
592
|
+
# This call may return results that include unconfirmed blocks, so it should not be
|
593
|
+
# used in any processes or integrations requiring only details from blocks confirmed
|
594
|
+
# by the network.
|
465
595
|
#
|
466
596
|
# ==== Examples:
|
467
597
|
#
|
@@ -470,42 +600,68 @@ class Nanook
|
|
470
600
|
# Example response:
|
471
601
|
#
|
472
602
|
# {
|
473
|
-
#
|
474
|
-
#
|
475
|
-
#
|
476
|
-
#
|
477
|
-
#
|
478
|
-
#
|
479
|
-
#
|
480
|
-
# balance: 1.45,
|
481
|
-
# modified_timestamp: 1511476234,
|
482
|
-
# block_count: 2
|
483
|
-
# },
|
484
|
-
# { ... }
|
485
|
-
# ]
|
486
|
-
# }
|
603
|
+
# balance: 1.0,
|
604
|
+
# pending: 2.3
|
605
|
+
# accounts_count: 3,
|
606
|
+
# adhoc_count: 1,
|
607
|
+
# deterministic_count: 2,
|
608
|
+
# deterministic_index: 2
|
609
|
+
# }
|
487
610
|
#
|
488
|
-
# @param unit (see #balance)
|
489
|
-
# @return [Hash{Symbol=>
|
490
|
-
#
|
611
|
+
# @param unit (see Nanook::Account#balance)
|
612
|
+
# @return [Hash{Symbol=>Integer|Float}] information about the wallet.
|
613
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
491
614
|
def info(unit: Nanook.default_unit)
|
492
|
-
|
493
|
-
raise ArgumentError.new("Unsupported unit: #{unit}")
|
494
|
-
end
|
615
|
+
validate_unit!(unit)
|
495
616
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
end
|
502
|
-
payload
|
617
|
+
response = rpc(:wallet_info, _coerce: Hash)
|
618
|
+
|
619
|
+
if unit == :nano
|
620
|
+
response[:balance] = raw_to_NANO(response[:balance])
|
621
|
+
response[:pending] = raw_to_NANO(response[:pending])
|
503
622
|
end
|
504
623
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
624
|
+
response
|
625
|
+
end
|
626
|
+
|
627
|
+
# Reports send/receive information for accounts in wallet. Change blocks are skipped,
|
628
|
+
# open blocks will appear as receive. Response will start with most recent blocks
|
629
|
+
# according to local ledger.
|
630
|
+
#
|
631
|
+
# ==== Example:
|
632
|
+
#
|
633
|
+
# wallet.history
|
634
|
+
#
|
635
|
+
# Example response:
|
636
|
+
#
|
637
|
+
# [
|
638
|
+
# {
|
639
|
+
# "type": "send",
|
640
|
+
# "account": Nanook::Account,
|
641
|
+
# "amount": 3.2,
|
642
|
+
# "block_account": Nanook::Account,
|
643
|
+
# "hash": Nanook::Block,
|
644
|
+
# "local_timestamp": Time
|
645
|
+
# },
|
646
|
+
# {
|
647
|
+
# ...
|
648
|
+
# }
|
649
|
+
# ]
|
650
|
+
#
|
651
|
+
# @param unit (see #balance)
|
652
|
+
# @return [Array<Hash{Symbol=>String|Nanook::Account|Nanook::WalletAccount|Nanook::Block|Integer|Float|Time}>]
|
653
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
654
|
+
def history(unit: Nanook.default_unit)
|
655
|
+
validate_unit!(unit)
|
656
|
+
|
657
|
+
rpc(:wallet_history, _access: :history, _coerce: Array).map do |h|
|
658
|
+
h[:account] = account(h[:account])
|
659
|
+
h[:block_account] = as_account(h[:block_account])
|
660
|
+
h[:amount] = raw_to_NANO(h[:amount]) if unit == :nano
|
661
|
+
h[:block] = as_block(h.delete(:hash))
|
662
|
+
h[:local_timestamp] = as_time(h[:local_timestamp])
|
663
|
+
h
|
664
|
+
end
|
509
665
|
end
|
510
666
|
|
511
667
|
# Locks the wallet. A locked wallet cannot pocket pending transactions or make payments. See {#unlock}.
|
@@ -516,9 +672,7 @@ class Nanook
|
|
516
672
|
#
|
517
673
|
# @return [Boolean] indicates if the wallet was successfully locked
|
518
674
|
def lock
|
519
|
-
|
520
|
-
response = rpc(:wallet_lock)
|
521
|
-
!response.empty? && response[:locked] == 1
|
675
|
+
rpc(:wallet_lock, _access: :locked) == 1
|
522
676
|
end
|
523
677
|
|
524
678
|
# Returns +true+ if the wallet is locked.
|
@@ -529,9 +683,7 @@ class Nanook
|
|
529
683
|
#
|
530
684
|
# @return [Boolean] indicates if the wallet is locked
|
531
685
|
def locked?
|
532
|
-
|
533
|
-
response = rpc(:wallet_locked)
|
534
|
-
!response.empty? && response[:locked] != 0
|
686
|
+
rpc(:wallet_locked, _access: :locked) == 1
|
535
687
|
end
|
536
688
|
|
537
689
|
# Unlocks a previously locked wallet.
|
@@ -541,9 +693,8 @@ class Nanook
|
|
541
693
|
# wallet.unlock("new_pass") #=> true
|
542
694
|
#
|
543
695
|
# @return [Boolean] indicates if the unlocking action was successful
|
544
|
-
def unlock(password)
|
545
|
-
|
546
|
-
rpc(:password_enter, password: password)[:valid] == 1
|
696
|
+
def unlock(password = nil)
|
697
|
+
rpc(:password_enter, password: password, _access: :valid) == 1
|
547
698
|
end
|
548
699
|
|
549
700
|
# Changes the password for a wallet.
|
@@ -553,33 +704,73 @@ class Nanook
|
|
553
704
|
# wallet.change_password("new_pass") #=> true
|
554
705
|
# @return [Boolean] indicates if the action was successful
|
555
706
|
def change_password(password)
|
556
|
-
|
557
|
-
|
707
|
+
rpc(:password_change, password: password, _access: :changed) == 1
|
708
|
+
end
|
709
|
+
|
710
|
+
# Tells the node to look for pending blocks for any account in the wallet.
|
711
|
+
#
|
712
|
+
# ==== Example:
|
713
|
+
#
|
714
|
+
# wallet.search_pending #=> true
|
715
|
+
# @return [Boolean] indicates if the action was successful
|
716
|
+
def search_pending
|
717
|
+
rpc(:search_pending, _access: :started) == 1
|
718
|
+
end
|
719
|
+
|
720
|
+
# Returns a list of pairs of {Nanook::WalletAccount} and work for wallet.
|
721
|
+
#
|
722
|
+
# ==== Example:
|
723
|
+
#
|
724
|
+
# wallet.work
|
725
|
+
#
|
726
|
+
# ==== Example response:
|
727
|
+
#
|
728
|
+
# {
|
729
|
+
# Nanook::WalletAccount: "432e5cf728c90f4f",
|
730
|
+
# Nanook::WalletAccount: "4efec5f63fc902cf"
|
731
|
+
# }
|
732
|
+
# @return [Boolean] indicates if the action was successful
|
733
|
+
def work
|
734
|
+
hash = rpc(:wallet_work_get, _access: :works, _coerce: Hash).map do |account_id, work|
|
735
|
+
[as_wallet_account(account_id), work]
|
736
|
+
end
|
737
|
+
|
738
|
+
Hash[hash]
|
558
739
|
end
|
559
740
|
|
560
741
|
private
|
561
742
|
|
562
|
-
def rpc(action, params={})
|
563
|
-
|
564
|
-
|
743
|
+
def rpc(action, params = {})
|
744
|
+
check_wallet_required!
|
745
|
+
|
746
|
+
p = { wallet: @wallet }.compact
|
747
|
+
@rpc.call(action, p.merge(params)).tap { reset_skip_wallet_required! }
|
565
748
|
end
|
566
749
|
|
567
|
-
def
|
568
|
-
|
569
|
-
|
570
|
-
|
750
|
+
def skip_wallet_required!
|
751
|
+
@skip_wallet_required_check = true
|
752
|
+
end
|
753
|
+
|
754
|
+
def reset_skip_wallet_required!
|
755
|
+
@skip_wallet_required_check = false
|
756
|
+
end
|
757
|
+
|
758
|
+
def check_wallet_required!
|
759
|
+
return if @wallet || @skip_wallet_required_check
|
760
|
+
|
761
|
+
raise ArgumentError, 'Wallet must be present'
|
571
762
|
end
|
572
763
|
|
573
764
|
def validate_wallet_contains_account!(account)
|
574
765
|
@known_valid_accounts ||= []
|
575
766
|
return if @known_valid_accounts.include?(account)
|
576
767
|
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
raise ArgumentError.new("Account does not exist in wallet. Account: #{account}, wallet: #{@wallet}")
|
768
|
+
unless contains?(account)
|
769
|
+
raise ArgumentError,
|
770
|
+
"Account does not exist in wallet. Account: #{account}, wallet: #{@wallet}"
|
581
771
|
end
|
582
|
-
end
|
583
772
|
|
773
|
+
@known_valid_accounts << account
|
774
|
+
end
|
584
775
|
end
|
585
776
|
end
|