You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
4.1 KiB

#!/usr/bin/perl
use strict;
use warnings;
use BerkeleyDB;
my $DEBUG = $ENV{DEBUG} || 0;
my %wallet;
my $filename = shift || 'wallet.dat';
my $filesize = -s $filename;
my $db = tie %wallet, 'BerkeleyDB::Btree',
-Filename => $filename,
-Subname => "main",
-Flags => DB_RDONLY,
or die "Cannot open file $filename: $! $BerkeleyDB::Error\n";
my $count = 0;
my $counts = {};
my $avg_xtn_size = 0;
my $meta = {};
# guess the coin a wallet.dat came from
# NOTE: Zcash wallets start out with no shielded addresses
# and look exactly like BTC wallets, except for address prefixes
sub is_zec_prefix { $_[0] eq 't1' or $_[0] eq 't3' }
sub is_kmd_prefix { $_[0] eq 'R' or $_[0] eq 'b' }
sub detect_coin {
my ($c,$meta) = @_;
my $coin = "BTC";
print "Address Prefix=" . $meta->{prefix} . "\n";
my $prefix = $meta->{prefix};
my $p1 = substr($prefix, 0, 1);
if (is_kmd_prefix($p1)) {
$coin = "KMD or asset chain";
} elsif (is_zec_prefix($prefix)) {
$coin = "ZEC/HUSH";
}
# NOTE: Some wallets will have a mix of Sprout
# and Sapling keys in same wallet
if ($c->{sapzkey} or $c->{sapzkeymeta}) {
$coin .= " Sapling";
# only zcash forks have these
} elsif ($c->{zkey} or $c->{zkeymeta}) {
$coin .= " Sprout";
}
return $coin;
}
while (my ($k,$v) = each %wallet) {
my $len = unpack("W", substr($k, 0, 1));
my $type = substr $k, 1, $len;
my $key = substr $k, $len+1;
my $klen = length $key;
my $vlen = length $v;
#printf "%s => %x\n", $k, $v;
if ($DEBUG) {
if ($type eq 'key') {
my $privkey = unpack("H*", $v);
$key = unpack("H*", $key);
print "key=$key, privkey=$privkey\n";
} elsif ($type eq 'cscript') {
my $cscript = unpack("H*", $v);
$key = unpack("H*", $key);
print "key=$key, cscript=$cscript\n";
} elsif ($type eq 'tx') {
# TODO: this could depend on the platform rendering the data
my $tx = reverse(unpack("h*", $key));
my $vtx = unpack("h*", $v);
my $l = length($tx);
my $lvtx= length($vtx);
print "len=$l tx=$tx ($lvtx bytes)\n";
$avg_xtn_size += $vlen;
} elsif ($type eq 'defaultkey') {
my $dkey = unpack("H*", $v);
print "defaultkey=$dkey\n";
} elsif ($type eq 'zkey') {
my $privkey = unpack("H*", $v);
$key = unpack("H*", $key);
print "zkey=$key, privkey=$privkey\n";
} elsif ($type eq 'name') {
print "name: $key, $v\n";
} elsif ($type eq 'bestblock') {
my $v = unpack("H*", $v);
print "$type ($klen,$vlen): ($key => $v)\n";
} elsif ($type eq 'version') {
my $len = length $v;
my $version = unpack("I", $v);
print "version ($len bytes): $version\n";
} elsif ($type eq 'watchs') {
my $key = unpack("H*", $key);
print "$type ($klen,$vlen): ($key => $v)\n";
} elsif ($type eq 'purpose') {
#my $key = unpack("H*", $key);
#TODO: what is this prefix byte?
my $addr = substr $k, $len+2;
my $prefix = substr $addr, 0, 2;
# multiple prefixes should not occur in the same wallet, right?
$meta->{prefix} = $prefix;
print "$type ($klen,$vlen): ($prefix $addr => $v)\n";
}
#printf "$type %s:\n", $key;
}
$counts->{$type}++;
$count++;
}
$avg_xtn_size = $counts->{tx} ? $avg_xtn_size / $counts->{tx} : 0;
printf "\n===== Wallet $filename Stats =====\n";
my @keys = sort { $counts->{$b} <=> $counts->{$a} } keys(%$counts);
my $numkeys = scalar @keys;
for my $k (@keys) {
if ($DEBUG && $k eq 'tx') {
printf "%-25s %s (%.2f bytes avg)\n", $k, $counts->{$k}, $avg_xtn_size;
} else {
printf "%-25s %s\n", $k, $counts->{$k};
}
}
printf "Total: $count keys in $numkeys key types ($filesize bytes on disk)\n";
my $coin = detect_coin($counts,$meta);
print "Coin detection: $coin\n";