nanook 2.2.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +145 -3
- data/README.md +157 -90
- data/bin/console +4 -3
- data/lib/nanook.rb +77 -21
- data/lib/nanook/account.rb +236 -168
- data/lib/nanook/block.rb +363 -119
- data/lib/nanook/errors.rb +10 -0
- data/lib/nanook/node.rb +229 -66
- 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 +68 -19
- data/lib/nanook/version.rb +3 -1
- data/lib/nanook/wallet.rb +413 -120
- data/lib/nanook/wallet_account.rb +154 -91
- data/lib/nanook/work_peer.rb +14 -7
- metadata +29 -28
- data/lib/nanook/error.rb +0 -5
- data/lib/nanook/key.rb +0 -46
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'util'
|
4
|
+
|
5
|
+
class Nanook
|
6
|
+
# The <tt>Nanook::PublicKey</tt> class lets you manage your node's keys.
|
7
|
+
class PublicKey
|
8
|
+
include Nanook::Util
|
9
|
+
|
10
|
+
def initialize(rpc, key)
|
11
|
+
@rpc = rpc
|
12
|
+
@key = key.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def id
|
16
|
+
@key
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param other [Nanook::PublicKey] public key to compare
|
20
|
+
# @return [Boolean] true if keys are equal
|
21
|
+
def ==(other)
|
22
|
+
other.class == self.class &&
|
23
|
+
other.id == id
|
24
|
+
end
|
25
|
+
alias eql? ==
|
26
|
+
|
27
|
+
# The hash value is used along with #eql? by the Hash class to determine if two objects
|
28
|
+
# reference the same hash key.
|
29
|
+
#
|
30
|
+
# @return [Integer]
|
31
|
+
def hash
|
32
|
+
id.hash
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the account for a public key
|
36
|
+
#
|
37
|
+
# @return [Nanook::Account] account for the public key
|
38
|
+
def account
|
39
|
+
account = rpc(:account_get, _access: :account)
|
40
|
+
as_account(account)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [String]
|
44
|
+
def to_s
|
45
|
+
"#{self.class.name}(id: \"#{short_id}\")"
|
46
|
+
end
|
47
|
+
alias inspect to_s
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def rpc(action, params = {})
|
52
|
+
@rpc.call(action, { key: @key }.merge(params))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/nanook/rpc.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'json'
|
2
4
|
require 'symbolized'
|
3
5
|
|
4
6
|
class Nanook
|
5
|
-
|
6
7
|
# The <tt>Nanook::Rpc</tt> class is responsible for maintaining the
|
7
8
|
# connection to the RPC server, calling the RPC and parsing its response
|
8
9
|
# into Ruby primitives.
|
@@ -14,23 +15,35 @@ class Nanook
|
|
14
15
|
# nanook = Nanook.new
|
15
16
|
# nanook.rpc(:accounts_create, wallet: wallet_id, count: 2)
|
16
17
|
class Rpc
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# Default request timeout in seconds
|
18
|
+
# Default RPC server and port to connect to.
|
19
|
+
DEFAULT_URI = 'http://[::1]:7076'
|
20
|
+
# Default request timeout in seconds.
|
21
21
|
DEFAULT_TIMEOUT = 60
|
22
|
+
# Error expected to be returned when the RPC makes a call that requires the
|
23
|
+
# `enable_control` setting to be enabled when it is disabled.
|
24
|
+
RPC_CONTROL_DISABLED_ERROR = 'RPC control is disabled'
|
22
25
|
|
23
|
-
def initialize(uri=DEFAULT_URI, timeout:DEFAULT_TIMEOUT)
|
26
|
+
def initialize(uri = DEFAULT_URI, timeout: DEFAULT_TIMEOUT)
|
24
27
|
@rpc_server = URI(uri)
|
25
28
|
|
26
|
-
unless [
|
27
|
-
raise ArgumentError
|
29
|
+
unless %w[http https].include?(@rpc_server.scheme)
|
30
|
+
raise ArgumentError, "URI must have http or https in it. Was given: #{uri}"
|
28
31
|
end
|
29
32
|
|
30
|
-
@http = Net::HTTP.new(@rpc_server.
|
33
|
+
@http = Net::HTTP.new(@rpc_server.hostname, @rpc_server.port)
|
31
34
|
@http.read_timeout = timeout
|
32
|
-
@request = Net::HTTP::Post.new(@rpc_server.request_uri, {
|
33
|
-
@request.content_type =
|
35
|
+
@request = Net::HTTP::Post.new(@rpc_server.request_uri, { 'user-agent' => "Ruby nanook gem v#{Nanook::VERSION}" })
|
36
|
+
@request.content_type = 'application/json'
|
37
|
+
end
|
38
|
+
|
39
|
+
# Tests the RPC connection. Returns +true+ if connection is successful,
|
40
|
+
# otherwise raises an exception.
|
41
|
+
#
|
42
|
+
# @raise [Errno::ECONNREFUSED] if connection is unsuccessful
|
43
|
+
# @return [Boolean] true if connection is successful
|
44
|
+
def test
|
45
|
+
call(:telemetry)
|
46
|
+
true
|
34
47
|
end
|
35
48
|
|
36
49
|
# Calls the RPC server and returns the response.
|
@@ -39,57 +52,104 @@ class Nanook
|
|
39
52
|
# expects an "action" param to identify what RPC action is being called.
|
40
53
|
# @param params [Hash] all other params to pass to the RPC
|
41
54
|
# @return [Hash] the response from the RPC
|
42
|
-
def call(action, params={})
|
43
|
-
|
44
|
-
|
55
|
+
def call(action, params = {})
|
56
|
+
coerce_to = params.delete(:_coerce)
|
57
|
+
access_as = params.delete(:_access)
|
45
58
|
|
46
|
-
|
59
|
+
raw_hash = make_call(action, params)
|
47
60
|
|
48
|
-
|
61
|
+
check_for_errors!(raw_hash)
|
49
62
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
63
|
+
hash = parse_values(raw_hash)
|
64
|
+
|
65
|
+
hash = hash[access_as] if access_as
|
66
|
+
hash = coerce_empty_string_to_type(hash, coerce_to) if coerce_to
|
67
|
+
|
68
|
+
hash
|
56
69
|
end
|
57
70
|
|
58
71
|
# @return [String]
|
59
|
-
def
|
60
|
-
"#{self.class.name}(host: \"#{@rpc_server}\", timeout: #{@http.read_timeout}
|
72
|
+
def to_s
|
73
|
+
"#{self.class.name}(host: \"#{@rpc_server}\", timeout: #{@http.read_timeout})"
|
61
74
|
end
|
75
|
+
alias inspect to_s
|
62
76
|
|
63
77
|
private
|
64
78
|
|
79
|
+
def make_call(action, params)
|
80
|
+
# Stringify param values
|
81
|
+
params = params.dup.transform_values do |v|
|
82
|
+
next v if v.is_a?(Array)
|
83
|
+
|
84
|
+
v.to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
@request.body = { action: action }.merge(params).to_json
|
88
|
+
|
89
|
+
response = @http.request(@request)
|
90
|
+
|
91
|
+
raise Nanook::ConnectionError, "Encountered net/http error #{response.code}: #{response.class.name}" \
|
92
|
+
unless response.is_a?(Net::HTTPSuccess)
|
93
|
+
|
94
|
+
JSON.parse(response.body)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raises a {Nanook::NodeRpcConfigurationError} or {Nanook::NodeRpcError} if the RPC
|
98
|
+
# response contains an `:error` key.
|
99
|
+
def check_for_errors!(response)
|
100
|
+
# Raise a special error for when `enable_control` should be enabled.
|
101
|
+
if response['error'] == RPC_CONTROL_DISABLED_ERROR
|
102
|
+
raise Nanook::NodeRpcConfigurationError,
|
103
|
+
'RPC must have the `enable_control` setting enabled to perform this action.'
|
104
|
+
end
|
105
|
+
|
106
|
+
# Raise any other error.
|
107
|
+
raise Nanook::NodeRpcError, "An error was returned from the RPC: #{response['error']}" if response.key?('error')
|
108
|
+
end
|
109
|
+
|
65
110
|
# Recursively parses the RPC response, sending values to #parse_value
|
66
|
-
def
|
67
|
-
new_hash =
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
111
|
+
def parse_values(hash)
|
112
|
+
new_hash = hash.map do |k, val|
|
113
|
+
new_val = case val
|
114
|
+
when Array
|
115
|
+
if val[0].is_a?(Hash)
|
116
|
+
val.map { |v| parse_values(v) }
|
117
|
+
else
|
118
|
+
val.map { |v| parse_value(v) }
|
119
|
+
end
|
120
|
+
when Hash
|
121
|
+
parse_values(val)
|
122
|
+
else
|
123
|
+
parse_value(val)
|
124
|
+
end
|
125
|
+
|
126
|
+
[k, new_val]
|
81
127
|
end
|
82
128
|
|
83
129
|
Hash[new_hash.sort].to_symbolized_hash
|
84
130
|
end
|
85
131
|
|
86
132
|
# Converts Strings to primitives
|
87
|
-
def parse_value(
|
88
|
-
return
|
89
|
-
return true if
|
90
|
-
return false if
|
91
|
-
|
133
|
+
def parse_value(value)
|
134
|
+
return value.to_i if value.match(/^\d+\Z/)
|
135
|
+
return true if value == 'true'
|
136
|
+
return false if value == 'false'
|
137
|
+
|
138
|
+
value
|
92
139
|
end
|
93
140
|
|
141
|
+
# Converts an empty String value into an empty version of another type.
|
142
|
+
#
|
143
|
+
# The RPC often returns an empty String as a value to signal
|
144
|
+
# emptiness, rather than consistent types like an empty Array,
|
145
|
+
# or empty Hash.
|
146
|
+
#
|
147
|
+
# @param response the value returned from the RPC server
|
148
|
+
# @param type the type to return an empty of
|
149
|
+
def coerce_empty_string_to_type(response, type)
|
150
|
+
return type.new if response == '' || response.nil?
|
151
|
+
|
152
|
+
response
|
153
|
+
end
|
94
154
|
end
|
95
155
|
end
|
data/lib/nanook/util.rb
CHANGED
@@ -1,44 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bigdecimal'
|
2
4
|
|
3
5
|
class Nanook
|
4
|
-
|
5
|
-
|
6
|
-
class Util
|
7
|
-
|
6
|
+
# Set of utility methods.
|
7
|
+
module Util
|
8
8
|
# Constant used to convert back and forth between raw and NANO.
|
9
|
-
STEP = BigDecimal
|
9
|
+
STEP = BigDecimal('10')**BigDecimal('30')
|
10
|
+
|
11
|
+
private
|
10
12
|
|
11
13
|
# Converts an amount of NANO to an amount of raw.
|
12
14
|
#
|
13
15
|
# @param nano [Float|Integer] amount in nano
|
14
16
|
# @return [Integer] amount in raw
|
15
|
-
def
|
16
|
-
|
17
|
+
def NANO_to_raw(nano)
|
18
|
+
return if nano.nil?
|
19
|
+
|
20
|
+
(BigDecimal(nano.to_s) * STEP).to_i
|
17
21
|
end
|
18
22
|
|
19
23
|
# Converts an amount of raw to an amount of NANO.
|
20
24
|
#
|
21
25
|
# @param raw [Integer] amount in raw
|
22
26
|
# @return [Float|Integer] amount in NANO
|
23
|
-
def
|
27
|
+
def raw_to_NANO(raw)
|
28
|
+
return if raw.nil?
|
29
|
+
|
24
30
|
(raw.to_f / STEP).to_f
|
25
31
|
end
|
26
32
|
|
27
|
-
#
|
33
|
+
# @return [TrueClass] if unit is valid.
|
34
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid.
|
35
|
+
def validate_unit!(unit)
|
36
|
+
unless Nanook::UNITS.include?(unit.to_sym)
|
37
|
+
raise Nanook::NanoUnitError, "Unit #{unit} must be one of #{Nanook::UNITS}"
|
38
|
+
end
|
39
|
+
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the +id+ of the object as a short id.
|
44
|
+
# See #shorten_id.
|
28
45
|
#
|
29
|
-
#
|
30
|
-
|
31
|
-
|
46
|
+
# @return [String]
|
47
|
+
def short_id
|
48
|
+
shorten_id(id)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns an id string (hash or nano account) truncated with an ellipsis.
|
52
|
+
# The first 7 and last 4 characters are retained for easy identification.
|
32
53
|
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
54
|
+
# ==== Examples:
|
55
|
+
#
|
56
|
+
# shorten_id('nano_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5')
|
57
|
+
# # => "16u1uuf...r8u5"
|
58
|
+
#
|
59
|
+
# shorten_id('A170D51B94E00371ACE76E35AC81DC9405D5D04D4CEBC399AEACE07AE05DD293')
|
60
|
+
# # => "A170D51...D293"
|
61
|
+
#
|
62
|
+
# @return [String]
|
63
|
+
def shorten_id(long_id)
|
64
|
+
return unless long_id
|
65
|
+
|
66
|
+
[long_id.sub('nano_', '')[0..6], long_id[-4, 4]].join('...')
|
67
|
+
end
|
39
68
|
|
40
|
-
|
69
|
+
def as_account(account_id)
|
70
|
+
Nanook::Account.new(@rpc, account_id)
|
41
71
|
end
|
42
72
|
|
73
|
+
def as_wallet_account(account_id)
|
74
|
+
Nanook::WalletAccount.new(@rpc, @wallet, account_id)
|
75
|
+
end
|
76
|
+
|
77
|
+
def as_block(block_id)
|
78
|
+
Nanook::Block.new(@rpc, block_id)
|
79
|
+
end
|
80
|
+
|
81
|
+
def as_private_key(key)
|
82
|
+
Nanook::PrivateKey.new(@rpc, key)
|
83
|
+
end
|
84
|
+
|
85
|
+
def as_public_key(key)
|
86
|
+
Nanook::PublicKey.new(@rpc, key)
|
87
|
+
end
|
88
|
+
|
89
|
+
def as_time(time)
|
90
|
+
Time.at(time).utc if time
|
91
|
+
end
|
43
92
|
end
|
44
93
|
end
|
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
|
#
|
@@ -18,7 +21,7 @@ class Nanook
|
|
18
21
|
# person needs to know your wallet id as well as have access to run
|
19
22
|
# RPC commands against your nano node to be able to control your accounts.
|
20
23
|
#
|
21
|
-
# A _seed_ on the
|
24
|
+
# A _seed_ on the other hand can be used to link any wallet to another
|
22
25
|
# wallet's accounts, from anywhere in the nano network. This happens
|
23
26
|
# by setting a wallet's seed to be the same as a previous wallet's seed.
|
24
27
|
# When a wallet has the same seed as another wallet, any accounts
|
@@ -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
|
@@ -67,17 +91,18 @@ class Nanook
|
|
67
91
|
#
|
68
92
|
# ==== Examples:
|
69
93
|
#
|
70
|
-
# wallet.account("
|
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
|
+
as_wallet_account(account)
|
81
106
|
end
|
82
107
|
|
83
108
|
# Array of {Nanook::WalletAccount} instances of accounts in the wallet.
|
@@ -91,13 +116,33 @@ class Nanook
|
|
91
116
|
#
|
92
117
|
# @return [Array<Nanook::WalletAccount>] all accounts in the wallet
|
93
118
|
def accounts
|
94
|
-
|
95
|
-
|
96
|
-
Nanook::Util.coerce_empty_string_to_type(response, Array).map do |account|
|
97
|
-
Nanook::WalletAccount.new(@rpc, @wallet, account)
|
119
|
+
rpc(:account_list, _access: :accounts, _coerce: Array).map do |account|
|
120
|
+
as_wallet_account(account)
|
98
121
|
end
|
99
122
|
end
|
100
123
|
|
124
|
+
# Move accounts from another {Nanook::Wallet} on the node to this {Nanook::Wallet}.
|
125
|
+
#
|
126
|
+
# ==== Example:
|
127
|
+
#
|
128
|
+
# wallet.move_accounts("0023200...", ["nano_3e3j5...", "nano_5f2a1..."]) # => true
|
129
|
+
#
|
130
|
+
# @return [Boolean] true when the move was successful
|
131
|
+
def move_accounts(wallet, accounts)
|
132
|
+
rpc(:account_move, source: wallet, accounts: accounts, _access: :moved) == 1
|
133
|
+
end
|
134
|
+
|
135
|
+
# Remove an {Nanook::Account} from this {Nanook::Wallet}.
|
136
|
+
#
|
137
|
+
# ==== Example:
|
138
|
+
#
|
139
|
+
# wallet.remove_account("nano_3e3j5...") # => true
|
140
|
+
#
|
141
|
+
# @return [Boolean] true when the remove was successful
|
142
|
+
def remove_account(account)
|
143
|
+
rpc(:account_remove, account: account, _access: :removed) == 1
|
144
|
+
end
|
145
|
+
|
101
146
|
# Balance of all accounts in the wallet, optionally breaking the balances down by account.
|
102
147
|
#
|
103
148
|
# ==== Examples:
|
@@ -128,58 +173,60 @@ class Nanook
|
|
128
173
|
# Example response:
|
129
174
|
#
|
130
175
|
# {
|
131
|
-
# "
|
176
|
+
# "nano_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"=>{
|
132
177
|
# "balance"=>2.5,
|
133
178
|
# "pending"=>1
|
134
179
|
# },
|
135
|
-
# "
|
180
|
+
# "nano_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"=>{
|
136
181
|
# "balance"=>51.4,
|
137
182
|
# "pending"=>0
|
138
183
|
# },
|
139
184
|
# }
|
140
185
|
#
|
141
|
-
# @param [Boolean]
|
186
|
+
# @param account_break_down [Boolean] (default is +false+). When +true+
|
142
187
|
# the response will contain balances per account.
|
143
188
|
# @param unit (see Nanook::Account#balance)
|
144
189
|
#
|
145
190
|
# @return [Hash{Symbol=>Integer|Float|Hash}]
|
191
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
146
192
|
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
|
193
|
+
validate_unit!(unit)
|
152
194
|
|
153
195
|
if account_break_down
|
154
|
-
return
|
196
|
+
return rpc(:wallet_balances, _access: :balances, _coerce: Hash).tap do |r|
|
155
197
|
if unit == :nano
|
156
|
-
r.each do |account,
|
157
|
-
r[account][:balance] =
|
158
|
-
r[account][:pending] =
|
198
|
+
r.each do |account, _balances|
|
199
|
+
r[account][:balance] = raw_to_NANO(r[account][:balance])
|
200
|
+
r[account][:pending] = raw_to_NANO(r[account][:pending])
|
159
201
|
end
|
160
202
|
end
|
161
203
|
end
|
162
204
|
end
|
163
205
|
|
164
|
-
rpc(:
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
206
|
+
response = rpc(:wallet_info, _coerce: Hash).slice(:balance, :pending)
|
207
|
+
return response unless unit == :nano
|
208
|
+
|
209
|
+
{
|
210
|
+
balance: raw_to_NANO(response[:balance]),
|
211
|
+
pending: raw_to_NANO(response[:pending])
|
212
|
+
}
|
170
213
|
end
|
171
214
|
|
172
215
|
# Changes a wallet's seed.
|
173
216
|
#
|
217
|
+
# It's recommended to only change the seed of a wallet that contains
|
218
|
+
# no accounts. This will clear all deterministic accounts in the wallet.
|
219
|
+
# To restore accounts after changing the seed, see Nanook::WalletAccount#create.
|
220
|
+
#
|
174
221
|
# ==== Example:
|
175
222
|
#
|
176
223
|
# wallet.change_seed("000D1BA...") # => true
|
224
|
+
# wallet.account.create(5) # Restores first 5 accounts for wallet with new seed
|
177
225
|
#
|
178
226
|
# @param seed [String] the seed to change to.
|
179
227
|
# @return [Boolean] indicating whether the change was successful.
|
180
228
|
def change_seed(seed)
|
181
|
-
|
182
|
-
rpc(:wallet_change_seed, seed: seed).has_key?(:success)
|
229
|
+
rpc(:wallet_change_seed, seed: seed).key?(:success)
|
183
230
|
end
|
184
231
|
|
185
232
|
# Creates a new wallet.
|
@@ -197,7 +244,8 @@ class Nanook
|
|
197
244
|
#
|
198
245
|
# @return [Nanook::Wallet]
|
199
246
|
def create
|
200
|
-
|
247
|
+
skip_wallet_required!
|
248
|
+
@wallet = rpc(:wallet_create, _access: :wallet)
|
201
249
|
self
|
202
250
|
end
|
203
251
|
|
@@ -209,49 +257,57 @@ class Nanook
|
|
209
257
|
#
|
210
258
|
# @return [Boolean] indicating success of the action
|
211
259
|
def destroy
|
212
|
-
|
213
|
-
rpc(:wallet_destroy)
|
214
|
-
true
|
260
|
+
rpc(:wallet_destroy, _access: :destroyed) == 1
|
215
261
|
end
|
216
262
|
|
217
263
|
# Generates a String containing a JSON representation of your wallet.
|
218
264
|
#
|
219
265
|
# ==== Example:
|
220
266
|
#
|
221
|
-
# wallet.export
|
267
|
+
# wallet.export
|
268
|
+
# # => "{\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"
|
269
|
+
#
|
270
|
+
# @return [String]
|
222
271
|
def export
|
223
|
-
|
224
|
-
|
272
|
+
rpc(:wallet_export, _access: :json)
|
273
|
+
end
|
274
|
+
|
275
|
+
# Returns true if wallet exists on the node.
|
276
|
+
#
|
277
|
+
# ==== Example:
|
278
|
+
#
|
279
|
+
# wallet.exists? # => true
|
280
|
+
#
|
281
|
+
# @return [Boolean] true if wallet exists on the node
|
282
|
+
def exists?
|
283
|
+
export
|
284
|
+
true
|
285
|
+
rescue Nanook::NodeRpcError
|
286
|
+
false
|
225
287
|
end
|
226
288
|
|
227
289
|
# Will return +true+ if the account exists in the wallet.
|
228
290
|
#
|
229
291
|
# ==== Example:
|
230
|
-
# wallet.contains?("
|
292
|
+
# wallet.contains?("nano_...") # => true
|
231
293
|
#
|
232
|
-
# @param account [String] id (will start with <tt>"
|
294
|
+
# @param account [String] id (will start with <tt>"nano_..."</tt>)
|
233
295
|
# @return [Boolean] indicating if the wallet contains the given account
|
234
296
|
def contains?(account)
|
235
|
-
|
236
|
-
response = rpc(:wallet_contains, account: account)
|
237
|
-
!response.empty? && response[:exists] == 1
|
238
|
-
end
|
239
|
-
|
240
|
-
# @return [String] the wallet id
|
241
|
-
def id
|
242
|
-
@wallet
|
297
|
+
rpc(:wallet_contains, account: account, _access: :exists) == 1
|
243
298
|
end
|
244
299
|
|
245
300
|
# @return [String]
|
246
|
-
def
|
247
|
-
"#{self.class.name}(id: \"#{
|
301
|
+
def to_s
|
302
|
+
"#{self.class.name}(id: \"#{short_id}\")"
|
248
303
|
end
|
304
|
+
alias inspect to_s
|
249
305
|
|
250
306
|
# Makes a payment from an account in your wallet to another account
|
251
307
|
# on the nano network.
|
252
308
|
#
|
253
309
|
# Note, there may be a delay in receiving a response due to Proof of
|
254
|
-
# Work being done. From the {Nano RPC}[https://
|
310
|
+
# Work being done. From the {Nano RPC}[https://docs.nano.org/commands/rpc-protocol/#send]:
|
255
311
|
#
|
256
312
|
# <i>Proof of Work is precomputed for one transaction in the
|
257
313
|
# background. If it has been a while since your last transaction it
|
@@ -260,8 +316,9 @@ class Nanook
|
|
260
316
|
#
|
261
317
|
# ==== Examples:
|
262
318
|
#
|
263
|
-
# wallet.pay(from: "
|
264
|
-
# wallet.pay(from: "
|
319
|
+
# wallet.pay(from: "nano_...", to: "nano_...", amount: 1.1, id: "myUniqueId123") # => "9AE2311..."
|
320
|
+
# wallet.pay(from: "nano_...", to: "nano_...", amount: 54000000000000, unit: :raw, id: "myUniqueId123")
|
321
|
+
# # => "9AE2311..."
|
265
322
|
#
|
266
323
|
# @param from [String] account id of an account in your wallet
|
267
324
|
# @param to (see Nanook::WalletAccount#pay)
|
@@ -270,8 +327,7 @@ class Nanook
|
|
270
327
|
# @params id (see Nanook::WalletAccount#pay)
|
271
328
|
# @return (see Nanook::WalletAccount#pay)
|
272
329
|
# @raise [Nanook::Error] if unsuccessful
|
273
|
-
def pay(from:, to:, amount:, unit: Nanook.default_unit
|
274
|
-
wallet_required!
|
330
|
+
def pay(from:, to:, amount:, id:, unit: Nanook.default_unit)
|
275
331
|
validate_wallet_contains_account!(from)
|
276
332
|
account(from).pay(to: to, amount: amount, unit: unit, id: id)
|
277
333
|
end
|
@@ -292,12 +348,12 @@ class Nanook
|
|
292
348
|
# Example response:
|
293
349
|
#
|
294
350
|
# {
|
295
|
-
#
|
296
|
-
#
|
297
|
-
# "
|
351
|
+
# Nanook::Account=>[
|
352
|
+
# Nanook::Block,
|
353
|
+
# Nanook::Block"
|
298
354
|
# ],
|
299
|
-
#
|
300
|
-
#
|
355
|
+
# Nanook::Account=>[
|
356
|
+
# Nanook::Block
|
301
357
|
# ]
|
302
358
|
# }
|
303
359
|
#
|
@@ -308,57 +364,69 @@ class Nanook
|
|
308
364
|
# Example response:
|
309
365
|
#
|
310
366
|
# {
|
311
|
-
#
|
367
|
+
# Nanook::Account=>[
|
312
368
|
# {
|
313
369
|
# :amount=>6.0,
|
314
|
-
# :source=>
|
315
|
-
# :block
|
370
|
+
# :source=>Nanook::Account,
|
371
|
+
# :block=>Nanook::Block
|
316
372
|
# },
|
317
373
|
# {
|
318
374
|
# :amount=>12.0,
|
319
|
-
# :source=>
|
320
|
-
# :block
|
375
|
+
# :source=>Nanook::Account,
|
376
|
+
# :block=>Nanook::Block
|
321
377
|
# }
|
322
378
|
# ],
|
323
|
-
#
|
379
|
+
# Nanook::Account=>[
|
324
380
|
# {
|
325
381
|
# :amount=>106.370018,
|
326
|
-
# :source=>
|
327
|
-
# :block
|
382
|
+
# :source=>Nanook::Account,
|
383
|
+
# :block=>Nanook::Block
|
328
384
|
# }
|
329
385
|
# ]
|
330
386
|
# }
|
331
|
-
|
332
|
-
|
387
|
+
#
|
388
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
389
|
+
def pending(limit: 1000, detailed: false, unit: Nanook.default_unit)
|
390
|
+
validate_unit!(unit)
|
333
391
|
|
334
|
-
|
335
|
-
|
336
|
-
|
392
|
+
params = {
|
393
|
+
count: limit,
|
394
|
+
_access: :blocks,
|
395
|
+
_coerce: Hash
|
396
|
+
}
|
337
397
|
|
338
|
-
params = { count: limit }
|
339
398
|
params[:source] = true if detailed
|
340
399
|
|
341
|
-
response = rpc(:wallet_pending, params)
|
342
|
-
response = Nanook::Util.coerce_empty_string_to_type(response, Hash)
|
400
|
+
response = rpc(:wallet_pending, params)
|
343
401
|
|
344
|
-
|
402
|
+
unless detailed
|
403
|
+
|
404
|
+
x = response.map do |account, block_ids|
|
405
|
+
blocks = block_ids.map { |block_id| as_block(block_id) }
|
406
|
+
[as_account(account), blocks]
|
407
|
+
end
|
408
|
+
|
409
|
+
return Hash[x]
|
410
|
+
end
|
345
411
|
|
346
412
|
# Map the RPC response, which is:
|
347
413
|
# account=>block=>[amount|source] into
|
348
414
|
# account=>[block|amount|source]
|
349
415
|
x = response.map do |account, data|
|
350
416
|
new_data = data.map do |block, amount_and_source|
|
351
|
-
d =
|
352
|
-
|
353
|
-
|
354
|
-
|
417
|
+
d = {
|
418
|
+
block: as_block(block),
|
419
|
+
source: as_account(amount_and_source[:source]),
|
420
|
+
amount: amount_and_source[:amount]
|
421
|
+
}
|
422
|
+
d[:amount] = raw_to_NANO(d[:amount]) if unit == :nano
|
355
423
|
d
|
356
424
|
end
|
357
425
|
|
358
|
-
[account, new_data]
|
426
|
+
[as_account(account), new_data]
|
359
427
|
end
|
360
428
|
|
361
|
-
Hash[x]
|
429
|
+
Hash[x]
|
362
430
|
end
|
363
431
|
|
364
432
|
# Receives a pending payment into an account in the wallet.
|
@@ -366,7 +434,7 @@ class Nanook
|
|
366
434
|
# When called with no +block+ argument, the latest pending payment
|
367
435
|
# for the account will be received.
|
368
436
|
#
|
369
|
-
# Returns a <i>receive</i> block
|
437
|
+
# Returns a <i>receive</i> block if a receive was successful,
|
370
438
|
# or +false+ if there were no pending payments to receive.
|
371
439
|
#
|
372
440
|
# You can receive a specific pending block if you know it by
|
@@ -374,19 +442,76 @@ class Nanook
|
|
374
442
|
#
|
375
443
|
# ==== Examples:
|
376
444
|
#
|
377
|
-
# wallet.receive(into: "xrb...") # =>
|
378
|
-
# wallet.receive("718CC21...", into: "xrb...") # =>
|
445
|
+
# wallet.receive(into: "xrb...") # => Nanook::Block
|
446
|
+
# wallet.receive("718CC21...", into: "xrb...") # => Nanook::Block
|
379
447
|
#
|
380
448
|
# @param block (see Nanook::WalletAccount#receive)
|
381
449
|
# @param into [String] account id of account in your wallet to receive the
|
382
450
|
# payment into
|
383
451
|
# @return (see Nanook::WalletAccount#receive)
|
384
|
-
def receive(block=nil, into:)
|
385
|
-
wallet_required!
|
452
|
+
def receive(block = nil, into:)
|
386
453
|
validate_wallet_contains_account!(into)
|
387
454
|
account(into).receive(block)
|
388
455
|
end
|
389
456
|
|
457
|
+
# Rebroadcast blocks for accounts from wallet starting at frontier down to count to the network.
|
458
|
+
#
|
459
|
+
# ==== Examples:
|
460
|
+
#
|
461
|
+
# wallet.republish_blocks # => [Nanook::Block, ...]
|
462
|
+
# wallet.republish_blocks(limit: 10) # => [Nanook::Block, ...
|
463
|
+
#
|
464
|
+
# @param limit [Integer] limit of blocks to publish. Default is 1000.
|
465
|
+
# @return [Array<Nanook::Block>] republished blocks
|
466
|
+
def republish_blocks(limit: 1000)
|
467
|
+
rpc(:wallet_republish, count: limit, _access: :blocks, _coerce: Array).map do |block|
|
468
|
+
as_block(block)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
# The default representative account id for the wallet. This is the
|
473
|
+
# representative that all new accounts created in this wallet will have.
|
474
|
+
#
|
475
|
+
# Changing the default representative for a wallet does not change
|
476
|
+
# the representatives for any accounts that have been created.
|
477
|
+
#
|
478
|
+
# ==== Example:
|
479
|
+
#
|
480
|
+
# wallet.default_representative # => "nano_3pc..."
|
481
|
+
#
|
482
|
+
# @return [Nanook::Account] Representative account. Can be nil.
|
483
|
+
def default_representative
|
484
|
+
representative = rpc(:wallet_representative, _access: :representative)
|
485
|
+
as_account(representative) if representative
|
486
|
+
end
|
487
|
+
alias representative default_representative
|
488
|
+
|
489
|
+
# Sets the default representative for the wallet. A wallet's default
|
490
|
+
# representative is the representative all new accounts created in
|
491
|
+
# the wallet will have. Changing the default representative for a
|
492
|
+
# wallet does not change the representatives for existing accounts
|
493
|
+
# in the wallet.
|
494
|
+
#
|
495
|
+
# ==== Example:
|
496
|
+
#
|
497
|
+
# wallet.change_default_representative("nano_...") # => "nano_..."
|
498
|
+
#
|
499
|
+
# @param representative [String] id of the representative account
|
500
|
+
# to set as this account's representative
|
501
|
+
# @return [Nanook::Account] the representative account
|
502
|
+
# @raise [Nanook::Error] if setting the representative fails
|
503
|
+
def change_default_representative(representative)
|
504
|
+
unless as_account(representative).exists?
|
505
|
+
raise Nanook::Error, "Representative account does not exist: #{representative}"
|
506
|
+
end
|
507
|
+
|
508
|
+
raise Nanook::Error, 'Setting the representative failed' \
|
509
|
+
unless rpc(:wallet_representative_set, representative: representative, _access: :set) == 1
|
510
|
+
|
511
|
+
as_account(representative)
|
512
|
+
end
|
513
|
+
alias change_representative change_default_representative
|
514
|
+
|
390
515
|
# Restores a previously created wallet by its seed.
|
391
516
|
# A new wallet will be created on your node (with a new wallet id)
|
392
517
|
# and will have its seed set to the given seed.
|
@@ -400,18 +525,149 @@ class Nanook
|
|
400
525
|
#
|
401
526
|
# @return [Nanook::Wallet] a new wallet
|
402
527
|
# @raise [Nanook::Error] if unsuccessful
|
403
|
-
def restore(seed, accounts:0)
|
528
|
+
def restore(seed, accounts: 0)
|
529
|
+
skip_wallet_required!
|
530
|
+
|
404
531
|
create
|
405
532
|
|
406
|
-
unless change_seed(seed)
|
407
|
-
|
533
|
+
raise Nanook::Error, 'Unable to set seed for wallet' unless change_seed(seed)
|
534
|
+
|
535
|
+
account.create(accounts) if accounts.positive?
|
536
|
+
|
537
|
+
self
|
538
|
+
end
|
539
|
+
|
540
|
+
# Information ledger information about this wallet's accounts.
|
541
|
+
#
|
542
|
+
# This call may return results that include unconfirmed blocks, so it should not be
|
543
|
+
# used in any processes or integrations requiring only details from blocks confirmed
|
544
|
+
# by the network.
|
545
|
+
#
|
546
|
+
# ==== Examples:
|
547
|
+
#
|
548
|
+
# wallet.ledger
|
549
|
+
#
|
550
|
+
# Example response:
|
551
|
+
#
|
552
|
+
# {
|
553
|
+
# Nanook::Account => {
|
554
|
+
# frontier: "E71AF3E9DD86BBD8B4620EFA63E065B34D358CFC091ACB4E103B965F95783321",
|
555
|
+
# open_block: "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
|
556
|
+
# representative_block: "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
|
557
|
+
# balance: 1.45,
|
558
|
+
# modified_timestamp: 1511476234,
|
559
|
+
# block_count: 2
|
560
|
+
# },
|
561
|
+
# Nanook::Account => { ... }
|
562
|
+
# }
|
563
|
+
#
|
564
|
+
# @param unit (see Nanook::Account#balance)
|
565
|
+
# @return [Hash{Nanook::Account=>Hash{Symbol=>Nanook::Block|Integer|Float|Time}}] ledger.
|
566
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
567
|
+
def ledger(unit: Nanook.default_unit)
|
568
|
+
validate_unit!(unit)
|
569
|
+
|
570
|
+
response = rpc(:wallet_ledger, _access: :accounts, _coerce: Hash)
|
571
|
+
|
572
|
+
accounts = response.map do |account_id, data|
|
573
|
+
data[:frontier] = as_block(data[:frontier]) if data[:frontier]
|
574
|
+
data[:open_block] = as_block(data[:open_block]) if data[:open_block]
|
575
|
+
data[:representative_block] = as_block(data[:representative_block]) if data[:representative_block]
|
576
|
+
data[:balance] = raw_to_NANO(data[:balance]) if unit == :nano && data[:balance]
|
577
|
+
data[:last_modified_at] = as_time(data.delete(:modified_timestamp))
|
578
|
+
|
579
|
+
[as_account(account_id), data]
|
408
580
|
end
|
409
581
|
|
410
|
-
|
411
|
-
|
582
|
+
Hash[accounts]
|
583
|
+
end
|
584
|
+
|
585
|
+
# Information about this wallet.
|
586
|
+
#
|
587
|
+
# This call may return results that include unconfirmed blocks, so it should not be
|
588
|
+
# used in any processes or integrations requiring only details from blocks confirmed
|
589
|
+
# by the network.
|
590
|
+
#
|
591
|
+
# ==== Examples:
|
592
|
+
#
|
593
|
+
# wallet.info
|
594
|
+
#
|
595
|
+
# Example response:
|
596
|
+
#
|
597
|
+
# {
|
598
|
+
# balance: 1.0,
|
599
|
+
# pending: 2.3
|
600
|
+
# accounts_count: 3,
|
601
|
+
# adhoc_count: 1,
|
602
|
+
# deterministic_count: 2,
|
603
|
+
# deterministic_index: 2
|
604
|
+
# }
|
605
|
+
#
|
606
|
+
# @param unit (see Nanook::Account#balance)
|
607
|
+
# @return [Hash{Symbol=>Integer|Float}] information about the wallet.
|
608
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
609
|
+
def info(unit: Nanook.default_unit)
|
610
|
+
validate_unit!(unit)
|
611
|
+
|
612
|
+
response = rpc(:wallet_info, _coerce: Hash)
|
613
|
+
|
614
|
+
if unit == :nano
|
615
|
+
response[:balance] = raw_to_NANO(response[:balance])
|
616
|
+
response[:pending] = raw_to_NANO(response[:pending])
|
412
617
|
end
|
413
618
|
|
414
|
-
|
619
|
+
response
|
620
|
+
end
|
621
|
+
|
622
|
+
# Reports send/receive information for accounts in wallet. Change blocks are skipped,
|
623
|
+
# open blocks will appear as receive. Response will start with most recent blocks
|
624
|
+
# according to local ledger.
|
625
|
+
#
|
626
|
+
# ==== Example:
|
627
|
+
#
|
628
|
+
# wallet.history
|
629
|
+
#
|
630
|
+
# Example response:
|
631
|
+
#
|
632
|
+
# [
|
633
|
+
# {
|
634
|
+
# "type": "send",
|
635
|
+
# "account": Nanook::Account,
|
636
|
+
# "amount": 3.2,
|
637
|
+
# "block_account": Nanook::Account,
|
638
|
+
# "hash": Nanook::Block,
|
639
|
+
# "local_timestamp": Time
|
640
|
+
# },
|
641
|
+
# {
|
642
|
+
# ...
|
643
|
+
# }
|
644
|
+
# ]
|
645
|
+
#
|
646
|
+
# @param unit (see #balance)
|
647
|
+
# @return [Array<Hash{Symbol=>String|Nanook::Account|Nanook::WalletAccount|Nanook::Block|Integer|Float|Time}>]
|
648
|
+
# @raise [Nanook::NanoUnitError] if `unit` is invalid
|
649
|
+
def history(unit: Nanook.default_unit)
|
650
|
+
validate_unit!(unit)
|
651
|
+
|
652
|
+
rpc(:wallet_history, _access: :history, _coerce: Array).map do |h|
|
653
|
+
h[:account] = account(h[:account])
|
654
|
+
h[:block_account] = as_account(h[:block_account])
|
655
|
+
h[:amount] = raw_to_NANO(h[:amount]) if unit == :nano
|
656
|
+
h[:block] = as_block(h.delete(:hash))
|
657
|
+
h[:local_timestamp] = as_time(h[:local_timestamp])
|
658
|
+
h
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
662
|
+
# Locks the wallet. A locked wallet cannot pocket pending transactions or make payments. See {#unlock}.
|
663
|
+
#
|
664
|
+
# ==== Example:
|
665
|
+
#
|
666
|
+
# wallet.lock #=> true
|
667
|
+
#
|
668
|
+
# @return [Boolean] indicates if the wallet was successfully locked
|
669
|
+
def lock
|
670
|
+
rpc(:wallet_lock, _access: :locked) == 1
|
415
671
|
end
|
416
672
|
|
417
673
|
# Returns +true+ if the wallet is locked.
|
@@ -422,9 +678,7 @@ class Nanook
|
|
422
678
|
#
|
423
679
|
# @return [Boolean] indicates if the wallet is locked
|
424
680
|
def locked?
|
425
|
-
|
426
|
-
response = rpc(:wallet_locked)
|
427
|
-
!response.empty? && response[:locked] != 0
|
681
|
+
rpc(:wallet_locked, _access: :locked) == 1
|
428
682
|
end
|
429
683
|
|
430
684
|
# Unlocks a previously locked wallet.
|
@@ -434,9 +688,8 @@ class Nanook
|
|
434
688
|
# wallet.unlock("new_pass") #=> true
|
435
689
|
#
|
436
690
|
# @return [Boolean] indicates if the unlocking action was successful
|
437
|
-
def unlock(password)
|
438
|
-
|
439
|
-
rpc(:password_enter, password: password)[:valid] == 1
|
691
|
+
def unlock(password = nil)
|
692
|
+
rpc(:password_enter, password: password, _access: :valid) == 1
|
440
693
|
end
|
441
694
|
|
442
695
|
# Changes the password for a wallet.
|
@@ -446,33 +699,73 @@ class Nanook
|
|
446
699
|
# wallet.change_password("new_pass") #=> true
|
447
700
|
# @return [Boolean] indicates if the action was successful
|
448
701
|
def change_password(password)
|
449
|
-
|
450
|
-
|
702
|
+
rpc(:password_change, password: password, _access: :changed) == 1
|
703
|
+
end
|
704
|
+
|
705
|
+
# Tells the node to look for pending blocks for any account in the wallet.
|
706
|
+
#
|
707
|
+
# ==== Example:
|
708
|
+
#
|
709
|
+
# wallet.search_pending #=> true
|
710
|
+
# @return [Boolean] indicates if the action was successful
|
711
|
+
def search_pending
|
712
|
+
rpc(:search_pending, _access: :started) == 1
|
713
|
+
end
|
714
|
+
|
715
|
+
# Returns a list of pairs of {Nanook::WalletAccount} and work for wallet.
|
716
|
+
#
|
717
|
+
# ==== Example:
|
718
|
+
#
|
719
|
+
# wallet.work
|
720
|
+
#
|
721
|
+
# ==== Example response:
|
722
|
+
#
|
723
|
+
# {
|
724
|
+
# Nanook::WalletAccount: "432e5cf728c90f4f",
|
725
|
+
# Nanook::WalletAccount: "4efec5f63fc902cf"
|
726
|
+
# }
|
727
|
+
# @return [Boolean] indicates if the action was successful
|
728
|
+
def work
|
729
|
+
hash = rpc(:wallet_work_get, _access: :works, _coerce: Hash).map do |account_id, work|
|
730
|
+
[as_wallet_account(account_id), work]
|
731
|
+
end
|
732
|
+
|
733
|
+
Hash[hash]
|
451
734
|
end
|
452
735
|
|
453
736
|
private
|
454
737
|
|
455
|
-
def rpc(action, params={})
|
456
|
-
|
457
|
-
|
738
|
+
def rpc(action, params = {})
|
739
|
+
check_wallet_required!
|
740
|
+
|
741
|
+
p = { wallet: @wallet }.compact
|
742
|
+
@rpc.call(action, p.merge(params)).tap { reset_skip_wallet_required! }
|
458
743
|
end
|
459
744
|
|
460
|
-
def
|
461
|
-
|
462
|
-
|
463
|
-
|
745
|
+
def skip_wallet_required!
|
746
|
+
@skip_wallet_required_check = true
|
747
|
+
end
|
748
|
+
|
749
|
+
def reset_skip_wallet_required!
|
750
|
+
@skip_wallet_required_check = false
|
751
|
+
end
|
752
|
+
|
753
|
+
def check_wallet_required!
|
754
|
+
return if @wallet || @skip_wallet_required_check
|
755
|
+
|
756
|
+
raise ArgumentError, 'Wallet must be present'
|
464
757
|
end
|
465
758
|
|
466
759
|
def validate_wallet_contains_account!(account)
|
467
760
|
@known_valid_accounts ||= []
|
468
761
|
return if @known_valid_accounts.include?(account)
|
469
762
|
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
raise ArgumentError.new("Account does not exist in wallet. Account: #{account}, wallet: #{@wallet}")
|
763
|
+
unless contains?(account)
|
764
|
+
raise ArgumentError,
|
765
|
+
"Account does not exist in wallet. Account: #{account}, wallet: #{@wallet}"
|
474
766
|
end
|
475
|
-
end
|
476
767
|
|
768
|
+
@known_valid_accounts << account
|
769
|
+
end
|
477
770
|
end
|
478
771
|
end
|