nanook 2.5.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/console CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "nanook"
4
+ require 'bundler/setup'
5
+ require 'nanook'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
8
9
 
9
- require "pry"
10
+ require 'pry'
10
11
  Pry.start
data/lib/nanook.rb CHANGED
@@ -1,12 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'net/http'
2
4
  require 'uri'
3
5
  require 'forwardable'
4
6
 
5
- Dir[File.dirname(__FILE__) + '/nanook/*.rb'].each { |file| require file }
7
+ Dir["#{File.dirname(__FILE__)}/nanook/*.rb"].sort.each { |file| require file }
8
+
9
+ require_relative 'nanook/util'
6
10
 
7
11
  # ==== Initializing
8
12
  #
9
- # Connect to the default RPC host at http://localhost:7076 and with a timeout of 500 seconds:
13
+ # Connect to the default RPC host at http://[::1]:7076 and with a timeout of 60 seconds:
10
14
  #
11
15
  # nanook = Nanook.new
12
16
  #
@@ -19,8 +23,9 @@ Dir[File.dirname(__FILE__) + '/nanook/*.rb'].each { |file| require file }
19
23
  # Nanook.new(timeout: 600)
20
24
  # Nanook.new("http://ip6-localhost.com:7076", timeout: 600)
21
25
  class Nanook
26
+ include Util
22
27
 
23
- UNITS = [:raw, :nano]
28
+ UNITS = %i[raw nano].freeze
24
29
  DEFAULT_UNIT = :nano
25
30
 
26
31
  # @return [Nanook::Rpc]
@@ -32,26 +37,27 @@ class Nanook
32
37
  def self.default_unit
33
38
  return DEFAULT_UNIT unless defined?(UNIT)
34
39
 
35
- unless UNITS.include?(UNIT.to_sym)
36
- raise Nanook::Error.new("UNIT #{UNIT} must be one of #{UNITS}")
37
- end
38
-
39
40
  UNIT.to_sym
40
41
  end
41
42
 
42
43
  # Returns a new instance of {Nanook}.
43
44
  #
44
45
  # ==== Examples:
45
- # Connecting to http://localhost:7076 with the default timeout of 10s:
46
+ # Connecting to http://[::1]:7076 with the default timeout of 60s:
47
+ #
46
48
  # Nanook.new
49
+ #
47
50
  # Setting a custom timeout:
51
+ #
48
52
  # Nanook.new(timeout: 10)
53
+ #
49
54
  # Connecting to a custom RPC host and setting a timeout:
50
- # Nanook.new("http://ip6-localhost.com:7076", timeout: 10)
55
+ #
56
+ # Nanook.new("http://ip6-localhost:7076", timeout: 10)
51
57
  #
52
58
  # @param uri [String] default is {Nanook::Rpc::DEFAULT_URI}. The RPC host to connect to
53
59
  # @param timeout [Integer] default is {Nanook::Rpc::DEFAULT_TIMEOUT}. Connection timeout in number of seconds
54
- def initialize(uri=Nanook::Rpc::DEFAULT_URI, timeout:Nanook::Rpc::DEFAULT_TIMEOUT)
60
+ def initialize(uri = Nanook::Rpc::DEFAULT_URI, timeout: Nanook::Rpc::DEFAULT_TIMEOUT)
55
61
  @rpc = Nanook::Rpc.new(uri, timeout: timeout)
56
62
  end
57
63
 
@@ -63,7 +69,7 @@ class Nanook
63
69
  # @param account [String] the id of the account you want to work with
64
70
  # @return [Nanook::Account]
65
71
  def account(account)
66
- Nanook::Account.new(@rpc, account)
72
+ as_account(account)
67
73
  end
68
74
 
69
75
  # Returns a new instance of {Nanook::Block}.
@@ -74,23 +80,35 @@ class Nanook
74
80
  # @param block [String] the id/hash of the block you want to work with
75
81
  # @return [Nanook::Block]
76
82
  def block(block)
77
- Nanook::Block.new(@rpc, block)
83
+ as_block(block)
78
84
  end
79
85
 
80
86
  # @return [String]
81
- def inspect
82
- "#{self.class.name}(rpc: #{@rpc.inspect}, object_id: \"#{"0x00%x" % (object_id << 1)}\")"
87
+ def to_s
88
+ "#{self.class.name}(rpc: #{@rpc})"
83
89
  end
