lapidar 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7550a6712aa1a4ec73b55dd89292c037e9d7c518
4
- data.tar.gz: 157f2757955b82097def09ccd07e8d34e6bd49e2
2
+ SHA256:
3
+ metadata.gz: 893cdddb939ffc0972e0ada67525b73216fb9b9a42cd8fa8afca7d9cb1d8103f
4
+ data.tar.gz: 30db59b22f248986db3ad80151adebb007ec19246dcabe8f08720341d682e929
5
5
  SHA512:
6
- metadata.gz: 2d7af0295583b6e07a2a329c9a93f478c1ec61f33d140262768dd2f384fd7cf341e5bac0a0efd10255399f60eab826baf527c78dceef95a7d5b42757013fa910
7
- data.tar.gz: a1b80a2db11d67d29c80a03113040ab8309e679961e71476282bdc4c64315865fb7dcb2bdd45da0a096fb127c89e5df1bc087e062b72ce4fe3f643a162ea7d3b
6
+ metadata.gz: fa71fb1f0e0ee6073d3e58fb104aa8f3f48bcd69bee01d2b381f9711606023f5760b86dfcfd887cea40b0dca484e2b51f054ca2ef1ff1bad58474d6ab7e41f88
7
+ data.tar.gz: 694f8dbf8bb41375f4c926ad0a7858bc72c0720139b8414478fa9797d736069c978b81385108d664544083264bfd9bb3710112c89a6cdf8ddad6e66008d1c223
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ * Buschtelefon remote endpoints are now connected via the local endpoint's outbound port
6
+ * `Chain` now queues up unconsolidated (future or invalid) blocks for future use.
7
+ * `Runner` now loads main chain into Buschtelefon on start.
8
+ * The miner thread now passes on thread execution after mining to make sure
9
+ that the produced block can be digested by the queue (otherwise we may mine blocks twice).
10
+ * We can now inquire all remote neighbors manually via `Runner#inquire_all_neighbors`
11
+
3
12
  ## 0.2.0
4
13
 
5
14
  * Better threading
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.org/renuo/lapidar.svg?branch=master)](https://travis-ci.org/renuo/lapidar)
2
+
1
3
  # Lapidar
2
4
 
3
5
  Carve it in stone. Only that these stones are easy to move across the internet.
@@ -21,7 +23,7 @@ Or install it yourself as:
21
23
 
22
24
  ## Usage
23
25
 
24
- Have a look at `bin/run`. You'll see that 5 nodes spin up and connect to each other
26
+ Have a look at [`bin/run`](./bin/run). You'll see that 5 nodes spin up and connect to each other
25
27
  via [buschtelefon](https://github.com/renuo/lapidar) and contest each other
26
28
  in a race which looks like this:
27
29
 
data/bin/run CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # $DEBUG = true
2
3
  Thread.abort_on_exception = true
3
4
 
4
5
  require "paint"
@@ -10,22 +11,15 @@ require_relative "../lib/lapidar"
10
11
  puts "Starting experiment…"
11
12
 
12
13
  runners = [
13
- Lapidar.runner(port: 9999, neighbors: [
14
- { host: "localhost", port: 9995 },
15
- { host: "localhost", port: 9996 },
16
- { host: "localhost", port: 9997 },
17
- { host: "localhost", port: 9998 },
18
- ]),
19
- Lapidar.runner(port: 9998, neighbors: [{ host: "localhost", port: 9996 }]),
20
- Lapidar.runner(port: 9997, neighbors: [{ host: "localhost", port: 9997 }]),
21
- Lapidar.runner(port: 9996, neighbors: [{ host: "localhost", port: 9998 }]),
22
- Lapidar.runner(port: 9995, neighbors: [{ host: "localhost", port: 9999 }])
14
+ Lapidar.runner(port: 9999, neighbors: [{ host: "127.0.0.1", port: 9998 }]),
15
+ Lapidar.runner(port: 9998, neighbors: [{ host: "127.0.0.1", port: 9997 }]),
16
+ Lapidar.runner(port: 9997, neighbors: [{ host: "127.0.0.1", port: 9999 }])
23
17
  ]
24
18
 
25
- threads = runners.map do |runner|
19
+ threads = runners.map.with_index(2) do |runner, i|
26
20
  [
27
- Thread.new { runner.start },
28
- Thread.new { loop { runner.punch_queue << rand.to_s } }
21
+ Thread.new { sleep(i); runner.start },
22
+ Thread.new { loop { runner.punch_queue << "#{runner.buschtelefon_endpoint.port} #{rand}" } }
29
23
  ]
30
24
  end.flatten
31
25
 
@@ -35,7 +29,7 @@ logger_thread = Thread.new do
35
29
  loop do
36
30
  system("clear")
37
31
  puts(runners.map do |runner|
38
- "Runner on port #{runner.network_endpoint.port}:\n#{runner.chain.to_colorful_string(5)}"
32
+ "Runner on port #{runner.buschtelefon_endpoint.port}:\n#{runner.chain.to_colorful_string(5)}"
39
33
  end.join("\n"))
40
34
  sleep(1)
41
35
  end
@@ -47,5 +41,12 @@ trap "SIGINT" do
47
41
  logger_thread.exit
48
42
  end
49
43
 
44
+ sleep(5)
45
+ puts 'All inquire their neighbors manually'
46
+ runners.reverse.each do |runner|
47
+ sleep(1)
48
+ runner.buschtelefon_endpoint.inquire_remote_neighbors
49
+ end
50
+
50
51
  threads.each(&:join)
51
52
  logger_thread.join
data/lapidar.gemspec CHANGED
@@ -22,7 +22,7 @@ and evaluates block order and correctness. Build any distributed business logic
22
22
  # Specify which files should be added to the gem when it is released.
23
23
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
24
  spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
25
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|docs)/}) }
26
26
  end
