Skip to content

Instantly share code, notes, and snippets.

@mfontani
Created June 27, 2011 08:16
Show Gist options
  • Save mfontani/1048491 to your computer and use it in GitHub Desktop.
Save mfontani/1048491 to your computer and use it in GitHub Desktop.
A (test,simplistic) Perl bitcoin miner
#!/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";
}
@taleso
Copy link

taleso commented Aug 31, 2012

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!

@ap0calypse
Copy link

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