90
+ alias inspect to_s
84
91
 
85
- # Returns a new instance of {Nanook::Key}.
92
+ # Returns a new instance of {Nanook::PrivateKey}.
86
93
  #
87
94
  # ==== Example:
88
- # key = Nanook.new.key("3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039")
95
+ # key = Nanook.new.private_key("3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039")
89
96
  #
90
97
  # @param key [String] a private key
91
- # @return [Nanook::Key]
92
- def key(key=nil)
93
- Nanook::Key.new(@rpc, key)
98
+ # @return [Nanook::PrivateKey]
99
+ def private_key(key = nil)
100
+ as_private_key(key, allow_blank: true)
101
+ end
102
+
103
+ # Returns a new instance of {Nanook::PublicKey}.
104
+ #
105
+ # ==== Example:
106
+ # key = Nanook.new.public_key("3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039")
107
+ #
108
+ # @param key [String] a public key
109
+ # @return [Nanook::PublicKey]
110
+ def public_key(key)
111
+ as_public_key(key)
94
112
  end
95
113
 
96
114
  # Returns a new instance of {Nanook::Node}.
@@ -110,7 +128,7 @@ class Nanook
110
128
  #
111
129
  # @param wallet [String] the id of the wallet you want to work with
112
130
  # @return [Nanook::Wallet]
113
- def wallet(wallet=nil)
131
+ def wallet(wallet = nil)
114
132
  Nanook::Wallet.new(@rpc, wallet)
115
133
  end
116
134
 
@@ -124,4 +142,42 @@ class Nanook
124
142
  Nanook::WorkPeer.new(@rpc)
125
143
  end
126
144
 
145
+ # Return summarized metrics received from other nodes of the whole network.
146
+ #
147
+ # ==== Example:
148
+ # Nanook.new.network_telemetry
149
+ #
150
+ # ==== Example response:
151
+ # {
152
+ # block_count: 5777903,
153
+ # cemented_count: 688819,
154
+ # unchecked_count: 443468,
155
+ # account_count: 620750,
156
+ # bandwidth_cap: 1572864,
157
+ # peer_count: 32,
158
+ # protocol_version: 18,
159
+ # uptime: 556896,
160
+ # genesis_block: Nanook::Block,
161
+ # major_version: 21,
162
+ # minor_version: 0,
163
+ # patch_version: 0,
164
+ # pre_release_version: 0,
165
+ # maker: 0,
166
+ # timestamp: Time,
167
+ # active_difficulty: "ffffffcdbf40aa45"
168
+ # }
169
+ #
170
+ # @return [Nanook::WorkPeer]
171
+ def network_telemetry
172
+ response = call_rpc(:telemetry, _coerce: Hash)
173
+ response[:genesis_block] = as_block(response[:genesis_block])
174
+ response[:timestamp] = as_time(response[:timestamp])
175
+ response
176
+ end
177
+
178
+ private
179
+
180
+ def call_rpc(action, params = {})
181
+ @rpc.call(action, params)
182
+ end
127
183
  end
@@ -1,5 +1,8 @@
1
- class Nanook
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'util'
2
4
 
5
+ class Nanook
3
6
  # The <tt>Nanook::Account</tt> class contains methods to discover
4
7
  # publicly-available information about accounts on the nano network.
5
8
  #
@@ -15,10 +18,38 @@ class Nanook
15
18
  # rpc_conn = Nanook::Rpc.new
16
19
  # account = Nanook::Account.new(rpc_conn, "nano_...")
17
20
  class Account
21
+ include Nanook::Util
18
22
 
19
23
  def initialize(rpc, account)
20
24
  @rpc = rpc
21
- @account = account
25
+ @account = account.to_s
26
+ end
27
+
28
+ # The id of the account.
29
+ #
30
+ # ==== Example:
31
+ #
32
+ # account.id # => "nano_16u..."
33
+ #
34
+ # @return [String] the id of the account
35
+ def id
36
+ @account
37
+ end
38
+
39
+ # @param other [Nanook::Account] account to compare
40
+ # @return [Boolean] true if accounts are equal
41
+ def ==(other)
42
+ other.class == self.class &&
43
+ other.id == id
44
+ end
45
+ alias eql? ==
46
+
47
+ # The hash value is used along with #eql? by the Hash class to determine if two objects
48
+ # reference the same hash key.
49
+ #
50
+ # @return [Integer]
51
+ def hash
52
+ id.hash
22
53
  end
23
54
 
24
55
  # Information about this accounts that have set this account as their representative.
@@ -30,28 +61,36 @@ class Nanook
30
61
  # Example response:
31
62
  #
32
63
  # {
33
- # :nano_13bqhi1cdqq8yb9szneoc38qk899d58i5rcrgdk5mkdm86hekpoez3zxw5sd=>500000000000000000000000000000000000,
34
- # :nano_17k6ug685154an8gri9whhe5kb5z1mf5w6y39gokc1657sh95fegm8ht1zpn=>961647970820730000000000000000000000
64
+ # Nanook::Account=>50.0,
65
+ # Nanook::Account=>961.64
35
66
  # }
36
67
  #
37
68
  # @param unit (see #balance)
38
- # @return [Hash{Symbol=>Integer}] account ids which delegate to this account, and their account balance
69
+ # @return [Hash{Nanook::Account=>Integer|Float}] {Nanook::Account} accounts which delegate to this account, and their account balance
70
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
39
71
  def delegators(unit: Nanook.default_unit)
40
- unless Nanook::UNITS.include?(unit)
41
- raise ArgumentError.new("Unsupported unit: #{unit}")
42
- end
72
+ validate_unit!(unit)
43
73
 
44
- response = rpc(:delegators)[:delegators]
45
-
46
- return response if unit == :raw
74
+ response = rpc(:delegators, _access: :delegators, _coerce: Hash)
47
75
 
48
76
  r = response.map do |account_id, balance|
49
- balance = Nanook::Util.raw_to_NANO(balance)
77
+ balance = raw_to_NANO(balance) if unit == :nano
50
78
 
51
- [account_id, balance]
79
+ [as_account(account_id), balance]
52
80
  end
53
81
 
54
- Hash[r].to_symbolized_hash
82
+ Hash[r]
83
+ end
84
+
85
+ # Number of accounts that have set this account as their representative.
86
+ #
87
+ # === Example:
88
+ #
89
+ # account.delegators_count # => 2
90
+ #
91
+ # @return [Integer]
92
+ def delegators_count
93
+ rpc(:delegators_count, _access: :count)
55
94
  end
56
95
 
57
96
  # Returns true if the account has an <i>open</i> block.
@@ -73,13 +112,21 @@ class Nanook
73
112
  #
74
113
  # @return [Boolean] Indicates if this account has an open block
75
114
  def exists?
76
- response = rpc(:account_info)
115
+ begin
116
+ response = rpc(:account_info)
117
+ rescue Nanook::Error
118
+ return false
119
+ end
120
+
77
121
  !response.empty? && !response[:open_block].nil?
78
122
  end
79
- alias_method :open?, :exists?
123
+ alias open? exists?
80
124
 
81
125
  # An account's history of send and receive payments.
82
126
  #
127
+ # This call may return results that include unconfirmed blocks, so it should not be used in
128
+ # any processes or integrations requiring only details from blocks confirmed by the network.
129
+ #
83
130
  # ==== Example:
84
131
  #
85
132
  # account.history
@@ -89,73 +136,90 @@ class Nanook
89
136
  # [
90
137
  # {
91
138
  # type: "send",
92
- # account: "nano_1kdc5u48j3hr5r7eof9iao47szqh81ndqgq5e5hrsn1g9a3sa4hkkcotn3uq",
139
+ # account: Nanook::Account,
93
140
  # amount: 2,
94
- # hash: "2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570"
141
+ # block: Nanook::Block
95
142
  # }
96
143
  # ]
97
144
  #
98
- # @param limit [Integer] maximum number of history items to return
145
+ # @param limit [Integer] maximum number of history items to return. Defaults to 1000
146
+ # @param sort [Symbol] default +:asc+. When set to +:desc+ the history will be returned oldest to newest.
99
147
  # @param unit (see #balance)
100
- # @return [Array<Hash{Symbol=>String}>] the history of send and receive payments for this account
101
- def history(limit: 1000, unit: Nanook.default_unit)
102
- unless Nanook::UNITS.include?(unit)
103
- raise ArgumentError.new("Unsupported unit: #{unit}")
104
- end
148
+ # @return [Array<Hash{Symbol=>String|Float|Integer|Nanook::Account|NanookBlock}>] the history of send and receive payments for this account
149
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
150
+ def history(limit: 1000, unit: Nanook.default_unit, sort: :asc)
151
+ validate_unit!(unit)
105
152
 
106
- response = rpc(:account_history, count: limit)[:history]
153
+ response = rpc(:account_history, count: limit, reverse: (sort == :desc), _access: :history, _coerce: Array)
107
154
 
108
- if unit == :raw
109
- return response
110
- end
155
+ response.map do |history|
156
+ history[:amount] = raw_to_NANO(history[:amount]) if unit == :nano
157
+ history[:account] = as_account(history[:account])
158
+ history[:block] = as_block(history.delete(:hash)) # Rename the key from `hash` to `block`
111
159
 
112
- response.map! do |history|
113
- history[:amount] = Nanook::Util.raw_to_NANO(history[:amount])
114
160
  history
115
161
  end
116
162
  end
117
163
 
118
- # The last modified time of the account in the time zone of
119
- # your nano node (usually UTC).
164
+ # Return blocks for the account.
165
+ #
166
+ # @param limit [Integer] maximum number of history items to return. Defaults to 1000
167
+ # @param sort [Symbol] default +:asc+. When set to +:desc+ the blocks will be returned oldest to newest.
168
+ # @return [Array<Nanook::Block>]
169
+ def blocks(limit: 1000, sort: :asc)
170
+ history(limit: limit, sort: sort).map { |i| i[:block] }
171
+ end
172
+
173
+ # Returns the open block for the account if it exists on the node.
174
+ #
175
+ # @return [Nanook::Block]
176
+ def open_block
177
+ blocks(limit: 1, sort: :desc).first
178
+ end
179
+
180
+ # The last modified time of the account in UTC. For many accounts on the node
181
+ # this will be the timestamp of when the node bootstrapped.
120
182
  #
121
183
  # ==== Example:
122
184
  #
123
185
  # account.last_modified_at # => Time
124
186
  #
125
- # @return [Time] last modified time of the account in the time zone of
126
- # your nano node (usually UTC).
187
+ # @return [Time] last modified time of the account in UTC. Can be nil
127
188
  def last_modified_at
128
- response = rpc(:account_info)
129
- Time.at(response[:modified_timestamp])
189
+ as_time(rpc(:account_info, _access: :modified_timestamp))
130
190
  end
131
191
 
132
192
  # The public key of the account.
133
193
  #
134
194
  # ==== Example:
135
195
  #
136
- # account.public_key # => "3068BB1..."
196
+ # account.public_key # => Nanook::PublicKey
137
197
  #
138
- # @return [String] public key of the account
198
+ # @return [Nanook::PublicKey] public key of the account
139
199
  def public_key
140
- rpc(:account_key)[:key]
200
+ as_public_key(rpc(:account_key, _access: :key))
141
201
  end
142
202
 
143
- # The representative account id for the account.
144
- # Representatives are accounts that cast votes in the case of a
145
- # fork in the network.
203
+ # The representative for the account.
146
204
  #
147
205
  # ==== Example:
148
206
  #
149
- # account.representative # => "nano_3pc..."
207
+ # account.representative # => Nanook::Account
150
208
  #
151
- # @return [String] Representative account of the account
209
+ # @return [Nanook::Account] Representative of the account. Can be nil.
152
210
  def representative
153
- rpc(:account_representative)[:representative]
211
+ representative = rpc(:account_representative, _access: :representative)
212
+ as_account(representative)
154
213
  end
155
214
 
156
215
  # The account's balance, including pending (unreceived payments).
157
216
  # To receive a pending amount see {WalletAccount#receive}.
158
217
  #
218
+ # This call returns information that may be based on unconfirmed blocks.
219
+ # These details should not be relied on for any process or integration that
220
+ # requires confirmed blocks. The pending balance is calculated from
221
+ # potentially unconfirmed blocks.
222
+ #
159
223
  # ==== Examples:
160
224
  #
161
225
  # account.balance
@@ -177,43 +241,36 @@ class Nanook
177
241
  # balance: 2000000000000000000000000000000,
