bitcourier 0.0.1

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.
@@ -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: