I found an old USB "ASIC Miner Block Erupter" thing in my box of old hardware that waits for the day i find a new purpose for it. This crypto dongle does about 330 Megahashes per second. Not planning to use it to mine bitcoin, but it could be a nice proof-of-concept hardware for a blockchain thing i'm playing around with. No commercial purpose, just me playing around on how a blockchain could be integrated with a project that uses a PostgreSQL database. Basically, just me looking into how crypto blockchains work, but hating the idea of using a big-ass file when you could use SQL ;-)

I normally wouldn't even play with mining hardware devices because they waste tons of power and they are bad for the environment. But this little toy only uses about 2.5 Watts, so i figured i could experiment with it without boiling the oceans.

Before we can use that device for anything, we'd better find out how to communicate with it. There isn't much technical info on the internet about the hardware. Most articles are all about "yadayada blockchains are the future yadayada get rich quick yadayada bitcoin not a pyramid scheme yadayada". I found some very old discussions that mention it uses the Icarus protocol, whatever that is.

Another few minutes of googling found this old article. It describes the "Communication protocol V3". It's not very detailed, but it says what data to send and what data comes back. Except it only mentions "last 12 bytes of block header", whatever that entails.

Eventually, Google found me this post, which helped me in finding out what those mysterious 12 bytes are supposed to be. Timestamp and Nonce are easy, these are just basically 8 bytes i can play around with. The Difficulty target supposedly changes on how difficult it is for the hardware to find a matching block.

To be honest, i have absolutely no idea how that "difficulty" is encoded and what it does on my specific hardware. Changing the value doesn't seem to change on how fast i get the result on average, it just changes the results. I'm probably doing something wrong or i'm missing a step or something. So, if you have any ideas/bugfixes/patches or just want to shout at me for doing something incredibly stupid, feel free to write a comment :-)

Ok, here is the code i have. It's not pretty, it's full of debug stuff. And i'm doing quite a bit of data manipulation the hard way, just to make it easier for me to play around with the bits. But, as a proof of concept, it should be a decent start for you to play around with.

#!/usr/bin/env perl use strict; use warnings; use Crypt::Digest::SHA256 qw(sha256); use Device::SerialPort qw( :PARAM :STAT 0.07 ); use English; use Carp; use Time::HiRes qw(time); use Data::Dumper; my $miner = Device::SerialPort->new('/dev/ttyUSB0') or croak("Modem er +ror $ERRNO"); $miner->baudrate(115_200); $miner->parity('none'); $miner->databits(8); $miner->stopbits(1); # One cycle takes about 12 seconds and there seems no way to # reset the thing. Hardware was designed by crypto-currency people, # so naturally, it is decently fast, very inefficient and has a # very clunky, amateurish interface. # # So, just wait the length of a cycle and empty # the result bytes. initHardware(); my @times; for(my $i = 0; $i < 5; $i++) { my $starttime = time; my $hash; my $loopcount = 0; while(1) { $loopcount++; my $rawdata = getRandomData(); if(1) { # use random data and proper timestamps $hash = generateHash($rawdata, $starttime, $loopcount); } else { # Use fixed data for validation between multiple runs $hash = generateHash('BLA', $i, $loopcount); } if(defined($hash)) { last; } } my $endtime = time; my $elapsed = $endtime - $starttime; my $decodedhash = unpack("H*", $hash); print "Calculated result $i: $decodedhash in $elapsed seconds\n"; push @times, $elapsed; } print "\n"; my $total; foreach my $val (@times) { $total += $val; } $total /= scalar @times; print "Took on average $total seconds to calculate a result.\n"; exit(0); sub initHardware { # This "cleans out" the hardware by running one hash without any n +ew hashing request. # This has the effect of making sure any active loop has time to f +inish and we empty # out all result bytes from the buffer my $targettime = time + 13.5; print "Hardware initialization...\n"; # Remove previous results from queue (is any) while(time < $targettime) { my ($count, $data) = $miner->read(1); if(!$count) { next; } print "! ", ord($data), "\n"; } print "Ready for work!\n"; return; } sub generateHash { my ($data, $timestamp, $nonce) = @_; my $request = buildRequest($data, $timestamp, $nonce); $miner->write($request); my @resultbytes; my $targettime = time + 13.5; # Remove previous results from queue (is any) while(1) { my ($count, $data) = $miner->read(1); if(!$count) { last; } print "! ", ord($data), "\n"; } while(time < $targettime) { my ($count, $data) = $miner->read(1); if(!$count) { next; } my $bytecount = 0; #print "$bytecount > ", ord($data), "\n"; $bytecount++; push @resultbytes, $data; } if(scalar @resultbytes < 4) { # No result; return; } my $result = ''; for(1..4) { $result .= shift @resultbytes; } return $result; } sub buildRequest { my ($data, $timestamp, $nonce) = @_; my $source = sha256($data); # Add fill my $fill = "0" x 160; $fill = pack("B*", $fill); $source .= $fill; $source .= toBinary($timestamp); # timestamp # Difficulty target in "bits" encoding. No idea how that really wo +rks, though. # If i didn't do something stupid, i just copied the one used to m +ine the first bitcoin. $source .= toBitsBinary("1d00ffff"); $source .= toBinary($nonce); # Nonce return $source; } sub toBinary { my ($val) = @_; if(1) { return pack('N', $val); } else { # "shuffle bytes by hand" version for easier experimentation # compared to fiddling with the fast but confusing pack/unpack + function my @bytes; for(1..4) { unshift @bytes, chr($val & 0xff); $val >>= 8; } return join('', @bytes); } } sub toBitsBinary { my ($val) = @_; my @parts = unpack"(A2)*", $val; my @bytes; foreach my $part (@parts) { push @bytes, chr(hex($part)); } return join('', @bytes); } sub getRandomData { my @validchars = split//, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; my $token = 'XPD'; for(1..500) { $token .= $validchars[rand @validchars]; } $token .= 'VT'; return $token; }

So, if you have even a basic understanding about the internals of Bitcoin, i appreciate if you can show me on how to properly calculate the difficulty bits and verify that i have got a valid result. And if you see any error in my protocol implementation, i'd also be glad for any help you can give me.

perl -e 'use Crypt::Digest::SHA256 qw[sha256_hex]; print substr(sha256_hex("the Answer To Life, The Universe And Everything"), 6, 2), "\n";'

In reply to Implementing the "Icarus" crypto mining protocol for some old hardware by cavac

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.