nanook 2.4.0 → 3.1.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,38 +37,39 @@ 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
 
58
64
  # Returns a new instance of {Nanook::Account}.
59
65
  #
60
66
  # ==== Example:
61
- # account = Nanook.new.account("xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000")
67
+ # account = Nanook.new.account("nano_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000")
62
68
  #
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)
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]) if response[:genesis_block]
174
+ response[:timestamp] = as_time(response[:timestamp]) if 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
  #
@@ -8,17 +11,45 @@ class Nanook
8
11
  # Initialize this class through the convenient {Nanook#account} method:
9
12
  #
10
13
  # nanook = Nanook.new
11
- # account = nanook.account("xrb_...")
14
+ # account = nanook.account("nano_...")
12
15
  #
13
16
  # Or compose the longhand way like this:
14
17
  #
15
18
  # rpc_conn = Nanook::Rpc.new
16
- # account = Nanook::Account.new(rpc_conn, "xrb_...")
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
- # :xrb_13bqhi1cdqq8yb9szneoc38qk899d58i5rcrgdk5mkdm86hekpoez3zxw5sd=>500000000000000000000000000000000000,
34
- # :xrb_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
43
-
44
- response = rpc(:delegators)[:delegators]
72
+ validate_unit!(unit)
45
73
 
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: "xrb_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 # => "xrb_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) if 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
@@ -183,37 +247,23 @@ class Nanook
183
247
  # Represents the unit that the balances will be returned in.
184
248
  # Note: this method interprets
185
249
  # +: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.
250
+ # See {https://docs.nano.org/protocol-design/distribution-and-units/#unit-dividers What are Nano's Units}
251
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
189
252
  # @return [Hash{Symbol=>Integer|Float}]
190
253
  def balance(unit: Nanook.default_unit)
191
- unless Nanook::UNITS.include?(unit)
192
- raise ArgumentError.new("Unsupported unit: #{unit}")
193
- end
254
+ validate_unit!(unit)
194
255
 
195
256
  rpc(:account_balance).tap do |r|
196
257
  if unit == :nano
197
- r[:balance] = Nanook::Util.raw_to_NANO(r[:balance])
198
- r[:pending] = Nanook::Util.raw_to_NANO(r[:pending])
258
+ r[:balance] = raw_to_NANO(r[:balance])
259
+ r[:pending] = raw_to_NANO(r[:pending])
199
260
  end
200
261
  end
201
262
  end
202
263
 
203
264
  # @return [Integer] number of blocks for this account
204
265
  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 # => "xrb_16u..."
213
- #
214
- # @return [String] the id of the account
215
- def id
216
- @account
266
+ rpc(:account_block_count, _access: :block_count)
217
267
  end
218
268
 
219
269
  # Information about the account.
@@ -225,7 +275,7 @@ class Nanook
225
275
  # Example response:
226
276
  #
227
277
  # {
228
- # id: "xrb_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5",
278
+ # id: "nano_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5",
229
279
  # balance: 11.439597000000001,
230
280
  # block_count: 4,
231
281
  # frontier: "2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570",
@@ -241,69 +291,64 @@ class Nanook
241
291
  # Example response:
242
292
  #
243
293
  # {
244
- # id: "xrb_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5",
294
+ # id: "nano_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5",
245
295
  # balance: 11.439597000000001,
246
296
  # block_count: 4,
247
- # frontier: "2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570",
248
- # modified_timestamp: 1520500357,
249
- # open_block: "C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9",
250
- # pending: 0,
251
- # public_key: "A82C906460046D230D7D37C6663723DC3EFCECC4B3254EBF45294B66746F4FEF",
252
- # representative: "xrb_3pczxuorp48td8645bs3m6c3xotxd3idskrenmi65rbrga5zmkemzhwkaznh",
253
- # representative_block: "C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9",
254
- # weight: 0
297
+ # frontier: Nanook::Block,
298
+ # last_modified_at: Time,
299
+ # open_block: Nanook::Block,
300
+ # pending: 1.0,
301
+ # representative: Nanook::Account,
302
+ # representative_block: Nanook::Block,
303
+ # weight: 0.1
255
304
  # }
256
305
  #
257
- # @param detailed [Boolean] (default is false). When +true+, four
258
- # additional calls are made to the RPC to return more information
259
306
  # @param unit (see #balance)
260
- # @return [Hash{Symbol=>String|Integer|Float}] information about the account containing:
307
+ # @return [Hash{Symbol=>String|Integer|Float|Nanook::Account|Nanook::Block|Time}] information about the account containing:
261
308
  # [+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
