File Coverage

blib/lib/Business/HashCash.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # -*-cperl-*-
2             #
3             # Business::HashCash - Accept HashCash payments online
4             # Copyright (c) 2017 Ashish Gulhati
5             #
6             # $Id: lib/Business/HashCash.pm v1.003 Fri Jun 16 02:43:24 PDT 2017 $
7              
8             package Business::HashCash;
9              
10 1     1   14289 use warnings;
  1         2  
  1         29  
11 1     1   4 use strict;
  1         2  
  1         25  
12              
13 1     1   217 use Crypt::HashCash qw(_dec);
  0            
  0            
14             use Crypt::HashCash::Client;
15             use Crypt::HashCash::Stash;
16             use Crypt::HashCash::Coin;
17             use vars qw( $VERSION $AUTOLOAD );
18              
19             our ( $VERSION ) = '$Revision: 1.003 $' =~ /\s+([\d\.]+)/;
20              
21             sub new {
22             my ($class, %arg) = @_;
23             return undef unless my $client = new Crypt::HashCash::Client;
24             bless { stash => $arg{Stash},
25             vaults => $arg{Vaults},
26             client => $client
27             }, $class;
28             }
29              
30             sub verify {
31             my ($self, $coinsin) = @_;
32             my $client = $self->client; my $stash = $self->stash; my %fee = %{$client->keydb->{fees}};
33             $coinsin =~ /^5b235d(52|45)([0-9a-f]{32})(.*)$/;
34             my ($sigscheme, $vaultid, $coins, $amt, @coinstrs, @coins) = ($1, _dec($2), $3);
35             my $coinsize = ($sigscheme == 52) ? 296 : 170;
36             while (my $coinstr = substr($coins, 0, $coinsize, '')) { push @coinstrs, $coinstr }
37             for (@coinstrs) {
38             my $coin = Crypt::HashCash::Coin->from_hex($_);
39             push @coins, $coin if $coin;
40             }
41             for my $coin (@coins) {
42             return undef unless $client->verify_coin($coin);
43             }
44             my $numcoins = scalar @coins; my ($denoms, $d); # TODO: populate $denoms
45             my $fee = $numcoins * ($fee{vf} + $fee{mf}) + int($amt * ($fee{mp} + $fee{vp}));
46             $fee = $fee + ($client->denoms->[0] - ($fee % $client->denoms->[0]));
47             return '-EFEE' if $fee > $self->stash->balance;
48             return '-ELOSSYTX' if $fee >= $amt;
49             my ($feecoins, $change) = $stash->getcoins($fee);
50             my ($numchgcoins, $chgdenoms) = (0); ($chgdenoms, $numchgcoins) = breakamt(-$change) if $change;
51             my %coins; for (@coins) { $coins{$_->d}++ }
52             return '-EVAULT' unless my $res =
53             $client->initexchange( Coins => \%coins, # Denominations of coins being exchanged
54             ReqDenoms => $denoms, # Denominations of coins being requested
55             ChangeDenoms => $chgdenoms, # Denominations of change coins from fee payment
56             ReplaceDenoms => $d, # Denominations of exchange coins replaced by change coins
57             FeeCoins => $feecoins ); # The fee coins
58             return $res if $res =~ /^-E/;
59             my @inits = split / /, $res; my $i = 0;
60             my @requests;
61             for my $denom (keys %{$denoms}) {
62             for (1..$denoms->{$denom}) {
63             push @requests, $client->request_coin( Denomination => $denom, Init => $inits[$i++] );
64             }
65             }
66             my @changereqs;
67             for my $denom (keys %{$chgdenoms}) {
68             for (1..$chgdenoms->{$denom}) {
69             push @changereqs, $client->request_coin( Denomination => $denom, Init => $inits[$i++] );
70             }
71             }
72             my %feecoins; for (@$feecoins) { $feecoins{$_->d}++ }
73             $res = $client->exchange( FeeCoins => \%feecoins, Coins => \@coins, Requests => \@requests, ChangeRequests => \@changereqs );
74             return '-EVAULT' unless $res;
75             return $res if $res =~ /^-E/;
76             $res =~ s/\s*$//;
77             my $vcoins = [ map { Crypt::HashCash::Coin::Blinded->from_string($_) } split / /, $res ];
78             for (@$vcoins) {
79             my $c = $client->unblind_coin($_);
80             if ($client->verify_coin($c)) {
81             $stash->addcoins('V',$c);
82             }
83             }
84             }
85              
86             sub AUTOLOAD {
87             my $self = shift; (my $auto = $AUTOLOAD) =~ s/.*:://;
88             return if $auto eq 'DESTROY';
89             if ($auto =~ /^(debug|client|stash)$/x) {
90             $self->{$auto} = shift if (defined $_[0]);
91             return $self->{$auto};
92             }
93             else {
94             die "Could not AUTOLOAD method $auto.";
95             }
96             }
97              
98             =head1 NAME
99              
100             Business::HashCash - Accept HashCash payments online
101              
102             =head1 VERSION
103              
104             $Revision: 1.003 $
105             $Date: Fri Jun 16 02:43:24 PDT 2017 $
106              
107             =head1 SYNOPSIS
108              
109             use Business::HashCash;
110              
111             my $bizhc = new Business::HashCash (Stash => '/tmp/bizhc.db',
112             Vaults => '/tmp/vaults');
113              
114             print 'Please input HashCash coins for $amount, and press ';
115             my $coins = readline(*STDIN);
116              
117             my $verified = $bizhc->verify($coins);
118              
119             print $verified ? "Thanks for your order.\n" : "Error: coins failed verification\n";
120              
121             =head1 CONSTRUCTOR
122              
123             =head2 new
124              
125             =head1 METHODS
126              
127             =head2 verify
128              
129             =head1 AUTHOR
130              
131             Ashish Gulhati, C<< >>
132              
133             =head1 BUGS
134              
135             Please report any bugs or feature requests to C, or through
136             the web interface at L. I will be notified, and then you'll
137             automatically be notified of progress on your bug as I make changes.
138              
139             =head1 SUPPORT
140              
141             You can find documentation for this module with the perldoc command.
142              
143             perldoc Business::HashCash
144              
145             You can also look for information at:
146              
147             =over 4
148              
149             =item * RT: CPAN's request tracker
150              
151             L
152              
153             =item * AnnoCPAN: Annotated CPAN documentation
154              
155             L
156              
157             =item * CPAN Ratings
158              
159             L
160              
161             =item * Search CPAN
162              
163             L
164              
165             =back
166              
167             =head1 LICENSE AND COPYRIGHT
168              
169             Copyright (c) 2016-2017 Ashish Gulhati.
170              
171             This program is free software; you can redistribute it and/or modify it
172             under the terms of the Artistic License 2.0.
173              
174             See L for the full
175             license terms.