ruby-ethereum 0.9.0

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 (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +40 -0
  4. data/lib/ethereum.rb +53 -0
  5. data/lib/ethereum/abi.rb +333 -0
  6. data/lib/ethereum/abi/contract_translator.rb +174 -0
  7. data/lib/ethereum/abi/type.rb +117 -0
  8. data/lib/ethereum/account.rb +72 -0
  9. data/lib/ethereum/address.rb +60 -0
  10. data/lib/ethereum/base_convert.rb +53 -0
  11. data/lib/ethereum/block.rb +1311 -0
  12. data/lib/ethereum/block_header.rb +211 -0
  13. data/lib/ethereum/bloom.rb +83 -0
  14. data/lib/ethereum/cached_block.rb +36 -0
  15. data/lib/ethereum/chain.rb +400 -0
  16. data/lib/ethereum/constant.rb +26 -0
  17. data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
  18. data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
  19. data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
  20. data/lib/ethereum/core_ext/object/truth.rb +47 -0
  21. data/lib/ethereum/db.rb +6 -0
  22. data/lib/ethereum/db/base_db.rb +9 -0
  23. data/lib/ethereum/db/ephem_db.rb +63 -0
  24. data/lib/ethereum/db/overlay_db.rb +72 -0
  25. data/lib/ethereum/db/refcount_db.rb +188 -0
  26. data/lib/ethereum/env.rb +64 -0
  27. data/lib/ethereum/ethash.rb +78 -0
  28. data/lib/ethereum/ethash_ruby.rb +38 -0
  29. data/lib/ethereum/ethash_ruby/cache.rb +47 -0
  30. data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
  31. data/lib/ethereum/ethash_ruby/utils.rb +53 -0
  32. data/lib/ethereum/exceptions.rb +28 -0
  33. data/lib/ethereum/external_call.rb +173 -0
  34. data/lib/ethereum/fast_rlp.rb +81 -0
  35. data/lib/ethereum/fast_vm.rb +625 -0
  36. data/lib/ethereum/fast_vm/call_data.rb +44 -0
  37. data/lib/ethereum/fast_vm/message.rb +29 -0
  38. data/lib/ethereum/fast_vm/state.rb +25 -0
  39. data/lib/ethereum/ffi/openssl.rb +390 -0
  40. data/lib/ethereum/index.rb +97 -0
  41. data/lib/ethereum/log.rb +43 -0
  42. data/lib/ethereum/miner.rb +84 -0
  43. data/lib/ethereum/opcodes.rb +131 -0
  44. data/lib/ethereum/private_key.rb +92 -0
  45. data/lib/ethereum/pruning_trie.rb +28 -0
  46. data/lib/ethereum/public_key.rb +88 -0
  47. data/lib/ethereum/receipt.rb +53 -0
  48. data/lib/ethereum/secp256k1.rb +58 -0
  49. data/lib/ethereum/secure_trie.rb +49 -0
  50. data/lib/ethereum/sedes.rb +42 -0
  51. data/lib/ethereum/special_contract.rb +95 -0
  52. data/lib/ethereum/spv.rb +79 -0
  53. data/lib/ethereum/spv/proof.rb +31 -0
  54. data/lib/ethereum/spv/proof_constructor.rb +19 -0
  55. data/lib/ethereum/spv/proof_verifier.rb +24 -0
  56. data/lib/ethereum/tester.rb +14 -0
  57. data/lib/ethereum/tester/abi_contract.rb +65 -0
  58. data/lib/ethereum/tester/fixture.rb +31 -0
  59. data/lib/ethereum/tester/language.rb +30 -0
  60. data/lib/ethereum/tester/log_recorder.rb +13 -0
  61. data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
  62. data/lib/ethereum/tester/state.rb +194 -0
  63. data/lib/ethereum/transaction.rb +196 -0
  64. data/lib/ethereum/transient_trie.rb +28 -0
  65. data/lib/ethereum/trie.rb +549 -0
  66. data/lib/ethereum/trie/nibble_key.rb +184 -0
  67. data/lib/ethereum/utils.rb +191 -0
  68. data/lib/ethereum/version.rb +5 -0
  69. data/lib/ethereum/vm.rb +606 -0
  70. data/lib/ethereum/vm/call_data.rb +40 -0
  71. data/lib/ethereum/vm/message.rb +30 -0
  72. data/lib/ethereum/vm/state.rb +25 -0
  73. metadata +284 -0
@@ -0,0 +1,196 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+
5
+ ##
6
+ # A transaction is stored as:
7
+ #
8
+ # `[nonce, gasprice, startgas, to, value, data, v, r, s]`
9
+ #
10
+ # `nonce` is the number of transactions already sent by that account, encoded
11
+ # in binary form (eg. 0 -> "", 7 -> "\x07", 1000 -> "\x03\xd8").
12
+ #
13
+ # `(v,r,s)` is the raw Electrum-style signature of the transaction without
14
+ # the signature made with the private key corresponding to the sending
15
+ # account, with `0 <= v <= 3`. From an Electrum-style signature (65 bytes) it
16
+ # is possible to extract the public key, and thereby the address, directly.
17
+ #
18
+ # A valid transaction is one where:
19
+ #
20
+ # 1. the signature is well-formed (ie. `0 <= v <= 3, 0 <= r < P, 0 <= s < N, 0
21
+ # <= r < P - N if v >= 2`), and
22
+ # 2. the sending account has enough funds to pay the fee and the value.
23
+ #
24
+ class Transaction
25
+ include RLP::Sedes::Serializable
26
+
27
+ extend Sedes
28
+ set_serializable_fields(
29
+ nonce: big_endian_int,
30
+
31
+ gasprice: big_endian_int,
32
+ startgas: big_endian_int,
33
+
34
+ to: address,
35
+ value: big_endian_int,
36
+ data: binary,
37
+
38
+ v: big_endian_int,
39
+ r: big_endian_int,
40
+ s: big_endian_int
41
+ )
42
+
43
+ class <<self
44
+ ##
45
+ # A contract is a special transaction without the `to` argument.
46
+ #
47
+ def contract(nonce, gasprice, startgas, endowment, code, v=0, r=0, s=0)
48
+ new nonce, gasprice, startgas, '', endowment, code, v, r, s
49
+ end
50
+ end
51
+
52
+ def initialize(*args)
53
+ fields = {v: 0, r: 0, s: 0}.merge parse_field_args(args)
54
+ fields[:to] = Utils.normalize_address(fields[:to], allow_blank: true)
55
+
56
+ serializable_initialize fields
57
+
58
+ @sender = nil
59
+ @logs = []
60
+
61
+ raise InvalidTransaction, "Values way too high!" if [gasprice, startgas, value, nonce].max > Constant::UINT_MAX
62
+ raise InvalidTransaction, "Startgas too low" if startgas < intrinsic_gas_used
63
+
64
+ logger.debug "deserialized tx #{Utils.encode_hex(full_hash)[0,8]}"
65
+ end
66
+
67
+ def sender
68
+ unless @sender
69
+ if v && v > 0
70
+ raise InvalidTransaction, "Invalid signature values!" if r >= Secp256k1::N || s >= Secp256k1::N || v < 27 || v > 28 || r == 0 || s == 0
71
+
72
+ logger.debug "recovering sender"
73
+ rlpdata = RLP.encode(self, sedes: UnsignedTransaction)
74
+ rawhash = Utils.keccak256 rlpdata
75
+
76
+ pub = nil
77
+ begin
78
+ pub = Secp256k1.recover_pubkey rawhash, [v,r,s]
79
+ rescue
80
+ raise InvalidTransaction, "Invalid signature values (x^3+7 is non-residue)"
81
+ end
82
+
83
+ raise InvalidTransaction, "Invalid signature (zero privkey cannot sign)" if pub[1..-1] == Constant::PUBKEY_ZERO
84
+
85
+ @sender = PublicKey.new(pub).to_address
86
+ end
87
+ end
88
+
89
+ @sender
90
+ end
91
+
92
+ def sender=(v)
93
+ @sender = v
94
+ end
95
+
96
+ ##
97
+ # Sign this transaction with a private key.
98
+ #
99
+ # A potentially already existing signature would be override.
100
+ #
101
+ def sign(key)
102
+ raise InvalidTransaction, "Zero privkey cannot sign" if [0, '', Constant::PRIVKEY_ZERO, Constant::PRIVKEY_ZERO_HEX].include?(key)
103
+
104
+ rawhash = Utils.keccak256 RLP.encode(self, sedes: UnsignedTransaction)
105
+ key = PrivateKey.new(key).encode(:bin)
106
+
107
+ vrs = Secp256k1.recoverable_sign rawhash, key
108
+ self.v = vrs[0]
109
+ self.r = vrs[1]
110
+ self.s = vrs[2]
111
+
112
+ self.sender = PrivateKey.new(key).to_address
113
+
114
+ self
115
+ end
116
+
117
+ ##
118
+ # This method should be called for block numbers >=
119
+ # config[:homestead_fork_blknum] only. The >= operator is replaced by >
120
+ # because the integer division N/2 always produces the value which is by
121
+ # 0.5 less than the real N/2.
122
+ #
123
+ def check_low_s
124
+ raise InvalidTransaction, "Invalid signature S value!" if s > Secp256k1::N/2 || s == 0
125
+ end
126
+
127
+ def full_hash
128
+ Utils.keccak256_rlp self
129
+ end
130
+
131
+ def log_bloom
132
+ bloomables = @logs.map {|l| l.bloomables }
133
+ Bloom.from_array bloomables.flatten
134
+ end
135
+
136
+ def log_bloom_b256
137
+ Bloom.b256 log_bloom
138
+ end
139
+
140
+ def intrinsic_gas_used
141
+ num_zero_bytes = data.count(Constant::BYTE_ZERO)
142
+ num_non_zero_bytes = data.size - num_zero_bytes
143
+
144
+ Opcodes::GTXCOST +
145
+ Opcodes::GTXDATAZERO*num_zero_bytes +
146
+ Opcodes::GTXDATANONZERO*num_non_zero_bytes
147
+ end
148
+
149
+ def to_h
150
+ h = {}
151
+ self.class.serializable_fields.keys.each do |field|
152
+ h[field] = send field
153
+ end
154
+
155
+ h[:sender] = sender
156
+ h[:hash] = Utils.encode_hex full_hash
157
+ h
158
+ end
159
+
160
+ def log_dict
161
+ h = to_h
162
+ h[:sender] = Utils.encode_hex(h[:sender] || '')
163
+ h[:to] = Utils.encode_hex(h[:to])
164
+ h
165
+ end
166
+
167
+ ##
168
+ # returns the address of a contract created by this tx
169
+ #
170
+ def creates
171
+ Utils.mk_contract_address(sender, nonce) if [Address::BLANK, Address::ZERO].include?(to)
172
+ end
173
+
174
+ def ==(other)
175
+ other.instance_of?(self.class) && full_hash == other.full_hash
176
+ end
177
+
178
+ def hash
179
+ Utils.big_endian_to_int full_hash
180
+ end
181
+
182
+ def to_s
183
+ "#<#{self.class.name}:#{object_id} #{Utils.encode_hex(full_hash)[0,8]}"
184
+ end
185
+
186
+ private
187
+
188
+ def logger
189
+ @logger ||= Logger.new 'eth.chain.tx'
190
+ end
191
+
192
+ end
193
+
194
+ UnsignedTransaction = Transaction.exclude %i(v r s)
195
+
196
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ class TransientTrie < Trie
5
+
6
+ class InvalidTransientTrieOperation < StandardError; end
7
+
8
+ def transient_trie_exception(*args)
9
+ raise InvalidTransientTrieOperation
10
+ end
11
+
12
+ alias :[] :transient_trie_exception
13
+ alias :[]= :transient_trie_exception
14
+ alias :delete :transient_trie_exception
15
+
16
+ def root_hash
17
+ @transient_root_hash
18
+ end
19
+
20
+ def set_root_hash(hash)
21
+ raise TypeError, "root hash must be String" unless hash.instance_of?(String)
22
+ raise ArgumentError, "root hash must be 0 or 32 bytes long" unless [0,32].include?(hash.size)
23
+
24
+ @transient_root_hash = hash
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,549 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'ethereum/trie/nibble_key'
4
+
5
+ module Ethereum
6
+
7
+ ##
8
+ # A implementation of Merkle Patricia Tree.
9
+ #
10
+ # @see https://github.com/ethereum/wiki/wiki/Patricia-Tree
11
+ #
12
+ class Trie
13
+ include Enumerable
14
+
15
+ NODE_TYPES = %i(blank leaf extension branch).freeze
16
+ NODE_KV_TYPE = %i(leaf extension).freeze
17
+
18
+ BRANCH_CARDINAL = 16
19
+ BRANCH_WIDTH = BRANCH_CARDINAL + 1
20
+ KV_WIDTH = 2
21
+
22
+ BLANK_NODE = "".freeze
23
+ BLANK_ROOT = Utils.keccak256_rlp('').freeze
24
+
25
+ class InvalidNode < StandardError; end
26
+ class InvalidNodeType < StandardError; end
27
+
28
+ attr :db
29
+
30
+ ##
31
+ # It presents a hash like interface.
32
+ #
33
+ # @param db [Object] key value database
34
+ # @param root_hash [String] blank or trie node in form of [key, value] or
35
+ # [v0, v1, .. v15, v]
36
+ #
37
+ def initialize(db, root_hash=BLANK_ROOT)
38
+ @db = db
39
+ set_root_hash root_hash
40
+ end
41
+
42
+ ##
43
+ # @return empty or 32 bytes string
44
+ #
45
+ def root_hash
46
+ # TODO: can I memoize computation below?
47
+ return BLANK_ROOT if @root_node == BLANK_NODE
48
+
49
+ raise InvalidNode, "invalid root node" unless @root_node.instance_of?(Array)
50
+
51
+ val = FastRLP.encode @root_node
52
+ key = Utils.keccak256 val
53
+
54
+ @db.put key, val
55
+ SPV.grabbing @root_node
56
+
57
+ key
58
+ end
59
+ alias :update_root_hash :root_hash
60
+
61
+ def set_root_hash(hash)
62
+ raise TypeError, "root hash must be String" unless hash.instance_of?(String)
63
+ raise ArgumentError, "root hash must be 0 or 32 bytes long" unless [0,32].include?(hash.size)
64
+
65
+ if hash == BLANK_ROOT
66
+ @root_node = BLANK_NODE
67
+ else
68
+ @root_node = decode_to_node hash
69
+ end
70
+ end
71
+
72
+ def root_hash_valid?
73
+ h = root_hash
74
+ return true if h == BLANK_ROOT
75
+ return @db.include?(h)
76
+ end
77
+
78
+ ##
79
+ # Get value from trie.
80
+ #
81
+ # @param key [String]
82
+ #
83
+ # @return [String] BLANK_NODE if does not exist, otherwise node value
84
+ #
85
+ def [](key)
86
+ find @root_node, NibbleKey.from_string(key)
87
+ end
88
+ alias :get :[]
89
+
90
+ ##
91
+ # Set value of key.
92
+ #
93
+ # @param key [String]
94
+ # @param value [String]
95
+ #
96
+ def []=(key, value)
97
+ raise ArgumentError, "key must be string" unless key.instance_of?(String)
98
+ raise ArgumentError, "value must be string" unless value.instance_of?(String)
99
+
100
+ @root_node = update_and_delete_storage(
101
+ @root_node,
102
+ NibbleKey.from_string(key),
103
+ value
104
+ )
105
+
106
+ update_root_hash
107
+ end
108
+ alias :set :[]=
109
+
110
+ ##
111
+ # Delete value at key.
112
+ #
113
+ # @param key [String] a string with length of [0,32]
114
+ #
115
+ def delete(key)
116
+ raise ArgumentError, "key must be string" unless key.instance_of?(String)
117
+ raise ArgumentError, "max key size is 32" if key.size > 32
118
+
119
+ @root_node = delete_and_delete_storage(
120
+ @root_node,
121
+ NibbleKey.from_string(key)
122
+ )
123
+
124
+ update_root_hash
125
+ end
126
+
127
+ ##
128
+ # Convert to hash.
129
+ #
130
+ def to_h
131
+ h = {}
132
+ to_hash(@root_node).each do |k, v|
133
+ key = k.terminate(false).to_string
134
+ h[key] = v
135
+ end
136
+ h
137
+ end
138
+
139
+ def each(&block)
140
+ to_h.each(&block)
141
+ end
142
+
143
+ def has_key?(key)
144
+ self[key] != BLANK_NODE
145
+ end
146
+ alias :include? :has_key?
147
+
148
+ ##
149
+ # Get count of all nodes of the trie.
150
+ #
151
+ def size
152
+ get_size @root_node
153
+ end
154
+
155
+ ##
156
+ # clear all tree data
157
+ #
158
+ def clear
159
+ delete_child_storage(@root_node)
160
+ delete_node_storage(@root_node)
161
+ @root_node = BLANK_NODE
162
+ end
163
+
164
+ ##
165
+ # Get value inside a node.
166
+ #
167
+ # @param node [Array, BLANK_NODE] node in form of array, or BLANK_NODE
168
+ # @param nbk [Array] nibble array without terminator
169
+ #
170
+ # @return [String] BLANK_NODE if does not exist, otherwise node value
171
+ #
172
+ def find(node, nbk)
173
+ node_type = get_node_type node
174
+
175
+ case node_type
176
+ when :blank
177
+ BLANK_NODE
178
+ when :branch
179
+ return node.last if nbk.empty?
180
+
181
+ sub_node = decode_to_node node[nbk[0]]
182
+ find sub_node, nbk[1..-1]
183
+ when :leaf
184
+ node_key = NibbleKey.decode(node[0]).terminate(false)
185
+ nbk == node_key ? node[1] : BLANK_NODE
186
+ when :extension
187
+ node_key = NibbleKey.decode(node[0]).terminate(false)
188
+ if node_key.prefix?(nbk)
189
+ sub_node = decode_to_node node[1]
190
+ find sub_node, nbk[node_key.size..-1]
191
+ else
192
+ BLANK_NODE
193
+ end
194
+ else
195
+ raise InvalidNodeType, "node type must be in #{NODE_TYPES}, given: #{node_type}"
196
+ end
197
+ end
198
+
199
+ private
200
+
201
+ ##
202
+ # Get counts of (key, value) stored in this and the descendant nodes.
203
+ #
204
+ # TODO: refactor into Node class
205
+ #
206
+ # @param node [Array, BLANK_NODE] node in form of array, or BLANK_NODE
207
+ #
208
+ # @return [Integer]
209
+ #
210
+ def get_size(node)
211
+ case get_node_type(node)
212
+ when :branch
213
+ sizes = node[0,BRANCH_CARDINAL].map {|n| get_size decode_to_node(n) }
214
+ sizes.push(node.last.false? ? 0 : 1)
215
+ sizes.reduce(0, &:+)
216
+ when :extension
217
+ get_size decode_to_node(node[1])
218
+ when :leaf
219
+ 1
220
+ when :blank
221
+ 0
222
+ end
223
+ end
224
+
225
+ def encode_node(node)
226
+ return BLANK_NODE if node == BLANK_NODE
227
+ raise ArgumentError, "node must be an array" unless node.instance_of?(Array)
228
+
229
+ rlp_node = FastRLP.encode node
230
+ return node if rlp_node.size < 32
231
+
232
+ hashkey = Utils.keccak256 rlp_node
233
+ @db.put hashkey, rlp_node
234
+ SPV.store node
235
+
236
+ hashkey
237
+ end
238
+
239
+ def decode_to_node(encoded)
240
+ return BLANK_NODE if encoded == BLANK_NODE
241
+ return encoded if encoded.instance_of?(Array)
242
+
243
+ RLP.decode(@db.get(encoded))
244
+ .tap {|o| SPV.grabbing o }
245
+ end
246
+
247
+ # TODO: refactor, abstract delete storage logic
248
+ def update_and_delete_storage(node, key, value)
249
+ old_node = node.dup
250
+ new_node = update_node(node, key, value)
251
+ delete_node_storage(old_node) if old_node != new_node
252
+ new_node
253
+ end
254
+
255
+ def delete_and_delete_storage(node, key)
256
+ old_node = node.dup
257
+ new_node = delete_node(node, key)
258
+ delete_node_storage(old_node) if old_node != new_node
259
+ new_node
260
+ end
261
+
262
+ ##
263
+ # Update item inside a node.
264
+ #
265
+ # If this node is changed to a new node, it's parent will take the
266
+ # responsibility to **store** the new node storage, and delete the old node
267
+ # storage.
268
+ #
269
+ # @param node [Array, BLANK_NODE] node in form of array, or BLANK_NODE
270
+ # @param key [Array] nibble key without terminator
271
+ # @param value [String] value string
272
+ #
273
+ # @return [Array, BLANK_NODE] new node
274
+ #
275
+ def update_node(node, key, value)
276
+ node_type = get_node_type node
277
+
278
+ case node_type
279
+ when :blank
280
+ [key.terminate(true).encode, value]
281
+ when :branch
282
+ if key.empty?
283
+ node.last = value
284
+ else
285
+ new_node = update_and_delete_storage(
286
+ decode_to_node(node[key[0]]),
287
+ key[1..-1],
288
+ value
289
+ )
290
+ node[key[0]] = encode_node new_node
291
+ end
292
+
293
+ node
294
+ else # kv node type
295
+ update_kv_node(node, key, value)
296
+ end
297
+ end
298
+
299
+ # TODO: refactor this crazy tall guy
300
+ def update_kv_node(node, key, value)
301
+ node_type = get_node_type node
302
+ node_key = NibbleKey.decode(node[0]).terminate(false)
303
+ inner = node_type == :extension
304
+
305
+ common_key = node_key.common_prefix(key)
306
+ remain_key = key[common_key.size..-1]
307
+ remain_node_key = node_key[common_key.size..-1]
308
+
309
+ if remain_key.empty? && remain_node_key.empty? # target key equals node's key
310
+ if inner
311
+ new_node = update_and_delete_storage(
312
+ decode_to_node(node[1]),
313
+ remain_key,
314
+ value
315
+ )
316
+ else
317
+ return [node[0], value]
318
+ end
319
+ elsif remain_node_key.empty? # target key includes node's key
320
+ if inner
321
+ new_node = update_and_delete_storage(
322
+ decode_to_node(node[1]),
323
+ remain_key,
324
+ value
325
+ )
326
+ else # node is a leaf, we need to replace it with branch node first
327
+ new_node = [BLANK_NODE] * BRANCH_WIDTH
328
+ new_node[-1] = node[1]
329
+ new_node[remain_key[0]] = encode_node([
330
+ remain_key[1..-1].terminate(true).encode,
331
+ value
332
+ ])
333
+ end
334
+ else
335
+ new_node = [BLANK_NODE] * BRANCH_WIDTH
336
+
337
+ if remain_node_key.size == 1 && inner
338
+ new_node[remain_node_key[0]] = node[1]
339
+ else
340
+ new_node[remain_node_key[0]] = encode_node([
341
+ remain_node_key[1..-1].terminate(!inner).encode,
342
+ node[1]
343
+ ])
344
+ end
345
+
346
+ if remain_key.empty? # node's key include target key
347
+ new_node[-1] = value
348
+ else
349
+ new_node[remain_key[0]] = encode_node([
350
+ remain_key[1..-1].terminate(true).encode,
351
+ value
352
+ ])
353
+ end
354
+ end
355
+
356
+ if common_key.empty?
357
+ new_node
358
+ else
359
+ [node_key[0, common_key.size].encode, encode_node(new_node)]
360
+ end
361
+ end
362
+
363
+ ##
364
+ # Delete item inside node.
365
+ #
366
+ # If this node is changed to a new node, it's parent will take the
367
+ # responsibility to **store** the new node storage, and delete the old node
368
+ # storage.
369
+ #
370
+ # @param node [Array, BLANK_NODE] node in form of array, or BLANK_NODE
371
+ # @param key [Array] nibble key without terminator. key maybe empty
372
+ #
373
+ # @return new node
374
+ #
375
+ def delete_node(node, key)
376
+ case get_node_type(node)
377
+ when :blank
378
+ BLANK_NODE
379
+ when :branch
380
+ delete_branch_node(node, key)
381
+ else # kv type
382
+ delete_kv_node(node, key)
383
+ end
384
+ end
385
+
386
+ def delete_branch_node(node, key)
387
+ if key.empty?
388
+ node[-1] = BLANK_NODE
389
+ return normalize_branch_node(node)
390
+ else
391
+ new_sub_node = delete_and_delete_storage decode_to_node(node[key[0]]), key[1..-1]
392
+ encoded_new_sub_node = encode_node new_sub_node
393
+
394
+ return node if encoded_new_sub_node == node[key[0]]
395
+
396
+ node[key[0]] = encoded_new_sub_node
397
+ return normalize_branch_node(node) if encoded_new_sub_node == BLANK_NODE
398
+
399
+ node
400
+ end
401
+ end
402
+
403
+ def delete_kv_node(node, key)
404
+ node_type = get_node_type node
405
+ raise ArgumentError, "node type is not one of key-value type (#{NODE_KV_TYPE})" unless NODE_KV_TYPE.include?(node_type)
406
+
407
+ node_key = NibbleKey.decode(node[0]).terminate(false)
408
+
409
+ # key not found
410
+ return node unless node_key.prefix?(key)
411
+
412
+ if node_type == :leaf
413
+ key == node_key ? BLANK_NODE : node
414
+ else # :extension
415
+ new_sub_node = delete_and_delete_storage decode_to_node(node[1]), key[node_key.size..-1]
416
+
417
+ return node if encode_node(new_sub_node) == node[1]
418
+ return BLANK_NODE if new_sub_node == BLANK_NODE
419
+
420
+ raise InvalidNode, "new sub node must be array" unless new_sub_node.instance_of?(Array)
421
+
422
+ new_sub_node_type = get_node_type new_sub_node
423
+
424
+ case new_sub_node_type
425
+ when :branch
426
+ [node_key.encode, encode_node(new_sub_node)]
427
+ when *NODE_KV_TYPE
428
+ new_key = node_key + NibbleKey.decode(new_sub_node[0])
429
+ [new_key.encode, new_sub_node[1]]
430
+ else
431
+ raise InvalidNodeType, "invalid kv sub node type #{new_sub_node_type}"
432
+ end
433
+ end
434
+ end
435
+
436
+ def normalize_branch_node(node)
437
+ non_blank_items = node.each_with_index.select {|(x, i)| x != BLANK_NODE }
438
+
439
+ raise ArgumentError, "node must has at least 1 non blank item" unless non_blank_items.size > 0
440
+ return node if non_blank_items.size > 1
441
+
442
+ non_blank_index = non_blank_items[0][1]
443
+
444
+ # if value is the only non blank item, convert it into a kv node
445
+ return [NibbleKey.new([]).terminate(true).encode, node.last] if non_blank_index == NibbleKey::NIBBLE_TERMINATOR
446
+
447
+ sub_node = decode_to_node node[non_blank_index]
448
+ sub_node_type = get_node_type sub_node
449
+
450
+ case sub_node_type
451
+ when :branch
452
+ [NibbleKey.new([non_blank_index]).encode, encode_node(sub_node)]
453
+ when *NODE_KV_TYPE
454
+ new_key = NibbleKey.decode(sub_node[0]).unshift(non_blank_index)
455
+ [new_key.encode, sub_node[1]]
456
+ else
457
+ raise InvalidNodeType, "invalid branch sub node type #{sub_node_type}"
458
+ end
459
+ end
460
+
461
+ ##
462
+ # Delete node storage.
463
+ #
464
+ # @param node [Array, BLANK_NODE] node in form of array, or BLANK_NODE
465
+ #
466
+ def delete_node_storage(node)
467
+ return if node == BLANK_NODE
468
+ raise ArgumentError, "node must be Array or BLANK_NODE" unless node.instance_of?(Array)
469
+
470
+ encoded = encode_node node
471
+ return if encoded.size < 32
472
+
473
+ # FIXME: in current trie implementation two nodes can share identical
474
+ # subtree thus we can not safely delete nodes for now
475
+ #
476
+ # \@db.delete encoded
477
+ end
478
+
479
+ def delete_child_storage(node)
480
+ node_type = get_node_type node
481
+ case node_type
482
+ when :branch
483
+ node[0,BRANCH_CARDINAL].each {|item| delete_child_storage decode_to_node(item) }
484
+ when :extension
485
+ delete_child_storage decode_to_node(node[1])
486
+ else
487
+ # do nothing
488
+ end
489
+ end
490
+
491
+ ##
492
+ # get node type and content
493
+ #
494
+ # @param node [Array, BLANK_NODE] node in form of array, or BLANK_NODE
495
+ #
496
+ # @return [Symbol] node type
497
+ #
498
+ def get_node_type(node)
499
+ return :blank if node == BLANK_NODE
500
+
501
+ case node.size
502
+ when KV_WIDTH # [k,v]
503
+ NibbleKey.decode(node[0]).terminate? ? :leaf : :extension
504
+ when BRANCH_WIDTH # [k0, ... k15, v]
505
+ :branch
506
+ else
507
+ raise InvalidNode, "node size must be #{KV_WIDTH} or #{BRANCH_WIDTH}"
508
+ end
509
+ end
510
+
511
+ ##
512
+ # Convert [key, value] stored in this and the descendant nodes to hash.
513
+ #
514
+ # @param node [Array, BLANK_NODE] node in form of array, or BLANK_NODE
515
+ #
516
+ # @return [Hash] equivalent hash. Hash key is in full form (Array).
517
+ def to_hash(node)
518
+ node_type = get_node_type node
519
+
520
+ case node_type
521
+ when :blank
522
+ {}
523
+ when :branch
524
+ hash = {}
525
+
526
+ 16.times do |i|
527
+ sub_hash = to_hash decode_to_node(node[i])
528
+ sub_hash.each {|k, v| hash[NibbleKey.new([i]) + k] = v }
529
+ end
530
+
531
+ hash[NibbleKey.terminator] = node.last if node.last.true?
532
+ hash
533
+ when *NODE_KV_TYPE
534
+ nibbles = NibbleKey.decode(node[0]).terminate(false)
535
+
536
+ sub_hash = node_type == :extension ?
537
+ to_hash(decode_to_node(node[1])) : {NibbleKey.terminator => node[1]}
538
+
539
+ {}.tap do |hash|
540
+ sub_hash.each {|k, v| hash[nibbles + k] = v }
541
+ end
542
+ else
543
+ # do nothing
544
+ end
545
+ end
546
+
547
+ end
548
+
549
+ end