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