xrbp 0.1.9 → 0.1.10

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.
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!