xrbp 0.1.9 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80c7817a86dc903b6eb5725bb37ecd562fe6a73b1aef5ee81e2b27ff1aeb50a6
4
- data.tar.gz: 7003cb99a4488aea91f41d4b8c6bf2fc3bdc577b980d251c858639508f6ae1f1
3
+ metadata.gz: e441fc01ed75b0df28778339ba809a235438773082f62f25e1dcac9fb04c3308
4
+ data.tar.gz: e303c49a8ae8f71d19f532b24f0120f1dd1201747e4d0d274655c44cd5634ba4
5
5
  SHA512:
6
- metadata.gz: 9792f31b6e62658be6b94f32b3dae6a8fd99a395b5b513d7d6f1cf1e766a121bc074a043b8a1f884c516bda807e06243806f83f7ad2e397655d7ab69b09a5e51
7
- data.tar.gz: 4e360f3e4464758f0b49158239d898b8b901d094ea5be52faadfaa29dccb4f12120c392bcce1d2c0a03a1058b8345bcce6016abce59ec9dd3524bb75142ec3a6
6
+ metadata.gz: 8dd5c9a48b69f732ebb11c72dba6b5345cedb6e89f52b0ca06f189bfd6592993425b4f2b2dfcd04b152a48c384b4e0b86248c30059adf7e5ae02018aaa9dc981
7
+ data.tar.gz: 261057099aeab635832d49bac987ad006fdb928a2b2507a8ec974cce1b4e2e59d9199aecc0f77412555b9fb5d33c4c77b47f6ff6ad489e40155ecc461f06f43d
@@ -38,7 +38,12 @@ module XRBP
38
38
  # @return [String, nil] unique account id or nil if input
39
39
  # if not an account
40
40
  def self.parse_account(account)
41
- bin = Base58.base58_to_binary(account, :ripple)
41
+ begin
42
+ bin = Base58.base58_to_binary(account, :ripple)
43
+ rescue ArgumentError
44
+ return nil
45
+ end
46
+
42
47
  typ = bin[0]
43
48
  chk = bin[-4..-1]
44
49
  bin = bin[1...-4]
@@ -60,14 +60,20 @@ module XRBP
60
60
  end
61
61
 
62
62
  # @return [Hash] new ed25519 key pair (both public and private components)
63
- def self.ed25519
63
+ def self.ed25519(seed=nil)
64
64
  # XXX openssl 1.1.1 needed for EdDSA support:
65
65
  # https://www.openssl.org/blog/blog/2018/09/11/release111/
66
66
  # Until then use this:
67
67
  require "ed25519"
68
68
 
69
- sd = Crypto.seed[:seed]
70
- pk = Crypto.parse_seed(sd)
69
+ sd, pk = nil
70
+ if seed
71
+ sd,pk = seed,seed
72
+
73
+ else
74
+ sd = Crypto.seed[:seed]
75
+ pk = Crypto.parse_seed(sd)
76
+ end
71
77
 
72
78
  sha512 = OpenSSL::Digest::SHA512.new
73
79
  pk = sha512.digest(pk)[0..31]
@@ -90,19 +96,15 @@ module XRBP
90
96
  raise "unknown key" unless key.is_a?(Hash) && key[:type] && key[:private]
91
97
  raise "invalid data" unless data.length == 32
92
98
 
93
-
94
99
  if key[:type] == :secp256k1
95
- # XXX: see note about this library above
96
100
  require 'secp256k1'
97
101
 
98
102
  pk = Secp256k1::PrivateKey.new
99
103
  pk.set_raw_privkey [key[:private]].pack("H*")
100
- #pk.pubkey.deserialize [key[:public]].pack("H*")
101
104
  sig_raw = pk.ecdsa_sign data, raw: true
102
105
  return pk.ecdsa_serialize sig_raw
103
106
 
104
107
  elsif key[:type] == :ed25519
105
- # XXX: see note about this library above
106
108
  require "ed25519"
107
109
 
108
110
  sd = key[:seed]
@@ -128,7 +130,6 @@ module XRBP
128
130
  # original data
129
131
  def self.verify(key, data, expected)
130
132
  if key[:type] == :secp256k1
131
- # XXX: see note about this library above
132
133
  require 'secp256k1'
133
134
 
134
135
  pb = Secp256k1::PublicKey.new :pubkey => [key[:public]].pack("H*"),
@@ -139,7 +140,6 @@ module XRBP
139
140
  pv.ecdsa_deserialize(data), raw: true
140
141
 
141
142
  elsif key[:type] == :ed25519
142
- # XXX: see note about this library above
143
143
  require "ed25519"
144
144
 
145
145
  pk = Ed25519::VerifyKey.new([key[:public]].pack("H*"))