178
242
  # pending: 1100000000000000000000000000000
179
243
  # }
180
- #
244
+ # @param allow_unconfirmed [Boolean] +false+ by default. When +false+, +balance+
245
+ # will only include blocks on this account that have already been confirmed
246
+ # and +pending+ will only include incoming send blocks that have already been
247
+ # confirmed on the sending account.
181
248
  # @param unit [Symbol] default is {Nanook.default_unit}.
182
249
  # Must be one of {Nanook::UNITS}.
183
250
  # Represents the unit that the balances will be returned in.
184
251
  # Note: this method interprets
185
252
  # +:nano+ as NANO, which is technically Mnano.
186
- # See {https://nano.org/en/faq#what-are-nano-units- What are Nano's Units}
187
- #
188
- # @raise ArgumentError if an invalid +unit+ was given.
253
+ # See {https://docs.nano.org/protocol-design/distribution-and-units/#unit-dividers What are Nano's Units}
254
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
189
255
  # @return [Hash{Symbol=>Integer|Float}]
190
- def balance(unit: Nanook.default_unit)
191
- unless Nanook::UNITS.include?(unit)
192
- raise ArgumentError.new("Unsupported unit: #{unit}")
193
- end
256
+ def balance(allow_unconfirmed: false, unit: Nanook.default_unit)
257
+ validate_unit!(unit)
258
+
259
+ params = {
260
+ include_only_confirmed: !allow_unconfirmed
261
+ }
194
262
 
195
- rpc(:account_balance).tap do |r|
263
+ rpc(:account_balance, params).tap do |r|
196
264
  if unit == :nano
197
- r[:balance] = Nanook::Util.raw_to_NANO(r[:balance])
198
- r[:pending] = Nanook::Util.raw_to_NANO(r[:pending])
265
+ r[:balance] = raw_to_NANO(r[:balance])
266
+ r[:pending] = raw_to_NANO(r[:pending])
199
267
  end
200
268
  end
201
269
  end
202
270
 
203
271
  # @return [Integer] number of blocks for this account
204
272
  def block_count
205
- rpc(:account_block_count)[:block_count]
206
- end
207
-
208
- # The id of the account.
209
- #
210
- # ==== Example:
211
- #
212
- # account.id # => "nano_16u..."
213
- #
214
- # @return [String] the id of the account
215
- def id
216
- @account
273
+ rpc(:account_block_count, _access: :block_count)
217
274
  end
218
275
 
219
276
  # Information about the account.
@@ -244,66 +301,80 @@ class Nanook
244
301
  # id: "nano_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5",
245
302
  # balance: 11.439597000000001,
246
303
  # block_count: 4,
247
- # frontier: "2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570",
248
- # modified_timestamp: 1520500357,
249
- # open_block: "C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9",
250
- # pending: 0,
251
- # public_key: "A82C906460046D230D7D37C6663723DC3EFCECC4B3254EBF45294B66746F4FEF",
252
- # representative: "nano_3pczxuorp48td8645bs3m6c3xotxd3idskrenmi65rbrga5zmkemzhwkaznh",
253
- # representative_block: "C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9",
254
- # weight: 0
304
+ # frontier: Nanook::Block,
305
+ # last_modified_at: Time,
306
+ # open_block: Nanook::Block,
307
+ # pending: 1.0,
308
+ # representative: Nanook::Account,
309
+ # representative_block: Nanook::Block,
310
+ # weight: 0.1
255
311
  # }
256
312
  #
257
- # @param detailed [Boolean] (default is false). When +true+, four
258
- # additional calls are made to the RPC to return more information
259
313
  # @param unit (see #balance)
260
- # @return [Hash{Symbol=>String|Integer|Float}] information about the account containing:
314
+ # @param allow_unconfirmed [Boolean] +false+ by default. When +false+ only confirmed +balance+
315
+ # +pending+, and +representative+ values are returned.
316
+ # @return [Hash{Symbol=>String|Integer|Float|Nanook::Account|Nanook::Block|Time}] information about the account containing:
261
317
  # [+id+] The account id