27
27
  spec.bindir = "exe"
28
28
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -30,13 +30,13 @@ and evaluates block order and correctness. Build any distributed business logic
30
30
 
31
31
  spec.required_ruby_version = ">= 2.3.0"
32
32
 
33
- spec.add_dependency "buschtelefon", "~> 0.3"
34
- spec.add_dependency "oj", ">= 1.0"
35
- spec.add_development_dependency "bundler", ">= 1.17"
33
+ spec.add_dependency "buschtelefon", "~> 0.4"
34
+ spec.add_dependency "oj", ">= 1.0", "< 4"
35
+ spec.add_development_dependency "bundler", "~> 1.16"
36
36
  spec.add_development_dependency "factory_bot", "~> 5.0"
37
37
  spec.add_development_dependency "paint", "~> 2.0"
38
- spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "rake", "~> 12.0"
39
39
  spec.add_development_dependency "rspec", "~> 3.8"
40
40
  spec.add_development_dependency "simplecov", "~> 0.17"
41
- spec.add_development_dependency "standard", "> 0"
41
+ spec.add_development_dependency "standard", "~> 0.1"
42
42
  end
data/lib/lapidar/block.rb CHANGED
@@ -19,5 +19,9 @@ module Lapidar
19
19
  created_at: @created_at,
20
20
  }
21
21
  end
22
+
23
+ def ==(other)
24
+ to_h == other.to_h
25
+ end
22
26
  end
23
27
  end
data/lib/lapidar/chain.rb CHANGED
@@ -4,22 +4,20 @@ module Lapidar
4
4
 
5
5
  def initialize
6
6
  @block_stacks = []
7
+ @unconsolidated_blocks = []
7
8
  end
8
9
 
9
- # TODO: Queue up future blocks for later use
10
10
  # TODO: Check for duplicates and dont add them to the chains
11
11
  def add(block)
12
- raise "future block?" if block.number > @block_stacks.count
13
- raise "invalid block" unless valid?(block)
14
-
15
- @block_stacks[block.number] ||= []
16
- @block_stacks[block.number].push(block) unless @block_stacks[block.number].map(&:hash).include?(block.hash)
17
-
18
- # Rebalance if second to last block has more than one candidate
19
- rebalance if !contested?(block.number) && contested?(block.number - 1)
12
+ if valid?(block)
13
+ connect(block)
14
+ consolidate_successors(block)
15
+ else
16
+ queue_unconsolidated(block)
17
+ end
20
18
  end
21
19
 
22
- # For each positition in the chain the candidate positioned first is considered the valid one
20
+ # For each position in the chain the candidate positioned first is considered the valid one
23
21
  def blocks
24
22
  @block_stacks.map { |candidates| candidates&.first }
25
23
  end
@@ -44,10 +42,19 @@ module Lapidar
44
42
 
45
43
  private
46
44
 
45
+ def connect(block)
46
+ @block_stacks[block.number] ||= []
47
+ @block_stacks[block.number].push(block) unless @block_stacks[block.number].map(&:hash).include?(block.hash)
48
+
49
+ # Rebalance if second to last block has more than one candidate
50
+ rebalance if !contested?(block.number) && contested?(block.number - 1)
51
+ end
52
+
47
53
  def valid?(block)