@@ -34,7 +34,12 @@ module XRBP
34
34
  # @return [String, nil] unique node id or nil if input
35
35
  # if not an node
36
36
  def self.parse_node(node)
37
- bin = Base58.base58_to_binary(node, :ripple)
37
+ begin
38
+ bin = Base58.base58_to_binary(node, :ripple)
39
+ rescue ArgumentError
40
+ return nil
41
+ end
42
+
38
43
  typ = bin[0]
39
44
  chk = bin[-4..-1]
40
45
  bin = bin[1...-4]
@@ -101,13 +101,13 @@ module XRBP
101
101
 
102
102
  out = [0, 0, 0, 0,
103
103
  0, 0, 0, 0,
104
- Format::NODE_OBJ_TYPES[:unknown]] +
104
+ Format::NODE_TYPES[:unknown]] +
105
105
  [Format::HASH_PREFIXES.invert[:inner_node]].pack("H*")
106
106
  .unpack("C*")
107
107
 
108
108
  # extract mask indicating which hashes are set
109
109
  bytes = data.bytes
110
- mask = bytes[0..1].pack("C*").unpack("S").first
110
+ mask = bytes[0..1].pack("C*").unpack("S>").first
111
111
  bytes = bytes[2..-1]
112
112
 
113
113
  raise "nodeobject codec v1: empty inner node" if mask == 0
@@ -146,14 +146,14 @@ module XRBP
146
146
 
147
147
  out = [0, 0, 0, 0,
148
148
  0, 0, 0, 0,
149
- Format::NODE_OBJ_TYPES[:unknown]] +
149
+ Format::NODE_TYPES[:unknown]] +
150
150
  [Format::HASH_PREFIXES.invert[:inner_node_v2]].pack("H*")
151
151
  .unpack("C*")
152
152
 
153
153
  # Same mask / hash extraction as V1
154
154
 
155
155
  bytes = data.bytes
156
- mask = bytes[0..1].pack("C*").unpack("S").first
156
+ mask = bytes[0..1].pack("C*").unpack("S>").first
157
157
  bytes = bytes[2..-1]
158
158
 
159
159
  raise "nodeobject codec v2: empty inner node" if mask == 0
@@ -204,7 +204,7 @@ module XRBP
204
204
 
205
205
  out = [0, 0, 0, 0,
206
206
  0, 0, 0, 0,
207
- Format::NODE_OBJ_TYPES[:unknown]] +
207
+ Format::NODE_TYPES[:unknown]] +
208
208
  [Format::HASH_PREFIXES.invert[:inner_node]].pack("H*").unpack("C*")
209
209
  (out + data[0...512].bytes).pack("C*")
210
210
  end
@@ -222,7 +222,7 @@ module XRBP
222
222
 
223
223
  out = [0, 0, 0, 0,
224
224
  0, 0, 0, 0,
225
- Format::NODE_OBJ_TYPES[:unknown]] +
225
+ Format::NODE_TYPES[:unknown]] +
226
226
  [Format::HASH_PREFIXES.invert[:inner_node_v2]].pack("H*").unpack("C*")
227
227
 
228
228
  out += bytes[0..511]
@@ -34,7 +34,9 @@ module XRBP
34
34
  # @param key [String] binary key to lookup
35
35
  # @return [String] binary value
36
36
  def [](key)
37
- decompress(@store.fetch(key)[0])
37
+ fetched = @store.fetch(key)[0]
38
+ return nil if fetched.empty?
39
+ decompress(fetched)
38
40
  end
39
41
 
40
42
  # Iterate over each database key/value pair,
@@ -13,7 +13,7 @@ module XRBP
13
13
  # puts rocksdb.ledger('B506ADD630CB707044B4BFFCD943C1395966692A13DD618E5BD0978A006B43BD')
14
14
  class RocksDB < DB
15
15
  # cap max open files for performance
16
- MAX_OPEN_FILES = 200
16
+ MAX_OPEN_FILES = 2000
17
17
 
18
18
  def initialize(path)
19
19
  @db = ::RocksDB::DB.new path,
@@ -24,26 +24,37 @@ module XRBP
24
24
  include Enumerable
25
25
  include EventEmitter
26
26
 
27
- # TODO return nil if db lookup not found
28
-
29
27
  # Return the NodeStore Ledger for the given lookup hash
30
28
  def ledger(hash)
31
- parse_ledger(self[hash])
29
+ val = self[hash]
30
+ return nil if val.nil?
31
+ parse_ledger(val)
32
32
  end
33
33
 
34
34
  # Return the NodeStore Account for the given lookup hash
35
35
  def account(hash)
