xrbp 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/crawl_nodes.rb +1 -1
- data/examples/nodestore1.rb +14 -0
- data/examples/nodestore2.rb +42 -0
- data/examples/p2p.rb +14 -0
- data/lib/xrbp.rb +3 -0
- data/lib/xrbp/common.rb +6 -0
- data/lib/xrbp/core_ext.rb +22 -0
- data/lib/xrbp/crypto.rb +3 -0
- data/lib/xrbp/crypto/account.rb +33 -0
- data/lib/xrbp/crypto/key.rb +93 -0
- data/lib/xrbp/crypto/node.rb +27 -0
- data/lib/xrbp/model/node.rb +2 -1
- data/lib/xrbp/nodestore.rb +7 -0
- data/lib/xrbp/nodestore/backends/nudb.rb +1 -0
- data/lib/xrbp/nodestore/backends/rocksdb.rb +43 -0
- data/lib/xrbp/nodestore/db.rb +324 -0
- data/lib/xrbp/nodestore/format.rb +297 -0
- data/lib/xrbp/overlay.rb +3 -0
- data/lib/xrbp/overlay/connection.rb +86 -0
- data/lib/xrbp/overlay/frame.rb +51 -0
- data/lib/xrbp/overlay/handshake.rb +78 -0
- data/lib/xrbp/overlay/ripple.proto.rb +306 -0
- data/lib/xrbp/version.rb +1 -1
- data/lib/xrbp/webclient/connection.rb +6 -1
- metadata +61 -2
@@ -0,0 +1,297 @@
|
|
1
|
+
require "bistro"
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module NodeStore
|
5
|
+
module Format
|
6
|
+
NODE_TYPES = {
|
7
|
+
1 => :ledger,
|
8
|
+
2 => :tx,
|
9
|
+
3 => :account_node,
|
10
|
+
4 => :tx_node
|
11
|
+
}
|
12
|
+
|
13
|
+
HASH_PREFIXES = {
|
14
|
+
"54584E00" => :tx_id,
|
15
|
+
"534E4400" => :tx_node,
|
16
|
+
"4D4C4E00" => :leaf_node,
|
17
|
+
"4D494E00" => :inner_node,
|
18
|
+
"4C575200" => :ledger_master,
|
19
|
+
"53545800" => :tx_sign,
|
20
|
+
"56414C00" => :validation,
|
21
|
+
"50525000" => :proposal
|
22
|
+
}
|
23
|
+
|
24
|
+
###
|
25
|
+
|
26
|
+
SERIALIZED_TYPES = {
|
27
|
+
1 => :uint16,
|
28
|
+
2 => :uint32,
|
29
|
+
3 => :uint64,
|
30
|
+
4 => :hash128,
|
31
|
+
5 => :hash256,
|
32
|
+
6 => :amount,
|
33
|
+
7 => :vl,
|
34
|
+
8 => :account,
|
35
|
+
14 => :object,
|
36
|
+
15 => :array,
|
37
|
+
16 => :uint8,
|
38
|
+
17 => :hash160,
|
39
|
+
18 => :pathset,
|
40
|
+
19 => :vector256
|
41
|
+
}
|
42
|
+
|
43
|
+
ENCODINGS = {
|
44
|
+
# 16-bit unsigned integers (common)
|
45
|
+
[:uint16, 1] => :ledger_entry_type,
|
46
|
+
[:uint16, 2] => :transaction_type,
|
47
|
+
[:uint16, 3] => :signer_weight,
|
48
|
+
|
49
|
+
# 32-bit unsigned integers (common)
|
50
|
+
[:uint32, 2] => :flags,
|
51
|
+
[:uint32, 3] => :source_tag,
|
52
|
+
[:uint32, 4] => :sequence,
|
53
|
+
[:uint32, 5] => :previous_txn_lgr_seq,
|
54
|
+
[:uint32, 6] => :ledger_sequence,
|
55
|
+
[:uint32, 7] => :close_time,
|
56
|
+
[:uint32, 8] => :parent_close_time,
|
57
|
+
[:uint32, 9] => :signing_time,
|
58
|
+
[:uint32, 10] => :expiration,
|
59
|
+
[:uint32, 11] => :transfer_rate,
|
60
|
+
[:uint32, 12] => :wallet_size,
|
61
|
+
[:uint32, 13] => :owner_count,
|
62
|
+
[:uint32, 14] => :destination_tag,
|
63
|
+
|
64
|
+
# 32-bit unsigned integers (uncommon)
|
65
|
+
[:uint32, 16] => :high_quality_in,
|
66
|
+
[:uint32, 17] => :high_quality_out,
|
67
|
+
[:uint32, 18] => :low_quality_in,
|
68
|
+
[:uint32, 19] => :low_quality_out,
|
69
|
+
[:uint32, 20] => :quality_in,
|
70
|
+
[:uint32, 21] => :quality_out,
|
71
|
+
[:uint32, 22] => :stamp_escrow,
|
72
|
+
[:uint32, 23] => :bond_amount,
|
73
|
+
[:uint32, 24] => :load_fee,
|
74
|
+
[:uint32, 25] => :offer_sequence,
|
75
|
+
|
76
|
+
[:uint32, 26] => :first_ledger_sequence,
|
77
|
+
[:uint32, 27] => :last_ledger_sequence,
|
78
|
+
|
79
|
+
[:uint32, 28] => :transaction_index,
|
80
|
+
[:uint32, 29] => :operation_limit,
|
81
|
+
|
82
|
+
[:uint32, 30] => :reference_fee_units,
|
83
|
+
[:uint32, 31] => :reserve_base,
|
84
|
+
[:uint32, 32] => :reserve_increment,
|
85
|
+
[:uint32, 33] => :set_flag,
|
86
|
+
[:uint32, 34] => :clear_flag,
|
87
|
+
[:uint32, 35] => :signer_quorum,
|
88
|
+
[:uint32, 36] => :cancel_after,
|
89
|
+
[:uint32, 37] => :finish_after,
|
90
|
+
[:uint32, 38] => :signer_list_id,
|
91
|
+
[:uint32, 39] => :settle_delay,
|
92
|
+
|
93
|
+
# 64-bit unsigned integers (common)
|
94
|
+
[:uint64, 1] => :index_next,
|
95
|
+
[:uint64, 2] => :index_previous,
|
96
|
+
[:uint64, 3] => :book_node,
|
97
|
+
[:uint64, 4] => :owner_node,
|
98
|
+
[:uint64, 5] => :base_fee,
|
99
|
+
[:uint64, 6] => :exchange_rate,
|
100
|
+
[:uint64, 7] => :low_node,
|
101
|
+
[:uint64, 8] => :high_node,
|
102
|
+
|
103
|
+
# 128-bit (common)
|
104
|
+
[:hash128, 1] => :email_hash,
|
105
|
+
|
106
|
+
# 256-bit (common)
|
107
|
+
[:hash256, 1] => :ledger_hash,
|
108
|
+
[:hash256, 2] => :parent_hash,
|
109
|
+
[:hash256, 3] => :tx_hash,
|
110
|
+
[:hash256, 4] => :account_hash,
|
111
|
+
[:hash256, 5] => :previous_txn_id,
|
112
|
+
[:hash256, 6] => :ledger_index,
|
113
|
+
[:hash256, 7] => :wallet_locator,
|
114
|
+
[:hash256, 8] => :root_index,
|
115
|
+
[:hash256, 9] => :account_txn_id,
|
116
|
+
|
117
|
+
# 256-bit (uncommon)
|
118
|
+
[:hash256, 16] => :book_directory,
|
119
|
+
[:hash256, 17] => :invoice_id,
|
120
|
+
[:hash256, 18] => :nickname,
|
121
|
+
[:hash256, 19] => :amendment,
|
122
|
+
[:hash256, 20] => :ticket_id,
|
123
|
+
[:hash256, 21] => :digest,
|
124
|
+
[:hash256, 22] => :channel,
|
125
|
+
[:hash256, 24] => :check_id,
|
126
|
+
|
127
|
+
# currency amount (common)
|
128
|
+
[:amount, 1] => :amount,
|
129
|
+
[:amount, 2] => :balance,
|
130
|
+
[:amount, 3] => :limit_amount,
|
131
|
+
[:amount, 4] => :taker_pays,
|
132
|
+
[:amount, 5] => :taker_gets,
|
133
|
+
[:amount, 6] => :low_limit,
|
134
|
+
[:amount, 7] => :high_limit,
|
135
|
+
[:amount, 8] => :fee,
|
136
|
+
[:amount, 9] => :send_max,
|
137
|
+
[:amount, 10] => :deliver_min,
|
138
|
+
|
139
|
+
# currency amount (uncommon)
|
140
|
+
[:amount, 16] => :minimum_offer,
|
141
|
+
[:amount, 17] => :ripple_escrow,
|
142
|
+
[:amount, 18] => :delivered_amount,
|
143
|
+
|
144
|
+
# variable length (common)
|
145
|
+
[:vl, 1] => :public_key,
|
146
|
+
[:vl, 2] => :message_key,
|
147
|
+
[:vl, 3] => :signing_pub_key,
|
148
|
+
[:vl, 4] => :txn_signature,
|
149
|
+
[:vl, 5] => :generator,
|
150
|
+
[:vl, 6] => :signature,
|
151
|
+
[:vl, 7] => :domain,
|
152
|
+
[:vl, 8] => :fund_code,
|
153
|
+
[:vl, 9] => :remove_code,
|
154
|
+
[:vl, 10] => :expire_code,
|
155
|
+
[:vl, 11] => :create_code,
|
156
|
+
[:vl, 12] => :memo_type,
|
157
|
+
[:vl, 13] => :memo_data,
|
158
|
+
[:vl, 14] => :memo_format,
|
159
|
+
|
160
|
+
# variable length (uncommon)
|
161
|
+
[:vl, 16] => :fulfillment,
|
162
|
+
[:vl, 17] => :condition,
|
163
|
+
[:vl, 18] => :master_signature,
|
164
|
+
|
165
|
+
# account
|
166
|
+
[:account, 1] => :account,
|
167
|
+
[:account, 2] => :owner,
|
168
|
+
[:account, 3] => :destination,
|
169
|
+
[:account, 4] => :issuer,
|
170
|
+
[:account, 7] => :target,
|
171
|
+
[:account, 8] => :regular_key,
|
172
|
+
|
173
|
+
# inner object
|
174
|
+
[:object, 1] => :end_of_object,
|
175
|
+
[:object, 2] => :transaction_metadata,
|
176
|
+
[:object, 3] => :created_node,
|
177
|
+
[:object, 4] => :deleted_node,
|
178
|
+
[:object, 5] => :modified_node,
|
179
|
+
[:object, 6] => :previous_fields,
|
180
|
+
[:object, 7] => :final_fields,
|
181
|
+
[:object, 8] => :new_fields,
|
182
|
+
[:object, 9] => :template_entry,
|
183
|
+
[:object, 10] => :memo,
|
184
|
+
[:object, 11] => :signer_entry,
|
185
|
+
|
186
|
+
# inner object (uncommon)
|
187
|
+
[:object, 16] => :signer,
|
188
|
+
[:object, 18] => :majority,
|
189
|
+
|
190
|
+
# array of objects
|
191
|
+
[:array, 1] => :end_of_array,
|
192
|
+
[:array, 2] => :signing_accounts,
|
193
|
+
[:array, 3] => :signers,
|
194
|
+
[:array, 4] => :signer_entries,
|
195
|
+
[:array, 5] => :template,
|
196
|
+
[:array, 6] => :necessary,
|
197
|
+
[:array, 7] => :sufficient,
|
198
|
+
[:array, 8] => :affected_nodes,
|
199
|
+
[:array, 9] => :memos,
|
200
|
+
|
201
|
+
# array of objects (uncommon)
|
202
|
+
[:array, 16] => :majorities,
|
203
|
+
|
204
|
+
# 8-bit unsigned integers (common)
|
205
|
+
[:uint8, 1] => :close_resolution,
|
206
|
+
[:uint8, 2] => :method,
|
207
|
+
[:uint8, 3] => :transaction_result,
|
208
|
+
|
209
|
+
# 8-bit unsigned integers (uncommon)
|
210
|
+
[:uint8, 16] => :tick_size,
|
211
|
+
|
212
|
+
# 160-bit (common)
|
213
|
+
[:hash160, 1] => :taker_pays_currency,
|
214
|
+
[:hash160, 2] => :taker_pays_issuer,
|
215
|
+
[:hash160, 3] => :taker_gets_currency,
|
216
|
+
[:hash160, 4] => :taker_gets_issuer,
|
217
|
+
|
218
|
+
# path set
|
219
|
+
[:pathset, 1] => :paths,
|
220
|
+
|
221
|
+
# vector of 256-bit
|
222
|
+
[:vector256, 1] => :indexes,
|
223
|
+
[:vector256, 2] => :hashes,
|
224
|
+
[:vector256, 3] => :amendments,
|
225
|
+
}
|
226
|
+
|
227
|
+
###
|
228
|
+
|
229
|
+
TYPE_INFER = Bistro.new([
|
230
|
+
'H16', nil, # unused
|
231
|
+
'c', 'node_type',
|
232
|
+
'H8', 'hash_prefix',
|
233
|
+
'H*', 'node'
|
234
|
+
])
|
235
|
+
|
236
|
+
INNER_NODE = Bistro.new([
|
237
|
+
'H16', nil, # unused
|
238
|
+
'c', 'node_type', # can be one of NODE_TYPES for: 'account_node' or 'tx_node'
|
239
|
+
'H8', 'hp_inner_node',
|
240
|
+
'H64', 'child0',
|
241
|
+
'H64', 'child1',
|
242
|
+
'H64', 'child2',
|
243
|
+
'H64', 'child3',
|
244
|
+
'H64', 'child4',
|
245
|
+
'H64', 'child5',
|
246
|
+
'H64', 'child6',
|
247
|
+
'H64', 'child7',
|
248
|
+
'H64', 'child8',
|
249
|
+
'H64', 'child9',
|
250
|
+
'H64', 'child10',
|
251
|
+
'H64', 'child11',
|
252
|
+
'H64', 'child12',
|
253
|
+
'H64', 'child13',
|
254
|
+
'H64', 'child14',
|
255
|
+
'H64', 'child15',
|
256
|
+
'H64', 'child16',
|
257
|
+
'H64', 'child17',
|
258
|
+
'H64', 'child18',
|
259
|
+
'H64', 'child19',
|
260
|
+
'H64', 'child20',
|
261
|
+
'H64', 'child21',
|
262
|
+
'H64', 'child22',
|
263
|
+
'H64', 'child23',
|
264
|
+
'H64', 'child24',
|
265
|
+
'H64', 'child25',
|
266
|
+
'H64', 'child26',
|
267
|
+
'H64', 'child27',
|
268
|
+
'H64', 'child28',
|
269
|
+
'H64', 'child29',
|
270
|
+
'H64', 'child30',
|
271
|
+
'H64', 'child31'
|
272
|
+
])
|
273
|
+
|
274
|
+
LEDGER = Bistro.new([
|
275
|
+
'H16', nil, # unused
|
276
|
+
'c', 'nt_ledger',
|
277
|
+
'H8', 'hp_ledger_master',
|
278
|
+
'N', 'index',
|
279
|
+
'Q', 'total_coins',
|
280
|
+
'H64', 'parent_hash',
|
281
|
+
'H64', 'tx_hash',
|
282
|
+
'H64', 'account_hash',
|
283
|
+
'N', 'parent_close_time',
|
284
|
+
'N', 'close_time',
|
285
|
+
'C', 'close_time_resolution',
|
286
|
+
'C', 'close_flags',
|
287
|
+
])
|
288
|
+
|
289
|
+
CURRENCY_CODE = Bistro.new([
|
290
|
+
'C', 'type_code',
|
291
|
+
'C11', 'reserved1',
|
292
|
+
'C3', 'iso_code',
|
293
|
+
'C5', 'reserved2'
|
294
|
+
])
|
295
|
+
end # module Format
|
296
|
+
end # module NodeStore
|
297
|
+
end # module XRBP
|
data/lib/xrbp/overlay.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module XRBP
|
2
|
+
module Overlay
|
3
|
+
class Connection
|
4
|
+
attr_reader :host, :port
|
5
|
+
attr_accessor :node
|
6
|
+
|
7
|
+
def initialize(host, port)
|
8
|
+
@host = host
|
9
|
+
@port = port
|
10
|
+
@node = Crypto.node
|
11
|
+
end
|
12
|
+
|
13
|
+
def socket
|
14
|
+
@socket ||= TCPSocket.open(host, port)
|
15
|
+
end
|
16
|
+
|
17
|
+
def closed?
|
18
|
+
socket.closed?
|
19
|
+
end
|
20
|
+
|
21
|
+
def ssl_socket
|
22
|
+
@ssl_socket ||= begin
|
23
|
+
ssl_context = OpenSSL::SSL::SSLContext.new
|
24
|
+
ssl_context.ssl_version = :SSLv23
|
25
|
+
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
26
|
+
|
27
|
+
_ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
|
28
|
+
_ssl_socket.sync_close = true
|
29
|
+
|
30
|
+
_ssl_socket
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def handshake
|
35
|
+
@handshake ||= Handshake.new self
|
36
|
+
end
|
37
|
+
|
38
|
+
###
|
39
|
+
|
40
|
+
def connect
|
41
|
+
ssl_socket.connect
|
42
|
+
handshake.execute!
|
43
|
+
end
|
44
|
+
|
45
|
+
def close
|
46
|
+
ssl_socket.close
|
47
|
+
end
|
48
|
+
|
49
|
+
def write(data)
|
50
|
+
ssl_socket.puts(data)
|
51
|
+
end
|
52
|
+
|
53
|
+
def read
|
54
|
+
ssl_socket.gets
|
55
|
+
end
|
56
|
+
|
57
|
+
def read_frames
|
58
|
+
frame = nil
|
59
|
+
remaining = nil
|
60
|
+
while !closed?
|
61
|
+
read_sockets, _, _ = IO.select([ssl_socket], nil, nil, 0.1)
|
62
|
+
if read_sockets && read_sockets[0]
|
63
|
+
out = ssl_socket.read_nonblock(1024)
|
64
|
+
|
65
|
+
if frame.nil?
|
66
|
+
type = Frame::TYPE_INFER.decode(out)
|
67
|
+
frame = Frame.new type["type"], type["size"]
|
68
|
+
out = out[Frame::TYPE_INFER.size..-1]
|
69
|
+
end
|
70
|
+
|
71
|
+
_, remaining = frame << out
|
72
|
+
if frame.complete?
|
73
|
+
# TODO extra specific protobuf data structure
|
74
|
+
# from data, set on frame
|
75
|
+
yield frame
|
76
|
+
frame = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
# XXX: doesn't feel right to just
|
80
|
+
# discard remaining, look into this
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end # class Connection
|
85
|
+
end # module WebClient
|
86
|
+
end # module XRBP
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "bistro"
|
2
|
+
require_relative './ripple.proto'
|
3
|
+
|
4
|
+
module XRBP
|
5
|
+
module Overlay
|
6
|
+
class Frame
|
7
|
+
TYPE_INFER = Bistro.new([
|
8
|
+
'L>', 'size',
|
9
|
+
'S>', 'type'
|
10
|
+
])
|
11
|
+
|
12
|
+
def self.header_size
|
13
|
+
TYPE_INFER.size
|
14
|
+
end
|
15
|
+
|
16
|
+
def header_size
|
17
|
+
self.class.header_size
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.type_name(t)
|
21
|
+
Protocol::MessageType.lookup(t)
|
22
|
+
end
|
23
|
+
|
24
|
+
###
|
25
|
+
|
26
|
+
attr_reader :type, :size
|
27
|
+
attr_accessor :data
|
28
|
+
|
29
|
+
def initialize(type, size)
|
30
|
+
@type = type
|
31
|
+
@size = size
|
32
|
+
|
33
|
+
@data = ""
|
34
|
+
end
|
35
|
+
|
36
|
+
def type_name
|
37
|
+
@type_name ||= self.class.type_name(type)
|
38
|
+
end
|
39
|
+
|
40
|
+
def <<(data)
|
41
|
+
remaining = size - (@data.size + header_size)
|
42
|
+
@data += data[0..remaining-1]
|
43
|
+
return @data, data[remaining..-1]
|
44
|
+
end
|
45
|
+
|
46
|
+
def complete?
|
47
|
+
(@data.size + header_size) == size
|
48
|
+
end
|
49
|
+
end # class Frame
|
50
|
+
end # module WebClient
|
51
|
+
end # module XRBP
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# XXX this module requires the openssl-ruby gem with the following patches:
|
2
|
+
# https://github.com/ruby/openssl/pull/250
|
3
|
+
#
|
4
|
+
# Otherwise the ssl_socket#finished and #peer_finished methods will
|
5
|
+
# not be available
|
6
|
+
#
|
7
|
+
# Currently the only way to apply this is to checkout openssl-ruby, apply
|
8
|
+
# the patches, and then rebuild/reinstall the gem locally!
|
9
|
+
|
10
|
+
require 'base64'
|
11
|
+
require 'openssl'
|
12
|
+
|
13
|
+
module XRBP
|
14
|
+
module Overlay
|
15
|
+
class Handshake
|
16
|
+
attr_reader :connection, :response
|
17
|
+
|
18
|
+
def initialize(connection)
|
19
|
+
@connection = connection
|
20
|
+
end
|
21
|
+
|
22
|
+
def socket
|
23
|
+
connection.ssl_socket
|
24
|
+
end
|
25
|
+
|
26
|
+
def node
|
27
|
+
connection.node
|
28
|
+
end
|
29
|
+
|
30
|
+
###
|
31
|
+
|
32
|
+
def shared
|
33
|
+
@shared ||= begin
|
34
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
35
|
+
sf = socket.finished
|
36
|
+
pf = socket.peer_finished
|
37
|
+
sf = sha512.digest(sf)
|
38
|
+
pf = sha512.digest(pf)
|
39
|
+
shared = sf.to_bn ^ pf.to_bn
|
40
|
+
shared = shared.bytes.reverse.pack("C*")
|
41
|
+
shared = sha512.digest(shared)[0..31]
|
42
|
+
|
43
|
+
shared = Crypto::Key.sign_digest(node, shared)
|
44
|
+
Base64.strict_encode64(shared)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def data
|
49
|
+
@data ||=
|
50
|
+
"GET / HTTP/1.1\r
|
51
|
+
User-Agent: rippled-1.1.2\r
|
52
|
+
Upgrade: RTXP/1.2, RTXP/1.3\r
|
53
|
+
Connection: Upgrade\r
|
54
|
+
Connect-As: Leaf, Peer\r
|
55
|
+
Public-Key: #{node[:node]}\r
|
56
|
+
Session-Signature: #{shared}\r
|
57
|
+
\r\n"
|
58
|
+
end
|
59
|
+
|
60
|
+
###
|
61
|
+
|
62
|
+
def execute!
|
63
|
+
socket.puts(data)
|
64
|
+
|
65
|
+
@response = ""
|
66
|
+
until connection.closed? # || connection.force_quit?
|
67
|
+
read_sockets, _, _ = IO.select([socket], nil, nil, 0.1)
|
68
|
+
|
69
|
+
if read_sockets && read_sockets[0]
|
70
|
+
out = socket.read_nonblock(1024)
|
71
|
+
@response += out.strip
|
72
|
+
break if out[-4..-1] == "\r\n\r\n"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end # class Handshake
|
77
|
+
end # module WebClient
|
78
|
+
end # module XRBP
|