| 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__ |