36
- parse_ledger_entry(self[hash])
36
+ ledger_entry(hash)
37
+ end
38
+
39
+ # Return the NodeStore Ledger Entry for the given lookup hash
40
+ def ledger_entry(hash)
41
+ val = self[hash]
42
+ return nil if val.nil?
43
+ parse_ledger_entry(val)
37
44
  end
38
45
 
39
46
  # Return the NodeStore Transaction for the given lookup hash
40
47
  def tx(hash)
41
- parse_tx(self[hash])
48
+ val = self[hash]
49
+ return nil if val.nil?
50
+ parse_tx(val)
42
51
  end
43
52
 
44
53
  # Return the NodeStore InnerNode for the given lookup hash
45
54
  def inner_node(hash)
46
- parse_inner_node(self[hash])
55
+ val = self[hash]
56
+ return nil if val.nil?
57
+ parse_inner_node(val)
47
58
  end
48
59
 
49
60
  ###
@@ -96,7 +107,7 @@ module XRBP
96
107
  def parse_ledger_entry(ledger_entry)
97
108
  # validate parsability
98
109
  obj = Format::TYPE_INFER.decode(ledger_entry)
99
- node_type = Format::NODE_TYPES[obj["node_type"]]
110
+ node_type = Format::NODE_TYPE_CODES[obj["node_type"]]
100
111
  hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
101
112
  raise unless node_type == :account_node &&
102
113
  hash_prefix == :leaf_node
@@ -119,12 +130,13 @@ module XRBP
119
130
  .upcase
120
131
 
121
132
  # remaining bytes are serialized object
122
- fields = parse_fields(ledger_entry[2...-32].pack("C*"))
133
+ fields, remaining = parse_fields(ledger_entry[2...-32].pack("C*"))
134
+ raise unless remaining.empty?
123
135
 
124
- # TODO instantiate class corresponding to prefix &
125
- # populate attributes w/ fields
136
+ # TODO instantiate class corresponding to type &
137
+ # populate attributes w/ fields (?)
126
138
 
