Advertisement
Guest User

constant-share-pool.pl

a guest
Jun 1st, 2011
232
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 7.01 KB | None | 0 0
  1. #!/usr/bin/perl
  2. use strict;
  3. use List::Util qw(min);
  4.  
  5. our $difficulty; #also equals the mean number of shares per round
  6. our $block_reward=49; #50-2% pool operator fee
  7. our $constant_fraction=0.5; #fraction of mean round shares that equals the number of latest shares that are rewarded upon round end; should be less than 1
  8. our $cache_multiple=10; #how big should the stack be in memory, in multiples of difficulty
  9. our @shares;
  10. our @segments; #time periods of given difficulty
  11. our @users;
  12. our %users;
  13. our $terminal_fund; #money to be distributed to stragler miners if/when the pool is shut down for good
  14.  
  15. init_pool();
  16. simulate(); #run the simulation for typical usage!
  17.  
  18. sub init_pool{
  19.     $difficulty=get_difficulty();
  20. }
  21. sub get_difficulty{
  22.     #return `bitcoind getdifficulty`;
  23.     return 434883;
  24. }
  25. sub set_difficulty{
  26.     my @shares_copy=@shares;
  27.     my $segment={difficulty => $difficulty, shares => \@shares_copy, num_shares => scalar @shares_copy};
  28.     push @segments, $segment; #archive current segment, start new segment with new difficulty
  29.     @shares=();
  30.     $difficulty=$_[0];
  31. }
  32. sub push_share{
  33. #use user id numbers instead of user name strings as stack elements, to save on memory
  34. #a stack may contain millions of shares, but there are only thousands of users, so better this way
  35.     my $user_id=$users{$_[0]};
  36.     if (! defined $user_id){
  37.     $user_id=@users;
  38.     push @users, $_[0];
  39.     $users{$_[0]}=$user_id;
  40.     }
  41.     push(@shares, $user_id);
  42. }
  43. sub payout{
  44. #call this each time a block is found and round ends
  45. #returns a hashtable with user's fractional reward
  46. #multiply each fractional reward by block reward, e.g. 49BTC, for 50BTC reward minus 1BTC (2%) pool operator fee
  47.     my %user_shares;
  48.     my $constant_shares=$difficulty*$constant_fraction;
  49.     my $pay_shares=$constant_shares;
  50.     while ($pay_shares>0 && @shares>0){ #first, pay for shares in this round
  51.     $user_shares{pop @shares}++;
  52.     $pay_shares--;
  53.     }
  54.     while ($pay_shares>0 && @segments>0){ #next, if there is any rollover, pay for shares in previous rounds
  55.     my $old_segment=pop @segments;
  56.     my @old_shares=@{$old_segment->{shares}};
  57.     my $old_difficulty=$old_segment->{difficulty};
  58.     while ($pay_shares>0 && @old_shares>0){
  59.         $user_shares{pop @old_shares}+= $difficulty/$old_difficulty;
  60.         $pay_shares-= $difficulty/$old_difficulty;
  61.     }
  62.     if (@old_shares>0){
  63.         $old_segment->{shares}=\@old_shares;
  64.         $old_segment->{num_shares}=@old_shares;
  65.         push @segments, $old_segment;
  66.     }
  67.     }
  68.     if ($pay_shares>0 && $terminal_fund<$block_reward){ #if all shares are paid for but money still remains, store it in the pool terminal fund
  69.     my $terminal_shares=min($pay_shares, $constant_shares*(1-$terminal_fund/$block_reward));
  70.     $terminal_fund+=$terminal_shares/$constant_shares*$block_reward;
  71.     $pay_shares-=$terminal_shares;
  72.     }
  73.     my $shares_distributed=$constant_shares-$pay_shares; #IF the shares stack is exhausted, and IF the fund is full, then just split the money equally
  74.     my %user_payouts;
  75.     foreach (keys %user_shares){
  76.     $user_payouts{$users[$_]}=$user_shares{$_}/$shares_distributed*$block_reward; #block reward includes pool operator fee
  77.     }
  78.  
  79.     return \%user_payouts;
  80. }
  81. sub terminate_pool{
  82.     my %terminal_payouts=%{payout()};
  83.     foreach (keys %terminal_payouts){
  84.     $terminal_payouts{$_}*=$terminal_fund/$block_reward;
  85.     }
  86.     $terminal_fund=0;
  87.     return \%terminal_payouts;
  88. }
  89. sub cull_cache{
  90.     my $keep_shares=$difficulty*$cache_multiple;
  91.     my $shares_culled=0;
  92.     my $total_shares=@shares;
  93.     if ($keep_shares<$total_shares){
  94.     foreach (@segments){
  95.         $shares_culled+=$_->{num_shares};
  96.     }
  97.     @segments=(); #clear all old shares
  98.     while($keep_shares<$total_shares && @shares>0){
  99.         shift @shares;
  100.         $total_shares--;
  101.         $shares_culled++;
  102.     }
  103.     }
  104.     for (my $i=@segments-1; $i>=0; $i--){
  105.     my $segment=$segments[$i];
  106.     my $difficulty_ratio=$difficulty/$segment->{difficulty};
  107.     $total_shares+=$segment->{num_shares}*$difficulty_ratio;
  108.     if ($keep_shares<$total_shares){
  109.         while ($i>0){
  110.         $total_shares-=$segments[0]->{num_shares}*$difficulty/$segments[0]->{difficulty};
  111.         $shares_culled+=$segments[0]->{num_shares};
  112.         shift @segments;
  113.         $i--;
  114.         }
  115.         my @segment_shares=@{$segment->{shares}};
  116.         while ($keep_shares<$total_shares && @segment_shares>0){
  117.         shift @segment_shares;
  118.         $total_shares-=$difficulty_ratio;
  119.         $shares_culled++;
  120.         }
  121.         $segment->{shares}=\@segment_shares;
  122.         $segment->{num_shares}=@segment_shares;
  123.         if (@segment_shares==0){
  124.         shift @segments;
  125.         }
  126.         last;
  127.     }
  128.     }
  129.     return ($shares_culled, $total_shares);
  130. }
  131.  
  132.  
  133. ### everything below here is not part of reference implementation ###
  134.  
  135. sub print_status{
  136.     my %accounts=%{$_[0]};
  137.     my %payouts=%{$_[1]};
  138.     my @sorted_users=sort {$accounts{$b} <=> $accounts{$a}} (keys %accounts);
  139.     my $pack_format="A5AA8AA12AA12A";
  140.     printf "top users:\n";
  141.     printf pack $pack_format,"rank"," ","user"," ","payout"," ","balance","\n";
  142.     for (my $i=0; $i<10; $i++){
  143.     my $user=$sorted_users[$i];
  144.     print pack $pack_format, $i, " ", $user, " ", sprintf("%.8f",$payouts{$user}), " ", sprintf("%.8f",$accounts{$user}), "\n";
  145.     }
  146. }
  147.  
  148. sub update_accounts{
  149.     my $accounts=$_[0];
  150.     my %rewards=%{$_[1]};
  151.     foreach (keys %rewards){
  152.     $accounts->{$_}=0 if (! defined $accounts->{$_});
  153.     $accounts->{$_}+=$rewards{$_};
  154.     }
  155. }
  156.  
  157.  
  158. sub simulate{ #run a simulation of normal pool operation with many users and difficulty changes
  159.     my %accounts;
  160.     my $shares_submitted=0;
  161.     my $time=time;
  162.     my $round_start=$time;
  163.     $|=1;
  164.     $SIG{'INT'}='interrupt';
  165.     while(1){
  166.     for (my $i=0; $i<$difficulty/600/10; $i++){ #a pool with 10% of total network hashing capacity
  167.         my $user="user".(int(rand()*10000)); # ten thousand users
  168.         push_share($user);
  169.         $shares_submitted++;
  170.  
  171.         if (rand()*$difficulty<1.00){ #found a block!
  172.         my $hours=int(($time-$round_start)/3600);
  173.         my $minutes=int(($time-$round_start-$hours*3600)/60);
  174.         my $hashrate=int($shares_submitted*(2**32)/($time-$round_start)/10**9);
  175.         my $payouts=payout();
  176.         update_accounts(\%accounts, $payouts);
  177.         printf "\r%s\nround finished in %d:%02d hours with shares=%d, hashrate=%dGHash/s, difficulty=%d, terminal fund=%.6f\n", scalar localtime $time, $hours, $minutes, $shares_submitted, $hashrate, $difficulty, $terminal_fund;
  178.         print_status(\%accounts, $payouts);
  179.         my ($num_culled, $total_shares)=cull_cache();
  180.         printf "%d shares culled, %d shares remaining in cache\n\n", $num_culled, $total_shares;
  181.         $shares_submitted=0;
  182.         $round_start=$time;
  183.         }
  184.     }
  185.     if ($time%3600==0){
  186.         printf "\r%s", scalar localtime $time;
  187.     }
  188.     if ($time%(3600*24*2)==0){
  189.         printf "\r%s\nrescaling difficulty to %d\n", scalar localtime $time, $difficulty*1.1;
  190.         set_difficulty($difficulty*1.1); #just as an example
  191.     }
  192.     $time++;
  193.     }
  194.     sub interrupt{
  195.     printf "%s\nterminating pool...\n", scalar localtime $time;
  196.     my $payouts=terminate_pool();
  197.     update_accounts(\%accounts, $payouts);
  198.     print_status(\%accounts, $payouts);
  199.     exit;
  200.     }
  201. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement