xrbp 0.1.3 → 0.1.4

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