48
54
  return true if Assessment.genesis?(block) # early valid if genesis
49
55
  return false if Assessment.first?(block) # early invalid if fake genesis
50
56
  return false unless Assessment.meets_difficulty?(block) # early invalid if difficulty not met
57
+ return false if block.number > @block_stacks.count # early invalid if future block
51
58
 
52
59
  # Check if there's an existing parent
53
60
  @block_stacks[block.number - 1].any? do |previous_block|
@@ -75,5 +82,23 @@ module Lapidar
75
82
  def contested?(block_number)
76
83
  @block_stacks[block_number].count > 1
77
84
  end
85
+
86
+ def queue_unconsolidated(block)
87
+ return if @unconsolidated_blocks.include?(block)
88
+ @unconsolidated_blocks.push(block)
89
+ @unconsolidated_blocks.sort_by!(&:number)
90
+ end
91
+
92
+ # Check if queued up unconsolidated blocks can be added to the chain after the currently added block
93
+ def consolidate_successors(head_block)
94
+ @unconsolidated_blocks
95
+ .select { |unconsolidated_block| unconsolidated_block.number > head_block.number }
96
+ .each do |possible_successor|
97
+ if valid?(possible_successor)
98
+ connect(possible_successor)
99
+ @unconsolidated_blocks.delete(possible_successor)
100
+ end
101
+ end
102
+ end
78
103
  end
79
104
  end
@@ -6,12 +6,12 @@ module Lapidar
6
6
 
7
7
  def self.save_chain(filename, chain)
8
8
  Dir.mkdir(CONFIG_DIR) unless File.exist?(CONFIG_DIR)
9
- File.write(File.join(CONFIG_DIR, filename), Oj.dump(chain))
9
+ File.write(File.join(CONFIG_DIR, filename), Oj.dump(chain, {}))
10
10
  end
11
11
 
12
12
  def self.load_chain(filename)
13
- Oj.load(File.read(File.join(CONFIG_DIR, filename)))
14
- rescue
13
+ Oj.load(File.read(File.join(CONFIG_DIR, filename)), {})
14
+ rescue Errno::ENOENT
15
15
  nil
16
16
  end
17
17
  end
@@ -2,16 +2,21 @@ require "logger"
2
2
 
3
3
  module Lapidar
4
4
  class Runner
5
- attr_reader :chain, :punch_queue, :network_endpoint, :logger
5
+ attr_reader :chain, :punch_queue, :buschtelefon_endpoint, :logger
6
6
 
7
- def initialize(network_endpoint)
7
+ def initialize(buschtelefon_endpoint)
8
8
  @logger = Logger.new(StringIO.new)
9
- @network_endpoint = network_endpoint
10
- @chain = Persistence.load_chain("#{@network_endpoint.port}.json") || Chain.new
9
+ @buschtelefon_endpoint = buschtelefon_endpoint
10
+ @chain = Persistence.load_chain("#{@buschtelefon_endpoint.port}.json") || Chain.new
11
11
  @incoming_blocks = Queue.new
12
12
  @punch_queue = SizedQueue.new(1)
13
13
  @should_stop = nil
14
14
  @threads = []
15
+
16
+ # Reload currently strongest chain into buschtelefon_endpoint
17
+ if @chain.blocks.any?
18
+ @buschtelefon_endpoint.load_messages(@chain.blocks.map(&:to_h).map(&:to_json))
19
+ end
15
20
  end
16
21
 
17
22
  def start
@@ -24,7 +29,7 @@ module Lapidar
24
29
  def stop
25
30
  @should_stop = true
26
31
  Thread.pass
27
- Persistence.save_chain("#{@network_endpoint.port}.json", @chain)
32
+ Persistence.save_chain("#{@buschtelefon_endpoint.port}.json", @chain)
28
33
  @threads.each(&:exit)
29
34
  end
30
35
 
@@ -50,8 +55,13 @@ module Lapidar
50
55
  until @should_stop
51
56
  begin
52
57
  new_block = miner.mine(@chain.blocks.last, @punch_queue.pop)
53
- @network_endpoint.feed(Buschtelefon::Gossip.new(new_block.to_h.to_json))
54
58
  @incoming_blocks << new_block
