I've been rediscovering Perl recently. It's the coolest programming language I know.
#!/usr/bin/perl
use strict;
use warnings;
use Digest::SHA qw(sha256);
my @base58 = (1 .. 9, 'A' .. 'H', 'J' .. 'N', 'P' .. 'Z', 'a' .. 'k', 'm' .. 'z');
my $base58 = join '', @base58;
sub decodeBase58($) {
my $_ = shift;
die "wrong format" if not m{^[$base58]+$};
for my $c (0..57) { s/$base58[$c]/ $c/g }
s/ /+58*/g;
return qx{dc -e "16o0d$_+n"};
}
sub encodeBase58($) {
my $_ = uc shift;
open my $bc, qq[echo "ibase=16; n=$_; while(n>0) { n%3A; n/=3A }" | bc |];
return join '', map { $base58[$_] } (reverse <$bc>);
}
sub checksum($) { return uc unpack 'H8', sha256 sha256 pack 'H*', shift }
sub checkBitcoinAddress($) {
my $address = shift;
die "wrong format" if $address !~ qr(^[$base58]{34,35}$);
(my $h = sprintf "%50s", decodeBase58 $address) =~ tr/ /0/;
(checksum substr $h, 0, -8) eq substr $h, -8;
}
sub hash160($) {
# I have to use the shell here for there is no rmd160 in Digest:: :-(
my $h = shift;
open my $sh, qq{
echo -n "$h" |
openssl dgst -sha256 -binary |
openssl dgst -rmd160 -binary |
};
return uc unpack 'H*', <$sh>;
}
sub hash160ToAddress($) {
my $_ = shift;
my $checksum = checksum "00$_";
$_ = sprintf "%34s", encodeBase58 "00$_$checksum";
tr/ /1/;
return $_;
}
sub publicKeyToAddress($) {
# Couldn't find a Perl library for ECDSA,
# so I use the shell here.
my $_ = shift;
return hash160ToAddress hash160 qx{
echo "$_" |
openssl ec -pubin -pubout -outform DER 2>&- |
tail -c 65
}
}