dcha 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.1
5
+ before_install: gem install bundler -v 1.16.0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in dcha.gemspec
6
+ gemspec
7
+
8
+ gem 'pry'
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
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
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
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'dcha'
4
+
5
+ Thread.abort_on_exception = true
6
+
7
+ peer = Dcha::Peer.new
8
+ peer.join
9
+
10
+ Dcha::UI.new(peer).show
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
@@ -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,8 @@
1
+ module Dcha
2
+ # Merkle Particia Tree
3
+ module MPT
4
+ autoload :Trie, 'dcha/mpt/trie'
5
+ autoload :Node, 'dcha/mpt/node'
6
+ autoload :NibbleKey, 'dcha/mpt/nibble_key'
7
+ end
8
+ end
@@ -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