xrbp 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|