bitcourier 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NThjZTExNWE0MzhkMzhiM2EyYTE3OTIwMzJiYmZhOGJlMGIyM2JlNA==
5
+ data.tar.gz: !binary |-
6
+ MDlmYTFiM2YzNDg4Mjk2MWFiNGFiZDYxNDgxYTgxZWNmNjM1ZDAyOQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ Mzg0NzdkNWJjZDQ2MTZmY2EzZDZmMDg3Njk4OGI1MDllNWY1MGQ3ZjIxZDAy
10
+ OWE5MjZjMDJhYjg0ZjljZjM4YjBlMTdlNDEzNDFiMGY1YmZkYjQ2MjQ2ZjQ4
11
+ NDZlYjczNjFiZThlYjUxNjkxMTg1ODc4MzY0ZGYzNzQ4ZmU2NTQ=
12
+ data.tar.gz: !binary |-
13
+ NjljMjk3ZmJjYjEzYzkxYTIyY2JkMDdmNGQzMDIzNDdmODYwNDJmMGY4NWVm
14
+ NGFkOGQ2ZWUxMjRlZTZkYTg4N2VkYTA3OWU0MDZkMDkwMWExZjQxZjY2MDZl
15
+ NGIzNGUwNWE4NDg0YmVkNjM5MzU1Mzk2NGMyY2U5NGIxMWRkNTQ=
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp/*
18
+ .*.swp
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ - 1.9.2
6
+ - jruby-18mode
7
+ - jruby-19mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ - ruby-head
11
+ - jruby-head
12
+ - 1.8.7
13
+ - ree
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bitcourier.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Karol Sarnacki
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,34 @@
1
+ # Bitcourier
2
+
3
+ [![Build Status](https://travis-ci.org/elpassion/bitcourier.png)](https://travis-ci.org/elpassion/bitcourier)
4
+ [![Dependency Status](https://gemnasium.com/elpassion/bitcourier.png)](https://gemnasium.com/elpassion/bitcourier)
5
+
6
+ Decentralized and trustless communication protocol used to anonymously send encrypted messages via peer-to-peer network.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'bitcourier'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install bitcourier
21
+
22
+ ## Usage
23
+
24
+ Start Bitcourier node on port 6081 (this is the default port):
25
+
26
+ $ bitcourier node -p 6081
27
+
28
+ ## Contributing
29
+
30
+ 1. Fork it
31
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
32
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
33
+ 4. Push to the branch (`git push origin my-new-feature`)
34
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+
6
+ Rake::TestTask.new do |task|
7
+ task.libs << 'lib' << 'spec'
8
+ task.pattern = 'spec/bitcourier/**/*_spec.rb'
9
+ task.verbose = true
10
+ end
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.dirname(__FILE__) + '/../lib'
4
+
5
+ require 'thor'
6
+ require 'bitcourier'
7
+
8
+ class BitcourierCli < Thor
9
+
10
+ desc 'node', 'Start Bitcourier node'
11
+ method_option :port, default: 6081, aliases: '-p', desc: 'port on which the Bitcourier node will listen'
12
+ def node
13
+ daemon = Bitcourier::Daemon.new
14
+ daemon.run(options.port)
15
+ end
16
+
17
+ end
18
+
19
+ BitcourierCli.start
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bitcourier/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'bitcourier'
8
+ spec.version = Bitcourier::VERSION
9
+ spec.authors = ['EL Passion']
10
+ spec.email = ['account@elpassion.com']
11
+ spec.description = %q{Decentralized and trustless communication protocol used to anonymously send encrypted messages via peer-to-peer network}
12
+ spec.summary = %q{Decentralized and trustless communication protocol used to anonymously send encrypted messages via peer-to-peer network}
13
+ spec.homepage = 'https://github.com/elpassion/bitcourier'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'thor', '~> 0.18.1'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'minitest', '>= 5.0.8'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'simplecov', '>= 0.7.1'
27
+ end
@@ -0,0 +1,11 @@
1
+ require 'bitcourier/protocol'
2
+ require 'bitcourier/network'
3
+ require 'bitcourier/node'
4
+ require 'bitcourier/node_manager'
5
+ require 'bitcourier/daemon'
6
+ require 'bitcourier/peer'
7
+ require 'bitcourier/peer_list'
8
+ require 'bitcourier/version'
9
+
10
+ module Bitcourier
11
+ end
@@ -0,0 +1,33 @@
1
+ require 'securerandom'
2
+
3
+ module Bitcourier
4
+
5
+ class Daemon
6
+ attr_accessor :server, :client, :node_manager, :peer_list, :nonce
7
+
8
+ def initialize
9
+ self.server = Network::Server.new self
10
+ self.node_manager = NodeManager.new self
11
+ self.client = Network::Client.new self
12
+ self.peer_list = PeerList.new
13
+ self.nonce = SecureRandom.random_number(2**64)
14
+ end
15
+
16
+ def start
17
+ server_thread = server.run
18
+ client_thread = client.run
19
+
20
+ server_thread.join
21
+ client_thread.join
22
+ end
23
+
24
+ def run(port)
25
+ server.port = port
26
+
27
+ start
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ Thread.abort_on_exception = true
@@ -0,0 +1,5 @@
1
+ require 'bitcourier/network/server'
2
+ require 'bitcourier/network/client'
3
+
4
+ module Bitcourier
5
+ end
@@ -0,0 +1,65 @@
1
+ require 'timeout'
2
+
3
+ module Bitcourier
4
+ module Network
5
+ class Client
6
+ PEER_CONNECTION_RETRY_DELAY = 5 # In seconds
7
+ NEXT_PEER_DELAY = 5
8
+ CONNECT_TIMEOUT = 5
9
+
10
+ def initialize context
11
+ @context = context
12
+ end
13
+
14
+ def run
15
+ @thread = Thread.new do
16
+ loop do
17
+ if @context.node_manager.needs_nodes?
18
+ if peer_connection = next_peer_connection
19
+ @context.node_manager.add_socket peer_connection, true
20
+ else
21
+ sleep(NEXT_PEER_DELAY)
22
+ end
23
+ else
24
+ sleep(NEXT_PEER_DELAY)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def next_peer_connection
33
+ socket = nil
34
+
35
+ if peer = @context.peer_list.next
36
+ print "Connecting to #{peer.ip}:#{peer.port}... "
37
+
38
+ timeout(CONNECT_TIMEOUT) do
39
+ socket = TCPSocket.new(peer.ip, peer.port)
40
+ end
41
+
42
+ print "Connected.\n"
43
+
44
+ peer.touch
45
+ @context.peer_list.store(peer)
46
+ end
47
+ rescue Errno::ECONNREFUSED
48
+ print "Connection refused.\n"
49
+
50
+ peer.retry_in PEER_CONNECTION_RETRY_DELAY
51
+ @context.peer_list.store(peer)
52
+ rescue Errno::ETIMEDOUT, Timeout::Error
53
+ print "Timed out.\n"
54
+
55
+ peer.retry_in PEER_CONNECTION_RETRY_DELAY
56
+ @context.peer_list.store(peer)
57
+ rescue Exception => e
58
+ print "#{e.class}: #{e}\n"
59
+ puts e.backtrace
60
+ ensure
61
+ return socket
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,32 @@
1
+ require 'socket'
2
+ module Bitcourier
3
+ module Network
4
+ class Server
5
+ DEFAULT_PORT = 6081
6
+
7
+ attr_accessor :port
8
+
9
+ def initialize context, port = DEFAULT_PORT
10
+ @port = port
11
+ @context = context
12
+ end
13
+
14
+ def run
15
+ @server = TCPServer.new @port
16
+
17
+ puts "Started server on port #{@port}"
18
+
19
+ @thread = Thread.new do
20
+ loop do
21
+ socket = @server.accept
22
+ @context.node_manager.add_socket socket, false
23
+ end
24
+ end
25
+ end
26
+
27
+ def ip_string
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,172 @@
1
+ module Bitcourier
2
+ class Node
3
+
4
+ class State
5
+ class Unimplemented < StandardError; end
6
+
7
+ attr_accessor :node
8
+
9
+ def initialize node
10
+ self.node = node
11
+ end
12
+
13
+ def method_missing method, args
14
+ name = method.id2name
15
+
16
+ if name.start_with? 'on_'
17
+ puts "Can't handle #{name[3..-1]} in current state"
18
+ else
19
+ super(method, args)
20
+ end
21
+ end
22
+
23
+ def set_state state
24
+ node.set_state state
25
+ end
26
+
27
+ def on_enter
28
+ end
29
+
30
+ def on_leave
31
+ end
32
+ end
33
+
34
+ class ActiveHandshake < State
35
+ def on_enter
36
+ node.send_hello
37
+ end
38
+
39
+ def on_hello msg
40
+ if msg.protocol_version == Protocol::Message::Hello::PROTOCOL_VERSION
41
+ node.remote_port = msg.port
42
+ set_state ReadyState
43
+ else
44
+ node.disconnect
45
+ end
46
+ end
47
+ end
48
+
49
+ class PassiveHandshake < State
50
+ def on_hello msg
51
+ version_ok = msg.protocol_version == Protocol::Message::Hello::PROTOCOL_VERSION
52
+ nonce_ok = msg.nonce != node.context.nonce
53
+
54
+ if version_ok and nonce_ok
55
+ node.remote_port = msg.port
56
+ node.send_hello
57
+ set_state ReadyState
58
+ else
59
+ node.disconnect
60
+ end
61
+ end
62
+ end
63
+
64
+ class ReadyState < State
65
+ def on_enter
66
+ node.remember_peer
67
+ node.send_message(Protocol::Message::GetPeerList.new)
68
+ end
69
+
70
+ def on_get_peer_list msg
71
+ node.send_peer_list
72
+ end
73
+
74
+ def on_peer_info msg
75
+ node.on_peer_info(msg)
76
+ end
77
+ end
78
+
79
+ attr_accessor :socket, :context, :remote_port
80
+
81
+ def initialize(context, socket)
82
+ self.socket = socket
83
+ self.context = context
84
+ end
85
+
86
+ def set_state state
87
+ self.state.on_leave if self.state
88
+ self.state = state.new(self)
89
+ self.state.on_enter
90
+ end
91
+
92
+ def disconnect
93
+ socket.close
94
+ end
95
+
96
+ def remember_peer
97
+ remote_ip = socket.peeraddr[3]
98
+ peer = Peer.new remote_ip, remote_port
99
+
100
+ context.peer_list.store peer
101
+ end
102
+
103
+ def send_hello
104
+ msg = Protocol::Message::Hello.new
105
+ msg.port = context.server.port
106
+ msg.nonce = context.nonce
107
+ send_message msg
108
+ end
109
+
110
+ def send_peer_list
111
+ context.peer_list.peers.each do |peer|
112
+ msg = Protocol::Message::PeerInfo.new
113
+ msg.ip = peer.ip
114
+ msg.port = peer.port
115
+ msg.last_seen_at = peer.last_seen_at
116
+ send_message msg
117
+ end
118
+ end
119
+
120
+ def send_message msg
121
+ data = msg.pack
122
+ socket.write data
123
+ end
124
+
125
+ def on_peer_info msg
126
+ context.peer_list.store Peer.new(msg.ip, msg.port, msg.last_seen_at)
127
+ end
128
+
129
+ def on_message msg
130
+ return if state.nil?
131
+
132
+ case msg
133
+ when Protocol::Message::Hello
134
+ state.on_hello(msg)
135
+ when Protocol::Message::GetPeerList
136
+ state.on_get_peer_list(msg)
137
+ when Protocol::Message::PeerInfo
138
+ state.on_peer_info(msg)
139
+ else
140
+ puts "Don't know how to handle message class #{msg.class}"
141
+ end
142
+ end
143
+
144
+ def run
145
+ @thread = Thread.new do
146
+ buffer = ''
147
+
148
+ begin
149
+ loop do
150
+ data = socket.recv(1024)
151
+
152
+ break if data.length == 0
153
+
154
+ buffer += data
155
+
156
+ while (message_size = Protocol::Message::Base.message_size(buffer)) > 0
157
+ message_data = buffer.slice!(0, message_size)
158
+ message = Protocol::Message::Base.unpack(message_data)
159
+ on_message message
160
+ end
161
+ end
162
+ rescue IOError
163
+ puts "Connection closed"
164
+ end
165
+
166
+ end
167
+ end
168
+
169
+ attr_accessor :state
170
+
171
+ end
172
+ end
@@ -0,0 +1,35 @@
1
+ module Bitcourier
2
+ class NodeManager
3
+ MINIMUM_NODES = 3
4
+
5
+ attr_accessor :context
6
+
7
+ def initialize context
8
+ self.nodes = []
9
+ self.context = context
10
+ end
11
+
12
+ def needs_nodes?
13
+ nodes.count < MINIMUM_NODES
14
+ end
15
+
16
+ def add_socket socket, active = false
17
+ node = Node.new context, socket
18
+
19
+ if active
20
+ node.set_state Node::ActiveHandshake
21
+ else
22
+ node.set_state Node::PassiveHandshake
23
+ end
24
+
25
+ nodes << node
26
+
27
+ node.run
28
+ end
29
+
30
+ private
31
+
32
+ attr_accessor :nodes
33
+
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ module Bitcourier
2
+ class Peer
3
+ attr_accessor :ip, :port, :last_seen_at, :next_connection_at
4
+
5
+ def initialize ip, port, last_seen_at = nil, next_connection_at = Time.now.utc
6
+ self.ip = ip
7
+ self.port = port
8
+ self.last_seen_at = last_seen_at
9
+ self.next_connection_at = next_connection_at
10
+ end
11
+
12
+ def touch(timestamp = Time.now.utc)
13
+ self.last_seen_at = [last_seen_at || Time.at(0).utc, timestamp || Time.now.utc].max
14
+ end
15
+
16
+ def update(peer)
17
+ touch(peer.last_seen_at)
18
+ self.next_connection_at = peer.next_connection_at
19
+ end
20
+
21
+ def can_connect?(time = Time.now.utc)
22
+ next_connection_at < time
23
+ end
24
+
25
+ def retry_in delay
26
+ self.next_connection_at = Time.now + delay
27
+ end
28
+
29
+ def to_a
30
+ [
31
+ ip,
32
+ port,
33
+ last_seen_at && last_seen_at.to_i,
34
+ next_connection_at && next_connection_at.to_i
35
+ ]
36
+ end
37
+
38
+ def same?(other)
39
+ other && (ip == other.ip) && (port == other.port)
40
+ end
41
+
42
+ def self.from_a a
43
+ new(a[0], a[1].to_i, a[2] && Time.at(a[2].to_i).utc, a[3] && Time.at(a[3].to_i).utc)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ module Bitcourier
2
+ class PeerList
3
+ attr_reader :peers
4
+
5
+ def initialize
6
+ @peers = []
7
+ @storage = Storage.new(self)
8
+
9
+ load
10
+ end
11
+
12
+ def store(peer)
13
+ if existing = find(peer)
14
+ existing.update(peer)
15
+ else
16
+ peers << peer
17
+ end
18
+
19
+ save
20
+ end
21
+
22
+ def next
23
+ peers.select(&:can_connect?).sample
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :storage
29
+
30
+ def find(peer)
31
+ peers.detect { |existing| existing.same?(peer) }
32
+ end
33
+
34
+ def save
35
+ storage.write
36
+ end
37
+
38
+ def load
39
+ storage.read
40
+
41
+ seed if peers.size == 0
42
+ end
43
+
44
+ def seed
45
+ store Peer.new('146.185.167.10', 6081)
46
+ end
47
+
48
+ class Storage
49
+ PATH = './tmp/peer_list.txt'
50
+
51
+ def initialize list
52
+ @list = list
53
+ end
54
+
55
+ def read
56
+ File.read(PATH).lines.map do |line|
57
+ @list.store Peer.from_a(line.split('|'))
58
+ end
59
+ rescue Errno::ENOENT
60
+ return []
61
+ end
62
+
63
+ def write
64
+ File.open(PATH, 'w') do |file|
65
+ @list.peers.each do |peer|
66
+ file.puts peer.to_a.join('|')
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1 @@
1
+ require 'bitcourier/protocol/message'
@@ -0,0 +1,4 @@
1
+ require 'bitcourier/protocol/message/base'
2
+ require 'bitcourier/protocol/message/get_peer_list'
3
+ require 'bitcourier/protocol/message/hello'
4
+ require 'bitcourier/protocol/message/peer_info'
@@ -0,0 +1,62 @@
1
+ module Bitcourier
2
+ module Protocol
3
+ module Message
4
+ class Base
5
+ MAGIC_BYTES = 0x1234
6
+ HEADER_SIZE = 6
7
+
8
+ def header
9
+ [MAGIC_BYTES, self.class::ID, payload.size].pack('SSS')
10
+ end
11
+
12
+ def payload
13
+ ''
14
+ end
15
+
16
+ def pack
17
+ header + payload
18
+ end
19
+
20
+ def extract bytes
21
+ end
22
+
23
+ def self.unpack bytes
24
+ magic, id, length = unpack_header(bytes)
25
+
26
+ return nil if magic != MAGIC_BYTES # wrong magic
27
+ return nil if length + HEADER_SIZE > bytes.size # partial package
28
+ return nil unless klass = find_message_class(id)
29
+
30
+ msg = klass.new
31
+ msg.extract bytes[HEADER_SIZE..-1]
32
+
33
+ msg
34
+ end
35
+
36
+ def self.message_size bytes
37
+ return 0 if bytes.size < HEADER_SIZE
38
+
39
+ _, _, length = unpack_header(bytes)
40
+
41
+ return 0 if bytes.size < (length + HEADER_SIZE)
42
+
43
+ return length + HEADER_SIZE
44
+ end
45
+
46
+ private
47
+
48
+ def self.unpack_header bytes
49
+ bytes[0...HEADER_SIZE].unpack('SSS')
50
+ end
51
+
52
+ def self.find_message_class id
53
+ [Hello, GetPeerList, PeerInfo].each do |message_class|
54
+ return message_class if message_class::ID == id
55
+ end
56
+
57
+ nil
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,9 @@
1
+ module Bitcourier
2
+ module Protocol
3
+ module Message
4
+ class GetPeerList < Base
5
+ ID = 0x2
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module Bitcourier
2
+ module Protocol
3
+ module Message
4
+ class Hello < Base
5
+ ID = 0x1
6
+ PROTOCOL_VERSION = 1
7
+
8
+ attr_accessor :protocol_version, :port, :nonce
9
+
10
+ def initialize
11
+ self.protocol_version = PROTOCOL_VERSION
12
+ end
13
+
14
+ def payload
15
+ [protocol_version.to_i, port.to_i, nonce.to_i].pack('SSQ')
16
+ end
17
+
18
+ def extract bytes
19
+ data = bytes.unpack('SSQ')
20
+
21
+ self.protocol_version = data[0]
22
+ self.port = data[1]
23
+ self.nonce = data[2]
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ module Bitcourier
2
+ module Protocol
3
+ module Message
4
+
5
+ class PeerInfo < Base
6
+ ID = 0x3
7
+
8
+ attr_accessor :ip, :port, :last_seen_at
9
+
10
+ def payload
11
+ [ip_array, port.to_i, last_seen_at.to_i].flatten.pack('CCCCSL')
12
+ end
13
+
14
+ def extract bytes
15
+ data = bytes.unpack('CCCCSL')
16
+
17
+ self.ip = data[0..3].join('.')
18
+ self.port = data[4].to_i
19
+ self.last_seen_at = Time.at(data[5].to_i).utc
20
+
21
+ self
22
+ end
23
+
24
+ private
25
+
26
+ def ip_array
27
+ ip.split('.').map(&:to_i)
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module Bitcourier
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bitcourier::Protocol::Message::Base do
4
+
5
+ describe '::unpack' do
6
+
7
+ describe 'given GetPeerList packet bytes' do
8
+ it 'returns GetPeerList message object' do
9
+ get_peer_list_bytes = "4\x12\x02\x00\x00\x00".force_encoding('ASCII-8BIT')
10
+ get_peer_list = Bitcourier::Protocol::Message::Base.unpack(get_peer_list_bytes)
11
+
12
+ get_peer_list.must_be_instance_of Bitcourier::Protocol::Message::GetPeerList
13
+ end
14
+ end
15
+
16
+ describe 'given Hello packet bytes' do
17
+ it 'returns Hello message object' do
18
+ hello_bytes = "4\x12\x01\x00\f\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".force_encoding('ASCII-8BIT')
19
+ hello = Bitcourier::Protocol::Message::Base.unpack(hello_bytes)
20
+
21
+ hello.must_be_instance_of Bitcourier::Protocol::Message::Hello
22
+ end
23
+ end
24
+
25
+ describe 'given PeerInfo packet bytes' do
26
+ it 'returns PeerInfo message object' do
27
+ peer_info_bytes = "4\x12\x03\x00\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00".force_encoding('ASCII-8BIT')
28
+ peer_info = Bitcourier::Protocol::Message::Base.unpack(peer_info_bytes)
29
+
30
+ peer_info.must_be_instance_of Bitcourier::Protocol::Message::PeerInfo
31
+ end
32
+ end
33
+
34
+ describe 'given unknown packet bytes' do
35
+ it 'returns nil' do
36
+ unknown_bytes = "4\x12\xFF\x00\x00\x00".force_encoding('ASCII-8BIT')
37
+ unknown = Bitcourier::Protocol::Message::Base.unpack(unknown_bytes)
38
+
39
+ unknown.must_be_nil
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bitcourier::Protocol::Message::Hello do
4
+ before do
5
+ @hello = Bitcourier::Protocol::Message::Hello.new
6
+ @hello_bytes = "{\x00\x11\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF".force_encoding('ASCII-8BIT')
7
+ end
8
+
9
+ it 'initiates with default version number' do
10
+ @hello.protocol_version.must_equal Bitcourier::Protocol::Message::Hello::PROTOCOL_VERSION
11
+ end
12
+
13
+ describe '#payload' do
14
+ it 'returns payload bytes' do
15
+ @hello.protocol_version = 123
16
+ @hello.port = 17
17
+ @hello.nonce = 2**64 - 1
18
+
19
+ @hello.payload.must_equal @hello_bytes
20
+ end
21
+ end
22
+
23
+ describe '#extract' do
24
+ it 'extracts payload bytes to object values' do
25
+ @hello.extract @hello_bytes
26
+
27
+ @hello.protocol_version.must_equal 123
28
+ @hello.port.must_equal 17
29
+ @hello.nonce.must_equal 2**64 - 1
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Bitcourier::Protocol::Message::PeerInfo do
4
+ before do
5
+ @peer_info = Bitcourier::Protocol::Message::PeerInfo.new
6
+ @peer_info_bytes = "\x01\x02\x03\x04\xFF\xFF\xAF5\xA24".force_encoding('ASCII-8BIT')
7
+ end
8
+
9
+ describe '#payload' do
10
+ it 'returns payload bytes' do
11
+ @peer_info.ip = '1.2.3.4'
12
+ @peer_info.port = 65535
13
+ @peer_info.last_seen_at = Time.utc(1997, 12, 25, 10, 30, 7)
14
+
15
+ @peer_info.payload.must_equal @peer_info_bytes
16
+ end
17
+ end
18
+
19
+ describe '#extract' do
20
+ it 'extracts payload bytes to object values' do
21
+ @peer_info.extract @peer_info_bytes
22
+
23
+ @peer_info.ip.must_equal '1.2.3.4'
24
+ @peer_info.port.must_equal 65535
25
+ @peer_info.last_seen_at.must_equal Time.utc(1997, 12, 25, 10, 30, 7)
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ if RUBY_VERSION < '1.9'
4
+ class String
5
+ def force_encoding(encoding)
6
+ self
7
+ end
8
+ end
9
+ end
10
+
11
+ gem 'minitest' # ensure we are using the gem version
12
+
13
+ require 'bitcourier'
14
+ require 'minitest/autorun'
15
+ require 'simplecov'
16
+
17
+ SimpleCov.start
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bitcourier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - EL Passion
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.18.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.18.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 5.0.8
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 5.0.8
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: 0.7.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: 0.7.1
83
+ description: Decentralized and trustless communication protocol used to anonymously
84
+ send encrypted messages via peer-to-peer network
85
+ email:
86
+ - account@elpassion.com
87
+ executables:
88
+ - bitcourier
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - .gitignore
93
+ - .travis.yml
94
+ - Gemfile
95
+ - Gemfile.lock
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - bin/bitcourier
100
+ - bitcourier.gemspec
101
+ - lib/bitcourier.rb
102
+ - lib/bitcourier/daemon.rb
103
+ - lib/bitcourier/network.rb
104
+ - lib/bitcourier/network/client.rb
105
+ - lib/bitcourier/network/server.rb
106
+ - lib/bitcourier/node.rb
107
+ - lib/bitcourier/node_manager.rb
108
+ - lib/bitcourier/peer.rb
109
+ - lib/bitcourier/peer_list.rb
110
+ - lib/bitcourier/protocol.rb
111
+ - lib/bitcourier/protocol/message.rb
112
+ - lib/bitcourier/protocol/message/base.rb
113
+ - lib/bitcourier/protocol/message/get_peer_list.rb
114
+ - lib/bitcourier/protocol/message/hello.rb
115
+ - lib/bitcourier/protocol/message/peer_info.rb
116
+ - lib/bitcourier/version.rb
117
+ - spec/bitcourier/protocol/message/base_spec.rb
118
+ - spec/bitcourier/protocol/message/hello_spec.rb
119
+ - spec/bitcourier/protocol/message/peer_info_spec.rb
120
+ - spec/spec_helper.rb
121
+ - tmp/.gitkeep
122
+ homepage: https://github.com/elpassion/bitcourier
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.1.10
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Decentralized and trustless communication protocol used to anonymously send
146
+ encrypted messages via peer-to-peer network
147
+ test_files:
148
+ - spec/bitcourier/protocol/message/base_spec.rb
149
+ - spec/bitcourier/protocol/message/hello_spec.rb
150
+ - spec/bitcourier/protocol/message/peer_info_spec.rb
151
+ - spec/spec_helper.rb
152
+ has_rdoc: