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 )