59
+
60
+ # We need to let the consumer digest the block, otherwise we maybe mine the same block twice.
61
+ # Notice that we feed the block into the network soon because adoption is also important.
62
+ Thread.pass
63
+
64
+ @buschtelefon_endpoint.feed(Buschtelefon::Gossip.new(new_block.to_h.to_json))
55
65
  @logger.info("local_producer") { "!" }
56
66
  rescue => e
57
67
  @logger.debug("local_producer") { "Mint block isn't valid: #{e.message}" }
@@ -63,7 +73,7 @@ module Lapidar
63
73
 
64
74
  def network_producer
65
75
  Thread.new do
66
- @network_endpoint.listen do |gossip|
76
+ @buschtelefon_endpoint.listen do |gossip, gossip_source|
67
77
  break if @should_stop
68
78
 
69
79
  begin
@@ -1,3 +1,3 @@
1
1
  module Lapidar
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/lapidar.rb CHANGED
@@ -11,10 +11,12 @@ require_relative "lapidar/version"
11
11
 
12
12
  module Lapidar
13
13
  def self.runner(port:, neighbors:)
14
- network_endpoint = Buschtelefon::NetTattler.new(port: port)
15
- neighbors.map! { |neighbor_location| Buschtelefon::RemoteTattler.new(neighbor_location) }
16
- neighbors.each { |neighbor| network_endpoint.connect(neighbor) }
14
+ buschtelefon_endpoint = Buschtelefon::NetTattler.new(port: port)
17
15
 
18
- Runner.new(network_endpoint)
16
+ neighbors.each do |neighbor_location|
17
+ buschtelefon_endpoint.connect_remote(neighbor_location)
18
+ end
19
+
20
+ Runner.new(buschtelefon_endpoint)
19
21
  end
20
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lapidar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josua Schmid
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-11 00:00:00.000000000 Z
11
+ date: 2019-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: buschtelefon
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.3'
19
+ version: '0.4'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.3'
26
+ version: '0.4'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: oj
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -31,6 +31,9 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.0'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '4'
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -38,20 +41,23 @@ dependencies:
38
41
  - - ">="
39
42
  - !ruby/object:Gem::Version
40
43
  version: '1.0'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '4'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: bundler
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
- - - ">="
51
+ - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: '1.17'
53
+ version: '1.16'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
- - - ">="
58
+ - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: '1.17'
60
+ version: '1.16'
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: factory_bot
57
63
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +92,14 @@ dependencies:
86
92
  requirements:
87
93
  - - "~>"
88
94
  - !ruby/object:Gem::Version
89
- version: '10.0'
95
+ version: '12.0'
90
96
  type: :development
91
97
  prerelease: false
92
98
  version_requirements: !ruby/object:Gem::Requirement
93
99
  requirements:
94
100
  - - "~>"
95
101
  - !ruby/object:Gem::Version
96
- version: '10.0'
102
+ version: '12.0'
97
103
  - !ruby/object:Gem::Dependency
98
104
  name: rspec
99
105
  requirement: !ruby/object:Gem::Requirement
@@ -126,16 +132,16 @@ dependencies:
126
132
  name: standard
127
133
  requirement: !ruby/object:Gem::Requirement
128
134
  requirements:
129
- - - ">"
135
+ - - "~>"
130
136
  - !ruby/object:Gem::Version
131
- version: '0'
137
+ version: '0.1'
132
138
  type: :development
133
139
  prerelease: false
134
140
  version_requirements: !ruby/object:Gem::Requirement
135
141
  requirements:
136
- - - ">"
142
+ - - "~>"
137
143
  - !ruby/object:Gem::Version
138
- version: '0'
144
+ version: '0.1'
139
145
  description: |-
140
146
  This is a custom blockchain with a working network layer. It just mines and receives blocks
141
147
  and evaluates block order and correctness. Build any distributed business logic on top of it.
@@ -158,7 +164,6 @@ files:
158
164
  - bin/console
159
165
  - bin/run
160
166
  - bin/setup
161
- - docs/visualization.png
162
167
  - lapidar.gemspec
163
168
  - lib/lapidar.rb
164
169
  - lib/lapidar/assessment.rb
@@ -190,8 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
195
  - !ruby/object:Gem::Version
191
196
  version: '0'
192
197
  requirements: []
193
- rubyforge_project:
194
- rubygems_version: 2.5.2.3
198
+ rubygems_version: 3.0.6
195
199
  signing_key:
196
200
  specification_version: 4
197
201
  summary: Carve it in stone. Only that these stones are easy to move across the internet.
Binary file