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