xrbp 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,3 @@
1
+ require_relative './overlay/connection'
2
+ require_relative './overlay/handshake'
3
+ require_relative './overlay/frame'
@@ -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