xrbp 0.2.1 → 0.2.2
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/examples/nodestore1.rb +12 -7
- data/lib/xrbp/core_ext.rb +27 -0
- data/lib/xrbp/crypto/account.rb +28 -3
- data/lib/xrbp/nodestore.rb +6 -0
- data/lib/xrbp/nodestore/amendments.rb +13 -0
- data/lib/xrbp/nodestore/backends/decompressor.rb +8 -6
- data/lib/xrbp/nodestore/backends/nudb.rb +2 -0
- data/lib/xrbp/nodestore/backends/rocksdb.rb +1 -0
- data/lib/xrbp/nodestore/db.rb +5 -387
- data/lib/xrbp/nodestore/fees.rb +19 -0
- data/lib/xrbp/nodestore/format.rb +72 -1
- data/lib/xrbp/nodestore/ledger.rb +272 -0
- data/lib/xrbp/nodestore/parser.rb +407 -0
- data/lib/xrbp/nodestore/protocol.rb +5 -0
- data/lib/xrbp/nodestore/protocol/currency.rb +11 -0
- data/lib/xrbp/nodestore/protocol/indexes.rb +109 -0
- data/lib/xrbp/nodestore/protocol/issue.rb +26 -0
- data/lib/xrbp/nodestore/protocol/quality.rb +10 -0
- data/lib/xrbp/nodestore/protocol/rate.rb +21 -0
- data/lib/xrbp/nodestore/shamap.rb +447 -0
- data/lib/xrbp/nodestore/shamap/errors.rb +8 -0
- data/lib/xrbp/nodestore/shamap/inner_node.rb +98 -0
- data/lib/xrbp/nodestore/shamap/item.rb +14 -0
- data/lib/xrbp/nodestore/shamap/node.rb +49 -0
- data/lib/xrbp/nodestore/shamap/node_factory.rb +120 -0
- data/lib/xrbp/nodestore/shamap/node_id.rb +83 -0
- data/lib/xrbp/nodestore/shamap/tagged_cache.rb +20 -0
- data/lib/xrbp/nodestore/shamap/tree_node.rb +21 -0
- data/lib/xrbp/nodestore/sle.rb +4 -0
- data/lib/xrbp/nodestore/sle/st_account.rb +8 -0
- data/lib/xrbp/nodestore/sle/st_amount.rb +226 -0
- data/lib/xrbp/nodestore/sle/st_ledger_entry.rb +24 -0
- data/lib/xrbp/nodestore/sle/st_object.rb +46 -0
- data/lib/xrbp/nodestore/sqldb.rb +23 -0
- data/lib/xrbp/nodestore/uint.rb +7 -0
- data/lib/xrbp/version.rb +1 -1
- data/spec/xrbp/nodestore/backends/nudb_spec.rb +3 -1
- data/spec/xrbp/nodestore/backends/rocksdb_spec.rb +1 -1
- data/spec/xrbp/nodestore/{backends/db_parser.rb → db_parser.rb} +2 -2
- data/spec/xrbp/nodestore/ledger_access.rb +17 -0
- metadata +30 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cdd7b83c144366a77c0a68287e2ae511fa2878ef5b3be0e0f24b0b4d9e6a1be5
|
4
|
+
data.tar.gz: 1db244c92cefaca23244dd7a20cbb89c0d9e2fc25ab10659483aaa8187f0f117
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 42cdd0d6b998b3550a89233b90c86676fe9e56f7a4981cfad96f209fb3272ae2f669f5b16de399318596d13fb9c0fc2b087fa968155a4f30f211fed46cce74c4
|
7
|
+
data.tar.gz: ee8e543e9e04557f8e8355dbd4f56a19f9176ba8e3f24c87db128298f38caa0386bf3763180bee734c94f7e91aaba3341c4689b4f7d4a53b3d4d0fa49d22b6d9
|
data/examples/nodestore1.rb
CHANGED
@@ -9,11 +9,16 @@ db = XRBP::NodeStore::Backends::RocksDB.new "/var/lib/rippled/rocksdb/rippledb.0
|
|
9
9
|
#require 'xrbp/nodestore/backends/nudb'
|
10
10
|
#db = XRBP::NodeStore::Backends::NuDB.new "/var/lib/rippled/nudb/"
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
ledger = "32E073D7E4D722D956F7FDE095F756FBB86DC9CA487EB0D9ABF5151A8D88F912"
|
13
|
+
ledger = [ledger].pack("H*")
|
14
|
+
puts db.ledger(ledger)
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
gw1 = 'razqQKzJRdB4UxFPWf5NEpEG3WMkmwgcXA'
|
17
|
+
iou1 = {:currency => 'XRP', :account => XRBP::Crypto.xrp_account}
|
18
|
+
iou2 = {:currency => 'CNY', :account => gw1}
|
19
|
+
nledger = XRBP::NodeStore::Ledger.new(:db => db, :hash => ledger)
|
20
|
+
puts nledger.order_book iou1, iou2
|
21
|
+
puts nledger.txs
|
22
|
+
|
23
|
+
require 'xrbp/nodestore/sqldb'
|
24
|
+
puts XRBP::NodeStore::SQLDB.new("/var/lib/rippled/").ledger_hash_for_seq(49340234)
|
data/lib/xrbp/core_ext.rb
CHANGED
@@ -29,6 +29,22 @@ class String
|
|
29
29
|
def to_bn
|
30
30
|
bytes.inject(0) { |bn, b| (bn << 8) | b }
|
31
31
|
end
|
32
|
+
|
33
|
+
def all?(&bl)
|
34
|
+
each_char { |c| return false unless bl.call(c) }
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
|
38
|
+
def zero?
|
39
|
+
return self == "\0" if size == 1
|
40
|
+
all? { |c| c.zero? }
|
41
|
+
end
|
42
|
+
|
43
|
+
# scan(regex) will not work as we need to process
|
44
|
+
# binary strings (\n's seem to trip scan up)
|
45
|
+
def chunk(size)
|
46
|
+
((self.length + size - 1) / size).times.collect { |i| self[i * size, size] }
|
47
|
+
end
|
32
48
|
end
|
33
49
|
|
34
50
|
# @private
|
@@ -44,3 +60,14 @@ class Integer
|
|
44
60
|
b
|
45
61
|
end
|
46
62
|
end
|
63
|
+
|
64
|
+
# @private
|
65
|
+
class Array
|
66
|
+
def rjust!(n, x)
|
67
|
+
insert(0, *Array.new([0, n-length].max, x))
|
68
|
+
end
|
69
|
+
|
70
|
+
def ljust!(n, x)
|
71
|
+
fill(x, length...n)
|
72
|
+
end
|
73
|
+
end
|
data/lib/xrbp/crypto/account.rb
CHANGED
@@ -2,12 +2,13 @@ require 'base58'
|
|
2
2
|
|
3
3
|
module XRBP
|
4
4
|
module Crypto
|
5
|
-
# Generate
|
5
|
+
# Generate a new XRPL account.
|
6
6
|
#
|
7
7
|
# @param key [Symbol, Hash] key type to generate or key itself (optional)
|
8
8
|
# @return [Hash] account details containing id and pub/priv key pair
|
9
9
|
def self.account(key=nil)
|
10
10
|
pub = nil
|
11
|
+
account_id = nil
|
11
12
|
if key == :secp256k1 || key.nil?
|
12
13
|
key = Key::secp256k1
|
13
14
|
pub = key[:public]
|
@@ -17,21 +18,45 @@ module XRBP
|
|
17
18
|
pub = "\xED" + key[:public]
|
18
19
|
|
19
20
|
elsif key.is_a?(Hash)
|
20
|
-
|
21
|
+
if key[:account_id]
|
22
|
+
account_id = key[:account_id]
|
23
|
+
elsif key[:public]
|
24
|
+
pub = key[:public]
|
25
|
+
end
|
21
26
|
|
22
27
|
else
|
23
28
|
pub = key
|
24
29
|
key = {:public => pub}
|
25
30
|
end
|
26
31
|
|
32
|
+
raise "must specify pub or account_id" unless pub || account_id
|
33
|
+
|
27
34
|
sha256 = OpenSSL::Digest::SHA256.new
|
28
35
|
ripemd160 = OpenSSL::Digest::RIPEMD160.new
|
29
|
-
account_id = [Key::TOKEN_TYPES[:account_id]].pack("C") + ripemd160.digest(sha256.digest([pub].pack("H*")))
|
36
|
+
account_id = [Key::TOKEN_TYPES[:account_id]].pack("C") + ripemd160.digest(sha256.digest([pub].pack("H*"))) unless account_id
|
30
37
|
chksum = sha256.digest(sha256.digest(account_id))[0..3]
|
31
38
|
|
32
39
|
{ :account => Base58.binary_to_base58(account_id + chksum, :ripple) }.merge(key)
|
33
40
|
end
|
34
41
|
|
42
|
+
# Account Zero: https://xrpl.org/accounts.html#special-addresses
|
43
|
+
def self.xrp_account
|
44
|
+
@xrp_account ||= account(:account_id => ([0] * 21).pack("C*"))[:account]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Account One: https://xrpl.org/accounts.html#special-addresses
|
48
|
+
def self.no_account
|
49
|
+
@no_account ||= account(:account_id => ([0] * 20 + [1]).pack("C*"))[:account]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return the account id for the specified XRP account.
|
53
|
+
# This is a simpler version of {parse_account} that just
|
54
|
+
# returns the binary account, _skipping_ the token and
|
55
|
+
# checksum verifications.
|
56
|
+
def self.account_id(account)
|
57
|
+
Base58.base58_to_binary(account, :ripple)[1..-5]
|
58
|
+
end
|
59
|
+
|
35
60
|
# Extract Account ID from Address.
|
36
61
|
#
|
37
62
|
# @param account [String] Base58 encoded account address
|
data/lib/xrbp/nodestore.rb
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
+
require_relative './nodestore/uint'
|
1
2
|
require_relative './nodestore/format'
|
3
|
+
require_relative './nodestore/sle'
|
4
|
+
require_relative './nodestore/shamap'
|
5
|
+
require_relative './nodestore/ledger'
|
6
|
+
|
7
|
+
require_relative './nodestore/protocol'
|
2
8
|
|
3
9
|
require_relative './nodestore/db'
|
4
10
|
|
@@ -102,8 +102,8 @@ module XRBP
|
|
102
102
|
out = [0, 0, 0, 0,
|
103
103
|
0, 0, 0, 0,
|
104
104
|
Format::NODE_TYPES[:unknown]] +
|
105
|
-
[Format::HASH_PREFIXES
|
106
|
-
|
105
|
+
[Format::HASH_PREFIXES[:inner_node]].pack("H*")
|
106
|
+
.unpack("C*")
|
107
107
|
|
108
108
|
# extract mask indicating which hashes are set
|
109
109
|
bytes = data.bytes
|
@@ -147,8 +147,8 @@ module XRBP
|
|
147
147
|
out = [0, 0, 0, 0,
|
148
148
|
0, 0, 0, 0,
|
149
149
|
Format::NODE_TYPES[:unknown]] +
|
150
|
-
[Format::HASH_PREFIXES
|
151
|
-
|
150
|
+
[Format::HASH_PREFIXES[:inner_node_v2]].pack("H*")
|
151
|
+
.unpack("C*")
|
152
152
|
|
153
153
|
# Same mask / hash extraction as V1
|
154
154
|
|
@@ -205,7 +205,8 @@ module XRBP
|
|
205
205
|
out = [0, 0, 0, 0,
|
206
206
|
0, 0, 0, 0,
|
207
207
|
Format::NODE_TYPES[:unknown]] +
|
208
|
-
[Format::HASH_PREFIXES
|
208
|
+
[Format::HASH_PREFIXES[:inner_node]].pack("H*")
|
209
|
+
.unpack("C*")
|
209
210
|
(out + data[0...512].bytes).pack("C*")
|
210
211
|
end
|
211
212
|
|
@@ -223,7 +224,8 @@ module XRBP
|
|
223
224
|
out = [0, 0, 0, 0,
|
224
225
|
0, 0, 0, 0,
|
225
226
|
Format::NODE_TYPES[:unknown]] +
|
226
|
-
[Format::HASH_PREFIXES
|
227
|
+
[Format::HASH_PREFIXES[:inner_node_v2]].pack("H*")
|
228
|
+
.unpack("C*")
|
227
229
|
|
228
230
|
out += bytes[0..511]
|
229
231
|
bytes = bytes[512..-1]
|
data/lib/xrbp/nodestore/db.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'base58'
|
2
2
|
require 'openssl'
|
3
3
|
|
4
|
+
require_relative './parser'
|
5
|
+
|
4
6
|
module XRBP
|
5
7
|
# The NodeStore is the Key/Value DB which rippled persistent stores
|
6
8
|
# ledger data. Implemented via a backend configured at run time,
|
@@ -24,6 +26,9 @@ module XRBP
|
|
24
26
|
include Enumerable
|
25
27
|
include EventEmitter
|
26
28
|
|
29
|
+
# NodeStore Parser
|
30
|
+
include Parser
|
31
|
+
|
27
32
|
# Return the NodeStore Ledger for the given lookup hash
|
28
33
|
def ledger(hash)
|
29
34
|
val = self[hash]
|
@@ -56,393 +61,6 @@ module XRBP
|
|
56
61
|
return nil if val.nil?
|
57
62
|
parse_inner_node(val)
|
58
63
|
end
|
59
|
-
|
60
|
-
###
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
# Parsers binary ledger representation into structured ledger.
|
65
|
-
#
|
66
|
-
# @private
|
67
|
-
def parse_ledger(ledger)
|
68
|
-
obj = Format::LEDGER.decode(ledger)
|
69
|
-
obj['close_time'] = XRBP::from_xrp_time(obj['close_time']).utc
|
70
|
-
obj['parent_close_time'] = XRBP::from_xrp_time(obj['parent_close_time']).utc
|
71
|
-
obj['parent_hash'].upcase!
|
72
|
-
obj['tx_hash'].upcase!
|
73
|
-
obj['account_hash'].upcase!
|
74
|
-
obj
|
75
|
-
end
|
76
|
-
|
77
|
-
# Certain data types are prefixed with an 'encoding' header
|
78
|
-
# consisting of a field and/or type. Field, type, and remaining
|
79
|
-
# bytes are returned
|
80
|
-
#
|
81
|
-
# @private
|
82
|
-
def parse_encoding(encoding)
|
83
|
-
enc = encoding.unpack("C").first
|
84
|
-
type = enc >> 4
|
85
|
-
field = enc & 0xF
|
86
|
-
encoding = encoding[1..-1]
|
87
|
-
|
88
|
-
if type == 0
|
89
|
-
type = encoding.unpack("C").first
|
90
|
-
encoding = encoding[1..-1]
|
91
|
-
end
|
92
|
-
|
93
|
-
if field == 0
|
94
|
-
field = encoding.unpack("C").first
|
95
|
-
encoding = encoding[1..-1]
|
96
|
-
end
|
97
|
-
|
98
|
-
type = Format::SERIALIZED_TYPES[type]
|
99
|
-
[[type, field], encoding]
|
100
|
-
end
|
101
|
-
|
102
|
-
# Parses binary ledger entry into hash. Data returned
|
103
|
-
# in hash includes ledger entry type prefix, index,
|
104
|
-
# and array of parsed fields.
|
105
|
-
#
|
106
|
-
# @private
|
107
|
-
def parse_ledger_entry(ledger_entry)
|
108
|
-
# validate parsability
|
109
|
-
obj = Format::TYPE_INFER.decode(ledger_entry)
|
110
|
-
node_type = Format::NODE_TYPE_CODES[obj["node_type"]]
|
111
|
-
hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
|
112
|
-
raise unless node_type == :account_node &&
|
113
|
-
hash_prefix == :leaf_node
|
114
|
-
|
115
|
-
# discard node type, and hash prefix
|
116
|
-
ledger_entry = ledger_entry[13..-1]
|
117
|
-
|
118
|
-
# verify encoding
|
119
|
-
encoding, ledger_entry = parse_encoding(ledger_entry)
|
120
|
-
raise "Invalid Ledger Entry" unless Format::ENCODINGS[encoding] == :ledger_entry_type
|
121
|
-
ledger_entry = ledger_entry.bytes
|
122
|
-
|
123
|
-
# first byte after encoding is ledger entry type prefix
|
124
|
-
prefix = ledger_entry[0..1].pack("C*")
|
125
|
-
|
126
|
-
# last 32 bytes is entry index
|
127
|
-
index = ledger_entry[-32..-1].pack("C*")
|
128
|
-
.unpack("H*")
|
129
|
-
.first
|
130
|
-
.upcase
|
131
|
-
|
132
|
-
# remaining bytes are serialized object
|
133
|
-
fields, remaining = parse_fields(ledger_entry[2...-32].pack("C*"))
|
134
|
-
raise unless remaining.empty?
|
135
|
-
|
136
|
-
# TODO instantiate class corresponding to type &
|
137
|
-
# populate attributes w/ fields (?)
|
138
|
-
|
139
|
-
{ :type => Format::LEDGER_ENTRY_TYPE_CODES[prefix[1]],
|
140
|
-
:index => index,
|
141
|
-
:fields => fields }
|
142
|
-
end
|
143
|
-
|
144
|
-
###
|
145
|
-
|
146
|
-
# Parse and return series of fields from binary data.
|
147
|
-
#
|
148
|
-
# @private
|
149
|
-
def parse_fields(fields)
|
150
|
-
parsed = {}
|
151
|
-
until fields == "" || fields == "\0" || fields.nil?
|
152
|
-
encoding, fields = parse_encoding(fields)
|
153
|
-
return parsed if encoding.first.nil?
|
154
|
-
|
155
|
-
e = Format::ENCODINGS[encoding]
|
156
|
-
value, fields = parse_field(fields, encoding)
|
157
|
-
break unless value
|
158
|
-
parsed[e] = convert_field(encoding, value)
|
159
|
-
end
|
160
|
-
|
161
|
-
return parsed, fields
|
162
|
-
end
|
163
|
-
|
164
|
-
# Parse single field of specified encoding from data.
|
165
|
-
# Dispatches to corresponding parsing method when appropriate.
|
166
|
-
#
|
167
|
-
# @private
|
168
|
-
def parse_field(data, encoding)
|
169
|
-
length = encoding.first
|
170
|
-
|
171
|
-
case length
|
172
|
-
when :uint8
|
173
|
-
return data.unpack("C").first, data[1..-1]
|
174
|
-
when :uint16
|
175
|
-
return data.unpack("S>").first, data[2..-1]
|
176
|
-
when :uint32
|
177
|
-
return data.unpack("L>").first, data[4..-1]
|
178
|
-
when :uint64
|
179
|
-
return data.unpack("Q>").first, data[8..-1]
|
180
|
-
when :hash128
|
181
|
-
return data.unpack("H32").first, data[16..-1]
|
182
|
-
when :hash160
|
183
|
-
return data.unpack("H40").first, data[20..-1]
|
184
|
-
when :hash256
|
185
|
-
return data.unpack("H64").first, data[32..-1]
|
186
|
-
when :amount
|
187
|
-
return parse_amount(data)
|
188
|
-
when :vl
|
189
|
-
vl, offset = parse_vl(data)
|
190
|
-
return data[offset..vl+offset-1], data[vl+offset..-1]
|
191
|
-
when :account
|
192
|
-
return parse_account(data)
|
193
|
-
when :array
|
194
|
-
return parse_array(data, encoding)
|
195
|
-
when :object
|
196
|
-
return parse_object(data, encoding)
|
197
|
-
when :pathset
|
198
|
-
return parse_pathset(data)
|
199
|
-
when :vector256
|
200
|
-
vl, offset = parse_vl(data)
|
201
|
-
return data[offset..vl+offset-1], data[vl+offset..-1]
|
202
|
-
end
|
203
|
-
|
204
|
-
raise
|
205
|
-
end
|
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
|
-
|
223
|
-
# Parse variable length header from data buffer. Returns length
|
224
|
-
# extracted from header and the number of bytes in header.
|
225
|
-
#
|
226
|
-
# @private
|
227
|
-
def parse_vl(data)
|
228
|
-
data = data.bytes
|
229
|
-
first = data.first.to_i
|
230
|
-
return first, 1 if first <= 192
|
231
|
-
|
232
|
-
data = data[1..-1]
|
233
|
-
second = data.first.to_i
|
234
|
-
if first <= 240
|
235
|
-
return (193+(first-193)*256+second), 2
|
236
|
-
|
237
|
-
elsif first <= 254
|
238
|
-
data = data[1..-1]
|
239
|
-
third = data.first.to_i
|
240
|
-
return (12481 + (first-241)*65536 + second*256 + third), 3
|
241
|
-
end
|
242
|
-
|
243
|
-
raise
|
244
|
-
end
|
245
|
-
|
246
|
-
# Parse 'Amount' data type from binary data.
|
247
|
-
#
|
248
|
-
# @see https://developers.ripple.com/currency-formats.html
|
249
|
-
#
|
250
|
-
# @private
|
251
|
-
def parse_amount(data)
|
252
|
-
amount = data[0..7].unpack("Q>").first
|
253
|
-
xrp = amount < 0x8000000000000000
|
254
|
-
return (amount & 0x3FFFFFFFFFFFFFFF), data[8..-1] if xrp
|
255
|
-
|
256
|
-
sign = (amount & 0x4000000000000000) >> 62 # 0 = neg / 1 = pos
|
257
|
-
exp = (amount & 0x3FC0000000000000) >> 54
|
258
|
-
mant = (amount & 0x003FFFFFFFFFFFFF)
|
259
|
-
|
260
|
-
data = data[8..-1]
|
261
|
-
currency = Format::CURRENCY_CODE.decode(data)
|
262
|
-
currency = currency["iso_code"].pack("C*")
|
263
|
-
|
264
|
-
data = data[Format::CURRENCY_CODE.size..-1]
|
265
|
-
issuer, data = parse_account(data, 20)
|
266
|
-
|
267
|
-
amount = (sign == 0 ? -1 : 1) * mant * 10 ** (exp-97)
|
268
|
-
|
269
|
-
return { :amount => amount,
|
270
|
-
:currency => currency,
|
271
|
-
:issuer => issuer }, data
|
272
|
-
end
|
273
|
-
|
274
|
-
# Parse 'Account' data type from binary data.
|
275
|
-
#
|
276
|
-
# @private
|
277
|
-
def parse_account(data, vl=nil)
|
278
|
-
unless vl
|
279
|
-
vl,offset = parse_vl(data)
|
280
|
-
data = data[offset..-1]
|
281
|
-
end
|
282
|
-
|
283
|
-
acct = "\0" + data[0..vl-1]
|
284
|
-
sha256 = OpenSSL::Digest::SHA256.new
|
285
|
-
digest = sha256.digest(sha256.digest(acct))[0..3]
|
286
|
-
acct += digest
|
287
|
-
acct.force_encoding(Encoding::BINARY) # required for Base58 gem
|
288
|
-
return Base58.binary_to_base58(acct, :ripple), data[vl..-1]
|
289
|
-
end
|
290
|
-
|
291
|
-
# Parse array of fields from binary data.
|
292
|
-
#
|
293
|
-
# @private
|
294
|
-
def parse_array(data, encoding)
|
295
|
-
e = Format::ENCODINGS[encoding]
|
296
|
-
return nil, data if e == :end_of_array
|
297
|
-
|
298
|
-
array = []
|
299
|
-
until data == "" || data.nil?
|
300
|
-
aencoding, data = parse_encoding(data)
|
301
|
-
break if aencoding.first.nil?
|
302
|
-
|
303
|
-
e = Format::ENCODINGS[aencoding]
|
304
|
-
break if e == :end_of_array
|
305
|
-
|
306
|
-
value, data = parse_field(data, aencoding)
|
307
|
-
break unless value
|
308
|
-
array << value
|
309
|
-
end
|
310
|
-
|
311
|
-
return array, data
|
312
|
-
end
|
313
|
-
|
314
|
-
# Parse Object consisting of multiple fields from binary data.
|
315
|
-
#
|
316
|
-
# @private
|
317
|
-
def parse_object(data, encoding)
|
318
|
-
e = Format::ENCODINGS[encoding]
|
319
|
-
case e
|
320
|
-
when :end_of_object
|
321
|
-
return nil, data
|
322
|
-
|
323
|
-
when :signer, :signer_entry,
|
324
|
-
:majority, :memo,
|
325
|
-
:modified_node, :created_node, :deleted_node,
|
326
|
-
:previous_fields, :final_fields, :new_fields
|
327
|
-
# TODO instantiate corresponding classes (?)
|
328
|
-
return parse_fields(data)
|
329
|
-
|
330
|
-
#else:
|
331
|
-
end
|
332
|
-
|
333
|
-
raise "unknown object type: #{e}"
|
334
|
-
end
|
335
|
-
|
336
|
-
# Parse PathSet from binary data.
|
337
|
-
#
|
338
|
-
# @private
|
339
|
-
def parse_pathset(data)
|
340
|
-
pathset = [[]]
|
341
|
-
until data == "" || data.nil?
|
342
|
-
segment = data.unpack("C").first
|
343
|
-
data = data[1..-1]
|
344
|
-
return pathset, data if segment == 0x00 # end of path
|
345
|
-
|
346
|
-
if segment == 0xFF # path boundry
|
347
|
-
pathset << []
|
348
|
-
else
|
349
|
-
account, current, issuer = nil
|
350
|
-
|
351
|
-
path = {}
|
352
|
-
|
353
|
-
if (segment & 0x01) != 0 # path account
|
354
|
-
account, data = parse_account(data, 20)
|
355
|
-
path[:account] = account
|
356
|
-
end
|
357
|
-
|
358
|
-
if (segment & 0x10) != 0 # path currency
|
359
|
-
currency = Format::CURRENCY_CODE.decode(data)
|
360
|
-
currency = currency["iso_code"].pack("C*")
|
361
|
-
data = data[Format::CURRENCY_CODE.size..-1]
|
362
|
-
path[:currency] = currency
|
363
|
-
end
|
364
|
-
|
365
|
-
if (segment & 0x20) != 0 # path issuer
|
366
|
-
issuer, data = parse_account(data, 20)
|
367
|
-
path[:issuer] = issuer
|
368
|
-
end
|
369
|
-
|
370
|
-
pathset.last << path
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
return pathset, data
|
375
|
-
end
|
376
|
-
|
377
|
-
###
|
378
|
-
|
379
|
-
# Parse Transaction from binary data
|
380
|
-
#
|
381
|
-
# @private
|
382
|
-
def parse_tx(tx)
|
383
|
-
obj = Format::TYPE_INFER.decode(tx)
|
384
|
-
node_type = Format::NODE_TYPE_CODES[obj["node_type"]]
|
385
|
-
hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
|
386
|
-
raise unless node_type == :tx_node &&
|
387
|
-
hash_prefix == :tx_node
|
388
|
-
|
389
|
-
# discard node type, and hash prefix
|
390
|
-
tx = tx[13..-1]
|
391
|
-
|
392
|
-
# get node length
|
393
|
-
vl, offset = parse_vl(tx)
|
394
|
-
node, _tx = tx.bytes[offset..vl+offset-1], tx.bytes[vl+offset..-1]
|
395
|
-
node, _remaining = parse_fields(node.pack("C*"))
|
396
|
-
|
397
|
-
# get meta length
|
398
|
-
vl, offset = parse_vl(_tx.pack("C*"))
|
399
|
-
meta, index = _tx[offset..vl+offset-1], _tx[vl+offset..-1]
|
400
|
-
meta, _remaining = parse_fields(meta.pack("C*"))
|
401
|
-
|
402
|
-
{ :node => node,
|
403
|
-
:meta => meta,
|
404
|
-
:index => index.pack("C*").unpack("H*").first.upcase }
|
405
|
-
end
|
406
|
-
|
407
|
-
# Parse InnerNode from binary data.
|
408
|
-
#
|
409
|
-
# @private
|
410
|
-
def parse_inner_node(node)
|
411
|
-
# verify parsability
|
412
|
-
obj = Format::TYPE_INFER.decode(node)
|
413
|
-
hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
|
414
|
-
raise unless hash_prefix == :inner_node
|
415
|
-
|
416
|
-
node = Format::INNER_NODE.decode(node)
|
417
|
-
node['node_type'] = Format::NODE_TYPE_CODES[node['node_type']]
|
418
|
-
node
|
419
|
-
end
|
420
|
-
|
421
|
-
protected
|
422
|
-
|
423
|
-
# Return type and extracted structure from binary data.
|
424
|
-
#
|
425
|
-
# @private
|
426
|
-
def infer_type(value)
|
427
|
-
obj = Format::TYPE_INFER.decode(value)
|
428
|
-
node_type = Format::NODE_TYPE_CODES[obj["node_type"]]
|
429
|
-
hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
|
430
|
-
|
431
|
-
if hash_prefix == :inner_node
|
432
|
-
return :inner_node, parse_inner_node(value)
|
433
|
-
|
434
|
-
elsif node_type == :account_node
|
435
|
-
return :ledger_entry, parse_ledger_entry(value)
|
436
|
-
|
437
|
-
elsif node_type == :tx_node
|
438
|
-
return :tx, parse_tx(value)
|
439
|
-
|
440
|
-
elsif node_type == :ledger
|
441
|
-
return :ledger, parse_ledger(value)
|
442
|
-
end
|
443
|
-
|
444
|
-
return nil
|
445
|
-
end
|
446
64
|
end # class DB
|
447
65
|
end # module NodeStore
|
448
66
|
end # module XRBP
|