xrbp 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/examples/nodestore1.rb +12 -7
  3. data/lib/xrbp/core_ext.rb +27 -0
  4. data/lib/xrbp/crypto/account.rb +28 -3
  5. data/lib/xrbp/nodestore.rb +6 -0
  6. data/lib/xrbp/nodestore/amendments.rb +13 -0
  7. data/lib/xrbp/nodestore/backends/decompressor.rb +8 -6
  8. data/lib/xrbp/nodestore/backends/nudb.rb +2 -0
  9. data/lib/xrbp/nodestore/backends/rocksdb.rb +1 -0
  10. data/lib/xrbp/nodestore/db.rb +5 -387
  11. data/lib/xrbp/nodestore/fees.rb +19 -0
  12. data/lib/xrbp/nodestore/format.rb +72 -1
  13. data/lib/xrbp/nodestore/ledger.rb +272 -0
  14. data/lib/xrbp/nodestore/parser.rb +407 -0
  15. data/lib/xrbp/nodestore/protocol.rb +5 -0
  16. data/lib/xrbp/nodestore/protocol/currency.rb +11 -0
  17. data/lib/xrbp/nodestore/protocol/indexes.rb +109 -0
  18. data/lib/xrbp/nodestore/protocol/issue.rb +26 -0
  19. data/lib/xrbp/nodestore/protocol/quality.rb +10 -0
  20. data/lib/xrbp/nodestore/protocol/rate.rb +21 -0
  21. data/lib/xrbp/nodestore/shamap.rb +447 -0
  22. data/lib/xrbp/nodestore/shamap/errors.rb +8 -0
  23. data/lib/xrbp/nodestore/shamap/inner_node.rb +98 -0
  24. data/lib/xrbp/nodestore/shamap/item.rb +14 -0
  25. data/lib/xrbp/nodestore/shamap/node.rb +49 -0
  26. data/lib/xrbp/nodestore/shamap/node_factory.rb +120 -0
  27. data/lib/xrbp/nodestore/shamap/node_id.rb +83 -0
  28. data/lib/xrbp/nodestore/shamap/tagged_cache.rb +20 -0
  29. data/lib/xrbp/nodestore/shamap/tree_node.rb +21 -0
  30. data/lib/xrbp/nodestore/sle.rb +4 -0
  31. data/lib/xrbp/nodestore/sle/st_account.rb +8 -0
  32. data/lib/xrbp/nodestore/sle/st_amount.rb +226 -0
  33. data/lib/xrbp/nodestore/sle/st_ledger_entry.rb +24 -0
  34. data/lib/xrbp/nodestore/sle/st_object.rb +46 -0
  35. data/lib/xrbp/nodestore/sqldb.rb +23 -0
  36. data/lib/xrbp/nodestore/uint.rb +7 -0
  37. data/lib/xrbp/version.rb +1 -1
  38. data/spec/xrbp/nodestore/backends/nudb_spec.rb +3 -1
  39. data/spec/xrbp/nodestore/backends/rocksdb_spec.rb +1 -1
  40. data/spec/xrbp/nodestore/{backends/db_parser.rb → db_parser.rb} +2 -2
  41. data/spec/xrbp/nodestore/ledger_access.rb +17 -0
  42. metadata +30 -3
@@ -0,0 +1,8 @@
1
+ module XRBP
2
+ class SHAMap
3
+ module Errors
4
+ class MissingNode < StandardError
5
+ end # class MissingNode
6
+ end
7
+ end # class SHAMap
8
+ end # module XRBP
@@ -0,0 +1,98 @@
1
+ module XRBP
2
+ class SHAMap
3
+ # A DB entry which may contain references of up to 16-child
4
+ # nodes, facilitating abstract tree-like traversal.
5
+ #
6
+ # This class simply encapsulates children w/ hashes
7
+ class InnerNode < Node
8
+ attr_accessor :depth, :common, :hashes, :is_branch
9
+
10
+ def initialize(args={})
11
+ @v2 = args[:v2]
12
+ @depth = args[:depth] || 0
13
+
14
+ @common = {}
15
+ @hashes = {}
16
+ @children = []
17
+ @is_branch = 0
18
+ end
19
+
20
+ def v2?
21
+ @v2
22
+ end
23
+
24
+ def inner?
25
+ true
26
+ end
27
+
28
+ def common_prefix?(key)
29
+ hd = depth/2
30
+ 0.upto(hd) do |d|
31
+ return false if common[d] != key[d]
32
+ end
33
+
34
+ return (common[hd] & 0xF0) &&
35
+ (key[hd] & 0xF0) if depth & 1
36
+
37
+ return true
38
+ end
39
+
40
+ # Returns true if node has no children
41
+ def empty?
42
+ is_branch == 0
43
+ end
44
+
45
+ # Return true if specified branch is empty,
46
+ # else false
47
+ def empty_branch?(branch)
48
+ (is_branch & (1 << branch)) == 0
49
+ end
50
+
51
+ # Returns hash of child on given branch
52
+ def child_hash(branch)
53
+ raise ArgumentError unless branch >= 0 &&
54
+ branch < 16
55
+ hashes[branch]
56
+ end
57
+
58
+ # Returns child containing in given branch
59
+ def child(branch)
60
+ raise ArgumentError unless branch >= 0 &&
61
+ branch < 16
62
+ @children[branch]
63
+ end
64
+
65
+ # Canonicalize and store child node at branch
66
+ def canonicalize_child(branch, node)
67
+ raise ArgumentError unless branch >= 0 &&
68
+ branch < 16
69
+ raise unless node
70
+ raise unless node.hash == hashes[branch]
71
+
72
+ if @children[branch]
73
+ return @children[branch]
74
+ else
75
+ return @children[branch] = node
76
+ end
77
+ end
78
+
79
+ # Update this node's hash from child hashes
80
+ def update_hash
81
+ nh = nil
82
+
83
+ if is_branch != 0
84
+ sha512 = OpenSSL::Digest::SHA512.new
85
+ sha512 << HASH_+PREFIXES[:inner_node]
86
+ hashes.each { |k,h|
87
+ sha512 << v
88
+ }
89
+ nh = sha512.digest
90
+ end
91
+
92
+ return false if nh == self.hash
93
+ self.hash = nh
94
+ return true
95
+ end
96
+ end # class InnerNode
97
+ end # class SHAMap
98
+ end # module XRBP
@@ -0,0 +1,14 @@
1
+ module XRBP
2
+ class SHAMap
3
+ # Binary data blog stored in DB w/ key
4
+ class Item
5
+ attr_reader :key
6
+ attr_reader :data
7
+
8
+ def initialize(args = {})
9
+ @key = args[:key]
10
+ @data = args[:data]
11
+ end
12
+ end # class Item
13
+ end # class SHAMap
14
+ end # module XRBP
@@ -0,0 +1,49 @@
1
+ require_relative './node_factory'
2
+
3
+ module XRBP
4
+ class SHAMap
5
+ # Base Node class, all entries stored in tree structures
6
+ # in nodestore DB inherit from this class
7
+ class Node
8
+ extend NodeFactory
9
+
10
+ attr_accessor :hash
11
+
12
+ TYPES = {
13
+ :error => 0,
14
+ :infer => 1,
15
+ :transaction_nm => 2,
16
+ :transaction_md => 3,
17
+ :account_state => 4
18
+ }
19
+
20
+ LEAF_TYPES = [
21
+ :transaction_nm,
22
+ :transaction_md,
23
+ :account_state
24
+ ]
25
+
26
+ def initialize(args={})
27
+ @hash = args[:hash]
28
+ @type = args[:type]
29
+ @seq = args[:seq]
30
+ end
31
+
32
+ def leaf?
33
+ LEAF_TYPES.include?(@type)
34
+ end
35
+
36
+ def inner?
37
+ false
38
+ end
39
+
40
+ def tree_node?
41
+ false
42
+ end
43
+
44
+ def update_hash
45
+ raise "abstract: must be called on a subclass"
46
+ end
47
+ end # class Node
48
+ end # class SHAMap
49
+ end # module XRBP
@@ -0,0 +1,120 @@
1
+ module XRBP
2
+ class SHAMap
3
+ module NodeFactory
4
+ # See rippled::SHAMapAbstractNode::make
5
+ def make(node, seq, format, hash, hash_valid)
6
+ node_id = NodeID.new
7
+
8
+ if format == :wire
9
+ # TODO
10
+
11
+ elsif format == :prefix
12
+ raise if node.size < 4
13
+
14
+ prefix = node[0].ord
15
+ prefix <<= 8
16
+ prefix |= node[1].ord
17
+ prefix <<= 8
18
+ prefix |= node[2].ord
19
+ prefix <<= 8
20
+ prefix |= node[3].ord
21
+ prefix = prefix.to_s(16).upcase
22
+
23
+ s = node[4..-1]
24
+
25
+ if prefix == NodeStore::Format::HASH_PREFIXES[:tx_id]
26
+ sha512 = OpenSSL::Digest::SHA512.new
27
+ sha512 << NodeStore::Format::HASH_PREFIXES[:tx_id]
28
+ sta512 << node
29
+ key = sha512.digest[0..31]
30
+
31
+ item = Item.new(:key => key,
32
+ :data => node)
33
+
34
+ tree_node = {:item => item,
35
+ :seq => seq,
36
+ :type => :transaction_nm}
37
+ tree_node[:hash] = hash if hash_valid
38
+
39
+ return TreeNode.new(tree_node)
40
+
41
+ elsif prefix == NodeStore::Format::HASH_PREFIXES[:leaf_node]
42
+ raise "short PLN node" if s.size < 32
43
+
44
+ u = s[-32..-1]
45
+ s = s[0..-33]
46
+ raise "invalid PLN node" if u.zero?
47
+
48
+ item = Item.new(:key => u,
49
+ :data => s)
50
+
51
+ tree_node = {:item => item,
52
+ :seq => seq,
53
+ :type => :account_state}
54
+ tree_node[:hash] = hash if hash_valid
55
+
56
+ return TreeNode.new(tree_node)
57
+
58
+ elsif (prefix == NodeStore::Format::HASH_PREFIXES[:inner_node]) ||
59
+ (prefix == NodeStore::Format::HASH_PREFIXES[:inner_node_v2])
60
+ len = s.size
61
+ isv2 = prefix == NodeStore::Format::HASH_PREFIXES[:inner_node_v2]
62
+
63
+ raise "invalid PIN node" if len < 512 ||
64
+ (!isv2 && (len != 512)) ||
65
+ ( isv2 && (len == 512))
66
+
67
+ ret = InnerNode.new :v2 => isv2
68
+
69
+ 0.upto(15) { |i|
70
+ ret.hashes[i] = s[i*32...(i+1)*32]
71
+ ret.is_branch |= (1 << i) unless ret.hashes[i].zero?
72
+ }
73
+
74
+ if isv2
75
+ ret.depth = s[512]
76
+ n = (ret.depth + 1)/2
77
+ raise "invalid PIN node" if len != 512 + 1 + n
78
+
79
+ 0.upto(n-1) { |i|
80
+ ret.common << s[512+1+i]
81
+ }
82
+ end
83
+
84
+ if hash_valid
85
+ ret.hash = hash
86
+ else
87
+ ret.update_hash
88
+ end
89
+
90
+ return ret
91
+
92
+ elsif prefix == NodeStore::Format::HASH_PREFIXES[:tx_node]
93
+ # transaction with metadata
94
+ raise "short TXN node" if s.size < 32
95
+
96
+ tx_id = s[-32..-1]
97
+ # XXX: tx_id is last field in binary transaction, keep so
98
+ # it can be parsed w/ other fields later:
99
+ #s = s[0..-33]
100
+
101
+ item = Item.new(:key => tx_id,
102
+ :data => s)
103
+
104
+ tree_node = {:item => item,
105
+ :seq => seq,
106
+ :type => :transaction_md}
107
+ tree_node[:hash] = hash if hash_valid
108
+
109
+ return TreeNode.new(tree_node)
110
+
111
+ else
112
+ raise "Unknown prefix #{prefix}"
113
+ end
114
+ end
115
+
116
+ raise "Unknown format"
117
+ end
118
+ end # module NodeFactory
119
+ end # class SHAMap
120
+ end # module XRBP
@@ -0,0 +1,83 @@
1
+ module XRBP
2
+ class SHAMap
3
+ # Encapsulates node key to allow for tree traversal.
4
+ #
5
+ # Provides branch extraction/generation logic.
6
+ # Since branch is between 0-15, only a nibble (4bits)
7
+ # are needed to store. Thus each char (8bits) can describe
8
+ # 2 tree branches
9
+ class NodeID
10
+ attr_reader :depth, :key
11
+
12
+ def initialize(args={})
13
+ @depth ||= args[:depth] || 0
14
+ @key ||= args[:key] || NodeStore.uint256
15
+ end
16
+
17
+ MASK_SIZE = 65
18
+
19
+ # Masks corresponding to each tree level.
20
+ # Used to calculate inner node hash for
21
+ # tree level:
22
+ # inner node = lookup key & mask
23
+ def masks
24
+ @masks ||= begin
25
+ masks = Array.new(MASK_SIZE)
26
+
27
+ i = 0
28
+ selector = NodeStore.uint256
29
+ while(i < MASK_SIZE-1)
30
+ masks[i] = String.new(selector)
31
+ selector[i / 2] = 0xF0.chr
32
+ masks[i+1] = String.new(selector)
33
+ selector[i / 2] = 0xFF.chr
34
+ i += 2
35
+ end
36
+ masks[MASK_SIZE-1] = selector
37
+
38
+ masks
39
+ end
40
+ end
41
+
42
+ # Return mask for current tree depth
43
+ def mask
44
+ @mask ||= masks[depth]
45
+ end
46
+
47
+ # Return branch number of specified hash.
48
+ def select_branch(hash)
49
+ #if RIPPLE_VERIFY_NODEOBJECT_KEYS
50
+ raise if depth >= 64
51
+ raise if (hash.to_bn & mask.to_bn) != key.to_bn
52
+ #end
53
+
54
+ # Extract hash byte at local node depth
55
+ br = hash[depth / 2].ord
56
+
57
+ # Reduce to relevant nibble
58
+ if (depth & 1) == 1
59
+ br &= 0xf
60
+ else
61
+ br >>= 4
62
+ end
63
+
64
+ raise unless (br >= 0) && (br < 16)
65
+ br
66
+ end
67
+
68
+ # Return NodeID for specified branch under this one.
69
+ def child_node_id(branch)
70
+ raise unless branch >= 0 && branch < 16
71
+ raise unless depth < 64
72
+
73
+ # Copy local key and assign branch number to
74
+ # nibble in byte at local depth
75
+ child = key.unpack("C*")
76
+ child[depth/2] |= ((depth & 1) == 1) ? branch : (branch << 4)
77
+
78
+ NodeID.new :depth => (depth + 1),
79
+ :key => child.pack("C*")
80
+ end
81
+ end # class NodeID
82
+ end # class SHAMap
83
+ end # module XRBP
@@ -0,0 +1,20 @@
1
+ module XRBP
2
+ class SHAMap
3
+ # Internal node caching mechanism.
4
+ #
5
+ # TODO timeout mechanism, metrics
6
+ class TaggedCache
7
+ def initialize
8
+ @cache = {}
9
+ end
10
+
11
+ def fetch(key)
12
+ @cache[key]
13
+ end
14
+
15
+ def canonicalize(key, node)
16
+ @cache[key] = node
17
+ end
18
+ end # class TaggedCache
19
+ end # class SHAMap
20
+ end # module XRBP
@@ -0,0 +1,21 @@
1
+ module XRBP
2
+ class SHAMap
3
+ # Terminating tree node referencing concrete data
4
+ class TreeNode < Node
5
+ attr_reader :item
6
+
7
+ def initialize(args={})
8
+ super
9
+ @item = args[:item]
10
+ end
11
+
12
+ def tree_node?
13
+ true
14
+ end
15
+
16
+ def peek_item
17
+ item
18
+ end
19
+ end # class TreeNode
20
+ end # class SHAMap
21
+ end # module XRBP
@@ -0,0 +1,4 @@
1
+ require_relative './sle/st_object'
2
+ require_relative './sle/st_account'
3
+ require_relative './sle/st_amount'
4
+ require_relative './sle/st_ledger_entry'
@@ -0,0 +1,8 @@
1
+ module XRBP
2
+ module NodeStore
3
+ # Serialized Account Representation
4
+ class STAccount
5
+ # TODO
6
+ end # class STAccount
7
+ end # module NodeStore
8
+ end # module XRBP
@@ -0,0 +1,226 @@
1
+ module XRBP
2
+ module NodeStore
3
+ # Serialized Amount Representation
4
+ class STAmount
5
+ # see: https://github.com/ripple/rippled/blob/b53fda1e1a7f4d09b766724274329df1c29988ab/src/ripple/protocol/STAmount.h#L67
6
+ MIN_VAL = 1000000000000000
7
+
8
+ attr_reader :mantissa, :exponent, :neg
9
+ attr_accessor :issue
10
+
11
+ alias :value :mantissa
12
+ alias :offset :exponent
13
+
14
+ def self.zero(args={})
15
+ STAmount.new args.merge({:mantissa => 0})
16
+ end
17
+
18
+ def self.from_quality(rate)
19
+ return STAmount.new(:issue => NodeStore.no_issue) if rate == 0
20
+
21
+ mantissa = rate & ~(255 << (64 - 8))
22
+ exponent = (rate >> (64 - 8)) - 100
23
+
24
+ return STAmount.new(:issue => NodeStore.no_issue,
25
+ :mantissa => mantissa,
26
+ :exponent => exponent)
27
+ end
28
+
29
+ def initialize(args={})
30
+ @issue = args[:issue]
31
+ @mantissa = args[:mantissa] || 0
32
+ @exponent = args[:exponent] || 0
33
+ @neg = !!args[:neg]
34
+ end
35
+
36
+ def native?
37
+ @issue.xrp?
38
+ end
39
+
40
+ def zero?
41
+ @mantissa == 0
42
+ end
43
+
44
+ def clear
45
+ # see: https://github.com/ripple/rippled/blob/b53fda1e1a7f4d09b766724274329df1c29988ab/src/ripple/protocol/STAmount.h#L224
46
+ @exponent = native? ? 0 : -100
47
+
48
+ @neg = false
49
+ @mantissa = 0
50
+ end
51
+
52
+ def negate!
53
+ return if zero?
54
+ @neg = !@neg
55
+ end
56
+
57
+ def sn_value
58
+ neg ? (-mantissa) : mantissa
59
+ end
60
+
61
+ ###
62
+
63
+ # In drops!
64
+ def xrp_amount
65
+ neg ? (-value) : value
66
+ end
67
+
68
+ alias :drops :xrp_amount
69
+
70
+ def iou_amount
71
+ (neg ? -1 : 1) * mantissa * 10 ** (exponent-97)
72
+ end
73
+
74
+ ###
75
+
76
+ def +(v)
77
+ e1 = exponent
78
+ e2 = v.exponent
79
+
80
+ m1 = mantissa
81
+ m2 = v.mantissa
82
+
83
+ m1 *= -1 if neg
84
+ m2 *= -1 if v.neg
85
+
86
+ while e1 < e2
87
+ m1 /= 10
88
+ e1 += 1
89
+ end
90
+
91
+ while e2 < e1
92
+ m2 /= 10
93
+ e2 += 1
94
+ end
95
+
96
+ m = m1 + m2
97
+ return STAmount.new :issue => issue if m >= -10 && m <= 10
98
+ return STAmount.new :mantissa => m,
99
+ :exponent => e1,
100
+ :issue => issue if m >= 0
101
+ return STAmount.new :mantissa => -m,
102
+ :exponent => e1,
103
+ :issue => issue
104
+ end
105
+
106
+ def -(v)
107
+ self + (-v)
108
+ end
109
+
110
+ def /(v)
111
+ if v.is_a?(Rate)
112
+ return self if v == Rate.parity
113
+ return self / v.to_amount
114
+ end
115
+
116
+ raise "divide by zero" if v.zero?
117
+ return STAmount.new :issue => issue
118
+
119
+ nm = mantissa
120
+ dm = v.mantissa
121
+
122
+ ne = exponent
123
+ de = v.exponent
124
+
125
+ if native?
126
+ while nm < MIN_VAL
127
+ nm *= 10
128
+ ne -= 1
129
+ end
130
+ end
131
+
132
+ if v.native?
133
+ while dm < MIN_VAL
134
+ dm *= 10
135
+ de -= 1
136
+ end
137
+ end
138
+
139
+ # see note: https://github.com/ripple/rippled/blob/b53fda1e1a7f4d09b766724274329df1c29988ab/src/ripple/protocol/impl/STAmount.cpp#L1075
140
+ STAmount.new :issue => issue,
141
+ :mantissa => (nm * 10**17)/dm,
142
+ :exponent => (ne - de - 17),
143
+ :neg => (neg != v.neg)
144
+ end
145
+
146
+ def *(o)
147
+ return STAmount.new :issue => issue if zero? || o.zero?
148
+
149
+ if native?
150
+ min = sn_value < o.sn_value ? sn_value : o.sn_value
151
+ max = sn_value < o.sn_value ? o.sn_value : sn_value
152
+
153
+ return STAmount.new :mantissa => min * max
154
+ end
155
+
156
+ m1 = mantissa
157
+ m2 = o.mantissa
158
+ e1 = exponent
159
+ e2 = o.exponent
160
+
161
+ if native?
162
+ while nm < MIN_VAL
163
+ m1 *= 10
164
+ e1 -= 1
165
+ end
166
+ end
167
+
168
+ if o.native?
169
+ while dm < MIN_VAL
170
+ m2 *= 10
171
+ e2 -= 1
172
+ end
173
+ end
174
+
175
+ # see note: https://github.com/ripple/rippled/blob/b53fda1e1a7f4d09b766724274329df1c29988ab/src/ripple/protocol/impl/STAmount.cpp#L1131
176
+ STAmount.new :issue => issue,
177
+ :mantissa => (m1 * m2)/(10**14),
178
+ :exponent => (e1 + e2 + 14),
179
+ :neg => (neg != o.neg)
180
+ end
181
+
182
+ def -@
183
+ STAmount.new(:mantissa => mantissa,
184
+ :exponent => exponent,
185
+ :issue => issue,
186
+ :neg => !neg)
187
+ end
188
+
189
+ def <(o)
190
+ return neg if neg && !o.neg
191
+ if mantissa == 0
192
+ return false if o.neg
193
+ return o.mantissa != 0
194
+ end
195
+
196
+ return false if o.mantissa == 0
197
+ return neg if exponent > o.exponent
198
+ return !neg if exponent < o.exponent
199
+ return neg if mantissa > o.mantissa
200
+ return !neg if mantissa < o.mantissa
201
+
202
+ return false
203
+ end
204
+
205
+ def >=(o)
206
+ !(self < o)
207
+ end
208
+
209
+ def >(o)
210
+ self >= o && self != o
211
+ end
212
+
213
+ def ==(o)
214
+ neg == o.neg &&
215
+ mantissa == o.mantissa &&
216
+ exponent == o.exponent
217
+ end
218
+
219
+ def <=>(o)
220
+ return 0 if self == o
221
+ return -1 if self < o
222
+ return 1 if self > o
223
+ end
224
+ end # class STAmount
225
+ end # module NodeStore
226
+ end # module XRBP