Skip to content

Instantly share code, notes, and snippets.

@sgbett
Last active May 2, 2018 22:23
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/a7cc67469a53609da2a5eab68b4024a6 to your computer and use it in GitHub Desktop.
Save sgbett/a7cc67469a53609da2a5eab68b4024a6 to your computer and use it in GitHub Desktop.
Selfish mine stats
#!/usr/bin/env ruby
# Dependancies:
# gem install random_variate_generator
require 'random_variate_generator'
class Block
attr_accessor :real_interval, :interval, :timestamp
end
@all_blocks = []
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
@all_blocks << block
block
end
def iterate(iterations,alpha)
d = 1.0 #starting difficulty
#stats
sm_won = 0
hm_won = 0
sm_orphans = 0
hm_orphans = 0
#potential forks being contested
selfish_chain = []
honest_chain = []
timestamp = Time.now
start_time = timestamp
i=0; while (i += 1) < iterations do
#first see when HM would find a block
honest_block = mine_block(d,timestamp,(1-alpha))
honest_chain << honest_block
selfish_block ||= mine_block(d,timestamp,alpha)
#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
selfish_block = mine_block(d,selfish_chain.last.timestamp,alpha)
end
if (selfish_chain.count - honest_chain.count) > 1
#sm keeps on private mining
elsif (selfish_chain.count - honest_chain.count) == 1
#SM is only 1 block ahead so orphans HM to cause them to reorg
sm_won += selfish_chain.count
hm_orphans += honest_chain.count
selfish_chain = []
honest_chain = []
elsif selfish_chain.count == honest_chain.count
#chain is forked SM/HM tied both work on their own block (assuming gamma zero)
else
#HM chain is longer, then SM gives up and starts on public chain tip again
hm_won += honest_chain.count
sm_orphans += selfish_chain.count
selfish_block = nil
selfish_chain = []
honest_chain = []
end
total_orphans = sm_orphans + hm_orphans
total_mined = sm_won + hm_won
total_blocks = total_orphans + total_mined
d = 1 - (1.0 * total_orphans / total_blocks) if total_blocks > 0
timestamp = honest_block.timestamp
end
puts "#{alpha},#{total_mined},#{hm_won},#{sm_won},#{(expected = (alpha*(timestamp - start_time)/600)).to_i},#{total_orphans}"
end
puts "alpha,total_mined,hm_won,sm_won,sm_expected,orphans"
(1..49).each do |a|
iterate(100000,a*0.01)
end
@cypherblock
Copy link

cypherblock commented May 2, 2018

There is a problem with the following scenario:

while Loop 1 (this part is fine wait for loop 2)
------------H1
-----S1
Selfish miner found 1 block before honest miner block, so chain counts are even, keep going no rewards.

while Loop 2
------------H1------------H2
-----S1---------S2
Selfish miner finds another block S2 before honest miner H2.
In your code now you still consider both chains even, so again no change, no rewards, no orphans.

But in REAL selfish mining algorithm selfish miner would publish both S2 and S1 as soon as S2 is mined. H1 would get orphaned, H2 would not exist yet. (optionally they can also publish S1 when H1 is found but makes no difference if gamma=0)

Selfish miner is ahead of honest when S2 is found with a private branch length ==2. They publish and orphan H1 and get 2 rewards. But your code does not do this, instead you add on H2 as well and they keep going and then if next loop H3 is before the S3 block you would have honest get 3 rewards and selfish 2 orphans. This is incorrect.

So the above error will throw off all of your statistics.

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