Bitcoin/public point to address

From Rosetta Code
Revision as of 04:13, 29 November 2012 by Grondilu (talk | contribs) (added perl solution)
Bitcoin/public point to address is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Bitcoin uses a specific encoding format to encode the digest of an elliptic curve public point into a short ASCII string. The purpose of this task is to perform such a conversion.

The encoding steps are:

  • take the X and Y coordinate of the given public point, and concatenate them in order to have a 64 byte-longed string ;
  • add one byte prefix equal to 4 (it is a convention for this way of encoding a public point) ;
  • compute the SHA-256 of this string ;
  • compute the RIPEMD-160 of this SHA-256 digest ;
  • compute the checksum of this RIPEMD-160 digest, as described in bitcoin/address validation ;
  • Base-58 encode (see below) the concatenation of the version number (zero in this case), the ripemd digest and the checksum

The base-58 encoding is based on an alphabet of alphanumeric characters (numbers, upper case and lower case, in that order) but without the four characters 0, O, l and I.

Here is an example public point:

X = 0x50863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352
Y = 0x2CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6

The corresponding address should be: 16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM

Nb. The leading '1' is not significant as 1 is zero in base-58. It is however often added to the bitcoin address for various reasons. There can actually be several of them. You can ignore this and output an address without the leading 1.

Extra credit: add a verification procedure about the public point, making sure it belongs to the secp256k1 elliptic curve

Perl

Here we'll use the standard Digest::SHA module, and the CPAN-available Crypt::RIPEMD160. <lang perl>use bigint; use Crypt::RIPEMD160; use Digest::SHA qw(sha256); my @b58 = qw{

     1 2 3 4 5 6 7 8 9
   A B C D E F G H   J K L M N   P Q R S T U V W X Y Z
   a b c d e f g h i j k   m n o p q r s t u v w x y z

}; my $b58 = qr/[@{[join , @b58]}]/x;

sub encode { my $_ = shift; $_ < 58 ? $b58[$_] : encode($_/58) . $b58[$_%58] }

sub public_point_to_address {

   my $x = hex '0x' . shift;
   my $y = hex '0x' . shift;
   my @byte;
   for (1 .. 32) { push @byte, $y % 256; $y /= 256 }
   for (1 .. 32) { push @byte, $x % 256; $x /= 256 }
   @byte = (4, reverse @byte);
   my $hash = Crypt::RIPEMD160->hash(sha256 join , map { chr } @byte);
   my $checksum = substr sha256(sha256 chr(0).$hash), 0, 4;
   my $value = 0;
   for ( (chr(0).$hash.$checksum) =~ /./gs ) { $value = $value * 256 + ord }
   (sprintf "%33s", encode $value) =~ y/ /1/r;

}

print public_point_to_address ;

__DATA__ 50863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B2352 2CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6 </lang>

Output:
16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM