File Coverage

blib/lib/Crypto/NanoRPC.pm
Criterion Covered Total %
statement 17 82 20.7
branch 0 26 0.0
condition n/a
subroutine 6 16 37.5
pod 0 8 0.0
total 23 132 17.4


line stmt bran cond sub pod time code
1             package Crypto::NanoRPC;
2              
3 1     1   55279 use 5.018001;
  1         3  
4 1     1   4 use strict;
  1         2  
  1         15  
5 1     1   4 use warnings;
  1         1  
  1         18  
6 1     1   373 use HTTP::Request;
  1         18568  
  1         29  
7 1     1   554 use LWP::UserAgent;
  1         20627  
  1         31  
8 1     1   611 use JSON;
  1         8279  
  1         4  
9              
10             require Exporter;
11             our @ISA = qw(Exporter);
12             our %EXPORT_TAGS = ( 'all' => [] );
13             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
14             our @EXPORT = ();
15              
16             our $VERSION = '0.9';
17              
18             my $rpc_actions = {
19             # Node RPC's
20             account_balance => [ 'account' ],
21             account_block_count => [ 'account' ],
22             account_get => [ 'key' ],
23             account_history => [ 'account', 'count', [ 'raw', 'head', 'offset', 'reverse' ] ],
24             account_info => [ 'account', [ 'representative', 'weight', 'pending' ] ],
25             account_key => [ 'account' ],
26             account_representative => [ 'account' ],
27             account_weight => [ 'account' ],
28             accounts_balances => [ 'accounts' ], # accounts is reference to a list
29             accounts_frontiers => [ 'accounts' ],
30             accounts_pending => [ 'accounts', 'count' ],
31             active_difficulty => [],
32             available_supply => [],
33             block_account => [ 'hash' ],
34             block_count => [],
35             block_count_type => [],
36             block_confirm => [ 'hash' ],
37             block_create => [ 'type', 'balance', 'representative', 'previous', [ 'account', 'wallet', 'source', 'destination', 'link', 'json_block', 'work' ] ],
38             block_hash => [ 'block' ],
39             block_info => [ 'hash', [ 'json_block' ] ],
40             blocks => [ 'hashes' ],
41             blocks_info => [ 'hashes', [ 'pending', 'source', 'balance', 'json_block' ] ],
42             bootstrap => [ 'address', 'port' ],
43             bootstrap_any => [],
44             bootstrap_lazy => [ 'hash', [ 'force' ] ],
45             bootstrap_status => [],
46             chain => [ 'block', 'count', [ 'offset', 'reverse' ] ],
47             confirmation_active => [ [ 'announcements' ] ],
48             confirmation_height_currently_processing => [],
49             confirmation_history => [ [ 'hash' ] ],
50             confirmation_info => [ 'root', [ 'contents', 'representatives', 'json_block' ] ],
51             confirmation_quorum => [ [ 'peer_details' ] ],
52             database_txn_tracker => [ 'min_read_time', 'min_write_time' ],
53             delegators => [ 'account' ],
54             delegators_count => [ 'account' ],
55             deterministic_key => [ 'seed', 'index' ],
56             frontier_count => [],
57             frontiers => [ 'account', 'count' ],
58             keepalive => [ 'address', 'port' ],
59             key_create => [],
60             key_expand => [ 'key' ],
61             ledger => [ 'account', 'count', [ 'representative', 'weight', 'pending', 'modified_since', 'sorting' ] ],
62             node_id => [],
63             node_id_delete => [],
64             peers => [ [ 'peer_details' ] ],
65             pending => [ 'account', [ 'count', 'threshold', 'source', 'include_active', 'sorting', 'include_only_confirmed' ] ],
66             pending_exists => [ 'hash', [ 'include_active', 'include_only_confirmed' ] ],
67             process => [ 'block', [ 'force', 'subtype', 'json_block' ] ],
68             representatives => [ [ 'count', 'sorting' ] ],
69             representatives_online => [ [ 'weight' ] ],
70             republish => [ 'hash', [ 'sources', 'destinations' ] ],
71             sign => [ 'block', [ 'key', 'wallet', 'account', 'json_block' ] ],
72             stats => [ 'type' ],
73             stats_clear => [],
74             stop => [],
75             successors => [ 'block', 'count', [ 'offset', 'reverse' ] ],
76             validate_account_number => [ 'account' ],
77             version => [],
78             unchecked => [ 'count' ],
79             unchecked_clear => [],
80             unchecked_get => [ 'hash', [ 'json_block' ] ],
81             unchecked_keys => [ 'key', 'count', [ 'json_block' ] ],
82             unopened => [ [ 'account', 'count' ] ],
83             uptime => [],
84             work_cancel => [ 'hash' ],
85             work_generate => [ 'hash', [ 'use_peers', 'difficulty' ] ],
86             work_peer_add => [ 'address', 'port' ],
87             work_peers => [],
88             work_peers_clear => [],
89             work_validate => [ 'work', 'hash', [ 'difficulty' ] ],
90              
91             # Wallet RPC's
92             account_create => [ 'wallet', [ 'index', 'work' ] ],
93             account_list => [ 'wallet' ],
94             account_move => [ 'wallet', 'source', 'accounts' ],
95             account_remove => [ 'wallet', 'account' ],
96             account_representative_set => [ 'wallet', 'account', 'representative', [ 'work' ] ],
97             accounts_create => [ 'wallet', 'count', [ 'work' ] ],
98             password_change => [ 'wallet', 'password' ],
99             password_enter => [ 'wallet', 'password' ],
100             password_valid => [ 'wallet' ],
101             receive => [ 'wallet', 'account', 'block', [ 'work' ] ],
102             receive_minimum => [],
103             receive_minimum_set => [ 'amount' ],
104             search_pending => [ 'wallet' ],
105             search_pending_all => [],
106             send => [ 'wallet', 'source', 'destination', 'amount', [ 'work' ] ],
107             wallet_add => [ 'wallet', 'key', [ 'work' ] ],
108             wallet_add_watch => [ 'wallet', 'accounts' ],
109             wallet_balances => [ 'wallet', [ 'threshold' ] ],
110             wallet_change_seed => [ 'wallet', 'seed', [ 'count' ] ],
111             wallet_contains => [ 'wallet', 'account' ],
112             wallet_create => [ [ 'seed' ] ],
113             wallet_destroy => [ 'wallet' ],
114             wallet_export => [ 'wallet' ],
115             wallet_frontiers => [ 'wallet' ],
116             wallet_history => [ 'wallet', [ 'modified_since' ] ],
117             wallet_info => [ 'wallet' ],
118             wallet_ledger => [ 'wallet', [ 'representative', 'weight', 'pending', 'modified_since' ] ],
119             wallet_lock => [ 'wallet' ],
120             wallet_locked => [ 'wallet' ],
121             wallet_pending => [ 'wallet', 'count', [ 'threshold', 'source', 'include_active', 'include_only_confirmed' ] ],
122             wallet_representative => [ 'wallet' ],
123             wallet_representative_set => [ 'wallet', 'representative', [ 'update_existing_accounts' ] ],
124             wallet_republish => [ 'wallet', 'count' ],
125             wallet_work_get => [ 'wallet' ],
126             work_get => [ 'wallet', 'account' ],
127             work_set => [ 'wallet', 'account', 'work' ],
128              
129             # Conversion RPC's
130             krai_from_raw => [ 'amount' ],
131             krai_to_raw => [ 'amount' ],
132             mrai_from_raw => [ 'amount' ],
133             mrai_to_raw => [ 'amount' ],
134             rai_from_raw => [ 'amount' ],
135             rai_to_raw => [ 'amount' ],
136             };
137              
138             sub new {
139 0     0 0   my $class = shift;
140 0           my $self = {
141             url => shift,
142             };
143 0 0         $self->{url} = 'http://[::1]:7076' unless defined $self->{url};
144 0           $self->{request} = HTTP::Request->new( 'POST', $self->{url} );
145 0           $self->{request}->content_type('application/json');
146 0           $self->{ua} = LWP::UserAgent->new;
147 0           bless $self, $class;
148 0           return $self;
149             }
150              
151             sub set_wallet {
152 0     0 0   my $self = shift;
153 0           $self->{wallet} = shift;
154 0           return $self;
155             }
156              
157             sub set_account {
158 0     0 0   my $self = shift;
159 0           $self->{account} = shift;
160 0           return $self;
161             }
162              
163             sub set_params {
164 0     0 0   my ($self,$extra) = @_;
165 0 0         if (ref $extra eq 'HASH') {
166 0           $self->{$_} = $extra->{$_} for (keys %$extra);
167             } else {
168             # assume key/values were specified as array
169 0           shift @_;
170 0           while (@_) {
171 0           my ($key,$value) = (shift,shift);
172 0           $self->{$key} = $value;
173             }
174             }
175 0           return $self;
176             }
177              
178             sub AUTOLOAD {
179 0     0     my ($self,$extra) = @_;
180 0 0         if (ref $extra eq 'HASH') {
181 0           $self->{$_} = $extra->{$_} for (keys %$extra);
182             } else {
183             # assume key/values were specified as array
184 0           shift @_;
185 0           while (@_) {
186 0           my ($key,$value) = (shift,shift);
187 0           $self->{$key} = $value;
188             }
189             }
190 0           our $AUTOLOAD;
191 0           my $action = $AUTOLOAD;
192 0           $action =~ s/.*:://;
193 0 0         if (defined $rpc_actions->{$action}) {
194 0           my $options;
195 0           my $json = '{"action": "'.$action.'"';
196 0           foreach my $param (@{$rpc_actions->{$action}}) {
  0            
197 0 0         return { error => "missing parameter $param" } unless defined $self->{$param};
198 0 0         if (ref $param eq 'ARRAY') {
199 0           $options = $param; next;
  0            
200             }
201             # assumes strings when params are not arrays
202 0 0         $json .= ', "'.$param.'": "'.$self->{$param}.'"' if ref $self->{$param} ne 'ARRAY';
203 0 0         $json .= ', "'.$param.'": '.$self->{$param} if ref $self->{$param} eq 'ARRAY';
204             }
205 0 0         if (ref $options eq 'ARRAY') {
206 0           foreach my $option (@$options) {
207 0 0         next unless defined $self->{$option};
208             # assumes strings when options are not arrays
209 0 0         $json .= ', "'.$option.'": "'.$self->{$option}.'"' if ref $self->{$option} ne 'ARRAY';
210 0 0         $json .= ', "'.$option.'": '.$self->{$option} if ref $self->{$option} eq 'ARRAY';
211             }
212             }
213 0           $json .= '}';
214 0           return __do_rpc($self,$json);
215             }
216 0           return { error => "action $action not defined" };
217             }
218              
219             sub rai_to_raw {
220 0     0 0   my $rai = shift;
221 0           return $rai * 1000000000000000000000000;
222             }
223              
224             sub mrai_to_raw {
225 0     0 0   my $rai = shift;
226 0           return $rai * 1000000000000000000000000000000;
227             }
228              
229             sub raw_to_rai {
230 0     0 0   my $raw = shift;
231 0           return $raw / 1000000000000000000000000;
232             }
233              
234             sub raw_to_mrai {
235 0     0 0   my $raw = shift;
236 0           return $raw / 1000000000000000000000000000000;
237             }
238              
239             sub __do_rpc {
240 0     0     my ($self,$json) = @_;
241 0           $self->{request}->content($json);
242 0           my $response = $self->{ua}->request($self->{request});
243 0 0         if ($response->is_success) {
244 0           return decode_json($response->decoded_content);
245             }
246 0           return { error => "RPC call failed" };
247             }
248              
249             1;
250             __END__