262
- # [+frontier+] The latest block hash
263
- # [+open_block+] The first block in every account's blockchain. When this block was published the account was officially open
264
- # [+representative_block+] The block that named the representative for the account
265
- # [+balance+] Amount in either NANO or raw (depending on the <tt>unit:</tt> argument)
266
- # [+last_modified+] Unix timestamp
318
+ # [+frontier+] The latest {Nanook::Block}
319
+ # [+confirmation_height+] Confirmation height
320
+ # [+confirmation_height_frontier+] The {Nanook::Block} of the confirmation height
321
+ # [+pending+] Pending balance in either NANO or raw (depending on the <tt>unit:</tt> argument)
322
+ # [+open_block+] The first {Nanook::Block} in every account's blockchain. When this block was published the account was officially open
323
+ # [+representative_block+] The {Nanook::Block} that named the representative for the account
324
+ # [+balance+] Balance in either NANO or raw (depending on the <tt>unit:</tt> argument)
325
+ # [+last_modified_at+] Time of when the account was last modified in UTC
326
+ # [+representative+] Representative {Nanook::Account}
267
327
  # [+block_count+] Number of blocks in the account's blockchain
268
- #
269
- # When <tt>detailed: true</tt> is passed as an argument, this method
270
- # makes four additional calls to the RPC to return more information
271
- # about an account:
272
- #
273
328
  # [+weight+] See {#weight}
274
- # [+pending+] See {#balance}
275
- # [+representative+] See {#representative}
276
- # [+public_key+] See {#public_key}
277
- def info(detailed: false, unit: Nanook.default_unit)
278
- unless Nanook::UNITS.include?(unit)
279
- raise ArgumentError.new("Unsupported unit: #{unit}")
280
- end
329
+ #
330
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
331
+ def info(allow_unconfirmed: false, unit: Nanook.default_unit)
332
+ validate_unit!(unit)
333
+
334
+ params = {
335
+ representative: true,
336
+ weight: true,
337
+ pending: true,
338
+ include_confirmed: !allow_unconfirmed
339
+ }
281
340
 
282
- response = rpc(:account_info)
341
+ response = rpc(:account_info, params)
283
342
  response.merge!(id: @account)
284
343
 
285
- if unit == :nano
286
- response[:balance] = Nanook::Util.raw_to_NANO(response[:balance])
344
+ # The RPC returned confirmed data when the `include_confirmed: true`
345
+ # has been passed. Normalize this data to the same keys as when
346
+ # unconfirmed data can be returned.
347
+ unless allow_unconfirmed
348
+ response[:balance] = response.delete(:confirmed_balance)
349
+ response[:pending] = response.delete(:confirmed_pending)
350
+ response[:representative] = response.delete(:confirmed_representative)
351
+ response[:frontier] = response.delete(:confirmed_frontier)
352
+ response[:confirmation_height] = response.delete(:confirmed_height)
287
353
  end
288
354
 
289
- # Return the response if we don't need any more info
290
- return response unless detailed
355
+ response[:frontier] = as_block(response[:frontier])
356
+ response[:open_block] = as_block(response[:open_block])
357
+ response[:representative_block] = as_block(response[:representative_block])
358
+ response[:representative] = as_account(response[:representative])
359
+ response[:last_modified_at] = as_time(response.delete(:modified_timestamp))
360
+ response[:confirmation_height_frontier] = as_block(response[:confirmation_height_frontier])
291
361
 
292
- # Otherwise make additional calls
293
- response.merge!({
294
- weight: weight,
295
- pending: balance(unit: unit)[:pending],
296
- representative: representative,
297
- public_key: public_key
298
- })
362
+ if unit == :nano
363
+ response.merge!(
364
+ balance: raw_to_NANO(response[:balance]),
365
+ pending: raw_to_NANO(response[:pending]),
366
+ weight: raw_to_NANO(response[:weight])
367
+ )
368
+ end
299
369
 
300
- # Sort this new hash by keys
301
- Hash[response.sort].to_symbolized_hash
370
+ response.compact
302
371
  end
303
372
 
304
- def inspect
305
- "#{self.class.name}(id: \"#{id}\", object_id: \"#{"0x00%x" % (object_id << 1)}\")"
373
+ # @return [String]
374
+ def to_s
375
+ "#{self.class.name}(id: \"#{short_id}\")"
306
376
  end
377
+ alias inspect to_s
307
378
 
308
379
  # Information about the given account as well as other
309
380
  # accounts up the ledger. The number of accounts returned is determined
