xrbp 0.1.8 → 0.1.9
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/.yardopts +1 -0
- data/examples/nodestore1.rb +10 -10
- data/examples/nodestore2.rb +4 -4
- data/examples/p2p.rb +1 -2
- data/lib/xrbp/crypto.rb +9 -0
- data/lib/xrbp/crypto/account.rb +28 -2
- data/lib/xrbp/crypto/key.rb +89 -25
- data/lib/xrbp/crypto/node.rb +29 -1
- data/lib/xrbp/crypto/seed.rb +58 -0
- data/lib/xrbp/crypto/validator.rb +20 -0
- data/lib/xrbp/nodestore/backends/decompressor.rb +46 -5
- data/lib/xrbp/nodestore/backends/nudb.rb +37 -3
- data/lib/xrbp/nodestore/backends/rocksdb.rb +30 -0
- data/lib/xrbp/nodestore/db.rb +69 -2
- data/lib/xrbp/nodestore/format.rb +14 -10
- data/lib/xrbp/overlay.rb +1 -0
- data/lib/xrbp/overlay/connection.rb +34 -6
- data/lib/xrbp/overlay/frame.rb +10 -2
- data/lib/xrbp/overlay/handshake.rb +16 -9
- data/lib/xrbp/overlay/messages.rb +27 -0
- data/lib/xrbp/overlay/ripple.proto.rb +20 -0
- data/lib/xrbp/version.rb +1 -1
- data/spec/xrbp/crypto/account_spec.rb +6 -0
- data/spec/xrbp/crypto/key_spec.rb +11 -0
- data/spec/xrbp/crypto/node_spec.rb +6 -0
- data/spec/xrbp/crypto/seed_spec.rb +6 -0
- metadata +9 -3
- data/examples/sandbox.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80c7817a86dc903b6eb5725bb37ecd562fe6a73b1aef5ee81e2b27ff1aeb50a6
|
4
|
+
data.tar.gz: 7003cb99a4488aea91f41d4b8c6bf2fc3bdc577b980d251c858639508f6ae1f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9792f31b6e62658be6b94f32b3dae6a8fd99a395b5b513d7d6f1cf1e766a121bc074a043b8a1f884c516bda807e06243806f83f7ad2e397655d7ab69b09a5e51
|
7
|
+
data.tar.gz: 4e360f3e4464758f0b49158239d898b8b901d094ea5be52faadfaa29dccb4f12120c392bcce1d2c0a03a1058b8345bcce6016abce59ec9dd3524bb75142ec3a6
|
data/.yardopts
CHANGED
data/examples/nodestore1.rb
CHANGED
@@ -2,18 +2,18 @@ $: << File.expand_path('../../lib', __FILE__)
|
|
2
2
|
require 'xrbp'
|
3
3
|
|
4
4
|
# for rocksdb:
|
5
|
-
|
6
|
-
|
5
|
+
require 'xrbp/nodestore/backends/rocksdb'
|
6
|
+
db = XRBP::NodeStore::Backends::RocksDB.new "/var/lib/rippled/rocksdb/rippledb.0899"
|
7
7
|
|
8
8
|
# for nudb:
|
9
|
-
require 'xrbp/nodestore/backends/nudb'
|
10
|
-
db = XRBP::NodeStore::Backends::NuDB.new "/var/lib/rippled/nudb/"
|
9
|
+
#require 'xrbp/nodestore/backends/nudb'
|
10
|
+
#db = XRBP::NodeStore::Backends::NuDB.new "/var/lib/rippled/nudb/"
|
11
11
|
|
12
|
-
ledger = "
|
13
|
-
ledger = [ledger].pack("H*")
|
14
|
-
puts db.ledger(ledger)
|
12
|
+
#ledger = "B506ADD630CB707044B4BFFCD943C1395966692A13DD618E5BD0978A006B43BD"
|
13
|
+
#ledger = [ledger].pack("H*")
|
14
|
+
#puts db.ledger(ledger)
|
15
15
|
|
16
16
|
#account = "0001bf7468341666f1f47a95e0f4d88e68b5fc7d20d77437cb22954fbbfe6127"
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
account = "02c46b3a4130d0a329c47f0da61b829aa5d1ae53c5817e475bcd794e5107be44"
|
18
|
+
account = [account].pack("H*")
|
19
|
+
puts db.account(account)
|
data/examples/nodestore2.rb
CHANGED
@@ -2,12 +2,12 @@ $: << File.expand_path('../../lib', __FILE__)
|
|
2
2
|
require 'xrbp'
|
3
3
|
|
4
4
|
# for rocksdb:
|
5
|
-
|
6
|
-
|
5
|
+
require 'xrbp/nodestore/backends/rocksdb'
|
6
|
+
db = XRBP::NodeStore::Backends::RocksDB.new "/var/lib/rippled/rocksdb/rippledb.0899"
|
7
7
|
|
8
8
|
# for nudb:
|
9
|
-
require 'xrbp/nodestore/backends/nudb'
|
10
|
-
db = XRBP::NodeStore::Backends::NuDB.new "/var/lib/rippled/nudb/"
|
9
|
+
#require 'xrbp/nodestore/backends/nudb'
|
10
|
+
#db = XRBP::NodeStore::Backends::NuDB.new "/var/lib/rippled/nudb/"
|
11
11
|
|
12
12
|
db.on :unknown do |hash, node|
|
13
13
|
puts "Unknown #{hash}: #{node}"
|
data/examples/p2p.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
gem 'openssl', '2.1.3'
|
2
|
-
|
3
1
|
$: << File.expand_path('../../lib', __FILE__)
|
4
2
|
require 'xrbp'
|
5
3
|
|
@@ -9,6 +7,7 @@ puts overlay.handshake.response
|
|
9
7
|
|
10
8
|
overlay.read_frames do |frame|
|
11
9
|
puts "Message: #{frame.type_name} (#{frame.size} bytes)"
|
10
|
+
# frame.message => protobuf message
|
12
11
|
end
|
13
12
|
|
14
13
|
overlay.close
|
data/lib/xrbp/crypto.rb
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
module XRBP
|
2
|
+
# The Crypto module defines methods to create XRPL compliant
|
3
|
+
# keys and subsequent entities (accounts, nodes, validators).
|
4
|
+
module Crypto
|
5
|
+
end # module Crypto
|
6
|
+
end # module XRBP
|
7
|
+
|
8
|
+
require 'xrbp/crypto/seed'
|
1
9
|
require 'xrbp/crypto/key'
|
2
10
|
require 'xrbp/crypto/account'
|
3
11
|
require 'xrbp/crypto/node'
|
12
|
+
require 'xrbp/crypto/validator'
|
data/lib/xrbp/crypto/account.rb
CHANGED
@@ -2,16 +2,18 @@ require 'base58'
|
|
2
2
|
|
3
3
|
module XRBP
|
4
4
|
module Crypto
|
5
|
+
# Generate and new XRPL account.
|
6
|
+
#
|
7
|
+
# @param key [Symbol, Hash] key type to generate or key itself (optional)
|
8
|
+
# @return [Hash] account details containing id and pub/priv key pair
|
5
9
|
def self.account(key=nil)
|
6
10
|
pub = nil
|
7
11
|
if key == :secp256k1 || key.nil?
|
8
12
|
key = Key::secp256k1
|
9
|
-
key[:type] = :secp256k1
|
10
13
|
pub = key[:public]
|
11
14
|
|
12
15
|
elsif key == :ed25519
|
13
16
|
key = Key::ed25519
|
14
|
-
key[:type] = :ed25519
|
15
17
|
pub = "\xED" + key[:public]
|
16
18
|
|
17
19
|
elsif key.is_a?(Hash)
|
@@ -29,5 +31,29 @@ module XRBP
|
|
29
31
|
|
30
32
|
{ :account => Base58.binary_to_base58(account_id + chksum, :ripple) }.merge(key)
|
31
33
|
end
|
34
|
+
|
35
|
+
# Extract Account ID from Address.
|
36
|
+
#
|
37
|
+
# @param account [String] Base58 encoded account address
|
38
|
+
# @return [String, nil] unique account id or nil if input
|
39
|
+
# if not an account
|
40
|
+
def self.parse_account(account)
|
41
|
+
bin = Base58.base58_to_binary(account, :ripple)
|
42
|
+
typ = bin[0]
|
43
|
+
chk = bin[-4..-1]
|
44
|
+
bin = bin[1...-4]
|
45
|
+
|
46
|
+
return nil unless typ.unpack("C*").first == Key::TOKEN_TYPES[:account_id]
|
47
|
+
|
48
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
49
|
+
return nil unless sha256.digest(sha256.digest(typ + bin))[0..3] == chk
|
50
|
+
|
51
|
+
return bin
|
52
|
+
end
|
53
|
+
|
54
|
+
# Return boolean indicating if the specified account is valid
|
55
|
+
def self.account?(account)
|
56
|
+
parse_account(account) != nil
|
57
|
+
end
|
32
58
|
end # module Crypto
|
33
59
|
end # module XRBP
|
data/lib/xrbp/crypto/key.rb
CHANGED
@@ -16,12 +16,8 @@ module XRBP
|
|
16
16
|
|
17
17
|
###
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
OpenSSL::Digest::SHA256.new.digest(seed)
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.secp256k1
|
19
|
+
# @return [Hash] new secp256k1 key pair (both public and private components)
|
20
|
+
def self.secp256k1(seed=nil)
|
25
21
|
# XXX: the bitcoin secp256k1 implementation (which rippled pulls in / vendors)
|
26
22
|
# has alot of nuances which require special configuration in openssl. For
|
27
23
|
# the time being, mitigate this by pulling in & using the ruby
|
@@ -34,36 +30,62 @@ module XRBP
|
|
34
30
|
|
35
31
|
spk = Secp256k1::PrivateKey.new
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
33
|
+
sd, pk = nil
|
34
|
+
if seed
|
35
|
+
sd,pk = seed,seed
|
36
|
+
|
37
|
+
else
|
38
|
+
sd = Crypto.seed[:seed]
|
39
|
+
pk = Crypto.parse_seed(sd)
|
40
|
+
end
|
41
|
+
|
42
|
+
# FIXME: rippled & ripple-keypairs (& by extension ripple-lib) repeatedly
|
43
|
+
# hash seed until certain it is less than the order of the
|
44
|
+
# curve, we should do this as well (for security)
|
45
|
+
#
|
46
|
+
# https://github.com/ripple/rippled/blob/develop/src/ripple/crypto/impl/GenerateDeterministicKey.cpp
|
47
|
+
# https://github.com/ripple/ripple-keypairs/blob/master/src/secp256k1.js
|
48
|
+
#
|
49
|
+
# Also look into if setting raw key here has same effect as
|
50
|
+
# secp256k1.mul (as invoked in keypairs)
|
51
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
52
|
+
pk = sha512.digest(pk)[0..31]
|
53
|
+
|
54
|
+
spk.set_raw_privkey pk
|
45
55
|
|
46
56
|
{ :public => spk.pubkey.serialize.unpack("H*").first,
|
47
|
-
:private => spk.send(:serialize)
|
57
|
+
:private => spk.send(:serialize),
|
58
|
+
:seed => sd,
|
59
|
+
:type => :secp256k1 }
|
48
60
|
end
|
49
61
|
|
62
|
+
# @return [Hash] new ed25519 key pair (both public and private components)
|
50
63
|
def self.ed25519
|
51
64
|
# XXX openssl 1.1.1 needed for EdDSA support:
|
52
65
|
# https://www.openssl.org/blog/blog/2018/09/11/release111/
|
53
66
|
# Until then use this:
|
54
67
|
require "ed25519"
|
55
68
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
69
|
+
sd = Crypto.seed[:seed]
|
70
|
+
pk = Crypto.parse_seed(sd)
|
71
|
+
|
72
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
73
|
+
pk = sha512.digest(pk)[0..31]
|
74
|
+
|
75
|
+
key = Ed25519::SigningKey.new(pk)
|
76
|
+
{ :public => key.verify_key.to_bytes.unpack("H*").first.upcase,
|
77
|
+
:private => key.to_bytes.unpack("H*").first.upcase,
|
78
|
+
:seed => sd,
|
79
|
+
:type => :ed25519 }
|
63
80
|
end
|
64
81
|
|
65
82
|
###
|
66
83
|
|
84
|
+
# Sign the digest using the specified key, returning the result
|
85
|
+
#
|
86
|
+
# @param key [Hash] key to sign digest with
|
87
|
+
# @param data [String] data to sign (must be exactly 32 bytes long!)
|
88
|
+
# @return [String] signed digest
|
67
89
|
def self.sign_digest(key, data)
|
68
90
|
raise "unknown key" unless key.is_a?(Hash) && key[:type] && key[:private]
|
69
91
|
raise "invalid data" unless data.length == 32
|
@@ -79,15 +101,57 @@ module XRBP
|
|
79
101
|
sig_raw = pk.ecdsa_sign data, raw: true
|
80
102
|
return pk.ecdsa_serialize sig_raw
|
81
103
|
|
82
|
-
|
83
|
-
#
|
104
|
+
elsif key[:type] == :ed25519
|
105
|
+
# XXX: see note about this library above
|
106
|
+
require "ed25519"
|
107
|
+
|
108
|
+
sd = key[:seed]
|
109
|
+
pk = Crypto.parse_seed(sd)
|
110
|
+
|
111
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
112
|
+
pk = sha512.digest(pk)[0..31]
|
113
|
+
|
114
|
+
pk = Ed25519::SigningKey.new(pk)
|
115
|
+
return pk.sign(data)
|
84
116
|
end
|
85
117
|
|
86
118
|
raise "unknown key type"
|
87
119
|
end
|
88
120
|
|
121
|
+
# Returns bool indicating if data is the result of
|
122
|
+
# signing expected value with given key.
|
123
|
+
#
|
124
|
+
# @param key [Hash] key to use to verify digest
|
125
|
+
# @param data [String] signed data
|
126
|
+
# @param expected [String] original unsigned data
|
127
|
+
# @return [Bool] indicating if signed digest matches
|
128
|
+
# original data
|
89
129
|
def self.verify(key, data, expected)
|
130
|
+
if key[:type] == :secp256k1
|
131
|
+
# XXX: see note about this library above
|
132
|
+
require 'secp256k1'
|
133
|
+
|
134
|
+
pb = Secp256k1::PublicKey.new :pubkey => [key[:public]].pack("H*"),
|
135
|
+
:raw => true
|
136
|
+
pv = Secp256k1::PrivateKey.new
|
137
|
+
|
138
|
+
return pb.ecdsa_verify expected,
|
139
|
+
pv.ecdsa_deserialize(data), raw: true
|
140
|
+
|
141
|
+
elsif key[:type] == :ed25519
|
142
|
+
# XXX: see note about this library above
|
143
|
+
require "ed25519"
|
144
|
+
|
145
|
+
pk = Ed25519::VerifyKey.new([key[:public]].pack("H*"))
|
146
|
+
begin
|
147
|
+
return pk.verify(data, expected)
|
148
|
+
rescue Ed25519::VerifyError
|
149
|
+
return false
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
raise "unknown key type"
|
90
154
|
end
|
91
|
-
end
|
155
|
+
end # module Key
|
92
156
|
end # module Crypto
|
93
157
|
end # module XRBP
|
data/lib/xrbp/crypto/node.rb
CHANGED
@@ -2,14 +2,18 @@ require 'base58'
|
|
2
2
|
|
3
3
|
module XRBP
|
4
4
|
module Crypto
|
5
|
+
# Generate a new XRPL node.
|
6
|
+
#
|
7
|
+
# @param key [Symbol, Hash] key type to generate or key itself (optional)
|
8
|
+
# @return [Hash] node details containing id and pub/priv key pair
|
5
9
|
def self.node(key=nil)
|
6
10
|
pub = nil
|
7
11
|
if key == :secp256k1 || key.nil?
|
8
12
|
key = Key::secp256k1
|
9
|
-
key[:type] = :secp256k1
|
10
13
|
pub = key[:public]
|
11
14
|
|
12
15
|
elsif key.is_a?(Hash)
|
16
|
+
key = Key::secp256k1(key[:seed]) if key[:seed]
|
13
17
|
pub = key[:public]
|
14
18
|
|
15
19
|
else
|
@@ -23,5 +27,29 @@ module XRBP
|
|
23
27
|
|
24
28
|
{ :node => Base58.binary_to_base58(node_id + chksum, :ripple) }.merge(key)
|
25
29
|
end
|
30
|
+
|
31
|
+
# Extract Node ID from Address.
|
32
|
+
#
|
33
|
+
# @param node [String] Base58 encoded node address
|
34
|
+
# @return [String, nil] unique node id or nil if input
|
35
|
+
# if not an node
|
36
|
+
def self.parse_node(node)
|
37
|
+
bin = Base58.base58_to_binary(node, :ripple)
|
38
|
+
typ = bin[0]
|
39
|
+
chk = bin[-4..-1]
|
40
|
+
bin = bin[1...-4]
|
41
|
+
|
42
|
+
return nil unless typ.unpack("C*").first == Key::TOKEN_TYPES[:node_public]
|
43
|
+
|
44
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
45
|
+
return nil unless sha256.digest(sha256.digest(typ + bin))[0..3] == chk
|
46
|
+
|
47
|
+
return bin
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return boolean indicating if the specified node is valid
|
51
|
+
def self.node?(node)
|
52
|
+
parse_node(node) != nil
|
53
|
+
end
|
26
54
|
end # module Crypto
|
27
55
|
end # module XRBP
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'base58'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module XRBP
|
5
|
+
module Crypto
|
6
|
+
# Generate a new XRPL seed.
|
7
|
+
#
|
8
|
+
# @param key [Symbol] key type to generate (optional)
|
9
|
+
# @return [Hash] seed details containing encoded seed and key type
|
10
|
+
def self.seed(key=nil)
|
11
|
+
prefix = nil
|
12
|
+
if key == :secp256k1 || key.nil?
|
13
|
+
prefix = [Key::TOKEN_TYPES[:family_seed]]
|
14
|
+
key = { :type => :secp256k1 }
|
15
|
+
|
16
|
+
elsif key == :ed25519
|
17
|
+
prefix = [0x01, 0xE1, 0x4B]
|
18
|
+
key = { :type => :ed25519 }
|
19
|
+
|
20
|
+
else
|
21
|
+
raise ArgumentError, key
|
22
|
+
end
|
23
|
+
|
24
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
25
|
+
base = SecureRandom.random_bytes(16)
|
26
|
+
pref = (prefix + base.bytes).pack("C*")
|
27
|
+
chk = sha256.digest(sha256.digest(pref))[0..3]
|
28
|
+
{ :seed => Base58.binary_to_base58(pref + chk, :ripple) }.merge(key)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Extract Seed ID from Encoding.
|
32
|
+
#
|
33
|
+
# @param seed [String] Base58 encoded seed
|
34
|
+
# @return [String, nil] extracted seed or nil
|
35
|
+
# if not valid
|
36
|
+
def self.parse_seed(seed)
|
37
|
+
bin = Base58.base58_to_binary(seed, :ripple)
|
38
|
+
typ = bin[0]
|
39
|
+
chk = bin[-4..-1]
|
40
|
+
bin = bin[1...-4]
|
41
|
+
|
42
|
+
# TODO also permit ED25519 prefix (?)
|
43
|
+
return nil unless typ.unpack("C*").first == Key::TOKEN_TYPES[:family_seed]
|
44
|
+
|
45
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
46
|
+
return nil unless sha256.digest(sha256.digest(typ + bin))[0..3] == chk
|
47
|
+
|
48
|
+
return nil unless bin.size == 16
|
49
|
+
|
50
|
+
return bin
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return boolean indicating if the specified seed is valid
|
54
|
+
def self.seed?(seed)
|
55
|
+
parse_seed(seed) != nil
|
56
|
+
end
|
57
|
+
end # module Crypto
|
58
|
+
end # module XRBP
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module XRBP
|
2
|
+
module Crypto
|
3
|
+
# Generate a new XRPL validator. Takes same params as {Crypto#node}
|
4
|
+
def self.validator(key=nil)
|
5
|
+
return node(key)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Extract Validator ID from Address.
|
9
|
+
# Takes same params as {Crypto#parse_node}
|
10
|
+
def self.parse_validator(validator)
|
11
|
+
return parse_node(validator)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Return bool indicating if Validator is valid.
|
15
|
+
# Takes same params as {Crypto#node?}
|
16
|
+
def self.validator?(validator)
|
17
|
+
return node?(validator)
|
18
|
+
end
|
19
|
+
end # module Crypto
|
20
|
+
end # module XRBP
|
@@ -4,10 +4,15 @@ require 'lz4-ruby'
|
|
4
4
|
module XRBP
|
5
5
|
module NodeStore
|
6
6
|
module Backends
|
7
|
+
# XRP Ledger Nodestore Decompressor. Currently only the NuDB backend
|
8
|
+
# employs compression but decompression is required to read those entries.
|
9
|
+
#
|
7
10
|
# Ported from:
|
8
|
-
#
|
11
|
+
# {https://github.com/ripple/rippled/blob/develop/src/ripple/nodestore/impl/codec.h codec.h}
|
9
12
|
module Decompressor
|
10
13
|
protected
|
14
|
+
# Principle decompression interface, extracts compression type (if any)
|
15
|
+
# from data and decompresses accordingly.
|
11
16
|
def decompress(data)
|
12
17
|
type, remaining = read_varint(data)
|
13
18
|
case(type)
|
@@ -30,7 +35,15 @@ module XRBP
|
|
30
35
|
end
|
31
36
|
|
32
37
|
private
|
33
|
-
#
|
38
|
+
# This is a custom type/size prefix preprended onto
|
39
|
+
# nodestore values.
|
40
|
+
#
|
41
|
+
# See: {https://github.com/ripple/rippled/blob/develop/src/ripple/nodestore/impl/varint.h varint.h}
|
42
|
+
#
|
43
|
+
# @param data [String] binary nodestore data
|
44
|
+
# @return value prefix and remaining data
|
45
|
+
#
|
46
|
+
# @private
|
34
47
|
def read_varint(data)
|
35
48
|
bytes = data.bytes
|
36
49
|
|
@@ -67,14 +80,22 @@ module XRBP
|
|
67
80
|
|
68
81
|
###
|
69
82
|
|
83
|
+
# Decompresses a LZ4-compressed value
|
84
|
+
#
|
85
|
+
# @private
|
70
86
|
def decompress_lz4(data)
|
71
87
|
size, remaining = read_varint(data)
|
72
|
-
|
73
|
-
o
|
88
|
+
LZ4::Raw::decompress(remaining, size)[0]
|
74
89
|
end
|
75
90
|
|
76
91
|
###
|
77
92
|
|
93
|
+
# Decompresses a Compressed Inner Node (Version 1).
|
94
|
+
# Inner nodes may contain up to 16 32-byte hashes,
|
95
|
+
# though whens stored on disk-the compression algorithm
|
96
|
+
# employs a hash-mask to skip storage of unpopulated hashes.
|
97
|
+
#
|
98
|
+
# @private
|
78
99
|
def decompress_compressed_v1_inner(data)
|
79
100
|
raise if data.size < 34
|
80
101
|
|
@@ -84,23 +105,27 @@ module XRBP
|
|
84
105
|
[Format::HASH_PREFIXES.invert[:inner_node]].pack("H*")
|
85
106
|
.unpack("C*")
|
86
107
|
|
108
|
+
# extract mask indicating which hashes are set
|
87
109
|
bytes = data.bytes
|
88
110
|
mask = bytes[0..1].pack("C*").unpack("S").first
|
89
111
|
bytes = bytes[2..-1]
|
90
112
|
|
91
113
|
raise "nodeobject codec v1: empty inner node" if mask == 0
|
92
114
|
|
115
|
+
# iterate over hashes...
|
93
116
|
bit = 0x8000
|
94
117
|
i = 16
|
95
118
|
while i > 0
|
96
119
|
i -= 1
|
97
120
|
|
121
|
+
# ...if set in mask, extract from data...
|
98
122
|
if (mask & bit) != 0
|
99
123
|
raise "nodeobject codec v1: short inner node" if bytes.size < 32
|
100
124
|
|
101
125
|
out += bytes[0..31]
|
102
126
|
bytes = bytes[32..-1]
|
103
127
|
|
128
|
+
# ...otherwise just set to 0
|
104
129
|
else
|
105
130
|
out += Array.new(32) { 0 }
|
106
131
|
end
|
@@ -111,6 +136,11 @@ module XRBP
|
|
111
136
|
out.pack("C*")
|
112
137
|
end
|
113
138
|
|
139
|
+
# Decompresses a Compressed Inner Node (Version 2).
|
140
|
+
# Similar to Version 1 but contains additional
|
141
|
+
# variable length data appended onto the result bytes.
|
142
|
+
#
|
143
|
+
# @private
|
114
144
|
def decompress_compressed_v2_inner(data)
|
115
145
|
raise if data.size < 67
|
116
146
|
|
@@ -120,6 +150,8 @@ module XRBP
|
|
120
150
|
[Format::HASH_PREFIXES.invert[:inner_node_v2]].pack("H*")
|
121
151
|
.unpack("C*")
|
122
152
|
|
153
|
+
# Same mask / hash extraction as V1
|
154
|
+
|
123
155
|
bytes = data.bytes
|
124
156
|
mask = bytes[0..1].pack("C*").unpack("S").first
|
125
157
|
bytes = bytes[2..-1]
|
@@ -147,6 +179,10 @@ module XRBP
|
|
147
179
|
bit = bit >> 1
|
148
180
|
end
|
149
181
|
|
182
|
+
###
|
183
|
+
|
184
|
+
# Append extra variable-length data onto result
|
185
|
+
|
150
186
|
out << depth
|
151
187
|
|
152
188
|
copy = (depth + 1)/2
|
@@ -160,6 +196,9 @@ module XRBP
|
|
160
196
|
|
161
197
|
###
|
162
198
|
|
199
|
+
# Decompresses a Full (Uncompressed) Inner Node (Version 1).
|
200
|
+
#
|
201
|
+
# @private
|
163
202
|
def decompress_full_v1_inner(data)
|
164
203
|
raise if data.size != 512 # 16 32-bit hashes
|
165
204
|
|
@@ -170,7 +209,9 @@ module XRBP
|
|
170
209
|
(out + data[0...512].bytes).pack("C*")
|
171
210
|
end
|
172
211
|
|
173
|
-
|
212
|
+
# Decompresses a Full (Uncompressed) Inner Node (Version 2).
|
213
|
+
#
|
214
|
+
# @private
|
174
215
|
def decompress_full_v2_inner(data)
|
175
216
|
bytes = data.bytes
|
176
217
|
depth = bytes[0]
|
@@ -8,6 +8,14 @@ require_relative './decompressor'
|
|
8
8
|
module XRBP
|
9
9
|
module NodeStore
|
10
10
|
module Backends
|
11
|
+
# NuDB nodestore backend, faciliates accessing XRP Ledger data
|
12
|
+
# in a NuDB database. This module accommodates for compression
|
13
|
+
# used in rippled's NuDB nodestore backend implementation
|
14
|
+
#
|
15
|
+
# @example retrieve data from NuDB backend
|
16
|
+
# require 'nodestore/backends/nudb'
|
17
|
+
# nudb = NodeStore::Backends::NuDB.new '/var/lib/rippled/db/nudb'
|
18
|
+
# puts nudb.ledger('B506ADD630CB707044B4BFFCD943C1395966692A13DD618E5BD0978A006B43BD')
|
11
19
|
class NuDB < DB
|
12
20
|
include Decompressor
|
13
21
|
|
@@ -21,16 +29,38 @@ module XRBP
|
|
21
29
|
open
|
22
30
|
end
|
23
31
|
|
32
|
+
# Retrieve database value for the specified key
|
33
|
+
#
|
34
|
+
# @param key [String] binary key to lookup
|
35
|
+
# @return [String] binary value
|
24
36
|
def [](key)
|
25
37
|
decompress(@store.fetch(key)[0])
|
26
38
|
end
|
27
39
|
|
40
|
+
# Iterate over each database key/value pair,
|
41
|
+
# invoking callback. During iteration will
|
42
|
+
# emit signals specific to the DB types being
|
43
|
+
# parsed
|
44
|
+
#
|
45
|
+
# @example iterating over NuDB entries
|
46
|
+
# nudb.each do |iterator|
|
47
|
+
# puts "Key/Value: #{iterator.key}/#{iterator.value}"
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# @example handling ledgers via event callback
|
51
|
+
# nudb.on(:ledger) do |hash, ledger|
|
52
|
+
# puts "Ledger #{hash}"
|
53
|
+
# pp ledger
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# # Any Enumerable method that invokes #each will
|
57
|
+
# # have intended effect
|
58
|
+
# nudb.to_a
|
28
59
|
def each
|
29
60
|
dat = File.join(path, "nudb.dat")
|
30
61
|
|
31
62
|
RuDB::each(dat) do |key, val|
|
32
63
|
val = decompress(val)
|
33
|
-
puts key.bytes.join(", ")
|
34
64
|
type, obj = infer_type(val)
|
35
65
|
|
36
66
|
if type
|
@@ -48,6 +78,9 @@ puts key.bytes.join(", ")
|
|
48
78
|
|
49
79
|
private
|
50
80
|
|
81
|
+
# Create database if it does not exist
|
82
|
+
#
|
83
|
+
# @private
|
51
84
|
def create!
|
52
85
|
dat = File.join(path, "nudb.dat")
|
53
86
|
key = File.join(path, "nudb.key")
|
@@ -63,8 +96,9 @@ puts key.bytes.join(", ")
|
|
63
96
|
:load_factor => 0.5
|
64
97
|
end
|
65
98
|
|
66
|
-
|
67
|
-
|
99
|
+
# Open existing database
|
100
|
+
#
|
101
|
+
# @private
|
68
102
|
def open
|
69
103
|
dat = File.join(path, "nudb.dat")
|
70
104
|
key = File.join(path, "nudb.key")
|
@@ -4,6 +4,13 @@ require "rocksdb"
|
|
4
4
|
module XRBP
|
5
5
|
module NodeStore
|
6
6
|
module Backends
|
7
|
+
# RocksDB nodestore backend, faciliates accessing XRP Ledger data
|
8
|
+
# in a RocksDB database.
|
9
|
+
#
|
10
|
+
# @example retrieve data from RocksDB backend
|
11
|
+
# require 'nodestore/backends/rocksdb'
|
12
|
+
# rocksdb = NodeStore::Backends::RocksDB.new '/var/lib/rippled/db/rocksdb'
|
13
|
+
# puts rocksdb.ledger('B506ADD630CB707044B4BFFCD943C1395966692A13DD618E5BD0978A006B43BD')
|
7
14
|
class RocksDB < DB
|
8
15
|
# cap max open files for performance
|
9
16
|
MAX_OPEN_FILES = 200
|
@@ -14,10 +21,33 @@ module XRBP
|
|
14
21
|
:max_open_files => MAX_OPEN_FILES}
|
15
22
|
end
|
16
23
|
|
24
|
+
# Retrieve database value for the specified key
|
25
|
+
#
|
26
|
+
# @param key [String] binary key to lookup
|
27
|
+
# @return [String] binary value
|
17
28
|
def [](key)
|
18
29
|
@db[key]
|
19
30
|
end
|
20
31
|
|
32
|
+
# Iterate over each database key/value pair,
|
33
|
+
# invoking callback. During iteration will
|
34
|
+
# emit signals specific to the DB types being
|
35
|
+
# parsed
|
36
|
+
#
|
37
|
+
# @example iterating over RocksDB entries
|
38
|
+
# rocksdb.each do |iterator|
|
39
|
+
# puts "Key/Value: #{iterator.key}/#{iterator.value}"
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# @example handling account via event callback
|
43
|
+
# rocksdb.on(:account) do |hash, account|
|
44
|
+
# puts "Account #{hash}"
|
45
|
+
# pp account
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# # Any Enumerable method that invokes #each will
|
49
|
+
# # have intended effect
|
50
|
+
# rocksdb.to_a
|
21
51
|
def each
|
22
52
|
iterator = @db.new_iterator
|
23
53
|
iterator.seek_to_first
|
data/lib/xrbp/nodestore/db.rb
CHANGED
@@ -2,25 +2,46 @@ require 'base58'
|
|
2
2
|
require 'openssl'
|
3
3
|
|
4
4
|
module XRBP
|
5
|
+
# The NodeStore is the Key/Value DB which rippled persistent stores
|
6
|
+
# ledger data. Implemented via a backend configured at run time,
|
7
|
+
# the NodeStore is used to store the tree-like structures that
|
8
|
+
# consistute the XRP ledger.
|
9
|
+
#
|
10
|
+
# The Keys and Values stored in the NodeStore are custom binary
|
11
|
+
# encodings of tree-node IDs and data. See this module
|
12
|
+
# and the others in this directory for specifics on how keys & values
|
13
|
+
# are stored and extracted.
|
5
14
|
module NodeStore
|
15
|
+
|
16
|
+
# Base NodeStore DB module, the client will use this class through
|
17
|
+
# specific DB-type subclass.
|
18
|
+
#
|
19
|
+
# Subclasses should define the <b>[ ]</b> (index) method taking key to
|
20
|
+
# lookup, returning corresponding NodeStore value and *each* method,
|
21
|
+
# iterating over nodestore values (see existing subclasses for
|
22
|
+
# implementation details)
|
6
23
|
class DB
|
7
24
|
include Enumerable
|
8
25
|
include EventEmitter
|
9
26
|
|
10
27
|
# TODO return nil if db lookup not found
|
11
28
|
|
29
|
+
# Return the NodeStore Ledger for the given lookup hash
|
12
30
|
def ledger(hash)
|
13
31
|
parse_ledger(self[hash])
|
14
32
|
end
|
15
33
|
|
34
|
+
# Return the NodeStore Account for the given lookup hash
|
16
35
|
def account(hash)
|
17
36
|
parse_ledger_entry(self[hash])
|
18
37
|
end
|
19
38
|
|
39
|
+
# Return the NodeStore Transaction for the given lookup hash
|
20
40
|
def tx(hash)
|
21
41
|
parse_tx(self[hash])
|
22
42
|
end
|
23
43
|
|
44
|
+
# Return the NodeStore InnerNode for the given lookup hash
|
24
45
|
def inner_node(hash)
|
25
46
|
parse_inner_node(self[hash])
|
26
47
|
end
|
@@ -29,6 +50,9 @@ module XRBP
|
|
29
50
|
|
30
51
|
private
|
31
52
|
|
53
|
+
# Parsers binary ledger representation into structured ledger.
|
54
|
+
#
|
55
|
+
# @private
|
32
56
|
def parse_ledger(ledger)
|
33
57
|
obj = Format::LEDGER.decode(ledger)
|
34
58
|
obj['close_time'] = XRBP::from_xrp_time(obj['close_time']).utc
|
@@ -39,6 +63,11 @@ module XRBP
|
|
39
63
|
obj
|
40
64
|
end
|
41
65
|
|
66
|
+
# Certain data types are prefixed with an 'encoding' header
|
67
|
+
# consisting of a field and/or type. Field, type, and remaining
|
68
|
+
# bytes are returned
|
69
|
+
#
|
70
|
+
# @private
|
42
71
|
def parse_encoding(encoding)
|
43
72
|
enc = encoding.unpack("C").first
|
44
73
|
type = enc >> 4
|
@@ -59,6 +88,11 @@ module XRBP
|
|
59
88
|
[[type, field], encoding]
|
60
89
|
end
|
61
90
|
|
91
|
+
# Parses binary ledger entry into hash. Data returned
|
92
|
+
# in hash includes ledger entry type prefix, index,
|
93
|
+
# and array of parsed fields.
|
94
|
+
#
|
95
|
+
# @private
|
62
96
|
def parse_ledger_entry(ledger_entry)
|
63
97
|
# validate parsability
|
64
98
|
obj = Format::TYPE_INFER.decode(ledger_entry)
|
@@ -97,6 +131,9 @@ module XRBP
|
|
97
131
|
|
98
132
|
###
|
99
133
|
|
134
|
+
# Parse and return series of fields from binary data.
|
135
|
+
#
|
136
|
+
# @private
|
100
137
|
def parse_fields(fields)
|
101
138
|
parsed = {}
|
102
139
|
until fields == "" || fields == "\0" || fields.nil?
|
@@ -112,6 +149,10 @@ module XRBP
|
|
112
149
|
return parsed
|
113
150
|
end
|
114
151
|
|
152
|
+
# Parse single field of specified encoding from data.
|
153
|
+
# Dispatches to corresponding parsing method when appropriate.
|
154
|
+
#
|
155
|
+
# @private
|
115
156
|
def parse_field(data, encoding)
|
116
157
|
length = encoding.first
|
117
158
|
|
@@ -135,7 +176,6 @@ module XRBP
|
|
135
176
|
when :vl
|
136
177
|
vl, offset = parse_vl(data)
|
137
178
|
return data[offset..vl+offset-1], data[vl+offset..-1]
|
138
|
-
|
139
179
|
when :account
|
140
180
|
return parse_account(data)
|
141
181
|
when :array
|
@@ -147,12 +187,15 @@ module XRBP
|
|
147
187
|
when :vector256
|
148
188
|
vl, offset = parse_vl(data)
|
149
189
|
return data[offset..vl+offset-1], data[vl+offset..-1]
|
150
|
-
|
151
190
|
end
|
152
191
|
|
153
192
|
raise
|
154
193
|
end
|
155
194
|
|
195
|
+
# Parse variable length header from data buffer. Returns length
|
196
|
+
# extracted from header and the number of bytes in header.
|
197
|
+
#
|
198
|
+
# @private
|
156
199
|
def parse_vl(data)
|
157
200
|
data = data.bytes
|
158
201
|
first = data.first.to_i
|
@@ -172,6 +215,9 @@ module XRBP
|
|
172
215
|
raise
|
173
216
|
end
|
174
217
|
|
218
|
+
# Parse 'Amount' data type from binary data.
|
219
|
+
#
|
220
|
+
# @private
|
175
221
|
def parse_amount(data)
|
176
222
|
amount = data[0..7].unpack("Q>").first
|
177
223
|
xrp = amount < 0x8000000000000000
|
@@ -195,6 +241,9 @@ module XRBP
|
|
195
241
|
:issuer => issuer }, data
|
196
242
|
end
|
197
243
|
|
244
|
+
# Parse 'Account' data type from binary data.
|
245
|
+
#
|
246
|
+
# @private
|
198
247
|
def parse_account(data, vl=nil)
|
199
248
|
unless vl
|
200
249
|
vl,offset = parse_vl(data)
|
@@ -209,6 +258,9 @@ module XRBP
|
|
209
258
|
return Base58.binary_to_base58(acct, :ripple), data[vl..-1]
|
210
259
|
end
|
211
260
|
|
261
|
+
# Parse array of fields from binary data.
|
262
|
+
#
|
263
|
+
# @private
|
212
264
|
def parse_array(data, encoding)
|
213
265
|
e = Format::ENCODINGS[encoding]
|
214
266
|
return nil, data if e == :end_of_array
|
@@ -229,6 +281,9 @@ module XRBP
|
|
229
281
|
return array, data
|
230
282
|
end
|
231
283
|
|
284
|
+
# Parse Object consisting of multiple fields from binary data.
|
285
|
+
#
|
286
|
+
# @private
|
232
287
|
def parse_object(data, encoding)
|
233
288
|
e = Format::ENCODINGS[encoding]
|
234
289
|
case e
|
@@ -248,6 +303,9 @@ module XRBP
|
|
248
303
|
raise "unknown object type"
|
249
304
|
end
|
250
305
|
|
306
|
+
# Parse PathSet from binary data.
|
307
|
+
#
|
308
|
+
# @private
|
251
309
|
def parse_pathset(data)
|
252
310
|
pathset = [[]]
|
253
311
|
until data == "" || data.nil?
|
@@ -287,6 +345,9 @@ module XRBP
|
|
287
345
|
|
288
346
|
###
|
289
347
|
|
348
|
+
# Parse Transaction from binary data
|
349
|
+
#
|
350
|
+
# @private
|
290
351
|
def parse_tx(tx)
|
291
352
|
obj = Format::TYPE_INFER.decode(tx)
|
292
353
|
node_type = Format::NODE_TYPES[obj["node_type"]]
|
@@ -312,6 +373,9 @@ module XRBP
|
|
312
373
|
:index => index.pack("C*").unpack("H*").first.upcase }
|
313
374
|
end
|
314
375
|
|
376
|
+
# Parse InnerNode from binary data.
|
377
|
+
#
|
378
|
+
# @private
|
315
379
|
def parse_inner_node(node)
|
316
380
|
# verify parsability
|
317
381
|
obj = Format::TYPE_INFER.decode(node)
|
@@ -323,6 +387,9 @@ module XRBP
|
|
323
387
|
|
324
388
|
protected
|
325
389
|
|
390
|
+
# Return type and extracted structure from binary data.
|
391
|
+
#
|
392
|
+
# @private
|
326
393
|
def infer_type(value)
|
327
394
|
obj = Format::TYPE_INFER.decode(value)
|
328
395
|
node_type = Format::NODE_TYPES[obj["node_type"]]
|
@@ -17,16 +17,20 @@ module XRBP
|
|
17
17
|
4 => :tx_node
|
18
18
|
}
|
19
19
|
|
20
|
-
HASH_PREFIXES = {
|
21
|
-
"54584E00" => :tx_id,
|
22
|
-
"534E4400" => :tx_node,
|
23
|
-
"4D4C4E00" => :leaf_node,
|
24
|
-
"4D494E00" => :inner_node,
|
25
|
-
"494E5200" => :inner_node_v2,
|
26
|
-
"4C575200" => :ledger_master,
|
27
|
-
"53545800" => :tx_sign,
|
28
|
-
"56414C00" => :validation,
|
29
|
-
"50525000" => :proposal
|
20
|
+
HASH_PREFIXES = { # ASCII value:
|
21
|
+
"54584E00" => :tx_id, # TXN
|
22
|
+
"534E4400" => :tx_node, # SND
|
23
|
+
"4D4C4E00" => :leaf_node, # MLN
|
24
|
+
"4D494E00" => :inner_node, # MIN
|
25
|
+
"494E5200" => :inner_node_v2, # INR
|
26
|
+
"4C575200" => :ledger_master, # LWR
|
27
|
+
"53545800" => :tx_sign, # STX
|
28
|
+
"56414C00" => :validation, # VAL
|
29
|
+
"50525000" => :proposal # PRP
|
30
|
+
|
31
|
+
# TODO: :tx_multi_sign # SMT
|
32
|
+
# :manifest # MAN
|
33
|
+
# :paychan_claim # CLM
|
30
34
|
}
|
31
35
|
|
32
36
|
###
|
data/lib/xrbp/overlay.rb
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
module XRBP
|
2
|
+
# The Overlay is the Peer-to-Peer (P2P) network established
|
3
|
+
# by rippled node instances to each other. It is what is used
|
4
|
+
# to relay transactions and network state as the consensus
|
5
|
+
# process is executed.
|
6
|
+
#
|
7
|
+
# This module facilitates communication with the Overlay P2P
|
8
|
+
# network from Ruby.
|
2
9
|
module Overlay
|
10
|
+
|
11
|
+
# Primary Overlay Connection Interface, use Connection
|
12
|
+
# to send and receive Peer-To-Peer data over the Overlay.
|
13
|
+
#
|
14
|
+
# @example establishing a connection, reading frames
|
15
|
+
# overlay = XRBP::Overlay::Connection.new "127.0.0.1", 51235
|
16
|
+
# overlay.connect
|
17
|
+
#
|
18
|
+
# overlay.read_frames do |frame|
|
19
|
+
# puts "Message: #{frame.type_name} (#{frame.size} bytes)"
|
20
|
+
# end
|
3
21
|
class Connection
|
4
22
|
attr_reader :host, :port
|
5
23
|
attr_accessor :node
|
@@ -10,14 +28,17 @@ module XRBP
|
|
10
28
|
@node = Crypto.node
|
11
29
|
end
|
12
30
|
|
31
|
+
# @private
|
13
32
|
def socket
|
14
33
|
@socket ||= TCPSocket.open(host, port)
|
15
34
|
end
|
16
35
|
|
36
|
+
# Indicates if the connection is closed
|
17
37
|
def closed?
|
18
38
|
socket.closed?
|
19
39
|
end
|
20
40
|
|
41
|
+
# @private
|
21
42
|
def ssl_socket
|
22
43
|
@ssl_socket ||= begin
|
23
44
|
ssl_context = OpenSSL::SSL::SSLContext.new
|
@@ -31,29 +52,38 @@ module XRBP
|
|
31
52
|
end
|
32
53
|
end
|
33
54
|
|
55
|
+
# @private
|
34
56
|
def handshake
|
35
57
|
@handshake ||= Handshake.new self
|
36
58
|
end
|
37
59
|
|
38
60
|
###
|
39
61
|
|
62
|
+
# Initiate new connection to peer
|
40
63
|
def connect
|
41
64
|
ssl_socket.connect
|
42
65
|
handshake.execute!
|
43
66
|
end
|
44
67
|
|
45
|
-
|
68
|
+
# Close the connection to peer
|
69
|
+
def close!
|
46
70
|
ssl_socket.close
|
47
71
|
end
|
48
72
|
|
73
|
+
alias :close :close!
|
74
|
+
|
75
|
+
# Send raw data via this connection
|
49
76
|
def write(data)
|
50
77
|
ssl_socket.puts(data)
|
51
78
|
end
|
52
79
|
|
80
|
+
# Read raw data from connection
|
53
81
|
def read
|
54
82
|
ssl_socket.gets
|
55
83
|
end
|
56
84
|
|
85
|
+
# Read frames from connection until closed, invoking
|
86
|
+
# passed block with each.
|
57
87
|
def read_frames
|
58
88
|
frame = nil
|
59
89
|
remaining = nil
|
@@ -70,17 +100,15 @@ module XRBP
|
|
70
100
|
|
71
101
|
_, remaining = frame << out
|
72
102
|
if frame.complete?
|
73
|
-
# TODO extra specific protobuf data structure
|
74
|
-
# from data, set on frame
|
75
103
|
yield frame
|
76
104
|
frame = nil
|
77
105
|
end
|
78
106
|
|
79
|
-
#
|
80
|
-
|
107
|
+
# static assertion: should have no more data
|
108
|
+
raise unless remaining.nil? || remaining.empty?
|
81
109
|
end
|
82
110
|
end
|
83
111
|
end
|
84
112
|
end # class Connection
|
85
|
-
end # module
|
113
|
+
end # module Overlay
|
86
114
|
end # module XRBP
|
data/lib/xrbp/overlay/frame.rb
CHANGED
@@ -3,6 +3,10 @@ require_relative './ripple.proto'
|
|
3
3
|
|
4
4
|
module XRBP
|
5
5
|
module Overlay
|
6
|
+
# Overlay Message Frame, prefixes Protobuf based message in
|
7
|
+
# with header describing size and type.
|
8
|
+
#
|
9
|
+
# @private
|
6
10
|
class Frame
|
7
11
|
TYPE_INFER = Bistro.new([
|
8
12
|
'L>', 'size',
|
@@ -37,14 +41,18 @@ module XRBP
|
|
37
41
|
@type_name ||= self.class.type_name(type)
|
38
42
|
end
|
39
43
|
|
44
|
+
def message
|
45
|
+
@message ||= MESSAGES[type_name].decode(data)
|
46
|
+
end
|
47
|
+
|
40
48
|
def <<(data)
|
41
|
-
remaining = size -
|
49
|
+
remaining = size - @data.size
|
42
50
|
@data += data[0..remaining-1]
|
43
51
|
return @data, data[remaining..-1]
|
44
52
|
end
|
45
53
|
|
46
54
|
def complete?
|
47
|
-
|
55
|
+
@data.size == size
|
48
56
|
end
|
49
57
|
end # class Frame
|
50
58
|
end # module WebClient
|
@@ -1,17 +1,24 @@
|
|
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
1
|
require 'base64'
|
11
2
|
require 'openssl'
|
12
3
|
|
13
4
|
module XRBP
|
14
5
|
module Overlay
|
6
|
+
|
7
|
+
# Overlay Connection Handshake, the first message sent from connection
|
8
|
+
# initiator to remote endpoint establishing session. Once connection
|
9
|
+
# is successfully established subsequent messages will be encapsulated
|
10
|
+
# Frames.
|
11
|
+
#
|
12
|
+
# XXX this module requires the openssl-ruby gem with the following patches:
|
13
|
+
# https://github.com/ruby/openssl/pull/250
|
14
|
+
#
|
15
|
+
# Otherwise the ssl_socket#finished and #peer_finished methods will
|
16
|
+
# not be available
|
17
|
+
#
|
18
|
+
# Currently the only way to apply this is to checkout openssl-ruby, apply
|
19
|
+
# the patches, and then rebuild/reinstall the gem locally!
|
20
|
+
#
|
21
|
+
# @private
|
15
22
|
class Handshake
|
16
23
|
attr_reader :connection, :response
|
17
24
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module XRBP
|
2
|
+
module Overlay
|
3
|
+
# Map of Protocol Message Type to Class
|
4
|
+
#
|
5
|
+
# See {https://github.com/ripple/rippled/blob/develop/src/ripple/overlay/impl/ProtocolMessage.h ProtocolMessage.h}
|
6
|
+
MESSAGES = {
|
7
|
+
:MTHELLO => Protocol::TMHello,
|
8
|
+
:MTMANIFESTS => Protocol::TMManifests,
|
9
|
+
:MTPING => Protocol::TMPing,
|
10
|
+
:MTCLUSTER => Protocol::TMCluster,
|
11
|
+
:MTGET_SHARD_INFO => Protocol::TMGetShardInfo,
|
12
|
+
:MTSHARD_INFO => Protocol::TMShardInfo,
|
13
|
+
:MTGET_PEER_SHARD_INFO => Protocol::TMGetPeerShardInfo,
|
14
|
+
:MTGET_PEERS => Protocol::TMGetPeers,
|
15
|
+
:MTPEERS => Protocol::TMPeers,
|
16
|
+
:MTENDPOINTS => Protocol::TMEndpoints,
|
17
|
+
:MTTRANSACTION => Protocol::TMTransaction,
|
18
|
+
:MTGET_LEDGER => Protocol::TMGetLedger,
|
19
|
+
:MTLEDGER_DATA => Protocol::TMLedgerData,
|
20
|
+
:MTPROPOSE_LEDGER => Protocol::TMProposeSet,
|
21
|
+
:MTSTATUS_CHANGE => Protocol::TMStatusChange,
|
22
|
+
:MTHAVE_SET => Protocol::TMHaveTransactionSet,
|
23
|
+
:MTVALIDATION => Protocol::TMValidation,
|
24
|
+
:MTGET_OBJECTS => Protocol::TMGetObjectByHash
|
25
|
+
}
|
26
|
+
end # module Overlay
|
27
|
+
end # module XRBP
|
@@ -74,6 +74,21 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
74
74
|
optional :lastLink, :bool, 4
|
75
75
|
repeated :peerchain, :uint32, 5
|
76
76
|
end
|
77
|
+
add_message "protocol.TMLink" do
|
78
|
+
optional :nodePubKey, :bytes, 1
|
79
|
+
end
|
80
|
+
add_message "protocol.TMGetPeerShardInfo" do
|
81
|
+
optional :hops, :uint32, 1
|
82
|
+
optional :lastLink, :bool, 2
|
83
|
+
repeated :peerChain, :message, 3, "protocol.TMLink"
|
84
|
+
end
|
85
|
+
add_message "protocol.TMPeerShardInfo" do
|
86
|
+
optional :shardIndexes, :string, 1
|
87
|
+
optional :nodePubKey, :bytes, 2
|
88
|
+
optional :endpoint, :string, 3
|
89
|
+
optional :lastLink, :bool, 4
|
90
|
+
repeated :peerChain, :message, 5, "protocol.TMLink"
|
91
|
+
end
|
77
92
|
add_message "protocol.TMTransaction" do
|
78
93
|
optional :rawTransaction, :bytes, 1
|
79
94
|
optional :status, :enum, 2, "protocol.TransactionStatus"
|
@@ -210,6 +225,8 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
|
|
210
225
|
value :MTGET_OBJECTS, 42
|
211
226
|
value :MTGET_SHARD_INFO, 50
|
212
227
|
value :MTSHARD_INFO, 51
|
228
|
+
value :MTGET_PEER_SHARD_INFO, 52
|
229
|
+
value :MTPEER_SHARD_INFO, 53
|
213
230
|
end
|
214
231
|
add_enum "protocol.TransactionStatus" do
|
215
232
|
value :TSZERO, 0
|
@@ -275,6 +292,9 @@ module Protocol
|
|
275
292
|
TMCluster = Google::Protobuf::DescriptorPool.generated_pool.lookup("protocol.TMCluster").msgclass
|
276
293
|
TMGetShardInfo = Google::Protobuf::DescriptorPool.generated_pool.lookup("protocol.TMGetShardInfo").msgclass
|
277
294
|
TMShardInfo = Google::Protobuf::DescriptorPool.generated_pool.lookup("protocol.TMShardInfo").msgclass
|
295
|
+
TMLink = Google::Protobuf::DescriptorPool.generated_pool.lookup("protocol.TMLink").msgclass
|
296
|
+
TMGetPeerShardInfo = Google::Protobuf::DescriptorPool.generated_pool.lookup("protocol.TMGetPeerShardInfo").msgclass
|
297
|
+
TMPeerShardInfo = Google::Protobuf::DescriptorPool.generated_pool.lookup("protocol.TMPeerShardInfo").msgclass
|
278
298
|
TMTransaction = Google::Protobuf::DescriptorPool.generated_pool.lookup("protocol.TMTransaction").msgclass
|
279
299
|
TMStatusChange = Google::Protobuf::DescriptorPool.generated_pool.lookup("protocol.TMStatusChange").msgclass
|
280
300
|
TMProposeSet = Google::Protobuf::DescriptorPool.generated_pool.lookup("protocol.TMProposeSet").msgclass
|
data/lib/xrbp/version.rb
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
describe XRBP::Crypto::Key do
|
2
|
+
it "generates valid sec256k1 key"
|
3
|
+
it "generates valid ed25519 key"
|
4
|
+
|
5
|
+
it "generates signs and verifies digest" do
|
6
|
+
key = described_class.secp256k1
|
7
|
+
dat = "ABCDEFGHIJLMNOPQRSTUVWYZ12345678"
|
8
|
+
signed = XRBP::Crypto::Key.sign_digest(key, dat)
|
9
|
+
expect(XRBP::Crypto::Key.verify(key, signed, dat))
|
10
|
+
end
|
11
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xrbp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dev Null Productions
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -196,7 +196,6 @@ files:
|
|
196
196
|
- examples/paginate.rb
|
197
197
|
- examples/prioritized.rb
|
198
198
|
- examples/round_robin.rb
|
199
|
-
- examples/sandbox.rb
|
200
199
|
- examples/username.rb
|
201
200
|
- examples/validator.rb
|
202
201
|
- examples/websocket.rb
|
@@ -207,6 +206,8 @@ files:
|
|
207
206
|
- lib/xrbp/crypto/account.rb
|
208
207
|
- lib/xrbp/crypto/key.rb
|
209
208
|
- lib/xrbp/crypto/node.rb
|
209
|
+
- lib/xrbp/crypto/seed.rb
|
210
|
+
- lib/xrbp/crypto/validator.rb
|
210
211
|
- lib/xrbp/dsl.rb
|
211
212
|
- lib/xrbp/dsl/accounts.rb
|
212
213
|
- lib/xrbp/dsl/ledgers.rb
|
@@ -238,6 +239,7 @@ files:
|
|
238
239
|
- lib/xrbp/overlay/connection.rb
|
239
240
|
- lib/xrbp/overlay/frame.rb
|
240
241
|
- lib/xrbp/overlay/handshake.rb
|
242
|
+
- lib/xrbp/overlay/messages.rb
|
241
243
|
- lib/xrbp/overlay/ripple.proto.rb
|
242
244
|
- lib/xrbp/plugins.rb
|
243
245
|
- lib/xrbp/plugins/base.rb
|
@@ -286,6 +288,10 @@ files:
|
|
286
288
|
- spec/helpers/force_serializable.rb
|
287
289
|
- spec/helpers/test_handshake.rb
|
288
290
|
- spec/spec_helper.rb
|
291
|
+
- spec/xrbp/crypto/account_spec.rb
|
292
|
+
- spec/xrbp/crypto/key_spec.rb
|
293
|
+
- spec/xrbp/crypto/node_spec.rb
|
294
|
+
- spec/xrbp/crypto/seed_spec.rb
|
289
295
|
- spec/xrbp/websocket/client_spec.rb
|
290
296
|
- spec/xrbp/websocket/command_spec.rb
|
291
297
|
- spec/xrbp/websocket/connection_spec.rb
|
data/examples/sandbox.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
$: << File.expand_path('../../lib', __FILE__)
|
2
|
-
require 'xrbp'
|
3
|
-
require 'xrbp/nodestore/backends/nudb'
|
4
|
-
|
5
|
-
db = XRBP::NodeStore::Backends::NuDB.new "/var/lib/rippled/nudb/"
|
6
|
-
store = db.instance_variable_get(:@store)
|
7
|
-
|
8
|
-
key = [47, 126, 65, 231, 47, 167, 3, 91, 161, 174, 133, 152, 2, 230, 171, 139, 209, 40, 100, 217, 100, 131, 160, 49, 186, 119, 31, 84, 75, 116, 125, 81].pack("C*")
|
9
|
-
val = store.fetch(key).first
|
10
|
-
#puts val.unpack("C*").join " "
|
11
|
-
decompressed = db.send(:decompress, val)
|
12
|
-
puts decompressed.unpack("C*").join " "
|
13
|
-
type, obj = db.send(:infer_type, decompressed)
|
14
|
-
puts obj
|