Created
June 27, 2011 08:16
-
-
Save mfontani/1048491 to your computer and use it in GitHub Desktop.
A (test,simplistic) Perl bitcoin miner
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 perl | |
use 5.010_001; | |
use strict; | |
use warnings; | |
use Digest::SHA; | |
use JSON::XS; | |
use WWW::Mechanize; | |
use Time::HiRes qw<time gettimeofday tv_interval>; | |
use Getopt::Long; | |
# How would you like your nonces tested? | |
my %range_choice = ( | |
#'linear' => sub { 0..(2**32-1) }, | |
'test1' => sub { (389330900 .. 389330999) }, | |
); | |
my $wanted_range_algorithm = 'linear'; | |
$wanted_range_algorithm = 'test1', | |
# A test: the find_share() should be given the "result" part of a getwork: | |
# curl -v -X POST -d '{"params":[],"method":"getwork","id":"json"}' http://user:pass@pool-server.com:port/ | |
# ... | |
# X-Long-Polling: /LP | |
# X-Roll-NTime: Y | |
# {"id":"json","error":null,"result":{"midstate":"...","target":"...","data":"...","hash1":"..."}} | |
find_share( | |
# Logged this getwork and the share found: | |
# Result 0000000053bafea4b45bdb13be58dc714c90f79a65f98e5ff2a70ef3168948ad (nonce 389330932): accepted | |
{ | |
"midstate" => "9f452bf09f82b78cc99b1277125c77844bbcfa382e294136a73a56a27188f585", | |
"data" => | |
"00000001e5b5979c98525e7bdde6bc8a4ba5d64ac0acbd7f501f474500000a0b00000000391393c3c94aacbb837347074f6e4fd62aa861536b1db5404f4a8441e214c29e4e051fdb1a0c2a1200000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000080020000", | |
"hash1" => | |
"00000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000010000", | |
"target" => "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000", | |
}, | |
); | |
die; | |
my %opts = ( | |
host => '127.0.0.1', | |
port => '', | |
user => '', | |
pass => '', | |
url => '', | |
); | |
my $rc = GetOptions( | |
'host=s' => \$opts{host}, | |
'port=i' => \$opts{port}, | |
'user=s' => \$opts{user}, | |
'pass=s' => \$opts{pass}, | |
'url=s' => \$opts{url}, | |
); | |
$opts{url} = shift unless $opts{url}; | |
delete $opts{url} unless $opts{url}; | |
if ( length $opts{url} ) { | |
die "URL doesn't look like http://user:pass\@host:port/\n" | |
unless $opts{url} =~ /^(?<protocol>http):\/\/(?<user>[^:]+):(?<pass>[^@]+)@(?<host>[^:]+):(?<port>\d+)\/?$/; | |
$opts{$_} = $+{$_} for (qw<host port user pass>); | |
} | |
die "Need --host HOSTNAME\n" unless length $opts{host}; | |
die "Need --port PORT\n" unless length $opts{port}; | |
die "Need --user USERNAME\n" unless length $opts{user}; | |
die "Need --pass PASSWORD\n" unless length $opts{pass}; | |
sub get_work { | |
my $json = encode_json( { id => 'json', method => 'getwork', params => $_[0] ? $_[0] : [] } ); | |
my $mech = WWW::Mechanize->new; | |
$mech->credentials( $opts{user}, $opts{pass} ); | |
my $rc = $mech->post( "http://$opts{host}:$opts{port}/", Content => $json ); | |
die "Error submitting $json to http://$opts{host}:$opts{port}: ", $rc->status_line, "\n", $rc->content, "\n" | |
unless $rc->is_success; | |
warn "Get_work ($json): returned: ", $rc->decoded_content, "\n"; | |
my $result = decode_json( $rc->decoded_content ); | |
return $result; | |
} | |
my $max_nonce = hex("0xfffffffa"); | |
sub sha256 { Digest::SHA::sha256( @_ ) } | |
sub find_share { | |
my $data = shift; | |
my $raw = pack 'H*', $data->{data}; | |
my $short = substr( $raw, 0, 80 ); # first 80 bytes | |
my $short_le = pack "N*", unpack "V*", $short; # to Little Endian | |
my @range = $range_choice{$wanted_range_algorithm}->(); | |
my @solutions; | |
for my $i_nonce (@range) { | |
my $nonce_le = pack 'N*', $i_nonce; | |
#print "Doing nonce $i_nonce ($nonce_le)\n"; | |
substr( $short_le, 76, 4 ) = $nonce_le; | |
# calculate sha256(sha256( $data )) | |
# UNoptimized: the optimized version should use the "midstate" | |
my $hash1 = sha256($short_le); | |
my $hash = sha256( sha256($short_le) ); | |
#my $hash1_be = pack "N*", unpack("V*", $hash1); # BE again | |
#my $hash_be = pack "N*", unpack("V*", $hash); | |
#print "hash1: ", unpack("H*", $hash1_be), "\n"; | |
#print "hash: ", unpack("H*", $hash_be), "\n"; | |
my $zero_starting = unpack( 'H*', reverse $hash ); | |
if ( $zero_starting =~ /^00000000/ ) { | |
warn "Found solution: $zero_starting (nonce $i_nonce)\n"; | |
push @solutions, $hash; | |
} | |
} | |
return @solutions; | |
# Solutions should be given back in Big Endian by the caller? | |
} | |
while (1) { | |
my $work = get_work(); | |
die "Error fetching work! ", $work->{error} // 'unknown error' if (!$work or $work->{error}); | |
my $start = time; | |
my (@found) = find_share( $work->{result} ); | |
if ( @found ) { | |
warn "Found share -- @found -- "; | |
# submit it using $work->{data} | |
} | |
my $end = time; | |
warn "Processed in ", ($end-$start), "\n"; | |
} |
thank you very much for publishing this :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello my friend, when you set the Range in this part of code 'test1' => sub { (389330900 .. 389330999) }, this values is for calc the difficulty stored in blocks or to calc the hash codes of tranasctions ? Thank you so much!