xrbp 0.2.1 → 0.2.2

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/examples/nodestore1.rb +12 -7
  3. data/lib/xrbp/core_ext.rb +27 -0
  4. data/lib/xrbp/crypto/account.rb +28 -3
  5. data/lib/xrbp/nodestore.rb +6 -0
  6. data/lib/xrbp/nodestore/amendments.rb +13 -0
  7. data/lib/xrbp/nodestore/backends/decompressor.rb +8 -6
  8. data/lib/xrbp/nodestore/backends/nudb.rb +2 -0
  9. data/lib/xrbp/nodestore/backends/rocksdb.rb +1 -0
  10. data/lib/xrbp/nodestore/db.rb +5 -387
  11. data/lib/xrbp/nodestore/fees.rb +19 -0
  12. data/lib/xrbp/nodestore/format.rb +72 -1
  13. data/lib/xrbp/nodestore/ledger.rb +272 -0
  14. data/lib/xrbp/nodestore/parser.rb +407 -0
  15. data/lib/xrbp/nodestore/protocol.rb +5 -0
  16. data/lib/xrbp/nodestore/protocol/currency.rb +11 -0
  17. data/lib/xrbp/nodestore/protocol/indexes.rb +109 -0
  18. data/lib/xrbp/nodestore/protocol/issue.rb +26 -0
  19. data/lib/xrbp/nodestore/protocol/quality.rb +10 -0
  20. data/lib/xrbp/nodestore/protocol/rate.rb +21 -0
  21. data/lib/xrbp/nodestore/shamap.rb +447 -0
  22. data/lib/xrbp/nodestore/shamap/errors.rb +8 -0
  23. data/lib/xrbp/nodestore/shamap/inner_node.rb +98 -0
  24. data/lib/xrbp/nodestore/shamap/item.rb +14 -0
  25. data/lib/xrbp/nodestore/shamap/node.rb +49 -0
  26. data/lib/xrbp/nodestore/shamap/node_factory.rb +120 -0
  27. data/lib/xrbp/nodestore/shamap/node_id.rb +83 -0
  28. data/lib/xrbp/nodestore/shamap/tagged_cache.rb +20 -0
  29. data/lib/xrbp/nodestore/shamap/tree_node.rb +21 -0
  30. data/lib/xrbp/nodestore/sle.rb +4 -0
  31. data/lib/xrbp/nodestore/sle/st_account.rb +8 -0
  32. data/lib/xrbp/nodestore/sle/st_amount.rb +226 -0
  33. data/lib/xrbp/nodestore/sle/st_ledger_entry.rb +24 -0
  34. data/lib/xrbp/nodestore/sle/st_object.rb +46 -0
  35. data/lib/xrbp/nodestore/sqldb.rb +23 -0
  36. data/lib/xrbp/nodestore/uint.rb +7 -0
  37. data/lib/xrbp/version.rb +1 -1
  38. data/spec/xrbp/nodestore/backends/nudb_spec.rb +3 -1
  39. data/spec/xrbp/nodestore/backends/rocksdb_spec.rb +1 -1
  40. data/spec/xrbp/nodestore/{backends/db_parser.rb → db_parser.rb} +2 -2
  41. data/spec/xrbp/nodestore/ledger_access.rb +17 -0
  42. metadata +30 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6390bfb57eaa26b5285cb3f3c3fb10d85d0a3a81e09e3c3b1e52eff6d4a00514
4
- data.tar.gz: b616a6a4324fbd34ec8e77529c2827fca77949f635a0785ab33d8ca867baccc2
3
+ metadata.gz: cdd7b83c144366a77c0a68287e2ae511fa2878ef5b3be0e0f24b0b4d9e6a1be5
4
+ data.tar.gz: 1db244c92cefaca23244dd7a20cbb89c0d9e2fc25ab10659483aaa8187f0f117
5
5
  SHA512:
