Skip to content

Instantly share code, notes, and snippets.

@sgbett
Last active May 3, 2018 10:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sgbett/0300eb612f46f44daae1780485cc35a1 to your computer and use it in GitHub Desktop.
Save sgbett/0300eb612f46f44daae1780485cc35a1 to your computer and use it in GitHub Desktop.
Selfish mining with DA
#!/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
@cypherblock
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment