ruby-ethereum 0.9.0

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