309
+ # [+frontier+] The latest {Nanook::Block}
310
+ # [+confirmation_height+] Confirmation height
311
+ # [+confirmation_height_frontier+] The {Nanook::Block} of the confirmation height
312
+ # [+pending+] Pending balance in either NANO or raw (depending on the <tt>unit:</tt> argument)
313
+ # [+open_block+] The first {Nanook::Block} in every account's blockchain. When this block was published the account was officially open
314
+ # [+representative_block+] The {Nanook::Block} that named the representative for the account
315
+ # [+balance+] Balance in either NANO or raw (depending on the <tt>unit:</tt> argument)
316
+ # [+last_modified_at+] Time of when the account was last modified in UTC
317
+ # [+representative+] Representative {Nanook::Account}
267
318
  # [+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
319
  # [+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
320
+ #
321
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
322
+ def info(unit: Nanook.default_unit)
323
+ validate_unit!(unit)
281
324
 
282
- response = rpc(:account_info)
325
+ response = rpc(:account_info, representative: true, weight: true, pending: true)
283
326
  response.merge!(id: @account)
327
+ response[:frontier] = as_block(response[:frontier]) if response[:frontier]
328
+ response[:open_block] = as_block(response[:open_block]) if response[:open_block]
329
+ response[:representative_block] = as_block(response[:representative_block]) if response[:representative_block]
330
+ response[:representative] = as_account(response[:representative]) if response[:representative]
331
+ if response[:confirmation_height_frontier]
332
+ response[:confirmation_height_frontier] = as_block(response[:confirmation_height_frontier])
333
+ end
334
+ response[:last_modified_at] = as_time(response.delete(:modified_timestamp))
284
335
 
285
336
  if unit == :nano
286
- response[:balance] = Nanook::Util.raw_to_NANO(response[:balance])
337
+ response.merge!(
338
+ balance: raw_to_NANO(response[:balance]),
339
+ pending: raw_to_NANO(response[:pending]),
340
+ weight: raw_to_NANO(response[:weight])
341
+ )
287
342
  end
288
343
 
289
- # Return the response if we don't need any more info
290
- return response unless detailed
291
-
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
- })
299
-
300
- # Sort this new hash by keys
301
- Hash[response.sort].to_symbolized_hash
344
+ response
302
345
  end
303
346
 
304
- def inspect
305
- "#{self.class.name}(id: \"#{id}\", object_id: \"#{"0x00%x" % (object_id << 1)}\")"
347
+ # @return [String]
348
+ def to_s
349
+ "#{self.class.name}(id: \"#{short_id}\")"
306
350
  end
351
+ alias inspect to_s
307
352
 
308
353
  # Information about the given account as well as other
309
354
  # accounts up the ledger. The number of accounts returned is determined
@@ -316,43 +361,56 @@ class Nanook
316
361
  # Example response:
317
362
  #
318
363
  # {
319
- # :xrb_3c3ek3k8135f6e8qtfy8eruk9q3yzmpebes7btzncccdest8ymzhjmnr196j=>{
320
- # :frontier=>"2C3C570EA8898443C0FD04A1C385A3E3A8C985AD792635FCDCEBB30ADF6A0570",
321
- # :open_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9",
322
- # :representative_block=>"C82376314C387080A753871A32AD70F4168080C317C5E67356F0A62EB5F34FF9",
323
- # :balance=>11439597000000000000000000000000,
324
- # :modified_timestamp=>1520500357,
325
- # :block_count=>4
364
+ # Nanook::Account => {
365
+ # :frontier => Nanook::Block,
366
+ # :open_block => Nanook::Block,
367
+ # :representative_block => Nanook::Block,
368
+ # :representative => Nanook::Account,
369
+ # :balance => 1143.7,
370
+ # :last_modified_at => Time,
371
+ # :block_count => 4
372
+ # :weight => 5
373
+ # :pending => 2.0
326
374
  # },
327
- # :xrb_3c3ettq59kijuuad5fnaq35itc9schtr4r7r6rjhmwjbairowzq3wi5ap7h8=>{ ... }
375
+ # Nanook::Account => { ... }
328
376
  # }
329
377
  #
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
378
+ # @param limit [Integer] number of accounts to return in the ledger (default is 1000)
379
+ # @param modified_since [Time] optional. Return only accounts modified in the local database after this time (default is from the unix epoch)
332
380
  # @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
381
+ # @param sort [Symbol] default +:asc+. When set to +:desc+ the ledger will be returned oldest to newest.
382
+ # @return [Hash{Nanook::Account=>String|Integer}]
383
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
384
+ def ledger(limit: 1000, modified_since: 0, unit: Nanook.default_unit, sort: :asc)
385
+ validate_unit!(unit)
386
+
387
+ params = {
388
+ count: limit,
389
+ sorting: (sort == :desc),
390
+ modified_since: modified_since.to_i,
391
+ _access: :accounts,
392
+ _coerce: Hash
393
+ }
394
+
395
+ response = rpc(:ledger, params)
396
+
397
+ r = response.map do |account_id, ledger|
398
+ if unit == :nano
399
+ ledger[:balance] = raw_to_NANO(ledger[:balance])
400
+ ledger[:pending] = raw_to_NANO(ledger[:pending])
401
+ ledger[:weight] = raw_to_NANO(ledger[:weight])
402
+ end
348
403
 