127
- { :prefix => prefix,
139
+ { :type => Format::LEDGER_ENTRY_TYPE_CODES[prefix[1]],
128
140
  :index => index,
129
141
  :fields => fields }
130
142
  end
@@ -143,10 +155,10 @@ module XRBP
143
155
  e = Format::ENCODINGS[encoding]
144
156
  value, fields = parse_field(fields, encoding)
145
157
  break unless value
146
- parsed[e] = value
158
+ parsed[e] = convert_field(encoding, value)
147
159
  end
148
160
 
149
- return parsed
161
+ return parsed, fields
150
162
  end
151
163
 
152
164
  # Parse single field of specified encoding from data.
@@ -160,11 +172,11 @@ module XRBP
160
172
  when :uint8
161
173
  return data.unpack("C").first, data[1..-1]
162
174
  when :uint16
163
- return data.unpack("S").first, data[2..-1]
175
+ return data.unpack("S>").first, data[2..-1]
164
176
  when :uint32
165
- return data.unpack("L").first, data[4..-1]
177
+ return data.unpack("L>").first, data[4..-1]
166
178
  when :uint64
167
- return data.unpack("Q").first, data[8..-1]
179
+ return data.unpack("Q>").first, data[8..-1]
168
180
  when :hash128
169
181
  return data.unpack("H32").first, data[16..-1]
170
182
  when :hash160
@@ -192,6 +204,22 @@ module XRBP
192
204
  raise
193
205
  end
194
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
+
195
223
  # Parse variable length header from data buffer. Returns length
196
224
  # extracted from header and the number of bytes in header.
197
225
  #
@@ -217,6 +245,8 @@ module XRBP
217
245
 
218
246
  # Parse 'Amount' data type from binary data.
219
247
  #
248
+ # @see https://developers.ripple.com/currency-formats.html
249
+ #
220
250
  # @private
221
251
  def parse_amount(data)
222
252
  amount = data[0..7].unpack("Q>").first
@@ -229,16 +259,16 @@ module XRBP
229
259
 
230
260
  data = data[8..-1]
231
261
  currency = Format::CURRENCY_CODE.decode(data)
262
+ currency = currency["iso_code"].pack("C*")
232
263
 
233
264
  data = data[Format::CURRENCY_CODE.size..-1]
234
265
  issuer, data = parse_account(data, 20)
235
266
 
236
- # TODO calculate value
237
- return { :sign => sign,
238
- :exp => exp,
239
- :mantissa => mant,
240
- :currency => currency,
241
- :issuer => issuer }, data
267
+ amount = (sign == 0 ? -1 : 1) * mant * 10 ** (exp-97)
268
+
269
+ return { :amount => amount,
270
+ :currency => currency,
271
+ :issuer => issuer }, data
242
272
  end
243
273
 
244
274
  # Parse 'Account' data type from binary data.
@@ -294,13 +324,13 @@ module XRBP
294
324
  :majority, :memo,
295
325
  :modified_node, :created_node, :deleted_node,
296
326
  :previous_fields, :final_fields, :new_fields
297
- # TODO instantiate corresponding classes
327
+ # TODO instantiate corresponding classes (?)
298
328
  return parse_fields(data)
299
329
 
300
330
  #else:
301
331
  end
302
332
 
303
- raise "unknown object type"
333
+ raise "unknown object type: #{e}"
304
334
  end
305
335
 
306
336
  # Parse PathSet from binary data.
@@ -327,6 +357,7 @@ module XRBP
327
357
 
328
358
  if (segment & 0x10) != 0 # path currency
329
359
  currency = Format::CURRENCY_CODE.decode(data)
360
+ currency = currency["iso_code"].pack("C*")
330
361
  data = data[Format::CURRENCY_CODE.size..-1]
331
362
  path[:currency] = currency
332
363
  end
@@ -350,7 +381,7 @@ module XRBP
350
381
  # @private
351
382
  def parse_tx(tx)
352
383
  obj = Format::TYPE_INFER.decode(tx)
353
- node_type = Format::NODE_TYPES[obj["node_type"]]
384
+ node_type = Format::NODE_TYPE_CODES[obj["node_type"]]
354
385
  hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
355
386
  raise unless node_type == :tx_node &&
356
387
  hash_prefix == :tx_node
@@ -361,12 +392,12 @@ module XRBP
361
392
  # get node length
362
393
  vl, offset = parse_vl(tx)
363
394
  node, _tx = tx.bytes[offset..vl+offset-1], tx.bytes[vl+offset..-1]
364
- node = parse_fields(node.pack("C*"))
395
+ node, _remaining = parse_fields(node.pack("C*"))
365
396
 
366
397
  # get meta length
367
398
  vl, offset = parse_vl(_tx.pack("C*"))
368
399
  meta, index = _tx[offset..vl+offset-1], _tx[vl+offset..-1]
369
- meta = parse_fields(meta.pack("C*"))
400
+ meta, _remaining = parse_fields(meta.pack("C*"))
370
401
 
371
402
  { :node => node,
372
403
  :meta => meta,
@@ -392,14 +423,14 @@ module XRBP
392
423
  # @private
393
424
  def infer_type(value)
394
425
  obj = Format::TYPE_INFER.decode(value)
395
- node_type = Format::NODE_TYPES[obj["node_type"]]
426
+ node_type = Format::NODE_TYPE_CODES[obj["node_type"]]
396
427
  hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
397
428
 
398
429
  if hash_prefix == :inner_node
399
430
  return :inner_node, parse_inner_node(value)
400
431
 
401
432
  elsif node_type == :account_node
402
- return :account, parse_ledger_entry(value)
433
+ return :ledger_entry, parse_ledger_entry(value)
403
434
 
404
435
  elsif node_type == :tx_node
405
436
  return :tx, parse_tx(value)
@@ -3,20 +3,16 @@ require "bistro"
3
3
  module XRBP
4
4
  module NodeStore
5
5
  module Format
6
- NODE_OBJ_TYPES = {
7
- :unknown => 0,
8
- :ledger => 1,
9
- :account_node => 2,
10
- :transaction_node => 3
11
- }
12
-
13
6
  NODE_TYPES = {
14
- 1 => :ledger,
15
- 2 => :tx,
16
- 3 => :account_node,
17
- 4 => :tx_node
7
+ :unknown => 0,
8
+ :ledger => 1,
9
+ #:tx => 2, # unused
10
+ :account_node => 3,
11
+ :tx_node => 4
18
12
  }
19
13
 
14
+ NODE_TYPE_CODES = NODE_TYPES.invert
15
+
20
16
  HASH_PREFIXES = { # ASCII value:
21
17
  "54584E00" => :tx_id, # TXN
22
18
  "534E4400" => :tx_node, # SND
@@ -298,6 +294,62 @@ module XRBP
298
294
  'C', 'close_flags',
299
295
  ])
300
296
 
297
+ ###
298
+
299
+ LEDGER_ENTRY_TYPES = {
300
+ :any => -3,
301
+ :child => -2,
302
+ :invalid => -1,
303
+ :account_root => 'a',
304
+ :dir_node => 'd',
305
+ :ripple_state => 'r',
306
+ :ticket => 'T',
307
+ :signer_list => 'S',
308
+ :offer => 'o',
309
+ :ledger_hashes => 'h',
310
+ :amendments => 'f',
311
+ :fee_settings => 's',
312
+ :escrow => 'u',
313
+ :paychan => 'x',
314
+ :check => 'C',
315
+ :deposit_preauth => 'p'
316
+ }
317
+
318
+ LEDGER_ENTRY_TYPE_CODES = LEDGER_ENTRY_TYPES.invert
319
+
320
+ ###
321
+
322
+ TX_TYPES = {
323
+ -1 => :invalid,
324
+ 0 => :payment,
325
+ 1 => :escrow_create,
326
+ 2 => :escrow_finish,
327
+ 3 => :account_set,
328
+ 4 => :escrow_cancel,
329
+ 5 => :regular_key_set,
330
+ 6 => :nickname_set, #open
331
+ 7 => :offer_create,
332
+ 8 => :offer_cancel,
333
+
334
+ 9 => :no_longer_used,
335
+
336
+ 11 => :ticket_create,
337
+ 12 => :ticket_cancel,
338
+ 13 => :signer_list_set,
339
+ 14 => :paychan_create,
340
+ 15 => :paychan_fund,
341
+ 16 => :paychan_claim,
342
+ 17 => :check_create,
343
+ 18 => :check_cash,
344
+ 19 => :check_cancel,
345
+ 20 => :deposit_preauth,
346
+
347
+ 100 => :amendment,
348
+ 101 => :fee
349
+ }
350
+
351
+ ###
352
+
301
353
  CURRENCY_CODE = Bistro.new([
302
354
  'C', 'type_code',
303
355
  'C11', 'reserved1',
@@ -77,6 +77,14 @@ module XRBP
77
77
  ssl_socket.puts(data)
78
78
  end
79
79
 
80
+ def write_frame(msg)
81
+ write(Frame.from_msg(msg))
82
+ end
83
+
84
+ def write_msg(data)
85
+ write_frame(Overlay.create_msg(data))
86
+ end
87
+
80
88
  # Read raw data from connection
81
89
  def read
82
90
  ssl_socket.gets
@@ -30,6 +30,10 @@ module XRBP
30
30
  attr_reader :type, :size
31
31
  attr_accessor :data
32
32
 
33
+ def self.from_msg(msg)
34
+ # ...
35
+ end
36
+
33
37
  def initialize(type, size)
34
38
  @type = type
35
39
  @size = size
@@ -23,5 +23,9 @@ module XRBP
23
23
  :MTVALIDATION => Protocol::TMValidation,
24
24
  :MTGET_OBJECTS => Protocol::TMGetObjectByHash
25
25
  }
26
+
27
+ def self.create_msg(hash)
28
+ # ...
29
+ end
26
30
  end # module Overlay
27
31
  end # module XRBP
@@ -1,3 +1,3 @@
1
1
  module XRBP
2
- VERSION = '0.1.9'
2
+ VERSION = '0.1.10'
3
3
  end # module XRBP
@@ -6,6 +6,8 @@ Camcorder.config.recordings_dir = 'spec/recordings'
6
6
  require 'xrbp'
7
7
 
8
8
  RSpec.configure do |config|
9
+ config.alias_it_should_behave_like_to :it_provides, 'provides:'
10
+
9
11
  config.expect_with :rspec do |expectations|
10
12
  expectations.include_chain_clauses_in_custom_matcher_descriptions = true
11
13
  end
@@ -1,6 +1,24 @@
1
1
  describe XRBP::Crypto do
2
+ let(:account) { "rDbWJ9C7uExThZYAwV8m6LsZ5YSX3sa6US" }
3
+ let(:expected) { [0x8a, 0x28, 0x1b, 0x5e, 0x46, 0xb0, 0x27, 0xc3, 0x70, 0x26, 0xe3, 0x8d, 0xbc, 0x5f, 0x9a, 0xa1, 0x4c, 0x37, 0x51, 0x45].pack("C*") }
4
+
2
5
  it "generates valid account" do
3
6
  acct = described_class.account
4
7
  expect(described_class.account?(acct[:account])).to be(true)
5
8
  end
9
+
10
+ it "parses account" do
11
+ a = described_class.parse_account(account)
12
+ expect(a).to_not be_nil
13
+ expect(a).to eq(expected)
14
+ end
15
+
16
+ it "does not parse invalid account" do
17
+ expect(described_class.parse_account("invalid")).to be_nil
18
+ end
19
+
20
+ it "verifies account" do
21
+ expect(described_class.account?(account)).to be(true)
22
+ expect(described_class.account?("invalid")).to be(false)
23
+ end
6
24
  end
@@ -2,10 +2,21 @@ describe XRBP::Crypto::Key do
2
2
  it "generates valid sec256k1 key"
3
3
  it "generates valid ed25519 key"
4
4
 
5
- it "generates signs and verifies digest" do
6
- key = described_class.secp256k1
7
- dat = "ABCDEFGHIJLMNOPQRSTUVWYZ12345678"
8
- signed = XRBP::Crypto::Key.sign_digest(key, dat)
9
- expect(XRBP::Crypto::Key.verify(key, signed, dat))
10
- end
5
+ [:secp256k1, :ed25519].each { |key_type|
6
+
7
+ let(:dat) { "ABCDEFGHIJLMNOPQRSTUVWYZ12345678" }
8
+
9
+ it "generates signs and verifies #{key_type} digest" do
10
+ key = described_class.send(key_type)
11
+ signed = XRBP::Crypto::Key.sign_digest(key, dat)
12
+ expect(XRBP::Crypto::Key.verify(key, signed, dat))
13
+ end
14
+
15
+ it "accepts #{key_type} seed" do
16
+ seed = XRBP::Crypto.seed[:seed]
17
+ key = described_class.send(key_type, seed)
18
+ signed = XRBP::Crypto::Key.sign_digest(key, dat)
19
+ expect(XRBP::Crypto::Key.verify(key, signed, dat))
20
+ end
21
+ }
11
22
  end
@@ -1,6 +1,24 @@
1
1
  describe XRBP::Crypto do
2
+ let(:node) { "n9Kgda1F7evck24hJUqFsn8m6pNERJd6UkjwLuNBSUJ6Ykm76NdJ" }
3
+ let(:expected) { [0x2, 0x96, 0x24, 0xe4, 0x37, 0x80, 0xeb, 0xcc, 0xf2, 0x77, 0x67, 0x48, 0x4d, 0x42, 0xc5, 0x54, 0xbe, 0x7b, 0xaa, 0x28, 0x78, 0x82, 0x7e, 0x91, 0x2f, 0x4e, 0x66, 0x94, 0xbf, 0x64, 0xa4, 0x55, 0x80].pack("C*") }
4
+
2
5
  it "generates valid node" do
3
6
  node = described_class.node
4
7
  expect(described_class.node?(node[:node])).to be(true)
5
8
  end
9
+
10
+ it "parses node" do
11
+ n = described_class.parse_node(node)
12
+ expect(n).to_not be_nil
13
+ expect(n).to eq(expected)
14
+ end
15
+
16
+ it "does not parse invalid node" do
17
+ expect(described_class.parse_node("invalid")).to be_nil
18
+ end
19
+
20
+ it "verifies node" do
21
+ expect(described_class.node?(node)).to be(true)
22
+ expect(described_class.node?("invalid")).to be(false)
23
+ end
6
24
  end
@@ -0,0 +1,15 @@
1
+ shared_examples "database access" do |opts={}|
2
+ let(:key) { ["516F940640172099A89F9733B8E69F2A56720E67C53EE7E0F3022BB6E55E6986"].pack("H*") }
3
+ let(:val) { "0000000000000000014c57520002d4ebd001633dd73a0b3efdc4fad6eb19f6a6089e7aebc73af596db471305bc0ebb99d9d1414bd4db8c9d43934a5e57c598f0505664f9349da87fd32afbe1c315a2cb6cfb5b690aecc6b1a3fea269daaa66c820978ac95f0399ecaca7a3abac91402c9fc7961a53d053332f247c5e24247c5e250a00" }
4
+
5
+ it "returns values for key" do
6
+ actual = db[key].unpack("H*").first
7
+ expect(actual).to eq(val)
8
+ end
9
+
10
+ context "key not found" do
11
+ it "returns nil" do
12
+ expect(db["key"]).to be_nil
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ shared_examples "a database parser" do |opts={}|
2
+ let(:ledger_key) { ["516F940640172099A89F9733B8E69F2A56720E67C53EE7E0F3022BB6E55E6986"].pack("H*") }
3
+ let(:ledger) { {"nt_ledger"=>1, "hp_ledger_master"=>"4c575200", "index"=>47508432, "total_coins"=>18248035087498961665, "parent_hash"=>"C4FAD6EB19F6A6089E7AEBC73AF596DB471305BC0EBB99D9D1414BD4DB8C9D43", "tx_hash"=>"934A5E57C598F0505664F9349DA87FD32AFBE1C315A2CB6CFB5B690AECC6B1A3", "account_hash"=>"FEA269DAAA66C820978AC95F0399ECACA7A3ABAC91402C9FC7961A53D053332F", "parent_close_time"=>Time.parse("2019-05-25T20:12:20Z").utc, "close_time"=>Time.parse("2019-05-25T20:12:21Z").utc, "close_time_resolution"=>10, "close_flags"=>0} }
4
+
5
+ let(:tx_key) { ["003b960d5e30ff91a91f7c900509a99a8a6b89441b76158e5ed295ad1d27b0e7"].pack("H*") }
6
+ let(:tx) { {:node=>{:transaction_type=>:payment, :flags=>2147942400, :sequence=>3366484, :last_ledger_sequence=>47713577, :amount=>{:amount=>(10000/1), :currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}, :fee=>11, :send_max=>10000000000, :signing_pub_key=>"030ac4f2ba6e1ff86beb234b639918dafdf0675032ae264d2b39641503822373fe", :txn_signature=>"304402207ac9c62a6e8a876e67945edebcd3f6c71c36c95fbbb0ce05a85871d0794b1b8c0220297fe7195beb083a78ff9de4d822b5e4cb42be7e5a8662bcbc2f42c737e264bf", :account=>"rKLpjpCoXgLQQYQyj13zgay73rsgmzNH13", :destination=>"rKLpjpCoXgLQQYQyj13zgay73rsgmzNH13", :paths=>[[{:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"}, {:currency=>"USD", :issuer=>"rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq"}, {:currency=>"\x00\x00\x00"}, {:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}], [{:currency=>"USD", :issuer=>"rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq"}, {:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"}, {:currency=>"\x00\x00\x00"}, {:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}], [{:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"}, {:currency=>"CNY", :issuer=>"razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA"}, {:currency=>"\x00\x00\x00"}, {:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}], [{:currency=>"CNY", :issuer=>"razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA"}, {:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"}, {:currency=>"\x00\x00\x00"}, {:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}], [{:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"}, {:currency=>"EUR", :issuer=>"rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq"}, {:currency=>"\x00\x00\x00"}, {:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}], [{:currency=>"EUR", :issuer=>"rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq"}, {:currency=>"CNY", :issuer=>"rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y"}, {:currency=>"\x00\x00\x00"}, {:currency=>"XCN", :issuer=>"rPFLkxQk6xUGdGYEykqe7PR25Gr7mLHDc8"}]]}, :meta=>{:transaction_index=>30, :affected_nodes=>[{:ledger_entry_type=>:account_root, :previous_txn_lgr_seq=>47713575, :previous_txn_id=>"2e397e516ccde852e829d36eb7d0d195dd9a974a188e213ad256c3ae3c131e58", :ledger_index=>"792ba4e4659c27cf3b63f96b34f158748b081cf532f6746a1e3ebd07acba1a0e", :previous_fields=>{:sequence=>3366484, :balance=>1920887273}, :final_fields=>{:flags=>0, :sequence=>3366485, :owner_count=>5, :balance=>1920887262, :account=>"rKLpjpCoXgLQQYQyj13zgay73rsgmzNH13"}}], :transaction_result=>128}, :index=>"5C28E293E88CCEEFBBAB3EF58C1F8C1C02A5194F42A42349597DDCBC86BFEB0D"} }
7
+
8
+ let(:ledger_entry_key) { ["002989b414eff398027fce4045f643d68aca440aeea11518a950550ca02c18dd"].pack("H*") }
9
+ let(:ledger_entry) { {:type=>:account_root, :index=>"9A34C6D1C46168ECE90EB867C78AB2BECE80FAF5D86D09082017E2C89BD68E56", :fields=>{:flags=>0, :sequence=>2, :previous_txn_lgr_seq=>45017187, :owner_count=>0, :previous_txn_id=>"3133839f2401cc9a719778f53319486d11e7ad7ec7891ac083e2aa7eb924516a", :balance=>20000000, :account=>"r4HW4bomLvvzA22dACJwUq95ZRr8ZAcXXs"}} }
10
+
11
+ let(:inner_node_key) { ["001b46daaea7a40d9dcdc30f83918db8c8f9110c22e1207bcd8f9145dbe032dd"].pack("H*") }
12
+ # XXX - see: https://github.com/ripple/rippled/issues/2960
13
+ let(:inner_node_type) { described_class == XRBP::NodeStore::Backends::NuDB ? 0 : 3 }
14
+ let(:inner_node) { {"node_type"=>inner_node_type, "hp_inner_node"=>"4d494e00", "child0"=>"0000000000000000000000000000000000000000000000000000000000000000", "child1"=>"0000000000000000000000000000000000000000000000000000000000000000", "child2"=>"0000000000000000000000000000000000000000000000000000000000000000", "child3"=>"0000000000000000000000000000000000000000000000000000000000000000", "child4"=>"0000000000000000000000000000000000000000000000000000000000000000", "child5"=>"0000000000000000000000000000000000000000000000000000000000000000", "child6"=>"0000000000000000000000000000000000000000000000000000000000000000", "child7"=>"0000000000000000000000000000000000000000000000000000000000000000", "child8"=>"0000000000000000000000000000000000000000000000000000000000000000", "child9"=>"0000000000000000000000000000000000000000000000000000000000000000", "child10"=>"0000000000000000000000000000000000000000000000000000000000000000", "child11"=>"0000000000000000000000000000000000000000000000000000000000000000", "child12"=>"0000000000000000000000000000000000000000000000000000000000000000", "child13"=>"0000000000000000000000000000000000000000000000000000000000000000", "child14"=>"0000000000000000000000000000000000000000000000000000000000000000", "child15"=>"666a34c47ed2890eab999536b6c0f728b3b2d616080bd38837f7c9f509b55204", "child16"=>"", "child17"=>"", "child18"=>"", "child19"=>"", "child20"=>"", "child21"=>"", "child22"=>"", "child23"=>"", "child24"=>"", "child25"=>"", "child26"=>"", "child27"=>"", "child28"=>"", "child29"=>"", "child30"=>"", "child31"=>""} }
15
+
16
+ it "returns ledger" do
17
+ actual = db.ledger(ledger_key)
18
+ expect(actual).to eq(ledger)
19
+ end
20
+
21
+ it "returns transaction" do
22
+ actual = db.tx(tx_key)
23
+ expect(actual).to eq(tx)
24
+ end
25
+
26
+ # TODO test other transaction types
27
+
28
+ it "returns ledger entry" do
29
+ actual = db.ledger_entry(ledger_entry_key)
30
+ expect(actual).to eq(ledger_entry)
31
+ end
32
+
33
+ ## TODO test other ledger entry types
34
+
35
+ it "returns inner node" do
36
+ actual = db.inner_node(inner_node_key)
37
+ expect(actual).to eq(inner_node)
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ require 'xrbp/nodestore/backends/nudb'
2
+
3
+ require_relative "./db_access"
4
+ #require_relative "./db_iterator_access"
5
+ #require_relative "./db_decompression"
6
+ require_relative "./db_parser"
7
+
8
+ describe XRBP::NodeStore::Backends::NuDB do
9
+ before(:each) do
10
+ Camcorder.intercept_constructor ::RuDB::Store
11
+ end
12
+
13
+ after(:each) do
14
+ Camcorder.deintercept_constructor ::RuDB::Store
15
+ end
16
+
17
+ let(:db) {
18
+ XRBP::NodeStore::Backends::NuDB.new("/var/lib/rippled/nudb")
19
+ }
20
+
21
+ it_provides "database access"
22
+ # it_provides "database iterator access"
23
+ # it_provides "database decompression"
24
+ it_provides "a database parser"
25
+ end
@@ -0,0 +1,23 @@
1
+ require 'xrbp/nodestore/backends/rocksdb'
2
+
3
+ require_relative "./db_access"
4
+ #require_relative "./db_iterator_access"
5
+ #require_relative "./db_parser"
6
+
7
+ describe XRBP::NodeStore::Backends::RocksDB do
8
+ before(:each) do
9
+ Camcorder.intercept_constructor ::RocksDB::DB
10
+ end
11
+
12
+ after(:each) do
13
+ Camcorder.deintercept_constructor ::RocksDB::DB
14
+ end
15
+
16
+ let(:db) {
17
+ XRBP::NodeStore::Backends::RocksDB.new("/var/lib/rippled/rocksdb/")
18
+ }
19
+
20
+ it_provides "database access"
21
+ #it_provides "database iterator access"
22
+ #it_provides "a database parser"
23
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xrbp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dev Null Productions
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-24 00:00:00.000000000 Z
11
+ date: 2019-06-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -292,6 +292,10 @@ files:
292
292
  - spec/xrbp/crypto/key_spec.rb
293
293
  - spec/xrbp/crypto/node_spec.rb
294
294
  - spec/xrbp/crypto/seed_spec.rb
295
+ - spec/xrbp/nodestore/backends/db_access.rb
296
+ - spec/xrbp/nodestore/backends/db_parser.rb
297
+ - spec/xrbp/nodestore/backends/nudb_spec.rb
298
+ - spec/xrbp/nodestore/backends/rocksdb_spec.rb
295
299
  - spec/xrbp/websocket/client_spec.rb
296
300
  - spec/xrbp/websocket/command_spec.rb
297
301
  - spec/xrbp/websocket/connection_spec.rb
@@ -316,8 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
316
320
  - !ruby/object:Gem::Version
317
321
  version: '0'
318
322
  requirements: []
319
- rubyforge_project:
320
- rubygems_version: 2.7.6
323
+ rubygems_version: 3.0.3
321
324
  signing_key:
322
325
  specification_version: 4
323
326
  summary: Helper module to read and write data from the XRP Ledger and related resources!