@@ -316,43 +387,56 @@ class Nanook
316
387
  # Example response:
317
388
  #
318
389
  # {
319
- # :nano_3c3ek3k8135f6e8qtfy8eruk9q3yzmpebes7btzncccdest8ymzhjmnr196j=>{
320
- # :frontier=>"2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570",
321
- # :open_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9",
322
- # :representative_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9",
323
- # :balance=>11439597000000000000000000000000,
324
- # :modified_timestamp=>1520500357,
325
- # :block_count=>4
390
+ # Nanook::Account => {
391
+ # :frontier => Nanook::Block,
392
+ # :open_block => Nanook::Block,
393
+ # :representative_block => Nanook::Block,
394
+ # :representative => Nanook::Account,
395
+ # :balance => 1143.7,
396
+ # :last_modified_at => Time,
397
+ # :block_count => 4
398
+ # :weight => 5
399
+ # :pending => 2.0
326
400
  # },
327
- # :nano_3c3ettq59kijuuad5fnaq35itc9schtr4r7r6rjhmwjbairowzq3wi5ap7h8=>{ ... }
401
+ # Nanook::Account => { ... }
328
402
  # }
329
403
  #
330
- # @param [Integer] limit number of accounts to return in the ledger (default is 1)
331
- # @param [Time] modified_since return only accounts modified in the local database after this time
404
+ # @param limit [Integer] number of accounts to return in the ledger (default is 1000)
405
+ # @param modified_since [Time] optional. Return only accounts modified in the local database after this time (default is from the unix epoch)
332
406
  # @param unit (see #balance)
333
- # @return [Hash{Symbol=>String|Integer}]
334
- def ledger(limit: 1, modified_since:nil, unit: Nanook.default_unit)
335
- unless Nanook::UNITS.include?(unit)
336
- raise ArgumentError.new("Unsupported unit: #{unit}")
337
- end
338
-
339
- params = { count: limit }
340
-
341
- unless modified_since.nil?
342
- params[:modified_since] = modified_since.to_i
343
- end
344
-
345
- response = rpc(:ledger, params)[:accounts]
346
-
347
- return response if unit == :raw
407
+ # @param sort [Symbol] default +:asc+. When set to +:desc+ the ledger will be returned oldest to newest.
408
+ # @return [Hash{Nanook::Account=>String|Integer}]
409
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
410
+ def ledger(limit: 1000, modified_since: 0, unit: Nanook.default_unit, sort: :asc)
411
+ validate_unit!(unit)
412
+
413
+ params = {
414
+ count: limit,
415
+ sorting: (sort == :desc),
416
+ modified_since: modified_since.to_i,
417
+ _access: :accounts,
418
+ _coerce: Hash
419
+ }
420
+
421
+ response = rpc(:ledger, params)
422
+
423
+ r = response.map do |account_id, ledger|
424
+ if unit == :nano
425
+ ledger[:balance] = raw_to_NANO(ledger[:balance])
426
+ ledger[:pending] = raw_to_NANO(ledger[:pending])
427
+ ledger[:weight] = raw_to_NANO(ledger[:weight])
428
+ end
348
429
 
349
- r = response.map do |account_id, l|
350
- l[:balance] = Nanook::Util.raw_to_NANO(l[:balance])
430
+ ledger[:last_modified_at] = as_time(ledger.delete(:modified_timestamp))
431
+ ledger[:representative] = as_account(ledger[:representative])
432
+ ledger[:representative_block] = as_block(ledger[:representative_block])
433
+ ledger[:open_block] = as_block(ledger[:open_block])
434
+ ledger[:frontier] = as_block(ledger[:frontier])
351
435
 
352
- [account_id, l]
436
+ [as_account(account_id), ledger]
353
437
  end
354
438
 
355
- Hash[r].to_symbolized_hash
439
+ Hash[r]
356
440
  end
357
441
 
358
442
  # Information about pending blocks (payments) that are
@@ -368,7 +452,7 @@ class Nanook
368
452
  #
369
453
  # ==== Examples:
370
454
  #
371
- # account.pending # => ["000D1BA..."]
455
+ # account.pending # => [Nanook::Block, ..."]
372
456
  #
373
457
  # Asking for more detail to be returned:
374
458
  #
