nanook 2.5.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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