349
- r = response.map do |account_id, l|
350
- l[:balance] = Nanook::Util.raw_to_NANO(l[:balance])
404
+ ledger[:last_modified_at] = as_time(ledger.delete(:modified_timestamp))
405
+ ledger[:representative] = as_account(ledger[:representative]) if ledger[:representative]
406
+ ledger[:representative_block] = as_block(ledger[:representative_block]) if ledger[:representative_block]
407
+ ledger[:open_block] = as_block(ledger[:open_block]) if ledger[:open_block]
408
+ ledger[:frontier] = as_block(ledger[:frontier]) if ledger[:frontier]
351
409
 
352
- [account_id, l]
410
+ [as_account(account_id), ledger]
353
411
  end
354
412
 
355
- Hash[r].to_symbolized_hash
413
+ Hash[r]
356
414
  end
357
415
 
358
416
  # Information about pending blocks (payments) that are
@@ -368,7 +426,7 @@ class Nanook
368
426
  #
369
427
  # ==== Examples:
370
428
  #
371
- # account.pending # => ["000D1BA..."]
429
+ # account.pending # => [Nanook::Block, ..."]
372
430
  #
373
431
  # Asking for more detail to be returned:
374
432
  #
@@ -378,9 +436,9 @@ class Nanook
378
436
  #
379
437
  # [
380
438
  # {
381
- # block: "000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
439
+ # block: Nanook::Block,
382
440
  # amount: 6,
383
- # source: "xrb_3dcfozsmekr1tr9skf1oa5wbgmxt81qepfdnt7zicq5x3hk65fg4fqj58mbr"
441
+ # source: Nanook::Account
384
442
  # },
385
443
  # { ... }
386
444
  # ]
@@ -389,28 +447,34 @@ class Nanook
389
447
  # @param detailed [Boolean]return a more complex Hash of pending block information (default is +false+)
390
448
  # @param unit (see #balance)
391
449
  #
392
- # @return [Array<String>]
393
- # @return [Array<Hash{Symbol=>String|Integer}>]
450
+ # @return [Array<Nanook::Block>]
451
+ # @return [Array<Hash{Symbol=>Nanook::Block|Nanook::Account|Integer}>]
452
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
394
453
  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
454
+ validate_unit!(unit)
398
455
 
399
- params = { count: limit }
456
+ params = {
457
+ count: limit,
458
+ _access: :blocks,
459
+ _coerce: (detailed ? Hash : Array)
460
+ }
400
461
  params[:source] = true if detailed
401
462
 
402
- response = rpc(:pending, params)[:blocks]
403
- response = Nanook::Util.coerce_empty_string_to_type(response, (detailed ? Hash : Array))
463
+ response = rpc(:pending, params)
404
464
 
405
- return response unless detailed
465
+ unless detailed
466
+ return response.map do |block|
467
+ as_block(block)
468
+ end
469
+ end
406
470
 
407
471
  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
472
+ p = val.merge(
473
+ block: as_block(key.to_s),
474
+ source: as_account(val[:source])
475
+ )
413
476
 
477
+ p[:amount] = raw_to_NANO(p[:amount]) if unit == :nano
414
478
  p
415
479
  end
416
480
  end
@@ -419,23 +483,29 @@ class Nanook
419
483
  #
420
484
  # Weight is determined by the account's balance, and represents
421
485
  # the voting weight that account has on the network. Only accounts
422
- # with greater than 256 weight can vote.
486
+ # with greater than 0.1% of the online voting weight and are on a node
487
+ # configured to vote can vote.
423
488
  #
424
489
  # ==== Example:
425
490
  #
426
491
  # account.weight # => 0
427
492
  #
428
- # @return [Integer] the account's weight
429
- def weight
430
- rpc(:account_weight)[:weight]
493
+ # @return [Integer|Float] the account's weight
494
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
495
+ def weight(unit: Nanook.default_unit)
496
+ validate_unit!(unit)
497
+
498
+ weight = rpc(:account_weight, _access: :weight)
499
+
500
+ return weight unless unit == :nano
501
+
502
+ raw_to_NANO(weight)
431
503
  end
432
504
 
433
505
  private
434
506
 
435
- def rpc(action, params={})
436
- p = @account.nil? ? {} : { account: @account }
437
- @rpc.call(action, p.merge(params))
507
+ def rpc(action, params = {})
508
+ @rpc.call(action, { account: @account }.merge(params))
438
509
  end
439
-
440
510
  end
441
511
  end