line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Crypto::NanoRPC; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
67668
|
use 5.018001; |
|
1
|
|
|
|
|
4
|
|
4
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
19
|
|
5
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
22
|
|
6
|
1
|
|
|
1
|
|
450
|
use HTTP::Request; |
|
1
|
|
|
|
|
22280
|
|
|
1
|
|
|
|
|
29
|
|
7
|
1
|
|
|
1
|
|
669
|
use LWP::UserAgent; |
|
1
|
|
|
|
|
25284
|
|
|
1
|
|
|
|
|
31
|
|
8
|
1
|
|
|
1
|
|
679
|
use JSON; |
|
1
|
|
|
|
|
9918
|
|
|
1
|
|
|
|
|
5
|
|
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.1'; |
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__ |