dcha 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +47 -0
- data/README.md +35 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/dcha.gemspec +29 -0
- data/exe/dcha +10 -0
- data/lib/dcha.rb +24 -0
- data/lib/dcha/block.rb +36 -0
- data/lib/dcha/chain.rb +49 -0
- data/lib/dcha/chunk.rb +43 -0
- data/lib/dcha/config.rb +24 -0
- data/lib/dcha/mpt.rb +8 -0
- data/lib/dcha/mpt/nibble_key.rb +110 -0
- data/lib/dcha/mpt/node.rb +64 -0
- data/lib/dcha/mpt/node/deletable.rb +83 -0
- data/lib/dcha/mpt/node/editable.rb +135 -0
- data/lib/dcha/mpt/node/findable.rb +37 -0
- data/lib/dcha/mpt/node/to_hashable.rb +42 -0
- data/lib/dcha/mpt/trie.rb +73 -0
- data/lib/dcha/packet_manager.rb +28 -0
- data/lib/dcha/peer.rb +90 -0
- data/lib/dcha/peer/can_heartbeat.rb +17 -0
- data/lib/dcha/peer/has_blockchain.rb +59 -0
- data/lib/dcha/peer/has_trie.rb +54 -0
- data/lib/dcha/peer/remote_executable.rb +25 -0
- data/lib/dcha/store.rb +9 -0
- data/lib/dcha/store/file.rb +37 -0
- data/lib/dcha/store/memory.rb +12 -0
- data/lib/dcha/ui.rb +84 -0
- data/lib/dcha/ui/window.rb +52 -0
- data/lib/dcha/version.rb +3 -0
- metadata +164 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
module Dcha
|
2
|
+
module MPT
|
3
|
+
# :nodoc:
|
4
|
+
class Node < Array
|
5
|
+
autoload :Findable, 'dcha/mpt/node/findable'
|
6
|
+
autoload :Editable, 'dcha/mpt/node/editable'
|
7
|
+
autoload :Deletable, 'dcha/mpt/node/deletable'
|
8
|
+
autoload :ToHashable, 'dcha/mpt/node/to_hashable'
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def decode(encoded)
|
12
|
+
return BLANK if encoded == BLANK.first
|
13
|
+
return encoded if encoded.is_a?(Node)
|
14
|
+
return new encoded if encoded.is_a?(Array)
|
15
|
+
new RLP.decode(Config.store[encoded])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
BLANK = new(['']).freeze
|
20
|
+
|
21
|
+
BRANCH_CARDINAL = 16
|
22
|
+
BRANCH_WIDTH = BRANCH_CARDINAL + 1
|
23
|
+
KV_WIDTH = 2
|
24
|
+
|
25
|
+
include Node::Findable
|
26
|
+
include Node::Editable
|
27
|
+
include Node::Deletable
|
28
|
+
include Node::ToHashable
|
29
|
+
|
30
|
+
def type
|
31
|
+
return :blank if size == 1 && first.empty?
|
32
|
+
|
33
|
+
case size
|
34
|
+
when KV_WIDTH
|
35
|
+
NibbleKey.decode(first).terminate? ? :leaf : :extension
|
36
|
+
when BRANCH_WIDTH
|
37
|
+
:branch
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def tree_size
|
42
|
+
case type
|
43
|
+
when :branch
|
44
|
+
children = take(BRANCH_CARDINAL).map(&:tree_size)
|
45
|
+
children.push(last != BLANK ? 0 : 1)
|
46
|
+
size.reduce(0, &:+)
|
47
|
+
when :extension then Node.decode(self[1]).tree_size
|
48
|
+
when :leaf then 1
|
49
|
+
when :blank then 0
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def save(force = false)
|
54
|
+
value = RLP.encode self
|
55
|
+
return self if value.size < 32 && !force
|
56
|
+
key = Config.digest.hexdigest(value)
|
57
|
+
|
58
|
+
Config.store[key] = value
|
59
|
+
|
60
|
+
key
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Dcha
|
2
|
+
module MPT
|
3
|
+
class Node < Array
|
4
|
+
# :nodoc:
|
5
|
+
module Deletable
|
6
|
+
def delete(key)
|
7
|
+
case type
|
8
|
+
when :blank then Node::BLANK
|
9
|
+
when :branch then delete_branch_node(key)
|
10
|
+
else
|
11
|
+
delete_kv_node(key)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def delete_branch_node(key)
|
18
|
+
if key.empty?
|
19
|
+
self[-1] = Node::BLANK
|
20
|
+
return normalize
|
21
|
+
end
|
22
|
+
|
23
|
+
new_sub_node = Node.decode(self[key[0]]).delete(key[1..-1])
|
24
|
+
encoded_new_sub_node = new_sub_node.save
|
25
|
+
return self if encoded_new_sub_node == self[key[0]]
|
26
|
+
|
27
|
+
self[key[0]] = encoded_new_sub_node
|
28
|
+
return normalize if encoded_new_sub_node == Node::BLANK
|
29
|
+
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete_kv_node(key)
|
34
|
+
node_key = NibbleKey.decode(first).terminate(false)
|
35
|
+
return self unless node_key.prefix?(key)
|
36
|
+
return key == node_key ? Node::BLANK : self if type == :leaf
|
37
|
+
|
38
|
+
new_sub_node = Node.decode(self[1]).delete(key[node_key.size..-1])
|
39
|
+
|
40
|
+
replace_with_new_sub_node(node_key, new_sub_node)
|
41
|
+
end
|
42
|
+
|
43
|
+
def replace_with_new_sub_node(node_key, new_sub_node)
|
44
|
+
return self if new_sub_node.save == self[1]
|
45
|
+
return Node::BLANK if new_sub_node == Node::BLANK
|
46
|
+
|
47
|
+
case new_sub_node.type
|
48
|
+
when :branch
|
49
|
+
Node.new([node_key.encode, new_sub_node.save])
|
50
|
+
when :leaf, :extension
|
51
|
+
new_key = node_key + NibbleKey.decode(new_sub_node[0])
|
52
|
+
Node.new([new_key.encode, new_sub_node[1]])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def normalize
|
57
|
+
return self if non_blank_items.size > 1
|
58
|
+
|
59
|
+
non_blank_index = non_blank_items[0][1]
|
60
|
+
new_node = Node.new([NibbleKey.new([]).terminate(true).encode, last])
|
61
|
+
return new_node if non_blank_index == NibbleKey::TERMINATOR
|
62
|
+
|
63
|
+
replace_with_sub_node(non_blank_index)
|
64
|
+
end
|
65
|
+
|
66
|
+
def replace_with_sub_node(non_blank_index)
|
67
|
+
sub_node = Node.decode(self[non_blank_index])
|
68
|
+
case sub_node.type
|
69
|
+
when :branch
|
70
|
+
Node.new([NibbleKey.new([non_blank_index]).encode, sub_node.save])
|
71
|
+
when :leaf, :extension
|
72
|
+
new_key = NibbleKey.decode(sub_node[0]).unshift(non_blank_index)
|
73
|
+
Node.new(new_key.encode, sub_node[1])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def non_blank_items
|
78
|
+
each_with_index.reject { |(x, _)| x == Node::BLANK }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# rubocop:disable Metrics/LineLength
|
2
|
+
module Dcha
|
3
|
+
module MPT
|
4
|
+
class Node < Array
|
5
|
+
# :nodoc:
|
6
|
+
module Editable
|
7
|
+
def update(key, value)
|
8
|
+
case type
|
9
|
+
when :blank then
|
10
|
+
dup.clear.push key.terminate(true).encode, value
|
11
|
+
when :branch then update_branch(key, value)
|
12
|
+
when :leaf then update_leaf(key, value)
|
13
|
+
else update_extension(key, value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def update_branch(key, value)
|
20
|
+
return dup.tap { |copy| copy[-1] = value } if key.empty?
|
21
|
+
|
22
|
+
new_node = Node.decode(self[key[0]])
|
23
|
+
.update(key[1..-1], value)
|
24
|
+
dup.tap do |copy|
|
25
|
+
copy[key[0]] = new_node
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_leaf(key, value)
|
30
|
+
node_key, common_key, remain_key, remain_node_key = common_key_pairs(key)
|
31
|
+
|
32
|
+
no_remain_key = remain_key.empty? && remain_node_key.empty?
|
33
|
+
return dup.clear.push first, value if no_remain_key
|
34
|
+
|
35
|
+
new_node = if remain_node_key.empty?
|
36
|
+
convert_leaf_to_branch_node(remain_key, value)
|
37
|
+
else
|
38
|
+
expand_leaf_to_branch_node(remain_key, remain_node_key, value)
|
39
|
+
end
|
40
|
+
|
41
|
+
apply_node(common_key, node_key, new_node)
|
42
|
+
end
|
43
|
+
|
44
|
+
def apply_node(common_key, node_key, new_node)
|
45
|
+
if common_key.empty?
|
46
|
+
dup.clear.concat new_node
|
47
|
+
else
|
48
|
+
dup.clear.push node_key[0, common_key.size].encode,
|
49
|
+
Node.new(new_node).save
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def convert_leaf_to_branch_node(key, value)
|
54
|
+
new_node = Node::BLANK * Node::BRANCH_WIDTH
|
55
|
+
new_node[-1] = self[1]
|
56
|
+
|
57
|
+
new_node[key[0]] = Node.new(
|
58
|
+
[
|
59
|
+
key[1..-1].terminate(true).encode,
|
60
|
+
value
|
61
|
+
]
|
62
|
+
).save
|
63
|
+
new_node
|
64
|
+
end
|
65
|
+
|
66
|
+
def expand_leaf_to_branch_node(key, node_key, value)
|
67
|
+
new_node = convert_leaf_to_branch_node(node_key, self[1])
|
68
|
+
|
69
|
+
if key.empty?
|
70
|
+
new_node[-1] = value
|
71
|
+
else
|
72
|
+
new_node[key[0]] = Node.new(
|
73
|
+
[key[1..-1].terminate(true).encode, value]
|
74
|
+
).save
|
75
|
+
end
|
76
|
+
|
77
|
+
new_node
|
78
|
+
end
|
79
|
+
|
80
|
+
def update_extension(key, value)
|
81
|
+
node_key, common_key, remain_key, remain_node_key = common_key_pairs(key)
|
82
|
+
|
83
|
+
new_node = if remain_node_key.empty?
|
84
|
+
Node.decode(self[1]).update(remain_key, value)
|
85
|
+
else
|
86
|
+
expand_extension_to_branch_node(
|
87
|
+
remain_key, remain_node_key, value
|
88
|
+
)
|
89
|
+
end
|
90
|
+
apply_node(common_key, node_key, new_node)
|
91
|
+
end
|
92
|
+
|
93
|
+
def convert_extension_to_branch_node(key, value)
|
94
|
+
new_node = Node::BLANK * Node::BRANCH_WIDTH
|
95
|
+
|
96
|
+
if key.size == 1
|
97
|
+
new_node[key[0]] = value
|
98
|
+
return new_node
|
99
|
+
end
|
100
|
+
|
101
|
+
new_node[key[0]] = Node.new(
|
102
|
+
[key[1..-1].terminate(false).encode, value]
|
103
|
+
).save
|
104
|
+
|
105
|
+
new_node
|
106
|
+
end
|
107
|
+
|
108
|
+
def expand_extension_to_branch_node(key, node_key, value)
|
109
|
+
new_node = convert_extension_to_branch_node(node_key, self[1])
|
110
|
+
|
111
|
+
if key.empty?
|
112
|
+
new_node[-1] = value
|
113
|
+
return new_node
|
114
|
+
end
|
115
|
+
|
116
|
+
new_node[key[0]] = Node.new(
|
117
|
+
[key[1..-1].terminate(true).encode, value]
|
118
|
+
).save
|
119
|
+
|
120
|
+
new_node
|
121
|
+
end
|
122
|
+
|
123
|
+
def common_key_pairs(key)
|
124
|
+
node_key = NibbleKey.decode(first).terminate(false)
|
125
|
+
|
126
|
+
common = node_key.common_prefix(key)
|
127
|
+
remain = key[common.size..-1]
|
128
|
+
remain_node = node_key[common.size..-1]
|
129
|
+
|
130
|
+
[node_key, common, remain, remain_node]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Dcha
|
2
|
+
module MPT
|
3
|
+
class Node < Array
|
4
|
+
# :nodoc:
|
5
|
+
module Findable
|
6
|
+
def find(nbk)
|
7
|
+
case type
|
8
|
+
when :blank then Node::BLANK.first
|
9
|
+
when :branch then find_branch(nbk)
|
10
|
+
when :leaf then find_leaf(nbk)
|
11
|
+
when :extension then find_extension(nbk)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def find_branch(nbk)
|
18
|
+
return last if nbk.empty?
|
19
|
+
node = Node.decode(self[nbk[0]])
|
20
|
+
node.find(nbk[1..-1])
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_leaf(nbk)
|
24
|
+
key = NibbleKey.decode(first).terminate(false)
|
25
|
+
nbk == key ? self[1] : Node::BLANK.first
|
26
|
+
end
|
27
|
+
|
28
|
+
def find_extension(nbk)
|
29
|
+
key = NibbleKey.decode(first).terminate(false)
|
30
|
+
return Node::BLANK.first unless key.prefix?(nbk)
|
31
|
+
node = Node.decode(self[1])
|
32
|
+
node.find nbk[key.size..-1]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Dcha
|
2
|
+
module MPT
|
3
|
+
class Node < Array
|
4
|
+
# :nodoc:
|
5
|
+
module ToHashable
|
6
|
+
def to_h
|
7
|
+
case type
|
8
|
+
when :blank then {}
|
9
|
+
when :branch then branch_to_h
|
10
|
+
when :leaf, :extension
|
11
|
+
kv_to_h
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def branch_to_h
|
18
|
+
hash = {}
|
19
|
+
16.times do |i|
|
20
|
+
sub_hash = Node.decode(self[i]).to_h
|
21
|
+
sub_hash.each { |k, v| hash[NibbleKey.new([i]) + k] = v }
|
22
|
+
end
|
23
|
+
hash[NibbleKey.terminator] = last if last
|
24
|
+
hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def kv_to_h
|
28
|
+
nibbles = NibbleKey.decode(first).terminate(false)
|
29
|
+
sub_hash = if type == :extension
|
30
|
+
Node.decode(self[1]).to_h
|
31
|
+
else
|
32
|
+
{ NibbleKey.terminator => self[1] }
|
33
|
+
end
|
34
|
+
|
35
|
+
{}.tap do |h|
|
36
|
+
sub_hash.each { |k, v| h[nibbles + k] = v }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Dcha
|
2
|
+
module MPT
|
3
|
+
# :nodoc:
|
4
|
+
class Trie
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(hash = nil)
|
8
|
+
@root = if hash.nil?
|
9
|
+
Node::BLANK
|
10
|
+
else
|
11
|
+
Node.decode(hash)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def root_hash
|
16
|
+
return blank_root if @root == Node::BLANK
|
17
|
+
@root.save(true)
|
18
|
+
end
|
19
|
+
alias update_root_hash root_hash
|
20
|
+
|
21
|
+
def get(key)
|
22
|
+
@root.find NibbleKey.from_string(key)
|
23
|
+
end
|
24
|
+
alias [] get
|
25
|
+
|
26
|
+
def set(key, value)
|
27
|
+
@root = @root.update(
|
28
|
+
NibbleKey.from_string(key),
|
29
|
+
value
|
30
|
+
)
|
31
|
+
|
32
|
+
update_root_hash
|
33
|
+
end
|
34
|
+
alias []= set
|
35
|
+
|
36
|
+
def delete(key)
|
37
|
+
@root = @root.delete(NibbleKey.from_string(key))
|
38
|
+
|
39
|
+
update_root_hash
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_h
|
43
|
+
Hash[
|
44
|
+
@root.to_h.map do |key, value|
|
45
|
+
[
|
46
|
+
key.terminate(false).to_s,
|
47
|
+
value
|
48
|
+
]
|
49
|
+
end
|
50
|
+
]
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(&block)
|
54
|
+
to_h.each(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def key?(key)
|
58
|
+
self[key] != Node::BLANK.first
|
59
|
+
end
|
60
|
+
alias include? key?
|
61
|
+
|
62
|
+
def size
|
63
|
+
@root.tree_size
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def blank_root
|
69
|
+
@blank_root ||= Config.digest.hexdigest(RLP.encode('')).freeze
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Dcha
|
2
|
+
# :nodoc:
|
3
|
+
class PacketManager < Hash
|
4
|
+
attr_reader :todo
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@todo = Queue.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def <<(chunk)
|
11
|
+
self[chunk.tag] ||= []
|
12
|
+
self[chunk.tag] << chunk
|
13
|
+
self[chunk.tag].uniq!
|
14
|
+
|
15
|
+
check(chunk)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def check(chunk)
|
21
|
+
return unless self[chunk.tag].size == chunk.total
|
22
|
+
event = Oj.load(self[chunk.tag].sort.map(&:to_s).join)
|
23
|
+
@todo << event
|
24
|
+
# TODO: Release memory if buffer not completed
|
25
|
+
delete(chunk.tag)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|