6
- metadata.gz: 6b96064684b5ef84f089808649e4b281e9f055610543deec66b8d09122c7a7b545da95f44b55e60a8e73e6402ec3d03f52cfc79ea9ee85bacc9e66003dbf0b01
7
- data.tar.gz: 1f9f959987d864d4500ffc5ba2172c5341a9705a3eec9e3af09de88bfa9bf5f8feabbae0a7030db3437af75348a924b2535abb0b4661192aa44b3d5e300715df
6
+ metadata.gz: 42cdd0d6b998b3550a89233b90c86676fe9e56f7a4981cfad96f209fb3272ae2f669f5b16de399318596d13fb9c0fc2b087fa968155a4f30f211fed46cce74c4
7
+ data.tar.gz: ee8e543e9e04557f8e8355dbd4f56a19f9176ba8e3f24c87db128298f38caa0386bf3763180bee734c94f7e91aaba3341c4689b4f7d4a53b3d4d0fa49d22b6d9
@@ -9,11 +9,16 @@ db = XRBP::NodeStore::Backends::RocksDB.new "/var/lib/rippled/rocksdb/rippledb.0
9
9
  #require 'xrbp/nodestore/backends/nudb'
10
10
  #db = XRBP::NodeStore::Backends::NuDB.new "/var/lib/rippled/nudb/"
11
11
 
12
- #ledger = "B506ADD630CB707044B4BFFCD943C1395966692A13DD618E5BD0978A006B43BD"
13
- #ledger = [ledger].pack("H*")
14
- #puts db.ledger(ledger)
12
+ ledger = "32E073D7E4D722D956F7FDE095F756FBB86DC9CA487EB0D9ABF5151A8D88F912"
13
+ ledger = [ledger].pack("H*")
14
+ puts db.ledger(ledger)
15
15
 
16
- #account = "0001bf7468341666f1f47a95e0f4d88e68b5fc7d20d77437cb22954fbbfe6127"
17
- account = "02c46b3a4130d0a329c47f0da61b829aa5d1ae53c5817e475bcd794e5107be44"
18
- account = [account].pack("H*")
19
- puts db.account(account)
16
+ gw1 = 'razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA'
17
+ iou1 = {:currency => 'XRP', :account => XRBP::Crypto.xrp_account}
18
+ iou2 = {:currency => 'CNY', :account => gw1}
19
+ nledger = XRBP::NodeStore::Ledger.new(:db => db, :hash => ledger)
20
+ puts nledger.order_book iou1, iou2
21
+ puts nledger.txs
22
+
23
+ require 'xrbp/nodestore/sqldb'
24
+ puts XRBP::NodeStore::SQLDB.new("/var/lib/rippled/").ledger_hash_for_seq(49340234)
@@ -29,6 +29,22 @@ class String
29
29
  def to_bn
30
30
  bytes.inject(0) { |bn, b| (bn << 8) | b }
31
31
  end
32
+
33
+ def all?(&bl)
34
+ each_char { |c| return false unless bl.call(c) }
35
+ return true
36
+ end
37
+
38
+ def zero?
39
+ return self == "\0" if size == 1
40
+ all? { |c| c.zero? }
41
+ end
42
+
43
+ # scan(regex) will not work as we need to process
44
+ # binary strings (\n's seem to trip scan up)
45
+ def chunk(size)
46
+ ((self.length + size - 1) / size).times.collect { |i| self[i * size, size] }
47
+ end
32
48
  end
33
49
 
34
50
  # @private
@@ -44,3 +60,14 @@ class Integer
44
60
  b
45
61
  end
46
62
  end
63
+
64
+ # @private
65
+ class Array
66
+ def rjust!(n, x)
67
+ insert(0, *Array.new([0, n-length].max, x))
68
+ end
69
+
70
+ def ljust!(n, x)
71
+ fill(x, length...n)
72
+ end
73
+ end
@@ -2,12 +2,13 @@ require 'base58'
2
2
 
3
3
  module XRBP
4
4
  module Crypto
5
- # Generate and new XRPL account.
5
+ # Generate a new XRPL account.
6
6
  #
