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 +5 -5
- data/CHANGELOG.md +9 -0
- data/README.md +3 -1
- data/bin/run +15 -14
- data/lapidar.gemspec +6 -6
- data/lib/lapidar/block.rb +4 -0
- data/lib/lapidar/chain.rb +35 -10
- data/lib/lapidar/persistence.rb +3 -3
- data/lib/lapidar/runner.rb +17 -7
- data/lib/lapidar/version.rb +1 -1
- data/lib/lapidar.rb +6 -4
- metadata +21 -17
- data/docs/visualization.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 893cdddb939ffc0972e0ada67525b73216fb9b9a42cd8fa8afca7d9cb1d8103f
|
4
|
+
data.tar.gz: 30db59b22f248986db3ad80151adebb007ec19246dcabe8f08720341d682e929
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
+
[](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
|
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
|
-
|
15
|
-
|
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 <<
|
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.
|
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{^(
|
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.
|
34
|
-
spec.add_dependency "oj", ">= 1.0"
|
35
|
-
spec.add_development_dependency "bundler", "
|
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", "~>
|
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", "
|
41
|
+
spec.add_development_dependency "standard", "~> 0.1"
|
42
42
|
end
|
data/lib/lapidar/block.rb
CHANGED
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
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
|
data/lib/lapidar/persistence.rb
CHANGED
@@ -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
|
data/lib/lapidar/runner.rb
CHANGED
@@ -2,16 +2,21 @@ require "logger"
|
|
2
2
|
|
3
3
|
module Lapidar
|
4
4
|
class Runner
|
5
|
-
attr_reader :chain, :punch_queue, :
|
5
|
+
attr_reader :chain, :punch_queue, :buschtelefon_endpoint, :logger
|
6
6
|
|
7
|
-
def initialize(
|
7
|
+
def initialize(buschtelefon_endpoint)
|
8
8
|
@logger = Logger.new(StringIO.new)
|
9
|
-
@
|
10
|
-
@chain = Persistence.load_chain("#{@
|
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("#{@
|
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
|
-
@
|
76
|
+
@buschtelefon_endpoint.listen do |gossip, gossip_source|
|
67
77
|
break if @should_stop
|
68
78
|
|
69
79
|
begin
|
data/lib/lapidar/version.rb
CHANGED
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
|
-
|
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
|
-
|
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.
|
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-
|
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.
|
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.
|
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.
|
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.
|
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: '
|
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: '
|
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
|
-
|
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.
|
data/docs/visualization.png
DELETED
Binary file
|