dcha 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 314de64cafe77939bedb982c40f3ee3e5eab7885
|
4
|
+
data.tar.gz: 55ef4a1efa2a294955a436b76f8d586784312e1f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 001c015d804aa09c5e5477bf6b080ffaf6952f883b291e51771949d11e16a25246d3a93226afacd696240e26f1e340349d79a344ed53d49180d94b6923479bf7
|
7
|
+
data.tar.gz: 7d271e47ec85bb8896b3cd53a44ce630a38de5d0763263d2df9157fbbcbe32884899f3be271da634711a9606a819b42be29a8756b204a05e6d8a44122cd764ce
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
dcha (0.1.0)
|
5
|
+
curses
|
6
|
+
oj
|
7
|
+
rlp
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
coderay (1.1.2)
|
13
|
+
curses (1.2.4)
|
14
|
+
diff-lcs (1.3)
|
15
|
+
method_source (0.9.0)
|
16
|
+
oj (3.3.10)
|
17
|
+
pry (0.11.3)
|
18
|
+
coderay (~> 1.1.0)
|
19
|
+
method_source (~> 0.9.0)
|
20
|
+
rake (10.5.0)
|
21
|
+
rlp (0.7.3)
|
22
|
+
rspec (3.7.0)
|
23
|
+
rspec-core (~> 3.7.0)
|
24
|
+
rspec-expectations (~> 3.7.0)
|
25
|
+
rspec-mocks (~> 3.7.0)
|
26
|
+
rspec-core (3.7.0)
|
27
|
+
rspec-support (~> 3.7.0)
|
28
|
+
rspec-expectations (3.7.0)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.7.0)
|
31
|
+
rspec-mocks (3.7.0)
|
32
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
33
|
+
rspec-support (~> 3.7.0)
|
34
|
+
rspec-support (3.7.0)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
bundler (~> 1.16)
|
41
|
+
dcha!
|
42
|
+
pry
|
43
|
+
rake (~> 10.0)
|
44
|
+
rspec (~> 3.0)
|
45
|
+
|
46
|
+
BUNDLED WITH
|
47
|
+
1.16.0
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Dcha
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/dcha`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'dcha'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install dcha
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/dcha.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'dcha'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require 'pry'
|
11
|
+
Pry.start
|
12
|
+
|
13
|
+
# require 'irb'
|
14
|
+
# IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/dcha.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'dcha/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'dcha'
|
7
|
+
spec.version = Dcha::VERSION
|
8
|
+
spec.authors = ['蒼時弦也']
|
9
|
+
spec.email = ['elct9620@frost.tw']
|
10
|
+
|
11
|
+
spec.summary = 'The decenterialize chat powered by blockchain'
|
12
|
+
spec.description = 'The decenterialize chat powered by blockchain'
|
13
|
+
spec.homepage = 'https://github.com/elct9620/dcha'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
|
+
f.match(%r{^(test|spec|features)/})
|
17
|
+
end
|
18
|
+
spec.bindir = 'exe'
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_runtime_dependency 'oj'
|
23
|
+
spec.add_runtime_dependency 'rlp'
|
24
|
+
spec.add_runtime_dependency 'curses'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
end
|
data/exe/dcha
ADDED
data/lib/dcha.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'oj'
|
2
|
+
require 'rlp'
|
3
|
+
require 'digest'
|
4
|
+
require 'socket'
|
5
|
+
require 'ipaddr'
|
6
|
+
require 'singleton'
|
7
|
+
require 'curses'
|
8
|
+
require 'observer'
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
require 'dcha/version'
|
12
|
+
|
13
|
+
# :nodoc:
|
14
|
+
module Dcha
|
15
|
+
autoload :Config, 'dcha/config'
|
16
|
+
autoload :MPT, 'dcha/mpt'
|
17
|
+
autoload :UI, 'dcha/ui'
|
18
|
+
autoload :Peer, 'dcha/peer'
|
19
|
+
autoload :Chunk, 'dcha/chunk'
|
20
|
+
autoload :PacketManager, 'dcha/packet_manager'
|
21
|
+
autoload :Block, 'dcha/block'
|
22
|
+
autoload :Chain, 'dcha/chain'
|
23
|
+
autoload :Store, 'dcha/store'
|
24
|
+
end
|
data/lib/dcha/block.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Dcha
|
2
|
+
# :nodoc:
|
3
|
+
class Block
|
4
|
+
attr_accessor :index, :hash, :root_hash, :parent_hash, :time, :proof
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@time = Time.now.to_i
|
8
|
+
@proof = ''
|
9
|
+
options.each { |k, v| send("#{k}=", v) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid_after?(previous_block)
|
13
|
+
(previous_block.hash == parent_hash) &&
|
14
|
+
(hash == Digest::SHA256.hexdigest(body.join)) &&
|
15
|
+
valid_proof?
|
16
|
+
end
|
17
|
+
|
18
|
+
def body
|
19
|
+
[index, time, root_hash, parent_hash]
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid_proof?
|
23
|
+
Digest::SHA256.hexdigest((body + [proof]).join).start_with?('abc')
|
24
|
+
end
|
25
|
+
|
26
|
+
def make_proof
|
27
|
+
@hash = Digest::SHA256.hexdigest(body.join)
|
28
|
+
letters = ('a'..'z').to_a
|
29
|
+
@proof << letters.sample until valid_proof?
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# rubocop:disable Metrics/LineLength
|
34
|
+
GENESIS = Block.new(index: 0, parent_hash: 0, time: 0, root_hash: "\0", proof: 'lvew')
|
35
|
+
end
|
36
|
+
end
|
data/lib/dcha/chain.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Dcha
|
2
|
+
# :nodoc:
|
3
|
+
class Chain
|
4
|
+
attr_reader :blocks
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@blocks = [Block::GENESIS]
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_block(block)
|
11
|
+
blocks << block if block.valid_after?(blocks.last)
|
12
|
+
|
13
|
+
unless valid_chain?
|
14
|
+
@blocks = @blocks[0..-2]
|
15
|
+
return false
|
16
|
+
end
|
17
|
+
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_and_add_block(root_hash)
|
22
|
+
add_block(Block.new(
|
23
|
+
index: blocks.last.index + 1,
|
24
|
+
parent_hash: blocks.last.hash,
|
25
|
+
root_hash: root_hash
|
26
|
+
).make_proof)
|
27
|
+
end
|
28
|
+
|
29
|
+
def replace_with(new_blocks)
|
30
|
+
new_blocks = [Block::GENESIS] + new_blocks
|
31
|
+
return unless valid_chain?(new_blocks) &&
|
32
|
+
new_blocks.length > blocks.length
|
33
|
+
@blocks = new_blocks
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def valid_chain?(blocks = @blocks)
|
38
|
+
valid = true
|
39
|
+
blocks.each.with_index(0) do |block, idx|
|
40
|
+
valid &&= if idx.zero?
|
41
|
+
Oj.dump(block) == Oj.dump(Block::GENESIS)
|
42
|
+
else
|
43
|
+
block.valid_after?(blocks[idx - 1])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
valid
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/dcha/chunk.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Dcha
|
2
|
+
# :nodoc:
|
3
|
+
class Chunk < Array
|
4
|
+
SIZE = 128
|
5
|
+
TAG_SIZE = 40 # SHA1
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def create(data)
|
9
|
+
split(data).map { |bytes| new bytes }
|
10
|
+
end
|
11
|
+
|
12
|
+
def split(data)
|
13
|
+
buffer = Oj.dump(data)
|
14
|
+
tag = Digest::SHA1.hexdigest(buffer).unpack('C*')
|
15
|
+
slices = buffer.unpack('C*').each_slice(SIZE)
|
16
|
+
size = slices.size
|
17
|
+
slices.map.with_index do |bytes, index|
|
18
|
+
[tag, index, size, bytes].flatten
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
include Comparable
|
24
|
+
|
25
|
+
attr_reader :tag, :index, :total
|
26
|
+
|
27
|
+
def initialize(bytes)
|
28
|
+
super
|
29
|
+
@tag = bytes.shift(TAG_SIZE).pack('C*')
|
30
|
+
@index = bytes.shift
|
31
|
+
@total = bytes.shift
|
32
|
+
@buffer = bytes.pack('C*')
|
33
|
+
end
|
34
|
+
|
35
|
+
def <=>(other)
|
36
|
+
index <=> other.index
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
@buffer
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/dcha/config.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Dcha
|
2
|
+
# :nodoc:
|
3
|
+
class Config
|
4
|
+
class << self
|
5
|
+
def respond_to_missing?(name)
|
6
|
+
instance.respond_to_missing?(name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(name, *args, &block)
|
10
|
+
return instance.send(name, *args, &block) if instance.respond_to?(name)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
include Singleton
|
16
|
+
|
17
|
+
attr_accessor :digest, :store
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@digest = Digest::SHA256
|
21
|
+
@store = Store::Memory.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/dcha/mpt.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
module Dcha
|
2
|
+
module MPT
|
3
|
+
# :nodoc:
|
4
|
+
class NibbleKey < Array
|
5
|
+
TERM_FLAG = 0b0010
|
6
|
+
ODD_FLAG = 0b0001
|
7
|
+
TERMINATOR = 16
|
8
|
+
|
9
|
+
HEX_VALUES = Hash[(0..15).map { |i| [i.to_s(16), i] }]
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def encode(nibbles)
|
13
|
+
flags = 0
|
14
|
+
|
15
|
+
if nibbles.last == TERMINATOR
|
16
|
+
flags |= TERM_FLAG
|
17
|
+
nibbles = nibbles[0...-1]
|
18
|
+
end
|
19
|
+
|
20
|
+
odd = nibbles.size % 2
|
21
|
+
flags |= odd
|
22
|
+
return pack [flags] + nibbles if odd == 1
|
23
|
+
pack [flags, 0b0000] + nibbles
|
24
|
+
end
|
25
|
+
|
26
|
+
def decode(bytes)
|
27
|
+
o = from_string bytes
|
28
|
+
flags = o[0]
|
29
|
+
|
30
|
+
o.push TERMINATOR if flags & TERM_FLAG > 0
|
31
|
+
fill = flags & ODD_FLAG > 0 ? 1 : 2
|
32
|
+
new o[fill..-1]
|
33
|
+
end
|
34
|
+
|
35
|
+
def from_string(s)
|
36
|
+
nibbles = s.unpack('H*')
|
37
|
+
.first
|
38
|
+
.each_char
|
39
|
+
.map { |c| HEX_VALUES[c] }
|
40
|
+
new nibbles
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_string(nibbles)
|
44
|
+
# TODO: Add error message
|
45
|
+
raise ArgumentError unless nibbles.any? { |x| x.between?(0, 15) }
|
46
|
+
raise ArgumentError if nibbles.size.odd?
|
47
|
+
|
48
|
+
pack nibbles
|
49
|
+
end
|
50
|
+
|
51
|
+
def terminator
|
52
|
+
new [TERMINATOR]
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def pack(nibbles)
|
58
|
+
size = nibbles.size / 2
|
59
|
+
(0...size).map do |i|
|
60
|
+
base = i * 2
|
61
|
+
(16 * nibbles[base] + nibbles[base + 1]).chr
|
62
|
+
end.join
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def terminate?
|
67
|
+
last == TERMINATOR
|
68
|
+
end
|
69
|
+
|
70
|
+
def terminate(flag)
|
71
|
+
dup.tap do |copy|
|
72
|
+
if flag
|
73
|
+
copy.push TERMINATOR unless copy.terminate?
|
74
|
+
elsif copy.terminate?
|
75
|
+
copy.pop
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def prefix?(another_key)
|
81
|
+
return false if another_key.size < size
|
82
|
+
another_key.take(size) == self
|
83
|
+
end
|
84
|
+
|
85
|
+
def common_prefix(another_key)
|
86
|
+
prefix = []
|
87
|
+
|
88
|
+
[size, another_key.size].min.times do |i|
|
89
|
+
break if self[i] != another_key[i]
|
90
|
+
prefix.push self[i]
|
91
|
+
end
|
92
|
+
|
93
|
+
self.class.new prefix
|
94
|
+
end
|
95
|
+
|
96
|
+
def encode
|
97
|
+
self.class.encode self
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_s
|
101
|
+
self.class.to_string self
|
102
|
+
end
|
103
|
+
alias to_string to_s
|
104
|
+
|
105
|
+
def +(other)
|
106
|
+
self.class.new super(other)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|