7
7
  # @param key [Symbol, Hash] key type to generate or key itself (optional)
8
8
  # @return [Hash] account details containing id and pub/priv key pair
9
9
  def self.account(key=nil)
10
10
  pub = nil
11
+ account_id = nil
11
12
  if key == :secp256k1 || key.nil?
12
13
  key = Key::secp256k1
13
14
  pub = key[:public]
@@ -17,21 +18,45 @@ module XRBP
17
18
  pub = "\xED" + key[:public]
18
19
 
19
20
  elsif key.is_a?(Hash)
20
- pub = key[:public]
21
+ if key[:account_id]
22
+ account_id = key[:account_id]
23
+ elsif key[:public]
24
+ pub = key[:public]
25
+ end
21
26
 
22
27
  else
23
28
  pub = key
24
29
  key = {:public => pub}
25
30
  end
26
31
 
32
+ raise "must specify pub or account_id" unless pub || account_id
33
+
27
34
  sha256 = OpenSSL::Digest::SHA256.new
28
35
  ripemd160 = OpenSSL::Digest::RIPEMD160.new
29
- account_id = [Key::TOKEN_TYPES[:account_id]].pack("C") + ripemd160.digest(sha256.digest([pub].pack("H*")))
36
+ account_id = [Key::TOKEN_TYPES[:account_id]].pack("C") + ripemd160.digest(sha256.digest([pub].pack("H*"))) unless account_id
30
37
  chksum = sha256.digest(sha256.digest(account_id))[0..3]
31
38
 
32
39
  { :account => Base58.binary_to_base58(account_id + chksum, :ripple) }.merge(key)
33
40
  end
34
41
 
42
+ # Account Zero: https://xrpl.org/accounts.html#special-addresses
43
+ def self.xrp_account
44
+ @xrp_account ||= account(:account_id => ([0] * 21).pack("C*"))[:account]
45
+ end
46
+
47
+ # Account One: https://xrpl.org/accounts.html#special-addresses
48
+ def self.no_account
49
+ @no_account ||= account(:account_id => ([0] * 20 + [1]).pack("C*"))[:account]
50
+ end
51
+
52
+ # Return the account id for the specified XRP account.
53
+ # This is a simpler version of {parse_account} that just
54
+ # returns the binary account, _skipping_ the token and
55
+ # checksum verifications.
56
+ def self.account_id(account)
57
+ Base58.base58_to_binary(account, :ripple)[1..-5]
58
+ end
59
+
35
60
  # Extract Account ID from Address.
36
61
  #
37
62
  # @param account [String] Base58 encoded account address
@@ -1,4 +1,10 @@
1
+ require_relative './nodestore/uint'
1
2
  require_relative './nodestore/format'
3
+ require_relative './nodestore/sle'
4
+ require_relative './nodestore/shamap'
5
+ require_relative './nodestore/ledger'
6
+
7
+ require_relative './nodestore/protocol'
2
8
 
3
9
  require_relative './nodestore/db'
4
10
 
@@ -0,0 +1,13 @@
1
+ module XRBP
2
+ module NodeStore
3
+ module Amendments
4
+ def fix1141_time
5
+ @fix1141_time ||= Time.new(2016, 6, 1, 17, 0, 0, 0)
6
+ end
7
+
8
+ def fix1141?(time)
9
+ time > fix1141_time
10
+ end
11
+ end
12
+ end # module NodeStore
13
+ end # module XRBP
@@ -102,8 +102,8 @@ module XRBP
102
102
  out = [0, 0, 0, 0,
103
103
  0, 0, 0, 0,
104
104
  Format::NODE_TYPES[:unknown]] +
105
- [Format::HASH_PREFIXES.invert[:inner_node]].pack("H*")
106
- .unpack("C*")
105
+ [Format::HASH_PREFIXES[:inner_node]].pack("H*")
106
+ .unpack("C*")
107
107
 
108
108
  # extract mask indicating which hashes are set
109
109
  bytes = data.bytes
@@ -147,8 +147,8 @@ module XRBP
147
147
  out = [0, 0, 0, 0,
148
148
  0, 0, 0, 0,
149
149
  Format::NODE_TYPES[:unknown]] +
150
- [Format::HASH_PREFIXES.invert[:inner_node_v2]].pack("H*")
151
- .unpack("C*")
150
+ [Format::HASH_PREFIXES[:inner_node_v2]].pack("H*")
151
+ .unpack("C*")
152
152
 
153
153
  # Same mask / hash extraction as V1
154
154
 
@@ -205,7 +205,8 @@ module XRBP
205
205
  out = [0, 0, 0, 0,
206
206
  0, 0, 0, 0,
207
207
  Format::NODE_TYPES[:unknown]] +
208
- [Format::HASH_PREFIXES.invert[:inner_node]].pack("H*").unpack("C*")
208
+ [Format::HASH_PREFIXES[:inner_node]].pack("H*")
209
+ .unpack("C*")
209
210
  (out + data[0...512].bytes).pack("C*")
210
211
  end
211
212
 
@@ -223,7 +224,8 @@ module XRBP
223
224
  out = [0, 0, 0, 0,
224
225
  0, 0, 0, 0,
225
226
  Format::NODE_TYPES[:unknown]] +
226
- [Format::HASH_PREFIXES.invert[:inner_node_v2]].pack("H*").unpack("C*")
227
+ [Format::HASH_PREFIXES[:inner_node_v2]].pack("H*")
228
+ .unpack("C*")
227
229
 
228
230
  out += bytes[0..511]
229
231
  bytes = bytes[512..-1]
@@ -76,6 +76,8 @@ module XRBP
76
76
  :value => val)
77
77
  yield iterator
78
78
  end
79
+
80
+ return self
79
81
  end
80
82
 
81
83
  private
@@ -67,6 +67,7 @@ module XRBP
67
67
  end
68
68
 
69
69
  iterator.close
70
+ return self
70
71
  end
71
72
  end # class RocksDB
72
73
  end # module Backends
@@ -1,6 +1,8 @@
1
1
  require 'base58'
2
2
  require 'openssl'
3
3
 
4
+ require_relative './parser'
5
+
4
6
  module XRBP
5
7
  # The NodeStore is the Key/Value DB which rippled persistent stores
6
8
  # ledger data. Implemented via a backend configured at run time,
@@ -24,6 +26,9 @@ module XRBP
24
26
  include Enumerable
25
27
  include EventEmitter
26
28
 
29
+ # NodeStore Parser
30
+ include Parser
31
+
27
32
  # Return the NodeStore Ledger for the given lookup hash
28
33
  def ledger(hash)
29
34
  val = self[hash]
@@ -56,393 +61,6 @@ module XRBP
56
61
  return nil if val.nil?
57
62
  parse_inner_node(val)
58
63
  end
