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.
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 which 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,62 @@ 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
+ response[:confirmation_height_frontier] = as_block(response[:confirmation_height_frontier]) if response[:confirmation_height_frontier]
332
+ response[:last_modified_at] = as_time(response.delete(:modified_timestamp))
284
333
 
285
334
  if unit == :nano
286
- response[:balance] = Nanook::Util.raw_to_NANO(response[:balance])
335
+ response.merge!(
336
+ balance: raw_to_NANO(response[:balance]),
337
+ pending: raw_to_NANO(response[:pending]),
338
+ weight: raw_to_NANO(response[:weight])
339
+ )
287
340
  end
288
341
 
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
342
+ response
302
343
  end
303
344
 
304
- def inspect
305
- "#{self.class.name}(id: \"#{id}\", object_id: \"#{"0x00%x" % (object_id << 1)}\")"
345
+ # @return [String]
346
+ def to_s
347
+ "#{self.class.name}(id: \"#{short_id}\")"
306
348
  end
349
+ alias inspect to_s
307
350
 
308
351
  # Information about the given account as well as other
309
352
  # accounts up the ledger. The number of accounts returned is determined
@@ -316,43 +359,56 @@ class Nanook
316
359
  # Example response:
317
360
  #
318
361
  # {
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
362
+ # Nanook::Account => {
363
+ # :frontier => Nanook::Block,
364
+ # :open_block => Nanook::Block,
365
+ # :representative_block => Nanook::Block,
366
+ # :representative => Nanook::Account,
367
+ # :balance => 1143.7,
368
+ # :last_modified_at => Time,
369
+ # :block_count => 4
370
+ # :weight => 5
371
+ # :pending => 2.0
326
372
  # },
327
- # :xrb_3c3ettq59kijuuad5fnaq35itc9schtr4r7r6rjhmwjbairowzq3wi5ap7h8=>{ ... }
373
+ # Nanook::Account => { ... }
328
374
  # }
329
375
  #
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
376
+ # @param limit [Integer] number of accounts to return in the ledger (default is 1000)
377
+ # @param modified_since [Time] optional. Return only accounts modified in the local database after this time (default is from the unix epoch)
332
378
  # @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
379
+ # @param sort [Symbol] default +:asc+. When set to +:desc+ the ledger will be returned oldest to newest.
380
+ # @return [Hash{Nanook::Account=>String|Integer}]
381
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
382
+ def ledger(limit: 1000, modified_since: 0, unit: Nanook.default_unit, sort: :asc)
383
+ validate_unit!(unit)
384
+
385
+ params = {
386
+ count: limit,
387
+ sorting: (sort == :desc),
388
+ modified_since: modified_since.to_i,
389
+ _access: :accounts,
390
+ _coerce: Hash
391
+ }
392
+
393
+ response = rpc(:ledger, params)
394
+
395
+ r = response.map do |account_id, ledger|
396
+ if unit == :nano
397
+ ledger[:balance] = raw_to_NANO(ledger[:balance])
398
+ ledger[:pending] = raw_to_NANO(ledger[:pending])
399
+ ledger[:weight] = raw_to_NANO(ledger[:weight])
400
+ end
348
401
 
349
- r = response.map do |account_id, l|
350
- l[:balance] = Nanook::Util.raw_to_NANO(l[:balance])
402
+ ledger[:last_modified_at] = as_time(ledger.delete(:modified_timestamp))
403
+ ledger[:representative] = as_account(ledger[:representative]) if ledger[:representative]
404
+ ledger[:representative_block] = as_block(ledger[:representative_block]) if ledger[:representative_block]
405
+ ledger[:open_block] = as_block(ledger[:open_block]) if ledger[:open_block]
406
+ ledger[:frontier] = as_block(ledger[:frontier]) if ledger[:frontier]
351
407
 
352
- [account_id, l]
408
+ [as_account(account_id), ledger]
353
409
  end
354
410
 
355
- Hash[r].to_symbolized_hash
411
+ Hash[r]
356
412
  end
357
413
 
358
414
  # Information about pending blocks (payments) that are
@@ -368,7 +424,7 @@ class Nanook
368
424
  #
369
425
  # ==== Examples:
370
426
  #
371
- # account.pending # => ["000D1BA..."]
427
+ # account.pending # => [Nanook::Block, ..."]
372
428
  #
373
429
  # Asking for more detail to be returned:
374
430
  #
@@ -378,9 +434,9 @@ class Nanook
378
434
  #
379
435
  # [
380
436
  # {
381
- # block: "000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
437
+ # block: Nanook::Block,
382
438
  # amount: 6,
383
- # source: "xrb_3dcfozsmekr1tr9skf1oa5wbgmxt81qepfdnt7zicq5x3hk65fg4fqj58mbr"
439
+ # source: Nanook::Account
384
440
  # },
385
441
  # { ... }
386
442
  # ]
@@ -389,28 +445,34 @@ class Nanook
389
445
  # @param detailed [Boolean]return a more complex Hash of pending block information (default is +false+)
390
446
  # @param unit (see #balance)
391
447
  #
392
- # @return [Array<String>]
393
- # @return [Array<Hash{Symbol=>String|Integer}>]
448
+ # @return [Array<Nanook::Block>]
449
+ # @return [Array<Hash{Symbol=>Nanook::Block|Nanook::Account|Integer}>]
450
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
394
451
  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
452
+ validate_unit!(unit)
398
453
 
399
- params = { count: limit }
454
+ params = {
455
+ count: limit,
456
+ _access: :blocks,
457
+ _coerce: (detailed ? Hash : Array)
458
+ }
400
459
  params[:source] = true if detailed
401
460
 
402
- response = rpc(:pending, params)[:blocks]
403
- response = Nanook::Util.coerce_empty_string_to_type(response, (detailed ? Hash : Array))
461
+ response = rpc(:pending, params)
404
462
 
405
- return response unless detailed
463
+ unless detailed
464
+ return response.map do |block|
465
+ as_block(block)
466
+ end
467
+ end
406
468
 
407
469
  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
470
+ p = val.merge(
471
+ block: as_block(key.to_s),
472
+ source: as_account(val[:source])
473
+ )
413
474
 
475
+ p[:amount] = raw_to_NANO(p[:amount]) if unit == :nano
414
476
  p
415
477
  end
416
478
  end
@@ -419,23 +481,29 @@ class Nanook
419
481
  #
420
482
  # Weight is determined by the account's balance, and represents
421
483
  # the voting weight that account has on the network. Only accounts
422
- # with greater than 256 weight can vote.
484
+ # with greater than 0.1% of the online voting weight and are on a node
485
+ # configured to vote can vote.
423
486
  #
424
487
  # ==== Example:
425
488
  #
426
489
  # account.weight # => 0
427
490
  #
428
- # @return [Integer] the account's weight
429
- def weight
430
- rpc(:account_weight)[:weight]
491
+ # @return [Integer|Float] the account's weight
492
+ # @raise [Nanook::NanoUnitError] if `unit` is invalid
493
+ def weight(unit: Nanook.default_unit)
494
+ validate_unit!(unit)
495
+
496
+ weight = rpc(:account_weight, _access: :weight)
497
+
498
+ return weight unless unit == :nano
499
+
500
+ raw_to_NANO(weight)
431
501
  end
432
502
 
433
503
  private
434
504
 
435
- def rpc(action, params={})
436
- p = @account.nil? ? {} : { account: @account }
437
- @rpc.call(action, p.merge(params))
505
+ def rpc(action, params = {})
506
+ @rpc.call(action, { account: @account }.merge(params))
438
507
  end
439
-
440
508
  end
441
509
  end