@@ -378,39 +462,50 @@ class Nanook
378
462
  #
379
463
  # [
380
464
  # {
381
- # block: "000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
465
+ # block: Nanook::Block,
382
466
  # amount: 6,
383
- # source: "nano_3dcfozsmekr1tr9skf1oa5wbgmxt81qepfdnt7zicq5x3hk65fg4fqj58mbr"
467
+ # source: Nanook::Account
384
468
  # },
385
469
  # { ... }
386
470
  # ]
387
471
  #
388
472
  # @param limit [Integer] number of pending blocks to return (default is 1000)
389
- # @param detailed [Boolean]return a more complex Hash of pending block information (default is +false+)
473
+ # @param detailed [Boolean] return a more complex Hash of pending block information (default is +false+)
474
+ # @param allow_unconfirmed [Boolean] +false+ by default. When +false+ only returns block which have their confirmation
475
+ # height set or are undergoing confirmation height processing.
390
476
  # @param unit (see #balance)
391
- #
392
- # @return [Array<String>]
393
- # @return [Array<Hash{Symbol=>String|Integer}>]
394
- def pending(limit: 1000, detailed: false, unit: Nanook.default_unit)
395
- unless Nanook::UNITS.include?(unit)
396
- raise ArgumentError.new("Unsupported unit: #{unit}")
397
- end
398
-
399
- params = { count: limit }
477
+ # @param sorted [Boolean] false by default. Additionally sorts the blocks by their amounts in descending order.
478
+ #
479
+ # @return [Array<Nanook::Block>]
480
+ # @return [Array<Hash{Symbol=>Nanook::Block|Nanook::Account|Integer}>]
481
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
482
+ def pending(limit: 1000, detailed: false, allow_unconfirmed: false, unit: Nanook.default_unit, sorted: false)
483
+ validate_unit!(unit)
484
+
485
+ params = {
486
+ count: limit,
487
+ sorting: sorted,
488
+ include_only_confirmed: !allow_unconfirmed,
489
+ _access: :blocks,
490
+ _coerce: (detailed ? Hash : Array)
491
+ }
400
492
  params[:source] = true if detailed
401
493
 
402
- response = rpc(:pending, params)[:blocks]
403
- response = Nanook::Util.coerce_empty_string_to_type(response, (detailed ? Hash : Array))
494
+ response = rpc(:pending, params)
404
495
 
405
- return response unless detailed
496
+ unless detailed
497
+ return response.map do |block|
498
+ as_block(block)
499
+ end
500
+ end
406
501
 
407
502
  response.map do |key, val|
408
- p = val.merge(block: key.to_s)
409
-
410
- if unit == :nano
411
- p[:amount] = Nanook::Util.raw_to_NANO(p[:amount])
412
- end
503
+ p = val.merge(
504
+ block: as_block(key.to_s),
505
+ source: as_account(val[:source])
506
+ )
413
507
 
508
+ p[:amount] = raw_to_NANO(p[:amount]) if unit == :nano
414
509
  p
415
510
  end
416
511
  end
@@ -419,23 +514,29 @@ class Nanook
419
514
  #
420
515
  # Weight is determined by the account's balance, and represents
421
516
  # the voting weight that account has on the network. Only accounts
422
- # with greater than 256 weight can vote.
517
+ # with greater than 0.1% of the online voting weight and are on a node
518
+ # configured to vote can vote.
423
519
  #
424
520
  # ==== Example:
425
521
  #
426
522
  # account.weight # => 0
427
523
  #
428
- # @return [Integer] the account's weight
429
- def weight
430
- rpc(:account_weight)[:weight]
524
+ # @return [Integer|Float] the account's weight
525
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
526
+ def weight(unit: Nanook.default_unit)
527
+ validate_unit!(unit)
528
+
529
+ weight = rpc(:account_weight, _access: :weight)
530
+
531
+ return weight unless unit == :nano
532
+
533
+ raw_to_NANO(weight)
431
534
  end
432
535
 
433
536
  private
434
537
 
435
- def rpc(action, params={})
436
- p = @account.nil? ? {} : { account: @account }
437
- @rpc.call(action, p.merge(params))
538
+ def rpc(action, params = {})
539
+ @rpc.call(action, { account: @account }.merge(params))
438
540
  end
439
-
440
541
  end
441
542
  end