nanook 2.2.0 → 3.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 +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
|