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/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
|