lapidar 0.2.0 → 0.3.0

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