App-NoodlePay
view release on metacpan or search on metacpan
bin/noodlepay.pl view on Meta::CPAN
#!/usr/bin/perl
# -*-cperl-*-
#
# noodlepay.pl - Convenient way to securely send Bitcoin from cold storage
# Copyright (c) Ashish Gulhati <noodlepay at hash.neo.tc>
#
# $Id: bin/noodlepay.pl v1.007 Tue Oct 16 22:06:51 PDT 2018 $
use warnings;
use Wx qw (:everything);
use Wx::Event qw (EVT_BUTTON);
use LWP::UserAgent;
use HTTP::Request;
use GD::Barcode::QRcode;
use Math::Prime::Util qw(fromdigits todigitstring);
use vars qw( $VERSION $AUTOLOAD );
my $electrum = 'electrum';
#my $electrum = 'python ~/src/Electrum-2.8.2/electrum';
# Initialize l8n
my ($lang, %lang) = initl8n();
# Initialize UI
my ($app, $frame, $topSizer, $boxSizer, $statusBar, %button) = initui();
# Actions for all the buttons
my %action;
$action{SendSign} = sub { # Send bitcoin / Sign transaction
if ($ARGV[0] and $ARGV[0] eq '--offline') {
system ('v4l2-ctl --set-fmt-overlay=width=400,top=0,left=0 --overlay=1');
open (ZBAR, "zbarcam --nodisplay --prescale=640x480 /dev/video0 |");
my $x = <ZBAR>; chomp $x; $x =~ s/^QR-Code://;
system ('killall -9 zbarcam');
close ZBAR;
system ('v4l2-ctl --overlay=0');
my $tx = "{\n\"complete\": false,\n\"final\": true,\n\"hex\": \"$x\"\n}";
my $txdetails = `$electrum deserialize '$tx'`; # Check transaction details
my $chgaddresses = `$electrum listaddresses --change`;
my @chgaddresses = map { /\"(\S+)\"/; $1; } split /,\s*/, $chgaddresses;
$txdetails =~ /\"inputs\": (.*)\"outputs:\"/s; my $inputs = $1;
$txdetails =~ /\"outputs\": (.*)/s; my $outputs = $1;
my @outputs = map { /\"address\": \"(\S+?)\".*\"value\": (\d+)/s; ($1 => $2) } split /\}\,/s, $outputs;
my @inputs = map { /\"address\": \"(\S+?)\".*\"value\": (\d+)/s; ($1 => $2) } split /\}\,/s, $inputs;
my $chgtotal = 0;
for (2..$#outputs) {
$chgtotal += $outputs[$_], next if $_ % 2; my $o = $outputs[$_];
$err = 1, last unless grep { /^$o$/ } @chgaddresses;
}
# my $feeamt = $total - $chgtotal;
unless ($err) {
my $dialog = Wx::MessageDialog->new( $frame, "Sign transaction of $outputs[1] Satoshi to $outputs[0]?", "Confirm", wxOK|wxCANCEL);
return if $dialog->ShowModal == wxID_CANCEL;
my $signedtx = `$electrum signtransaction '$tx'`;
$signedtx =~ /"hex": "(\S+)"/s;
$dialog = qrdialog(fromdigits($1,16), 'Scan the QR code on Noodle Pi', 'Broadcast Transaction');
$dialog->ShowModal;
}
}
else {
my $dialog = Wx::TextEntryDialog->new( $frame, "Enter amount (in Satoshi)", "Send Bitcoin", "", wxOK|wxCANCEL, [70,280] );
system ('xvkbd -geometry 480x250+0+520 -keypad&');
my $ret = $dialog->ShowModal;
system ('killall -9 xvkbd');
return if $ret == wxID_CANCEL;
my $amount = $dialog->GetValue(); return unless $amount =~ /^\d+$/; $amount = sprintf("%f",$amount / 100000000);
system ('v4l2-ctl --set-fmt-overlay=width=400,top=0,left=0 --overlay=1');
open (ZBAR, "zbarcam --nodisplay --prescale=640x480 /dev/video0 |");
my $sendto = <ZBAR>;
system ('killall -9 zbarcam');
close ZBAR;
system ('v4l2-ctl --overlay=0');
chomp $sendto; $sendto =~ s/^QR-Code://; $sendto =~ s/^bitcoin://;
my $ProgressDialog = Wx::ProgressDialog->new("Send Money", "Creating transaction", 3, $frame,
wxPD_AUTO_HIDE | wxPD_APP_MODAL );
$ProgressDialog->Update(0,"Checking balance...");
my $balance = `$electrum getbalance`;
if (defined $balance and $balance) {
$balance =~ /"confirmed": "(\S+)"/s; $balance = $1 * 100000000;
# TODO: Return error if wallet balance lower than send amount
$ProgressDialog->Update(1,"Creating transaction...");
my $tx = `$electrum payto $sendto $amount -f 0 -u`;
$ProgressDialog->Update(2,"Looking up fees...");
$tx =~ /"hex": "(\S+)"/s; my $txsize = length($1)/2 + 65;
my $ua = new LWP::UserAgent; $ua->agent('Mozilla/5.0');
my $req = HTTP::Request->new(GET => 'https://bitcoinfees.earn.com/api/v1/fees/recommended');
my $res = $ua->request($req);
my $fees = $res->content; $fees =~ s/\{\s*(.*)\s*\}/$1/; $fees =~ s/\"//g;
my %fees = split /[:,]\s*/, $fees;
my $fastest_fee = $fees{fastestFee} * $txsize;
my $halfhour_fee = $fees{halfHourFee} * $txsize;
my $hour_fee = $fees{hourFee} * $txsize;
$ProgressDialog->Update(3);
$dialog = Wx::TextEntryDialog->new( $frame, "Enter fee amount (in Satoshi). Recommended fees:\n\n" .
"- Fastest (10-20 mins): $fastest_fee\n- Within half an hour: $halfhour_fee\n" .
"- Within an hour: $hour_fee\n", "Send Bitcoin", $fastest_fee, wxOK|wxCANCEL, [50, 180]);
system ('xvkbd -geometry 480x250+0+520 -keypad&');
$ret = $dialog->ShowModal;
system ('killall -9 xvkbd');
return if $ret == wxID_CANCEL;
my $feeamt = $dialog->GetValue(); return unless $feeamt =~ /^\d+$/; $feeamt = sprintf("%f",$feeamt / 100000000);
my $signedtx;
if ($ARGV[0] and $ARGV[0] eq '--online') {
$ProgressDialog = Wx::ProgressDialog->new("Send Money", "Signing transaction", 1, $frame,
wxPD_AUTO_HIDE | wxPD_APP_MODAL );
$ProgressDialog->Update(0,"Signing transaction...");
$signedtx = `$electrum payto $sendto $amount -f $feeamt`;
$signedtx =~ s/\"final\": false/\"final\": true/;
$ProgressDialog->Update(1);
}
else {
$tx = `$electrum payto $sendto $amount -f $feeamt -u`;
$tx =~ /"hex": "(\S+)"/s;
my $dialog = qrdialog(fromdigits($1,16), 'Scan the QR code on Noodle Air', 'Sign Transaction');
$dialog->ShowModal;
system ('v4l2-ctl --set-fmt-overlay=width=400,top=0,left=0 --overlay=1');
open (ZBAR, "zbarcam --nodisplay --prescale=640x480 /dev/video0 |");
my $signed = <ZBAR>; chomp $signed; $signed =~ s/^QR-Code://; $signed = todigitstring($signed,16);
system ('killall -9 zbarcam');
close ZBAR;
system ('v4l2-ctl --overlay=0');
$signedtx = "{\n\"complete\": true,\n\"final\": true,\n\"hex\": \"$signed\"\n}";
}
$dialog = Wx::MessageDialog->new( $frame, "Broadcast transaction?", "Confirm", wxOK|wxCANCEL);
return if $dialog->ShowModal == wxID_CANCEL;
my $id = `$electrum broadcast '$signedtx'`;
$dialog = Wx::MessageDialog->new( $frame, "Transaction ID is $id", "Payment Sent", wxOK);
$dialog->ShowModal;
}
}
show();
};
$action{Exit} = sub { # Exit
$statusBar->SetStatusText("Exit", 0);
my $dialog = Wx::MessageDialog->new( $frame, "Do you really want to exit?", "Confirm", wxOK|wxCANCEL);
return if $dialog->ShowModal == wxID_CANCEL;
$app->ExitMainLoop;
};
EVT_BUTTON($frame, $button{$_}, $action{$_}) for (qw(SendSign Exit));
# Start the UI
show();
$statusBar->SetStatusText(_('Noodle Pay Ready'), 0);
( run in 0.866 second using v1.01-cache-2.11-cpan-acf6aa7dc9e )