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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0ce0d6d6704d00d7fe16278ad96bfd68514f73e7b76486894ae479dee90eef8
|
4
|
+
data.tar.gz: 149a45a103104f1e5cef4da27d6870ba26f98dcb96a4f7e6e0e1bc18038d7367
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed3630f368f3706c6c87e7031d25a6c3136489d6de8e3c83120c8a638a9e198d376615ed0c2fe7f5dab1f897f6aff6502a0296b0d3e03ea4ec27813d9d62b027
|
7
|
+
data.tar.gz: f43f8de4dc245cd2b3fe5996fd8a33f5b009ba60ea08858d0e1af9b008a4bfe71855172e734699d32c1b1dc97d853e94285efb4f11a94feac4f2c5b0087754eb
|
data/examples/crawl_nodes.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
$: << File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'xrbp'
|
3
|
+
require 'xrbp/nodestore/backends/rocksdb'
|
4
|
+
|
5
|
+
db = XRBP::NodeStore::Backends::RocksDB.new "/var/lib/rippled/rocksdb/rippledb.0899"
|
6
|
+
|
7
|
+
#ledger = "B506ADD630CB707044B4BFFCD943C1395966692A13DD618E5BD0978A006B43BD"
|
8
|
+
#ledger = [ledger].pack("H*")
|
9
|
+
#puts db.ledger(ledger)
|
10
|
+
|
11
|
+
#account = "0001bf7468341666f1f47a95e0f4d88e68b5fc7d20d77437cb22954fbbfe6127"
|
12
|
+
account = "02c46b3a4130d0a329c47f0da61b829aa5d1ae53c5817e475bcd794e5107be44"
|
13
|
+
account = [account].pack("H*")
|
14
|
+
puts db.account(account)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
$: << File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'xrbp'
|
3
|
+
require 'xrbp/nodestore/backends/rocksdb'
|
4
|
+
|
5
|
+
db = XRBP::NodeStore::Backends::RocksDB.new "/var/lib/rippled/rocksdb/rippledb.0899"
|
6
|
+
|
7
|
+
db.on :unknown do |hash, node|
|
8
|
+
puts "Unknown #{hash}: #{node}"
|
9
|
+
end
|
10
|
+
|
11
|
+
db.on :inner_node do |hash, node|
|
12
|
+
#puts "Inner Node #{hash}"
|
13
|
+
end
|
14
|
+
|
15
|
+
db.on :ledger do |hash, ledger|
|
16
|
+
#puts "Ledger #{ledger['index']}"
|
17
|
+
end
|
18
|
+
|
19
|
+
db.on :tx do |hash, tx|
|
20
|
+
puts "Tx #{tx}"
|
21
|
+
end
|
22
|
+
|
23
|
+
db.on :account do |hash, account|
|
24
|
+
#puts "Account #{account}"
|
25
|
+
end
|
26
|
+
|
27
|
+
###
|
28
|
+
|
29
|
+
tallys = {}
|
30
|
+
|
31
|
+
# object iterator invokes event emitters
|
32
|
+
db.each do |node|
|
33
|
+
obj = XRBP::NodeStore::Format::TYPE_INFER.decode(node.value)
|
34
|
+
node_type = XRBP::NodeStore::Format::NODE_TYPES[obj["node_type"]]
|
35
|
+
hash_prefix = XRBP::NodeStore::Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
|
36
|
+
|
37
|
+
type = node_type.to_s + "/" + hash_prefix.to_s
|
38
|
+
tallys[type] ||= 0
|
39
|
+
tallys[type] += 1
|
40
|
+
end
|
41
|
+
|
42
|
+
puts tallys
|
data/examples/p2p.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
gem 'openssl', '2.1.3'
|
2
|
+
|
3
|
+
$: << File.expand_path('../../lib', __FILE__)
|
4
|
+
require 'xrbp'
|
5
|
+
|
6
|
+
overlay = XRBP::Overlay::Connection.new "127.0.0.1", 51235
|
7
|
+
overlay.connect
|
8
|
+
puts overlay.handshake.response
|
9
|
+
|
10
|
+
overlay.read_frames do |frame|
|
11
|
+
puts "Message: #{frame.type_name} (#{frame.size} bytes)"
|
12
|
+
end
|
13
|
+
|
14
|
+
overlay.close
|
data/lib/xrbp.rb
CHANGED
data/lib/xrbp/common.rb
CHANGED
@@ -7,4 +7,10 @@ module XRBP
|
|
7
7
|
# Created on 2013-01-01
|
8
8
|
# https://data.ripple.com/v2/ledgers/32570
|
9
9
|
GENESIS_TIME = DateTime.new(2013, 1, 1, 0, 0, 0)
|
10
|
+
|
11
|
+
# Convert XRP Ledger time to local time
|
12
|
+
def self.from_xrp_time(xrp_time)
|
13
|
+
return nil if xrp_time.nil?
|
14
|
+
Time.at(xrp_time + 946684800)
|
15
|
+
end
|
10
16
|
end # module XRBP
|
data/lib/xrbp/core_ext.rb
CHANGED
@@ -22,3 +22,25 @@ class Queue
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
# @private
|
27
|
+
class String
|
28
|
+
# return bignum corresponding to string
|
29
|
+
def to_bn
|
30
|
+
bytes.inject(0) { |bn, b| (bn << 8) | b }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @private
|
35
|
+
class Integer
|
36
|
+
# return bytes
|
37
|
+
def bytes
|
38
|
+
i = dup
|
39
|
+
b = []
|
40
|
+
until i == 0
|
41
|
+
b << (i & 0xFF)
|
42
|
+
i = i >> 8
|
43
|
+
end
|
44
|
+
b
|
45
|
+
end
|
46
|
+
end
|
data/lib/xrbp/crypto.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'base58'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module Crypto
|
5
|
+
def self.account(key=nil)
|
6
|
+
pub = nil
|
7
|
+
if key == :secp256k1 || key.nil?
|
8
|
+
key = Key::secp256k1
|
9
|
+
key[:type] = :secp256k1
|
10
|
+
pub = key[:public]
|
11
|
+
|
12
|
+
elsif key == :ed25519
|
13
|
+
key = Key::ed25519
|
14
|
+
key[:type] = :ed25519
|
15
|
+
pub = "\xED" + key[:public]
|
16
|
+
|
17
|
+
elsif key.is_a?(Hash)
|
18
|
+
pub = key[:public]
|
19
|
+
|
20
|
+
else
|
21
|
+
pub = key
|
22
|
+
key = {:public => pub}
|
23
|
+
end
|
24
|
+
|
25
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
26
|
+
ripemd160 = OpenSSL::Digest::RIPEMD160.new
|
27
|
+
account_id = [Key::TOKEN_TYPES[:account_id]].pack("C") + ripemd160.digest(sha256.digest([pub].pack("H*")))
|
28
|
+
chksum = sha256.digest(sha256.digest(account_id))[0..3]
|
29
|
+
|
30
|
+
{ :account => Base58.binary_to_base58(account_id + chksum, :ripple) }.merge(key)
|
31
|
+
end
|
32
|
+
end # module Crypto
|
33
|
+
end # module XRBP
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module Crypto
|
5
|
+
module Key
|
6
|
+
TOKEN_TYPES = {
|
7
|
+
:none => 1, # unused
|
8
|
+
:node_public => 28,
|
9
|
+
:node_private => 32,
|
10
|
+
:account_id => 0,
|
11
|
+
:account_public => 35,
|
12
|
+
:account_secret => 34,
|
13
|
+
:family_generator => 41,
|
14
|
+
:family_seed => 33
|
15
|
+
}
|
16
|
+
|
17
|
+
###
|
18
|
+
|
19
|
+
def self.priv
|
20
|
+
seed = SecureRandom.random_bytes(32)
|
21
|
+
OpenSSL::Digest::SHA256.new.digest(seed)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.secp256k1
|
25
|
+
# XXX: the bitcoin secp256k1 implementation (which rippled pulls in / vendors)
|
26
|
+
# has alot of nuances which require special configuration in openssl. For
|
27
|
+
# the time being, mitigate this by pulling in & using the ruby
|
28
|
+
# btc-secp256k1 bindings:
|
29
|
+
# https://github.com/cryptape/ruby-bitcoin-secp256k1
|
30
|
+
#
|
31
|
+
# Perhaps at some point, we can look into implementing this logic in pure-ruby:
|
32
|
+
# https://medium.com/coinmonks/introduction-to-blockchains-bedrock-the-elliptic-curve-secp256k1-e4bd3bc17d
|
33
|
+
require 'secp256k1'
|
34
|
+
|
35
|
+
spk = Secp256k1::PrivateKey.new
|
36
|
+
|
37
|
+
# XXX: I'd like to generate the private key first & set,
|
38
|
+
# but for some reason this doesn't work. When the
|
39
|
+
# keys are loaded for signing/verification later
|
40
|
+
# the public key is not able to verify the signature
|
41
|
+
# generated from the private key set in this way.
|
42
|
+
# TODO: Investigate
|
43
|
+
#pk = priv
|
44
|
+
#spk.set_raw_privkey [pk].pack("H*")
|
45
|
+
|
46
|
+
{ :public => spk.pubkey.serialize.unpack("H*").first,
|
47
|
+
:private => spk.send(:serialize) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.ed25519
|
51
|
+
# XXX openssl 1.1.1 needed for EdDSA support:
|
52
|
+
# https://www.openssl.org/blog/blog/2018/09/11/release111/
|
53
|
+
# Until then use this:
|
54
|
+
require "ed25519"
|
55
|
+
|
56
|
+
# FIXME: this works for now (eg generates valid keys),
|
57
|
+
# but we should do this in the same way rippled does:
|
58
|
+
# Generate private key, then generate corresponding
|
59
|
+
# Ed25519 public key
|
60
|
+
key = Ed25519::SigningKey.generate
|
61
|
+
{ :public => key.to_bytes.unpack("H*").first.upcase,
|
62
|
+
:private => key.verify_key.to_bytes.unpack("H*").first.upcase }
|
63
|
+
end
|
64
|
+
|
65
|
+
###
|
66
|
+
|
67
|
+
def self.sign_digest(key, data)
|
68
|
+
raise "unknown key" unless key.is_a?(Hash) && key[:type] && key[:private]
|
69
|
+
raise "invalid data" unless data.length == 32
|
70
|
+
|
71
|
+
|
72
|
+
if key[:type] == :secp256k1
|
73
|
+
# XXX: see note about this library above
|
74
|
+
require 'secp256k1'
|
75
|
+
|
76
|
+
pk = Secp256k1::PrivateKey.new
|
77
|
+
pk.set_raw_privkey [key[:private]].pack("H*")
|
78
|
+
#pk.pubkey.deserialize [key[:public]].pack("H*")
|
79
|
+
sig_raw = pk.ecdsa_sign data, raw: true
|
80
|
+
return pk.ecdsa_serialize sig_raw
|
81
|
+
|
82
|
+
#elsif key[:type] == :ed25519
|
83
|
+
# TODO
|
84
|
+
end
|
85
|
+
|
86
|
+
raise "unknown key type"
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.verify(key, data, expected)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end # module Crypto
|
93
|
+
end # module XRBP
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'base58'
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module Crypto
|
5
|
+
def self.node(key=nil)
|
6
|
+
pub = nil
|
7
|
+
if key == :secp256k1 || key.nil?
|
8
|
+
key = Key::secp256k1
|
9
|
+
key[:type] = :secp256k1
|
10
|
+
pub = key[:public]
|
11
|
+
|
12
|
+
elsif key.is_a?(Hash)
|
13
|
+
pub = key[:public]
|
14
|
+
|
15
|
+
else
|
16
|
+
pub = key
|
17
|
+
key = {:public => pub}
|
18
|
+
end
|
19
|
+
|
20
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
21
|
+
node_id = [Key::TOKEN_TYPES[:node_public]].pack("C") + [pub].pack("H*")
|
22
|
+
chksum = sha256.digest(sha256.digest(node_id))[0..3]
|
23
|
+
|
24
|
+
{ :node => Base58.binary_to_base58(node_id + chksum, :ripple) }.merge(key)
|
25
|
+
end
|
26
|
+
end # module Crypto
|
27
|
+
end # module XRBP
|
data/lib/xrbp/model/node.rb
CHANGED
@@ -11,7 +11,7 @@ module XRBP
|
|
11
11
|
DEFAULT_CRAWL_PORT = 51235
|
12
12
|
|
13
13
|
attr_accessor :ip, :port
|
14
|
-
attr_accessor :addr, :version, :uptime, :type
|
14
|
+
attr_accessor :addr, :version, :uptime, :type, :ledgers
|
15
15
|
|
16
16
|
# Return unique node id
|
17
17
|
def id
|
@@ -71,6 +71,7 @@ module XRBP
|
|
71
71
|
n.version = p["version"].split("-").last
|
72
72
|
n.uptime = p["uptime"]
|
73
73
|
n.type = p["type"]
|
74
|
+
n.ledgers = p["complete_ledgers"]
|
74
75
|
|
75
76
|
n
|
76
77
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
# TODO
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "rocksdb"
|
2
|
+
|
3
|
+
module XRBP
|
4
|
+
module NodeStore
|
5
|
+
module Backends
|
6
|
+
class RocksDB < DB
|
7
|
+
# cap max open files for performance
|
8
|
+
MAX_OPEN_FILES = 200
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
@db = ::RocksDB::DB.new path,
|
12
|
+
{:readonly => true,
|
13
|
+
:max_open_files => MAX_OPEN_FILES}
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](key)
|
17
|
+
@db[key]
|
18
|
+
end
|
19
|
+
|
20
|
+
def each
|
21
|
+
iterator = @db.new_iterator
|
22
|
+
iterator.seek_to_first
|
23
|
+
|
24
|
+
while(iterator.valid)
|
25
|
+
type, obj = infer_type(iterator.value)
|
26
|
+
|
27
|
+
if type
|
28
|
+
emit type, iterator.key, obj
|
29
|
+
else
|
30
|
+
emit :unknown, iterator.key,
|
31
|
+
iterator.value
|
32
|
+
end
|
33
|
+
|
34
|
+
yield iterator
|
35
|
+
iterator.next
|
36
|
+
end
|
37
|
+
|
38
|
+
iterator.close
|
39
|
+
end
|
40
|
+
end # class RocksDB
|
41
|
+
end # module Backends
|
42
|
+
end # module NodeStore
|
43
|
+
end # module XRBP
|
@@ -0,0 +1,324 @@
|
|
1
|
+
require 'base58'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
module XRBP
|
5
|
+
module NodeStore
|
6
|
+
class DB
|
7
|
+
include Enumerable
|
8
|
+
include EventEmitter
|
9
|
+
|
10
|
+
def ledger(hash)
|
11
|
+
parse_ledger(self[hash])
|
12
|
+
end
|
13
|
+
|
14
|
+
def account(hash)
|
15
|
+
parse_ledger_entry(self[hash])
|
16
|
+
end
|
17
|
+
|
18
|
+
def tx(hash)
|
19
|
+
parse_tx(self[hash])
|
20
|
+
end
|
21
|
+
|
22
|
+
def inner_node(hash)
|
23
|
+
parse_inner_node(self[hash])
|
24
|
+
end
|
25
|
+
|
26
|
+
###
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def parse_ledger(ledger)
|
31
|
+
obj = Format::LEDGER.decode(ledger)
|
32
|
+
obj['close_time'] = XRBP::from_xrp_time(obj['close_time']).utc
|
33
|
+
obj['parent_close_time'] = XRBP::from_xrp_time(obj['parent_close_time']).utc
|
34
|
+
obj['parent_hash'].upcase!
|
35
|
+
obj['tx_hash'].upcase!
|
36
|
+
obj['account_hash'].upcase!
|
37
|
+
obj
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_encoding(encoding)
|
41
|
+
enc = encoding.unpack("C").first
|
42
|
+
type = enc >> 4
|
43
|
+
field = enc & 0xF
|
44
|
+
encoding = encoding[1..-1]
|
45
|
+
|
46
|
+
if type == 0
|
47
|
+
type = encoding.unpack("C").first
|
48
|
+
encoding = encoding[1..-1]
|
49
|
+
end
|
50
|
+
|
51
|
+
if field == 0
|
52
|
+
field = encoding.unpack("C").first
|
53
|
+
encoding = encoding[1..-1]
|
54
|
+
end
|
55
|
+
|
56
|
+
type = Format::SERIALIZED_TYPES[type]
|
57
|
+
[[type, field], encoding]
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_ledger_entry(ledger_entry)
|
61
|
+
# validate parsability
|
62
|
+
obj = Format::TYPE_INFER.decode(ledger_entry)
|
63
|
+
node_type = Format::NODE_TYPES[obj["node_type"]]
|
64
|
+
hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
|
65
|
+
raise unless node_type == :account_node &&
|
66
|
+
hash_prefix == :leaf_node
|
67
|
+
|
68
|
+
# discard node type, and hash prefix
|
69
|
+
ledger_entry = ledger_entry[13..-1]
|
70
|
+
|
71
|
+
# verify encoding
|
72
|
+
encoding, ledger_entry = parse_encoding(ledger_entry)
|
73
|
+
raise "Invalid Ledger Entry" unless Format::ENCODINGS[encoding] == :ledger_entry_type
|
74
|
+
ledger_entry = ledger_entry.bytes
|
75
|
+
|
76
|
+
# first byte after encoding is ledger entry type prefix
|
77
|
+
prefix = ledger_entry[0..1].pack("C*")
|
78
|
+
|
79
|
+
# last 32 bytes is entry index
|
80
|
+
index = ledger_entry[-32..-1].pack("C*")
|
81
|
+
.unpack("H*")
|
82
|
+
.first
|
83
|
+
.upcase
|
84
|
+
|
85
|
+
# remaining bytes are serialized object
|
86
|
+
fields = parse_fields(ledger_entry[2...-32].pack("C*"))
|
87
|
+
|
88
|
+
# TODO instantiate class corresponding to prefix &
|
89
|
+
# populate attributes w/ fields
|
90
|
+
|
91
|
+
{ :prefix => prefix,
|
92
|
+
:index => index,
|
93
|
+
:fields => fields }
|
94
|
+
end
|
95
|
+
|
96
|
+
###
|
97
|
+
|
98
|
+
def parse_fields(fields)
|
99
|
+
parsed = {}
|
100
|
+
until fields == "" || fields.nil?
|
101
|
+
encoding, fields = parse_encoding(fields)
|
102
|
+
return parsed if encoding.first.nil?
|
103
|
+
|
104
|
+
e = Format::ENCODINGS[encoding]
|
105
|
+
value, fields = parse_field(fields, encoding)
|
106
|
+
break unless value
|
107
|
+
parsed[e] = value
|
108
|
+
end
|
109
|
+
|
110
|
+
return parsed
|
111
|
+
end
|
112
|
+
|
113
|
+
def parse_field(data, encoding)
|
114
|
+
length = encoding.first
|
115
|
+
|
116
|
+
case length
|
117
|
+
when :uint8
|
118
|
+
return data.unpack("C").first, data[1..-1]
|
119
|
+
when :uint16
|
120
|
+
return data.unpack("S").first, data[2..-1]
|
121
|
+
when :uint32
|
122
|
+
return data.unpack("L").first, data[4..-1]
|
123
|
+
when :uint64
|
124
|
+
return data.unpack("Q").first, data[8..-1]
|
125
|
+
when :hash128
|
126
|
+
return data.unpack("H32").first, data[16..-1]
|
127
|
+
when :hash160
|
128
|
+
return data.unpack("H40").first, data[20..-1]
|
129
|
+
when :hash256
|
130
|
+
return data.unpack("H64").first, data[32..-1]
|
131
|
+
|
132
|
+
when :amount
|
133
|
+
amount = data[0..7].unpack("Q>").first
|
134
|
+
xrp = amount < 0x8000000000000000
|
135
|
+
return (amount & 0x3FFFFFFFFFFFFFFF), data[8..-1] if xrp
|
136
|
+
|
137
|
+
sign = (amount & 0x4000000000000000) >> 62 # 0 = neg / 1 = pos
|
138
|
+
exp = (amount & 0x3FC0000000000000) >> 54
|
139
|
+
mant = (amount & 0x003FFFFFFFFFFFFF)
|
140
|
+
|
141
|
+
data = data[8..-1]
|
142
|
+
currency = Format::CURRENCY_CODE.decode(data)
|
143
|
+
|
144
|
+
data = data[Format::CURRENCY_CODE.size..-1]
|
145
|
+
issuer, data = parse_account(data, 20)
|
146
|
+
|
147
|
+
# TODO calculate value
|
148
|
+
return { :sign => sign,
|
149
|
+
:exp => exp,
|
150
|
+
:mantissa => mant,
|
151
|
+
:currency => currency,
|
152
|
+
:issuer => issuer }, data
|
153
|
+
|
154
|
+
when :vl
|
155
|
+
vl, offset = parse_vl(data)
|
156
|
+
return data[offset..vl+offset-1], data[vl+offset..-1]
|
157
|
+
|
158
|
+
when :account
|
159
|
+
return parse_account(data)
|
160
|
+
|
161
|
+
when :array
|
162
|
+
e = Format::ENCODINGS[encoding]
|
163
|
+
return nil, data if e == :end_of_array
|
164
|
+
|
165
|
+
array = []
|
166
|
+
until data == "" || data.nil?
|
167
|
+
aencoding, data = parse_encoding(data)
|
168
|
+
break if aencoding.first.nil?
|
169
|
+
|
170
|
+
e = Format::ENCODINGS[aencoding]
|
171
|
+
break if e == :end_of_array
|
172
|
+
|
173
|
+
value, data = parse_field(data, aencoding)
|
174
|
+
break unless value
|
175
|
+
array << value
|
176
|
+
end
|
177
|
+
|
178
|
+
return array, data
|
179
|
+
|
180
|
+
when :object
|
181
|
+
e = Format::ENCODINGS[encoding]
|
182
|
+
case e
|
183
|
+
when :end_of_object
|
184
|
+
return nil, data
|
185
|
+
|
186
|
+
when :signer, :signer_entry,
|
187
|
+
:majority, :memo,
|
188
|
+
:modified_node, :created_node, :deleted_node,
|
189
|
+
:previous_fields, :final_fields, :new_fields
|
190
|
+
# TODO instantiate corresponding classes
|
191
|
+
return parse_fields(data)
|
192
|
+
|
193
|
+
#else:
|
194
|
+
end
|
195
|
+
|
196
|
+
when :pathset
|
197
|
+
pathset = []
|
198
|
+
until data == "" || data.nil?
|
199
|
+
segment = data.unpack("C").first
|
200
|
+
data = data[1..-1]
|
201
|
+
return pathset, data if segment == 0x00 # end of path
|
202
|
+
|
203
|
+
if segment == 0xFF # path boundry
|
204
|
+
pathset << []
|
205
|
+
else
|
206
|
+
if segment & 0x01 # path account
|
207
|
+
issuer, data = parse_account(data, 20)
|
208
|
+
end
|
209
|
+
|
210
|
+
if segment & 0x02 # path currency
|
211
|
+
currency = Format::CURRENCY_CODE.decode(data)
|
212
|
+
data = data[Format::CURRENCY_CODE.size..-1]
|
213
|
+
end
|
214
|
+
|
215
|
+
if segment & 0x03 # path issuer
|
216
|
+
issuer, data = parse_account(data, 20)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
return pathset, data
|
222
|
+
|
223
|
+
when :vector256
|
224
|
+
vl, offset = parse_vl(data)
|
225
|
+
return data[offset..vl+offset-1], data[vl+offset..-1]
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
raise
|
230
|
+
end
|
231
|
+
|
232
|
+
def parse_vl(data)
|
233
|
+
data = data.bytes
|
234
|
+
first = data.first.to_i
|
235
|
+
return first, 1 if first <= 192
|
236
|
+
|
237
|
+
data = data[1..-1]
|
238
|
+
second = data.first.to_i
|
239
|
+
if first <= 240
|
240
|
+
return (193+(first-193)*256+second), 2
|
241
|
+
|
242
|
+
elsif first <= 254
|
243
|
+
data = data[1..-1]
|
244
|
+
third = data.first.to_i
|
245
|
+
return (12481 + (first-241)*65536 + second*256 + third), 3
|
246
|
+
end
|
247
|
+
|
248
|
+
raise
|
249
|
+
end
|
250
|
+
|
251
|
+
def parse_account(data, vl=nil)
|
252
|
+
unless vl
|
253
|
+
vl,offset = parse_vl(data)
|
254
|
+
data = data[offset..-1]
|
255
|
+
end
|
256
|
+
|
257
|
+
acct = "\0" + data[0..vl-1]
|
258
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
259
|
+
digest = sha256.digest(sha256.digest(acct))[0..3]
|
260
|
+
acct += digest
|
261
|
+
return Base58.binary_to_base58(acct, :ripple), data[vl..-1]
|
262
|
+
end
|
263
|
+
|
264
|
+
###
|
265
|
+
|
266
|
+
def parse_tx(tx)
|
267
|
+
obj = Format::TYPE_INFER.decode(tx)
|
268
|
+
node_type = Format::NODE_TYPES[obj["node_type"]]
|
269
|
+
hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
|
270
|
+
raise unless node_type == :tx_node &&
|
271
|
+
hash_prefix == :tx_node
|
272
|
+
|
273
|
+
# discard node type, and hash prefix
|
274
|
+
tx = tx[13..-1]
|
275
|
+
|
276
|
+
# get node length
|
277
|
+
vl, offset = parse_vl(tx)
|
278
|
+
node, _tx = tx.bytes[offset..vl+offset-1], tx.bytes[vl+offset..-1]
|
279
|
+
node = parse_fields(node.pack("C*"))
|
280
|
+
|
281
|
+
# get meta length
|
282
|
+
vl, offset = parse_vl(_tx.pack("C*"))
|
283
|
+
meta, index = _tx[offset..vl+offset-1], _tx[vl+offset..-1]
|
284
|
+
meta = parse_fields(meta.pack("C*"))
|
285
|
+
|
286
|
+
{ :node => node,
|
287
|
+
:meta => meta,
|
288
|
+
:index => index.pack("C*").unpack("H*").first.upcase }
|
289
|
+
end
|
290
|
+
|
291
|
+
def parse_inner_node(node)
|
292
|
+
# verify parsability
|
293
|
+
obj = Format::TYPE_INFER.decode(node)
|
294
|
+
hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
|
295
|
+
raise unless hash_prefix == :inner_node
|
296
|
+
|
297
|
+
Format::INNER_NODE.decode(node)
|
298
|
+
end
|
299
|
+
|
300
|
+
protected
|
301
|
+
|
302
|
+
def infer_type(value)
|
303
|
+
obj = Format::TYPE_INFER.decode(value)
|
304
|
+
node_type = Format::NODE_TYPES[obj["node_type"]]
|
305
|
+
hash_prefix = Format::HASH_PREFIXES[obj["hash_prefix"].upcase]
|
306
|
+
|
307
|
+
if hash_prefix == :inner_node
|
308
|
+
return :inner_node, parse_inner_node(value)
|
309
|
+
|
310
|
+
elsif node_type == :account_node
|
311
|
+
return :account, parse_ledger_entry(value)
|
312
|
+
|
313
|
+
elsif node_type == :tx_node
|
314
|
+
return :tx, parse_tx(value)
|
315
|
+
|
316
|
+
elsif node_type == :ledger
|
317
|
+
return :ledger, parse_ledger(value)
|
318
|
+
end
|
319
|
+
|
320
|
+
return nil
|
321
|
+
end
|
322
|
+
end # class DB
|
323
|
+
end # module NodeStore
|
324
|
+
end # module XRBP
|