59
-
60
- ###
61
-
62
- private
63
-
64
- # Parsers binary ledger representation into structured ledger.
65
- #
66
- # @private
67
- def parse_ledger(ledger)
68
- obj = Format::LEDGER.decode(ledger)
69
- obj['close_time'] = XRBP::from_xrp_time(obj['close_time']).utc
70
- obj['parent_close_time'] = XRBP::from_xrp_time(obj['parent_close_time']).utc
71
- obj['parent_hash'].upcase!
72
- obj['tx_hash'].upcase!
73
- obj['account_hash'].upcase!
74
- obj
75
- end
76
-
77
- # Certain data types are prefixed with an 'encoding' header
78
- # consisting of a field and/or type. Field, type, and remaining
79
- # bytes are returned
80
- #
81
- # @private
82
- def parse_encoding(encoding)
83
- enc = encoding.unpack("C").first
84
- type = enc >> 4
85
- field = enc & 0xF
86
- encoding = encoding[1..-1]
87
-
88
- if type == 0
89
- type = encoding.unpack("C").first
90
- encoding = encoding[1..-1]
91
- end
92
-
93
- if field == 0
94
- field = encoding.unpack("C").first
95
- encoding = encoding[1..-1]
96
- end
97
-
98
- type = Format::SERIALIZED_TYPES[type]
99
- [[type, field], encoding]
100
- end
101
-
102
- # Parses binary ledger entry into hash. Data returned
103
- # in hash includes ledger entry type prefix, index,
104
- # and array of parsed fields.
105
- #
106
- # @private
107
- def parse_ledger_entry(ledger_entry)
108
- # validate parsability
109
- obj = Format::TYPE_INFER.decode(ledger_entry)
110
- node_type = Format::NODE_TYPE_CODES[obj["node_type"]]
111
- hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
112
- raise unless node_type == :account_node &&
113
- hash_prefix == :leaf_node
114
-
115
- # discard node type, and hash prefix
116
- ledger_entry = ledger_entry[13..-1]
117
-
118
- # verify encoding
119
- encoding, ledger_entry = parse_encoding(ledger_entry)
120
- raise "Invalid Ledger Entry" unless Format::ENCODINGS[encoding] == :ledger_entry_type
121
- ledger_entry = ledger_entry.bytes
122
-
123
- # first byte after encoding is ledger entry type prefix
124
- prefix = ledger_entry[0..1].pack("C*")
125
-
126
- # last 32 bytes is entry index
127
- index = ledger_entry[-32..-1].pack("C*")
128
- .unpack("H*")
129
- .first
130
- .upcase
131
-
132
- # remaining bytes are serialized object
133
- fields, remaining = parse_fields(ledger_entry[2...-32].pack("C*"))
134
- raise unless remaining.empty?
135
-
136
- # TODO instantiate class corresponding to type &
137
- # populate attributes w/ fields (?)
138
-
139
- { :type => Format::LEDGER_ENTRY_TYPE_CODES[prefix[1]],
140
- :index => index,
141
- :fields => fields }
142
- end
143
-
144
- ###
145
-
146
- # Parse and return series of fields from binary data.
147
- #
148
- # @private
149
- def parse_fields(fields)
150
- parsed = {}
151
- until fields == "" || fields == "\0" || fields.nil?
152
- encoding, fields = parse_encoding(fields)
153
- return parsed if encoding.first.nil?
154
-
155
- e = Format::ENCODINGS[encoding]
156
- value, fields = parse_field(fields, encoding)
157
- break unless value
158
- parsed[e] = convert_field(encoding, value)
159
- end
160
-
161
- return parsed, fields
162
- end
163
-
164
- # Parse single field of specified encoding from data.
165
- # Dispatches to corresponding parsing method when appropriate.
166
- #
167
- # @private
168
- def parse_field(data, encoding)
169
- length = encoding.first
170
-
171
- case length
172
- when :uint8
173
- return data.unpack("C").first, data[1..-1]
174
- when :uint16
175
- return data.unpack("S>").first, data[2..-1]
176
- when :uint32
177
- return data.unpack("L>").first, data[4..-1]
178
- when :uint64
179
- return data.unpack("Q>").first, data[8..-1]
180
- when :hash128
181
- return data.unpack("H32").first, data[16..-1]
182
- when :hash160
183
- return data.unpack("H40").first, data[20..-1]
184
- when :hash256
185
- return data.unpack("H64").first, data[32..-1]
186
- when :amount
187
- return parse_amount(data)
188
- when :vl
189
- vl, offset = parse_vl(data)
190
- return data[offset..vl+offset-1], data[vl+offset..-1]
191
- when :account
192
- return parse_account(data)
193
- when :array
194
- return parse_array(data, encoding)
195
- when :object
196
- return parse_object(data, encoding)
197
- when :pathset
198
- return parse_pathset(data)
199
- when :vector256
200
- vl, offset = parse_vl(data)
201
- return data[offset..vl+offset-1], data[vl+offset..-1]
202
- end
203
-
204
- raise
205
- end
206
-
207
- def convert_field(encoding, value)
208
- e = Format::ENCODINGS[encoding]
209
-
210
- if encoding.first == :vl
211
- return value.unpack("H*").first
212
-
213
- elsif e == :transaction_type
214
- return Format::TX_TYPES[value]
215
-
216
- elsif e == :ledger_entry_type
217
- return Format::LEDGER_ENTRY_TYPE_CODES[value.chr]
218
- end
219
-
220
- value
221
- end
222
-
223
- # Parse variable length header from data buffer. Returns length
224
- # extracted from header and the number of bytes in header.
225
- #
226
- # @private
227
- def parse_vl(data)
228
- data = data.bytes
229
- first = data.first.to_i
230
- return first, 1 if first <= 192
231
-
232
- data = data[1..-1]
233
- second = data.first.to_i
234
- if first <= 240
235
- return (193+(first-193)*256+second), 2
236
-
237
- elsif first <= 254
238
- data = data[1..-1]
239
- third = data.first.to_i
240
- return (12481 + (first-241)*65536 + second*256 + third), 3
241
- end
242
-
243
- raise
244
- end
245
-
246
- # Parse 'Amount' data type from binary data.
247
- #
248
- # @see https://developers.ripple.com/currency-formats.html
249
- #
250
- # @private
251
- def parse_amount(data)
252
- amount = data[0..7].unpack("Q>").first
253
- xrp = amount < 0x8000000000000000
254
- return (amount & 0x3FFFFFFFFFFFFFFF), data[8..-1] if xrp
255
-
256
- sign = (amount & 0x4000000000000000) >> 62 # 0 = neg / 1 = pos
257
- exp = (amount & 0x3FC0000000000000) >> 54
258
- mant = (amount & 0x003FFFFFFFFFFFFF)
259
-
260
- data = data[8..-1]
261
- currency = Format::CURRENCY_CODE.decode(data)
262
- currency = currency["iso_code"].pack("C*")
263
-
264
- data = data[Format::CURRENCY_CODE.size..-1]
265
- issuer, data = parse_account(data, 20)
266
-
267
- amount = (sign == 0 ? -1 : 1) * mant * 10 ** (exp-97)
268
-
269
- return { :amount => amount,
270
- :currency => currency,
271
- :issuer => issuer }, data
272
- end
273
-
274
- # Parse 'Account' data type from binary data.
275
- #
276
- # @private
277
- def parse_account(data, vl=nil)
278
- unless vl
279
- vl,offset = parse_vl(data)
280
- data = data[offset..-1]
281
- end
282
-
283
- acct = "\0" + data[0..vl-1]
284
- sha256 = OpenSSL::Digest::SHA256.new
285
- digest = sha256.digest(sha256.digest(acct))[0..3]
286
- acct += digest
287
- acct.force_encoding(Encoding::BINARY) # required for Base58 gem
288
- return Base58.binary_to_base58(acct, :ripple), data[vl..-1]
289
- end
290
-
291
- # Parse array of fields from binary data.
292
- #
293
- # @private
294
- def parse_array(data, encoding)
295
- e = Format::ENCODINGS[encoding]
296
- return nil, data if e == :end_of_array
297
-
298
- array = []
299
- until data == "" || data.nil?
300
- aencoding, data = parse_encoding(data)
301
- break if aencoding.first.nil?
302
-
303
- e = Format::ENCODINGS[aencoding]
304
- break if e == :end_of_array
305
-
306
- value, data = parse_field(data, aencoding)
307
- break unless value
308
- array << value
309
- end
310
-
311
- return array, data
312
- end
313
-
314
- # Parse Object consisting of multiple fields from binary data.
315
- #
316
- # @private
317
- def parse_object(data, encoding)
318
- e = Format::ENCODINGS[encoding]
319
- case e
320
- when :end_of_object
321
- return nil, data
322
-
323
- when :signer, :signer_entry,
324
- :majority, :memo,
325
- :modified_node, :created_node, :deleted_node,
326
- :previous_fields, :final_fields, :new_fields
327
- # TODO instantiate corresponding classes (?)
328
- return parse_fields(data)
329
-
330
- #else:
331
- end
332
-
333
- raise "unknown object type: #{e}"
334
- end
335
-
336
- # Parse PathSet from binary data.
337
- #
338
- # @private
339
- def parse_pathset(data)
340
- pathset = [[]]
341
- until data == "" || data.nil?
342
- segment = data.unpack("C").first
343
- data = data[1..-1]
344
- return pathset, data if segment == 0x00 # end of path
345
-
346
- if segment == 0xFF # path boundry
347
- pathset << []
348
- else
349
- account, current, issuer = nil
350
-
351
- path = {}
352
-
353
- if (segment & 0x01) != 0 # path account
354
- account, data = parse_account(data, 20)
355
- path[:account] = account
356
- end
357
-
358
- if (segment & 0x10) != 0 # path currency
359
- currency = Format::CURRENCY_CODE.decode(data)
360
- currency = currency["iso_code"].pack("C*")
361
- data = data[Format::CURRENCY_CODE.size..-1]
362
- path[:currency] = currency
363
- end
364
-
365
- if (segment & 0x20) != 0 # path issuer
366
- issuer, data = parse_account(data, 20)
367
- path[:issuer] = issuer
368
- end
369
-
370
- pathset.last << path
371
- end
372
- end
373
-
374
- return pathset, data
375
- end
376
-
377
- ###
378
-
379
- # Parse Transaction from binary data
380
- #
381
- # @private
382
- def parse_tx(tx)
383
- obj = Format::TYPE_INFER.decode(tx)
384
- node_type = Format::NODE_TYPE_CODES[obj["node_type"]]
385
- hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
386
- raise unless node_type == :tx_node &&
387
- hash_prefix == :tx_node
388
-
389
- # discard node type, and hash prefix
390
- tx = tx[13..-1]
391
-
392
- # get node length
393
- vl, offset = parse_vl(tx)
394
- node, _tx = tx.bytes[offset..vl+offset-1], tx.bytes[vl+offset..-1]
395
- node, _remaining = parse_fields(node.pack("C*"))
396
-
397
- # get meta length
398
- vl, offset = parse_vl(_tx.pack("C*"))
399
- meta, index = _tx[offset..vl+offset-1], _tx[vl+offset..-1]
400
- meta, _remaining = parse_fields(meta.pack("C*"))
401
-
402
- { :node => node,
403
- :meta => meta,
404
- :index => index.pack("C*").unpack("H*").first.upcase }
405
- end
406
-
407
- # Parse InnerNode from binary data.
408
- #
409
- # @private
410
- def parse_inner_node(node)
411
- # verify parsability
412
- obj = Format::TYPE_INFER.decode(node)
413
- hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
414
- raise unless hash_prefix == :inner_node
415
-
416
- node = Format::INNER_NODE.decode(node)
417
- node['node_type'] = Format::NODE_TYPE_CODES[node['node_type']]
418
- node
419
- end
420
-
421
- protected
422
-
423
- # Return type and extracted structure from binary data.
424
- #
425
- # @private
426
- def infer_type(value)
427
- obj = Format::TYPE_INFER.decode(value)
428
- node_type = Format::NODE_TYPE_CODES[obj["node_type"]]
429
- hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
430
-
431
- if hash_prefix == :inner_node
432
- return :inner_node, parse_inner_node(value)
433
-
434
- elsif node_type == :account_node
435
- return :ledger_entry, parse_ledger_entry(value)
436
-
437
- elsif node_type == :tx_node
438
- return :tx, parse_tx(value)
439
-
440
- elsif node_type == :ledger
441
- return :ledger, parse_ledger(value)
442
- end
443
-
444
- return nil
445
- end
446
64
  end # class DB
447
65
  end # module NodeStore
448
66
  end # module XRBP