App-Bitcoin-PaperWallet

 view release on metacpan or  search on metacpan

bin/paper-wallet  view on Meta::CPAN

#!/usr/bin/env perl

use v5.12;
use warnings;
use utf8;

use Text::QRCode;
use List::Util qw(pairs);
use App::Bitcoin::PaperWallet;
use Time::Piece;
use Getopt::Long;
use Pod::Usage;

sub get_qr
{
	my ($data) = @_;
	my $arrayref = Text::QRCode->new->plot($data);
	return map { $_ =~ s/\*/â–ˆ/g; $_ } map { join '', map { $_, $_ } @$_ } @$arrayref;
}

sub align_qrs
{
	my ($qr1, $qr2) = @_;
	return @$qr1 unless $qr2;

	my $offset = ' ' x (length $qr1->[0]);

	my @output;
	while (@$qr1 || @$qr2) {
		my $line = shift(@$qr1) // $offset;
		$line .= ' ' x 4;
		$line .= shift(@$qr2) // '';
		push @output, $line;
	}

	return @output;
}

binmode STDIN, ':encoding(UTF-8)';

my $stdout = !!0;
my $filename = 'wallet.txt';
my $auto_entropy = !!0;
my $help = !!0;
my $words = 24;
my $compat_addresses = 1;
my $segwit_addresses = 0;
my $taproot_addresses = 3;
my $network = 'bitcoin';

GetOptions(
	'auto|a' => \$auto_entropy,
	'file|f=s' => \$filename,
	'stdout|o' => \$stdout,
	'words|w=n' => \$words,
	'compat_addrs|c=n' => \$compat_addresses,
	'segwit_addrs|s=n' => \$segwit_addresses,
	'taproot_addrs|t=n' => \$taproot_addresses,
	'network|n=s' => \$network,
	'help|h' => \$help,
);

my $interactive = !$stdout;

sub echo
{
	my ($message) = @_;

	say $message if $interactive;
	return;
}

sub prompt
{
	my ($info) = @_;
	echo $info;

	my $data = <STDIN>;
	chomp $data;

	return $data;
}

sub get_entropy
{
	return prompt "Enter any random entropy: by rolling dice, drawing cards or other means available";
}

sub get_passphrase
{
	return prompt 'Enter passphrase for your private key. Warning: plaintext!';
}

if ($help) {
	pod2usage(1);
}

if (!$stdout && -e $filename) {
	die "wallet file $filename already exists";
}

die 'words must be between 12 and 24'
	unless $words >= 12 && $words <= 24;
die 'words must be divisible by 3'
	unless $words % 3 == 0;
my $entropy_length = 128 + ($words - 12) * 32 / 3;

my $entropy = $auto_entropy ? undef : get_entropy;
my $pass = get_passphrase;

my $bitcoin_data = App::Bitcoin::PaperWallet->generate($entropy, $pass, {
	compat_addresses => $compat_addresses,
	segwit_addresses => $segwit_addresses,
	taproot_addresses => $taproot_addresses,
	entropy_length => $entropy_length,
	network => $network,
});

my @data;
my $id = substr $bitcoin_data->{addresses}[0], -4;

push @data,
	"-- PASSWORD PROTECTED PRIVATE KEY FOR ID $id --",
	$bitcoin_data->{mnemonic},
	'',
	"-- ADDRESSES FOR ID $id --",
	''
;

my @qrs;
for my $addr (@{$bitcoin_data->{addresses}}) {
	push @data, $addr;
	push @qrs, [get_qr $addr];
}

push @data, '';

push @qrs, undef unless @qrs % 2 == 0;
for my $qr (pairs @qrs) {
	push @data,
		align_qrs(@$qr),
		'',
		''
	;
}

push @data, '[Generated ' . localtime->cdate . ']';

if ($stdout) {
	binmode STDOUT, ':encoding(UTF-8)';
	print join "\n", @data;
}
else {
	open my $fh, '>:utf8', $filename
		or die "cannot open $filename";

	print {$fh} join "\n", @data;

	close $fh
		or die "could not close $filename";
}

echo "done - see $filename";

__END__

=head1 NAME

paper-wallet - Script to generate a paper wallet file

=head1 SYNOPSIS

	paper-wallet [OPTIONS]

=head1 OPTIONS

=over

=item -a, --auto

Generate entropy automatically using cryptographically-secure pseudorandom
number generator.

=item -o, --stdout

Do not print to file, use standard output instead. The script will not behave
interactively and instead will wait for data on standard input without
prompting anything. Best used with C<--auto> and a single standard input line
for password, or two lines: one for entropy, one for password

=item -f [FILE], --file [FILE]

Specify filename to print to - default is C<wallet.txt>. Has no effect if
C<--stdout> is passed.

=item -w [NUMBER], --words [NUMBER]

A number of words to be generated. Must be 12, 15, 18, 21 or 24. Default 24.

=item -c [NUMBER], --compat_addrs [NUMBER]

A number of compat addresses to generate, by default 1.

=item -s [NUMBER], --segwit_addrs [NUMBER]

A number of segwit addresses to generate, by default 0.

=item -t [NUMBER], --taproot_addrs [NUMBER]

A number of taproot addresses to generate, by default 3.



( run in 1.388 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )