xrbp 0.2.1 → 0.2.2

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