Last active
May 3, 2018 10:09
-
-
Save sgbett/0300eb612f46f44daae1780485cc35a1 to your computer and use it in GitHub Desktop.
Selfish mining with DA
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# Dependancies: | |
# gem install random_variate_generator | |
iterations = 100000 | |
alpha = 0.39 #sm hash_rate | |
require 'random_variate_generator' | |
class Block | |
attr_accessor :real_interval, :interval, :timestamp | |
end | |
def mine_block(difficulty,timestamp,alpha=1.0) | |
block = Block.new | |
#simulates poisson process for 1 block per 10 mins by using exponential randoms, adjusted for difficulty/hash | |
block.real_interval = RandomVariateGenerator::Random.exponential(lambda: alpha/(10)) | |
block.interval = difficulty * block.real_interval | |
block.timestamp = timestamp + 60*block.interval | |
block | |
end | |
#stats | |
sm_won = 0 | |
hm_won = 0 | |
sm_orphans = 0 | |
hm_orphans = 0 | |
sm_latency = 0 | |
#last 144 blocks (minimum) | |
chain = [] | |
#potential forks being contested | |
selfish_chain = [] | |
honest_chain = [] | |
start_time = timestamp = Time.now | |
end_time = start_time + (365*24*60*60) | |
d = 1.0 #starting difficulty | |
#start selfish mining | |
selfish_block = mine_block(d,timestamp,alpha) | |
while timestamp < end_time | |
#first see when HM would find a block | |
honest_block = mine_block(d,timestamp,(1-alpha)) | |
puts " hm will find at: #{honest_block.timestamp}" | |
honest_chain << honest_block | |
print " sm would find at: #{selfish_block.timestamp}" | |
#if SM block would arrive first start building private chain until their next block would be found after the HM block | |
while selfish_block.timestamp < (honest_block.timestamp) | |
selfish_chain << selfish_block | |
puts " sm extends private chain to #{selfish_chain.count}" | |
selfish_block = mine_block(d,selfish_chain.last.timestamp,alpha) | |
print " sm would find at: #{selfish_block.timestamp}" | |
end | |
puts | |
if (selfish_chain.count - honest_chain.count) > 1 | |
puts " sm lead #{selfish_chain.count - honest_chain.count} blocks - keeps on private mining" | |
elsif (selfish_chain.count - honest_chain.count) == 1 && (honest_chain.last.timestamp - selfish_chain.last.timestamp) > 30 | |
puts " sm lead #{selfish_chain.count - honest_chain.count} block - attempts to orphan" | |
#SM is only 1 block ahead so orphans HM to cause them to reorg | |
sm_won += selfish_chain.count | |
hm_orphans += honest_chain.count | |
chain += selfish_chain | |
puts "SM publishes extends public chain by #{selfish_chain.count}, orphans #{honest_chain.count} HM block" | |
selfish_chain = [] | |
honest_chain = [] | |
d = 1 - (0.0+sm_orphans + hm_orphans) / (sm_orphans + hm_orphans + sm_won + hm_won) | |
else | |
#HM chain is equal or longer then SM gives up and starts on public chain tip again | |
hm_won += honest_chain.count | |
sm_orphans += selfish_chain.count | |
chain += honest_chain | |
if selfish_chain.count > honest_chain.count | |
puts "*** LATENCY CAUSES SM orphan attempt to fail ****" | |
sm_latency +=1 | |
end | |
print "HM wins public chain extended by #{honest_chain.count} " | |
print "SM discards private chain length #{selfish_chain.count}" if selfish_chain.any? | |
puts | |
# sm_timestamp = selfish_block.timestamp | |
# selfish_block = nil | |
selfish_chain = [] | |
honest_chain = [] | |
end | |
total_blocks = 1.0 * sm_orphans + hm_orphans + sm_won + hm_won | |
total_orphans = 1.0 * sm_orphans + hm_orphans | |
d = 1 - (total_orphans / total_blocks) if total_blocks > 0 | |
timestamp = honest_block.timestamp | |
puts "#{timestamp} difficulty: #{"%.4f" %d} sm_won: #{sm_won} (expected: #{(expected = (alpha*(timestamp - start_time)/600)).to_i}) hm_won: #{hm_won} Selfish: #{selfish_chain.count} Honest: #{honest_chain.count} sm_orphans: #{sm_orphans} (#{sm_latency}) hm_orphans: #{hm_orphans} SM Profit: #{-100+(100*(sm_won+selfish_chain.count)/expected).to_i}% " | |
# STDIN.getc | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This one also (similar to other gist) misses the case where selfish can get ahead of honest miner in this scenario:
---------H1------------H2
---S1--------S2
In selfish mining approach, S1&S2 would both get rewarded. S2 is published immediately (s1 can be published when H1 is found or together with S2). Your code doesn't do this. It thinks selfish is holding on to S2 till H2 and so it rewards Honest miner with 2 blocks and orphans 2 selfish blocks. That is completely wrong.