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 +4 -4
- data/lib/xrbp/crypto/account.rb +6 -1
- data/lib/xrbp/crypto/key.rb +9 -9
- data/lib/xrbp/crypto/node.rb +6 -1
- data/lib/xrbp/nodestore/backends/decompressor.rb +6 -6
- data/lib/xrbp/nodestore/backends/nudb.rb +3 -1
- data/lib/xrbp/nodestore/backends/rocksdb.rb +1 -1
- data/lib/xrbp/nodestore/db.rb +60 -29
- data/lib/xrbp/nodestore/format.rb +63 -11
- data/lib/xrbp/overlay/connection.rb +8 -0
- data/lib/xrbp/overlay/frame.rb +4 -0
- data/lib/xrbp/overlay/messages.rb +4 -0
- data/lib/xrbp/version.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/xrbp/crypto/account_spec.rb +18 -0
- data/spec/xrbp/crypto/key_spec.rb +17 -6
- data/spec/xrbp/crypto/node_spec.rb +18 -0
- data/spec/xrbp/nodestore/backends/db_access.rb +15 -0
- data/spec/xrbp/nodestore/backends/db_parser.rb +39 -0
- data/spec/xrbp/nodestore/backends/nudb_spec.rb +25 -0
- data/spec/xrbp/nodestore/backends/rocksdb_spec.rb +23 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e441fc01ed75b0df28778339ba809a235438773082f62f25e1dcac9fb04c3308
|
4
|
+
data.tar.gz: e303c49a8ae8f71d19f532b24f0120f1dd1201747e4d0d274655c44cd5634ba4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8dd5c9a48b69f732ebb11c72dba6b5345cedb6e89f52b0ca06f189bfd6592993425b4f2b2dfcd04b152a48c384b4e0b86248c30059adf7e5ae02018aaa9dc981
|
7
|
+
data.tar.gz: 261057099aeab635832d49bac987ad006fdb928a2b2507a8ec974cce1b4e2e59d9199aecc0f77412555b9fb5d33c4c77b47f6ff6ad489e40155ecc461f06f43d
|
data/lib/xrbp/crypto/account.rb
CHANGED
@@ -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
|
-
|
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]
|
data/lib/xrbp/crypto/key.rb
CHANGED
@@ -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 =
|
70
|
-
|
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*"))
|
data/lib/xrbp/crypto/node.rb
CHANGED
@@ -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
|
-
|
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::
|
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::
|
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::
|
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::
|
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
|
-
|
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 =
|
16
|
+
MAX_OPEN_FILES = 2000
|
17
17
|
|
18
18
|
def initialize(path)
|
19
19
|
@db = ::RocksDB::DB.new path,
|
data/lib/xrbp/nodestore/db.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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::
|
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
|
125
|
-
# populate attributes w/ fields
|
136
|
+
# TODO instantiate class corresponding to type &
|
137
|
+
# populate attributes w/ fields (?)
|
126
138
|
|
127
|
-
{ :
|
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
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
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::
|
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::
|
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 :
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
data/lib/xrbp/overlay/frame.rb
CHANGED
data/lib/xrbp/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -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
|
-
|
6
|
-
|
7
|
-
dat
|
8
|
-
|
9
|
-
|
10
|
-
|
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.
|
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-
|
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
|
-
|
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!
|