File Coverage

blib/lib/Net/Ethereum.pm
Criterion Covered Total %
statement 26 440 5.9
branch 0 82 0.0
condition 0 12 0.0
subroutine 9 60 15.0
pod 40 40 100.0
total 75 634 11.8


line stmt bran cond sub pod time code
1             package Net::Ethereum;
2              
3 1     1   55881 use 5.020000;
  1         3  
4 1     1   4 use strict;
  1         1  
  1         26  
5 1     1   5 use warnings;
  1         1  
  1         20  
6              
7 1     1   372 use HTTP::Request;
  1         17952  
  1         27  
8 1     1   583 use LWP::UserAgent;
  1         20477  
  1         30  
9 1     1   550 use JSON;
  1         7931  
  1         4  
10 1     1   994 use Math::BigInt;
  1         19559  
  1         6  
11 1     1   18882 use Math::BigFloat;
  1         21091  
  1         18  
12 1     1   941 use File::Slurper 'read_text';
  1         10555  
  1         3931  
13              
14             our $VERSION = '0.30';
15              
16              
17             =pod
18              
19             =encoding utf8
20              
21             =head1 NAME
22              
23             Net::Ethereum - Perl Framework for Ethereum JSON RPC API.
24              
25             =head1 SYNOPSIS
26              
27             # Deploy contract
28              
29             use Net::Ethereum;
30              
31             my $contract_name = $ARGV[0];
32             my $password = $ARGV[1];
33              
34             my $node = Net::Ethereum->new('http://localhost:8545/');
35             $node->set_debug_mode(0);
36             $node->set_show_progress(1);
37              
38             # my $src_account = "0x0f687ab2be314d311a714adde92fd9055df18b48";
39             my $src_account = $node->eth_accounts()->[0];
40             print 'My account: '.$src_account, "\n";
41              
42             my $constructor_params={};
43             $constructor_params->{ initString } = '+ Init string for constructor +';
44             $constructor_params->{ initValue } = 102; # init value for constructor
45              
46             my $contract_status = $node->compile_and_deploy_contract($contract_name, $constructor_params, $src_account, $password);
47             my $new_contract_id = $contract_status->{contractAddress};
48             my $transactionHash = $contract_status->{transactionHash};
49             my $gas_used = hex($contract_status->{gasUsed});
50             print "\n", 'Contract mined.', "\n", 'Address: '.$new_contract_id, "\n", 'Transaction Hash: '.$transactionHash, "\n";
51              
52             my $gas_price=$node->eth_gasPrice();
53             my $contract_deploy_price = $gas_used * $gas_price;
54             my $price_in_eth = $node->wei2ether($contract_deploy_price);
55             print 'Gas used: '.$gas_used.' ('.sprintf('0x%x', $gas_used).') wei, '.$price_in_eth.' ether', "\n\n";
56              
57              
58             # Contract sample
59              
60             pragma solidity ^0.4.10;
61              
62             contract HelloSol {
63             string savedString;
64             uint savedValue;
65             address contractOwner;
66             function HelloSol(uint initValue, string initString) public {
67             contractOwner = msg.sender;
68             savedString = initString;
69             savedValue = initValue;
70             }
71             function setString( string newString ) public {
72             savedString = newString;
73             }
74             function getString() public constant returns( string curString) {
75             return savedString;
76             }
77             function setValue( uint newValue ) public {
78             savedValue = newValue;
79             }
80             function getValue() public constant returns( uint curValue) {
81             return savedValue;
82             }
83             function setAll(uint newValue, string newString) public {
84             savedValue = newValue;
85             savedString = newString;
86             }
87             function getAll() public constant returns( uint curValue, string curString) {
88             return (savedValue, savedString);
89             }
90             function getAllEx() public constant returns( bool isOk, address msgSender, uint curValue, string curString, uint val1, string str1, uint val2, uint val3) {
91             string memory sss="++ ==================================== ++";
92             return (true, msg.sender, 33333, sss, 9999, "Line 9999", 7777, 8888);
93             }
94              
95             function repiter(bool pBool, address pAddress, uint pVal1, string pStr1, uint pVal2, string pStr2, uint pVal3, int pVal4) public pure
96             returns( bool rbBool, address rpAddress, uint rpVal1, string rpStr1, uint rpVal2, string rpStr2, uint rpVal3, int rpVal4) {
97             return (pBool, pAddress, pVal1, pStr1, pVal2, pStr2, pVal3, pVal4);
98             }
99             }
100              
101              
102             # Call contract contract_methods
103              
104             use Net::Ethereum;
105             use Data::Dumper;
106              
107             my $contract_name = $ARGV[0];
108             my $password = $ARGV[1];
109             # my $src_account = "0x0f687ab2be314d311a714adde92fd9055df18b48";
110             my $contract_id = $ARGV[2];;
111              
112             my $node = Net::Ethereum->new('http://localhost:8545/');
113             $node->set_debug_mode(0);
114             $node->set_show_progress(1);
115              
116             my $src_account = $node->eth_accounts()->[0];
117             print 'My account: '.$src_account, "\n";
118              
119             my $abi = $node->_read_file('build/'.$contract_name.'.abi');
120             $node->set_contract_abi($abi);
121             $node->set_contract_id($contract_id);
122              
123              
124             # Call contract methods without transactions
125              
126             my $function_params={};
127             my $test1 = $node->contract_method_call('getValue', $function_params);
128             print Dumper($test1);
129              
130             my $test = $node->contract_method_call('getString');
131             print Dumper($test);
132              
133             my $testAll = $node->contract_method_call('getAll');
134             print Dumper($testAll);
135              
136             my $testAllEx = $node->contract_method_call('getAllEx');
137             print Dumper($testAllEx);
138              
139             $function_params={};
140             $function_params->{ pBool } = 1;
141             $function_params->{ pAddress } = "0xa3a514070f3768e657e2e574910d8b58708cdb82";
142             $function_params->{ pVal1 } = 1111;
143             $function_params->{ pStr1 } = "This is string 1";
144             $function_params->{ pVal2 } = 222;
145             $function_params->{ pStr2 } = "And this is String 2, very long string +++++++++++++++++=========";
146             $function_params->{ pVal3 } = 333;
147             $function_params->{ pVal4 } = '-999999999999999999999999999999999999999999999999999999999999999977777777';
148              
149             my $rc = $node->contract_method_call('repiter', $function_params);
150             print Dumper($rc);
151              
152              
153             # Send Transaction 1
154              
155             my $rc = $node->personal_unlockAccount($src_account, $password, 600);
156             print 'Unlock account '.$src_account.'. Result: '.$rc, "\n";
157              
158             my $function_params={};
159             $function_params->{ newString } = '+++ New string for save +++';
160              
161             my $used_gas = $node->contract_method_call_estimate_gas('setString', $function_params);
162             my $gas_price=$node->eth_gasPrice();
163             my $transaction_price = $used_gas * $gas_price;
164             my $call_price_in_eth = $node->wei2ether($transaction_price);
165             print 'Estimate Transaction Gas: '.$used_gas.' ('.sprintf('0x%x', $used_gas).') wei, '.$call_price_in_eth.' ether', "\n";
166              
167             my $tr = $node->sendTransaction($src_account, $node->get_contract_id(), 'setString', $function_params, $used_gas);
168              
169             print 'Waiting for transaction: ', "\n";
170             my $tr_status = $node->wait_for_transaction($tr, 25, $node->get_show_progress());
171             print Dumper($tr_status);
172              
173              
174             # Send Transaction 2
175              
176             $rc = $node->personal_unlockAccount($src_account, $password, 600);
177             print 'Unlock account '.$src_account.'. Result: '.$rc, "\n";
178              
179             $function_params={};
180             $function_params->{ newValue } = 77777;
181              
182             $used_gas = $node->contract_method_call_estimate_gas('setValue', $function_params);
183              
184             $transaction_price = $used_gas * $gas_price;
185             $call_price_in_eth = $node->wei2ether($transaction_price);
186             print 'Estimate Transaction Gas: '.$used_gas.' ('.sprintf('0x%x', $used_gas).') wei, '.$call_price_in_eth.' ether', "\n";
187              
188             $tr = $node->sendTransaction($src_account, $node->get_contract_id(), 'setValue', $function_params, $used_gas);
189              
190             print 'Waiting for transaction: ', "\n";
191             $tr_status = $node->wait_for_transaction($tr, 25, $node->get_show_progress());
192             print Dumper($tr_status);
193              
194              
195             $testAllEx = $node->contract_method_call('getAllEx');
196             print Dumper($testAllEx);
197              
198              
199             # Some other methods
200              
201             my $res = $node->eth_accounts();
202             print Dumper($res);
203              
204             my $web3_version = $node->web3_clientVersion();
205             print Dumper($web3_version);
206              
207             my $web3_sha3 = $node->web3_sha3("0x68656c6c6f20776f726c64");
208             print Dumper($web3_sha3);
209              
210             my $net_version = $node->net_version();
211             print Dumper($net_version);
212              
213             my $net_listening = $node->net_listening();
214             print Dumper($net_listening);
215              
216             my $net_peerCount = $node->net_peerCount();
217             print Dumper($net_peerCount);
218              
219             my $eth_protocolVersion = $node->eth_protocolVersion();
220             print Dumper($eth_protocolVersion);
221              
222             my $eth_syncing = $node->eth_syncing();
223             print Dumper($eth_syncing);
224              
225             my $eth_coinbase = $node->eth_coinbase();
226             print Dumper($eth_coinbase);
227              
228             my $eth_mining = $node->eth_mining();
229             print Dumper($eth_mining);
230              
231             my $eth_hashrate = $node->eth_hashrate();
232             print Dumper($eth_hashrate);
233              
234             my $eth_gasPrice = $node->eth_gasPrice();
235             print Dumper($eth_gasPrice);
236              
237             my $eth_blockNumber = $node->eth_blockNumber();
238             print Dumper($eth_blockNumber);
239              
240             my $eth_getBalance = $node->eth_getBalance('0xa15862b34abfc4b423fe52f153c95d83f606cc97', "latest");
241             print Dumper($eth_getBalance);
242              
243             my $eth_getTransactionCount = $node->eth_getTransactionCount('0xa15862b34abfc4b423fe52f153c95d83f606cc97', "latest");
244             print Dumper($eth_getTransactionCount);
245              
246             my $eth_getBlockTransactionCountByHash = $node->eth_getBlockTransactionCountByHash('0xe79342277d7e95cedf0409e0887c2cddb3ebc5f0d952b9f7c1c1c5cef845cb97', "latest");
247             print Dumper($eth_getBlockTransactionCountByHash);
248              
249             my $eth_getCode = $node->eth_getCode('0x11c63c5ebc2c6851111d881cb58c213c609c92d4', "latest");
250             print Dumper($eth_getCode);
251              
252              
253              
254             =head1 DESCRIPTION
255              
256             Net::Ethereum - Perl Framework for Ethereum JSON RPC API.
257              
258             This is alpha debugging version.
259              
260             Currently support marshaling only uint, int, bool, string types.
261              
262             Start node cmd:
263              
264             geth --datadir node1 --nodiscover --mine --minerthreads 1 --maxpeers 0 --verbosity 3 --networkid 98765 --rpc --rpcapi="db,eth,net,web3,personal,web3" console
265              
266             Attach node:
267              
268             geth --datadir node1 --networkid 98765 attach ipc://home/frolov/node1/geth.ipc
269              
270              
271              
272             =head1 FUNCTIONS
273              
274             =head2 new()
275              
276             my $node = Net::Ethereum->new('http://localhost:8545/');
277              
278             =cut
279              
280             sub new
281             {
282 0     0 1   my ($this, $api_url) = @_;
283 0           my $self = {};
284 0           bless( $self, $this );
285              
286 0           $self->{api_url} = $api_url;
287 0           $self->{abi} = {};
288 0           $self->{debug} = 0;
289 0           $self->{return_raw_data} = 0;
290 0           $self->{show_progress} = 0;
291              
292 0           return $self;
293             }
294              
295              
296             =pod
297              
298             =head2 web3_clientVersion
299              
300             Returns the current client version.
301             my $web3_version = $node->web3_clientVersion();
302              
303             =cut
304              
305             sub web3_clientVersion()
306             {
307 0     0 1   my ($this) = @_;
308 0           my $rq = { jsonrpc => "2.0", method => "web3_clientVersion", params => [], id => 67};
309 0           return $this->_node_request($rq)-> { result };
310             }
311              
312             =pod
313              
314             =head2 web3_sha3
315              
316             Returns Keccak-256 (not the standardized SHA3-256) of the given data.
317             my $web3_sha3 = $node->web3_sha3("0x68656c6c6f20776f726c64");
318              
319             =cut
320              
321             sub web3_sha3()
322             {
323 0     0 1   my ($this, $val) = @_;
324 0           my $rq = { jsonrpc => "2.0", method => "web3_sha3", params => [ $val ], id => 64};
325 0           return $this->_node_request($rq)-> { result };
326             }
327             =pod
328              
329             =head2 net_version
330              
331             Returns the current network id:
332              
333             =over
334              
335             =item "1": Ethereum Mainnet
336             =item "2": Morden Testnet (deprecated)
337             =item "3": Ropsten Testnet
338             =item "4": Rinkeby Testnet
339             =item "42": Kovan Testnet
340              
341             =back
342              
343             my $net_version = $node->net_version();
344              
345             =cut
346              
347             sub net_version()
348             {
349 0     0 1   my ($this) = @_;
350 0           my $rq = { jsonrpc => "2.0", method => "net_version", params => [], id => 67};
351 0           my $num = $this->_node_request($rq)-> { result };
352 0           my $dec = sprintf("%d", hex($num)) + 0;
353 0           return $dec;
354             }
355              
356             =pod
357              
358             =head2 net_listening
359              
360             Returns 1 (true) if client is actively listening for network connections.
361             my $net_listening = $node->net_listening();
362              
363             =cut
364              
365             sub net_listening()
366             {
367 0     0 1   my ($this) = @_;
368 0           my $rq = { jsonrpc => "2.0", method => "net_listening", params => [], id => 67};
369 0           my $rc = $this->_node_request($rq)-> { result };
370 0 0         $rc ? return 1 : return 0;
371             }
372              
373             =pod
374              
375             =head2 net_peerCount
376              
377             Returns number of peers currently connected to the client.
378             my $net_peerCount = $node->net_peerCount();
379              
380             =cut
381              
382             sub net_peerCount()
383             {
384 0     0 1   my ($this) = @_;
385 0           my $rq = { jsonrpc => "2.0", method => "net_peerCount", params => [], id => 74};
386 0           my $num = $this->_node_request($rq)-> { result };
387 0           my $dec = sprintf("%d", hex($num)) + 0;
388 0           return $dec;
389             }
390              
391             =pod
392              
393             =head2 personal_unlockAccount
394              
395             Decrypts the key with the given address from the key store.
396              
397             my $rc = $node->personal_unlockAccount($account, 'PASSWORD', 600);
398             print 'Unlock account '.$src_account.'. Result: '.$rc, "\n";
399              
400             $account - account to unlock;
401             $password - passphrase;
402             $timeout - the unlock duration
403              
404             The unencrypted key will be held in memory until the unlock duration expires.
405             The account can be used with eth_sign and eth_sendTransaction while it is unlocked.
406              
407             =cut
408              
409             sub personal_unlockAccount()
410             {
411 0     0 1   my ($this, $account, $password, $timeout) = @_;
412 0           my $rq = { jsonrpc => "2.0",method => "personal_unlockAccount", params => [ $account, $password, $timeout ], id => 1 };
413 0           my $rc = $this->_node_request($rq);
414 0           return $rc-> { result };
415             }
416              
417              
418             =pod
419              
420             =head2 set_contract_abi
421              
422             Store contract ABI in this object
423              
424             my $node = Net::Ethereum->new('http://localhost:8545/');
425             $node->set_contract_abi($src_abi);
426              
427             =cut
428              
429             sub set_contract_abi()
430             {
431 0     0 1   my ($this, $abi_json) = @_;
432 0           $this->{abi} = decode_json($abi_json);
433             }
434              
435             =pod
436              
437             =head2 get_contract_abi
438              
439             Returns ABI for contract, stored in this object
440              
441             my $contract_abi = node->get_contract_abi();
442              
443             =cut
444              
445             sub get_contract_abi()
446             {
447 0     0 1   my ($this) = @_;
448 0           return $this->{abi};
449             }
450              
451             =pod
452              
453             =head2 set_debug_mode
454              
455             Set dubug mode. Debug info printed to console.
456             $node->set_debug_mode($mode);
457              
458             $mode: 1 - debug on, 0 - debug off.
459              
460             =cut
461              
462             sub set_debug_mode()
463             {
464 0     0 1   my ($this, $debug_mode) = @_;
465 0           $this->{debug} = $debug_mode;
466             }
467              
468              
469             =pod
470              
471             =head2 set_show_progress
472              
473             Set show progress mode to waiting contract deploying.
474             $node->set_show_progress($mode);
475              
476             $mode: 1 - show progress mode on, 0 - show progress mode off.
477              
478             =cut
479              
480             sub set_show_progress()
481             {
482 0     0 1   my ($this, $show_progress) = @_;
483 0           $this->{show_progress} = $show_progress;
484             }
485              
486             =pod
487              
488             =head2 get_show_progress
489              
490             Get show progress mode to waiting contract deploying.
491             $node->get_show_progress();
492              
493             Returns: 1 - show progress mode on, 0 - show progress mode off.
494              
495             =cut
496              
497             sub get_show_progress()
498             {
499 0     0 1   my ($this) = @_;
500 0           return $this->{show_progress};
501             }
502              
503              
504             =pod
505              
506             =head2 set_contract_id
507              
508             Store contract Id in this object
509              
510             my $contract_id = "0x432e816769a2657029db98303e4946d7dedbcd8f";
511             my $node = Net::Ethereum->new('http://localhost:8545/');
512             $node->set_contract_id($contract_id);
513              
514             $contract_id - contract address
515              
516             =cut
517              
518             sub set_contract_id()
519             {
520 0     0 1   my ($this, $contract_id) = @_;
521 0           $this->{contract_id} = $contract_id;
522             }
523              
524             =pod
525              
526             =head2 get_contract_id
527              
528             Get contract id from this object
529             Returns Contract id
530              
531             print 'New Contract id: '.$node->get_contract_id(), "\n";
532              
533             =cut
534              
535              
536             sub get_contract_id()
537             {
538 0     0 1   my ($this) = @_;
539 0           return $this->{contract_id};
540             }
541              
542              
543             =pod
544              
545             =head2 eth_protocolVersion
546              
547             Returns the current ethereum protocol version.
548              
549             my $eth_protocolVersion = $node->eth_protocolVersion();
550              
551             =cut
552              
553             sub eth_protocolVersion()
554             {
555 0     0 1   my ($this) = @_;
556 0           my $rq = { jsonrpc => "2.0", method => "eth_protocolVersion", params => [], id => 67};
557 0           my $num = $this->_node_request($rq)-> { result };
558 0           my $dec = sprintf("%d", hex($num)) + 0;
559 0           return $dec;
560             }
561              
562             =pod
563              
564             =head2 eth_syncing
565              
566             Returns an object with data about the sync status or false.
567              
568             Object|Boolean, An object with sync status data or 0 (FALSE), when not syncing:
569              
570             startingBlock: QUANTITY - The block at which the import started (will only be reset, after the sync reached his head)
571             currentBlock: QUANTITY - The current block, same as eth_blockNumber
572             highestBlock: QUANTITY - The estimated highest block
573              
574              
575             my $eth_syncing = $node->eth_syncing();
576              
577             =cut
578              
579             sub eth_syncing()
580             {
581 0     0 1   my ($this) = @_;
582 0           my $rq = { jsonrpc => "2.0", method => "eth_syncing", params => [], id => 1};
583 0           my $rc = $this->_node_request($rq)-> { result };
584 0 0         if(!$rc)
585             {
586 0           return 0;
587             }
588 0           return $rc;
589             }
590              
591             =pod
592              
593             =head2 eth_coinbase
594              
595             Returns the client coinbase address (20 bytes - the current coinbase address).
596              
597             my $eth_coinbase = $node->eth_coinbase();
598              
599             =cut
600              
601             sub eth_coinbase()
602             {
603 0     0 1   my ($this) = @_;
604 0           my $rq = { jsonrpc => "2.0", method => "eth_coinbase", params => [], id => 64};
605 0           return $this->_node_request($rq)-> { result };
606             }
607              
608             =pod
609              
610             =head2 eth_mining
611              
612             Returns true if client is actively mining new blocks.
613              
614             my $eth_mining = $node->eth_mining();
615              
616             =cut
617              
618             sub eth_mining()
619             {
620 0     0 1   my ($this) = @_;
621 0           my $rq = { jsonrpc => "2.0", method => "eth_mining", params => [], id => 71};
622 0           my $rc = $this->_node_request($rq)-> { result };
623 0 0         $rc ? return 1 : return 0;
624             }
625              
626             =pod
627              
628             =head2 eth_hashrate
629              
630             Returns the number of hashes per second that the node is mining with.
631              
632             my $eth_hashrate = $node->eth_hashrate();
633              
634             =cut
635              
636             sub eth_hashrate()
637             {
638 0     0 1   my ($this) = @_;
639 0           my $rq = { jsonrpc => "2.0", method => "eth_hashrate", params => [], id => 71};
640 0           my $num = $this->_node_request($rq)-> { result };
641 0           my $dec = sprintf("%d", hex($num)) + 0;
642 0           return $dec;
643             }
644              
645             =pod
646              
647             =head2 eth_gasPrice
648              
649             Returns the current price per gas in wei.
650              
651             my $eth_gasPrice = $node->eth_gasPrice();
652              
653             =cut
654              
655             sub eth_gasPrice()
656             {
657 0     0 1   my ($this) = @_;
658 0           my $rq = { jsonrpc => "2.0", method => "eth_gasPrice", params => [], id => 73};
659 0           my $hex_string = $this->_node_request($rq)-> { result };
660 0           my $dec = Math::BigInt->new($hex_string);
661 0           return $dec;
662             }
663              
664             =pod
665              
666             =head2 eth_accounts
667              
668             Returns a list of addresses owned by client.
669              
670             my $res = $node->eth_accounts();
671              
672             =cut
673              
674             sub eth_accounts()
675             {
676 0     0 1   my ($this) = @_;
677 0           my $rq = { jsonrpc => "2.0",method => "eth_accounts", params => [], id => 1 };
678 0           return $this->_node_request($rq)-> { result };
679             }
680              
681             =pod
682              
683             =head2 eth_blockNumber
684              
685             Returns the number of most recent block.
686              
687             my $eth_blockNumber = $node->eth_blockNumber();
688              
689             =cut
690              
691             sub eth_blockNumber()
692             {
693 0     0 1   my ($this) = @_;
694 0           my $rq = { jsonrpc => "2.0", method => "eth_blockNumber", params => [], id => 83};
695 0           my $num = $this->_node_request($rq)-> { result };
696 0           my $dec = sprintf("%d", hex($num)) + 0;
697 0           return $dec;
698             }
699              
700             =pod
701              
702             =head2 eth_getBalance
703              
704             Returns the balance of the account of given address as Math::BigInt object.
705              
706             $addr - address to check for balance;
707             $block_number - integer block number, or the string "latest", "earliest" or "pending"
708              
709             my $eth_getBalance = $node->eth_getBalance('0xa15862b34abfc4b423fe52f153c95d83f606cc97', "latest");
710              
711             =cut
712              
713             sub eth_getBalance()
714             {
715 0     0 1   my ($this, $addr, $block_number) = @_;
716 0           my $rq = { jsonrpc => "2.0", method => "eth_getBalance", params => [ $addr, $block_number ], id => 1};
717 0           my $hex_string = $this->_node_request($rq)-> { result };
718 0           my $dec = Math::BigInt->new($hex_string);
719 0           return $dec;
720             #return $hex_string;
721             }
722              
723              
724             =pod
725              
726             =head2 eth_getStorageAt
727              
728             ## TODO
729              
730             =cut
731              
732              
733              
734             =pod
735              
736             =head2 eth_getTransactionCount
737              
738             Returns the number of transactions sent from an address.
739              
740             $addr - address;
741             $block_number - integer block number, or the string "latest", "earliest" or "pending"
742              
743             my $eth_getTransactionCount = $node->eth_getTransactionCount('0xa15862b34abfc4b423fe52f153c95d83f606cc97', "latest");
744              
745             =cut
746              
747             sub eth_getTransactionCount()
748             {
749 0     0 1   my ($this, $addr, $block_number) = @_;
750 0           my $rq = { jsonrpc => "2.0", method => "eth_getTransactionCount", params => [ $addr, $block_number ], id => 1};
751 0           my $num = $this->_node_request($rq)-> { result };
752 0           my $dec = sprintf("%d", hex($num)) + 0;
753 0           return $dec;
754             }
755              
756              
757             =pod
758              
759             =head2 eth_getTransactionByHash
760              
761             Returns the information about a transaction requested by transaction hash.
762              
763             $hash - hash of a block;
764              
765             my $eth_getTransactionByHash = $node->eth_getBlockTransactionCountByHash('0xe79342277d7e95cedf0409e0887c2cddb3ebc5f0d952b9f7c1c1c5cef845cb97', "latest");
766              
767             =cut
768              
769             sub eth_getTransactionByHash()
770             {
771 0     0 1   my ($this, $hash) = @_;
772 0           my $rq = { jsonrpc => "2.0", method => "eth_getTransactionByHash", params => [ $hash ], id => 1};
773 0           my $num = $this->_node_request($rq)-> { result };
774             ##my $dec = sprintf("%d", hex($num)) + 0;
775 0           return $num;
776             }
777              
778              
779              
780              
781             =pod
782              
783             =head2 eth_getBlockTransactionCountByHash
784              
785             Returns the number of transactions in a block from a block matching the given block hash.
786              
787             $hash - hash of a block;
788              
789             my $eth_getBlockTransactionCountByHash = $node->eth_getBlockTransactionCountByHash('0xe79342277d7e95cedf0409e0887c2cddb3ebc5f0d952b9f7c1c1c5cef845cb97', "latest");
790              
791             =cut
792              
793             sub eth_getBlockTransactionCountByHash()
794             {
795 0     0 1   my ($this, $hash) = @_;
796 0           my $rq = { jsonrpc => "2.0", method => "eth_getBlockTransactionCountByHash", params => [ $hash ], id => 1};
797 0           my $num = $this->_node_request($rq)-> { result };
798             ##my $dec = sprintf("%d", hex($num)) + 0;
799 0           return $num;
800             }
801              
802              
803             =pod
804              
805             =head2 eth_getBlockTransactionCountByNumber
806              
807             ## TODO
808              
809             =cut
810              
811              
812             =pod
813              
814             =head2 eth_getUncleCountByBlockHash
815              
816             ## TODO
817              
818             =cut
819              
820             =pod
821              
822             =head2 eth_getUncleCountByBlockNumber
823              
824             ## TODO
825              
826             =cut
827              
828              
829              
830              
831             =pod
832              
833             =head2 eth_getCode
834              
835             Returns code at a given address.
836              
837             my $contract_code=$node->eth_getCode($contract_status->{contractAddress}, "latest");
838              
839             $addr - address;
840             $block_number - integer block number, or the string "latest", "earliest" or "pending"
841              
842             The following options are possible for the defaultBlock parameter:
843              
844             HEX String - an integer block number
845             String "earliest" for the earliest/genesis block
846             String "latest" - for the latest mined block
847             String "pending" - for the pending state/transactions
848              
849             =cut
850              
851             sub eth_getCode()
852             {
853 0     0 1   my ($this, $addr, $block_number) = @_;
854 0           my $rq = { jsonrpc => "2.0", method => "eth_getCode", params => [ $addr, $block_number ], id => 1};
855 0           return $this->_node_request($rq)-> { result };
856             }
857              
858              
859             =pod
860              
861             =head2 eth_sign
862              
863             ## TODO
864              
865             =cut
866              
867              
868              
869             =pod
870              
871             =head2 eth_sendTransaction
872              
873             Send message to contract.
874             Returns result
875              
876             my $rc = $this->eth_sendTransaction($params);
877             return $rc;
878              
879             $from - account;
880             $to - contract id to send message;
881             $data - marshalled data
882             $gas - gas used
883              
884             =cut
885              
886             sub eth_sendTransaction()
887             {
888 0     0 1   my ($this, $params) = @_;
889 0           my $from = $params->{ from };
890 0           my $to = $params->{ to };
891 0           my $gas = $params->{ gas };
892 0           my $data = $params->{ data };
893              
894 0           my $rq = { jsonrpc => "2.0", method => "eth_sendTransaction", params => [ { from => $from, to => $to, gas => $gas, data => $data } ], id => 1 };
895 0           my $rc = $this->_node_request($rq);
896 0           return $rc-> { result };
897             }
898              
899              
900             =pod
901              
902             =head2 eth_sendRawTransaction
903              
904             ## TODO
905              
906             =cut
907              
908              
909              
910             =pod
911              
912             =head2 eth_call
913              
914             Call contract method without transaction
915             Returns result
916              
917             my $raw_params=$this->_marshal($function_name, $function_params);
918              
919             my $params = {};
920             $params-> {to} = $this->{contract_id};
921             $params-> {data} = $raw_params;
922             my $rc = $this->eth_call($params);
923              
924             =cut
925              
926             sub eth_call()
927             {
928 0     0 1   my ($this, $params) = @_;
929 0           my $to = $params->{ to };
930 0           my $data = $params->{ data };
931 0           my $rq = { jsonrpc => "2.0", method => "eth_call", params => [ { to => $to, data => $data},"latest" ], id => 1 };
932 0           return $this->_node_request($rq)-> { result };
933             }
934              
935             =pod
936              
937             =head2 eth_estimateGas
938              
939             Makes a call or transaction, which won't be added to the blockchain and returns the used gas, which can be used for estimating the used gas.
940             Returns the amount of gas used.
941              
942             my $contract_used_gas = $node->deploy_contract_estimate_gas($src_account, $contract_binary);
943             print 'Estimate GAS: ', Dumper($contract_used_gas);
944              
945             $params - See eth_call parameters, expect that all properties are optional.
946             If no gas limit is specified geth uses the block gas limit from the pending block as an upper bound.
947             As a result the returned estimate might not be enough to executed the call/transaction when the amount of gas is higher than the pending block gas limit.
948              
949             Returns the amount of gas used.
950              
951             =cut
952              
953             sub eth_estimateGas()
954             {
955 0     0 1   my ($this, $params) = @_;
956 0           my $to = $params->{ to };
957 0           my $data = $params->{ data };
958 0           my $rq = { jsonrpc => "2.0", method => "eth_estimateGas", params => [ { to => $to, data => $data} ], id => 1 };
959 0           return $this->_node_request($rq)-> { result };
960             }
961              
962              
963             =pod
964              
965             =head2 eth_getTransactionReceipt
966              
967             Returns the receipt of a transaction by transaction hash.
968             That the receipt is not available for pending transactions.
969              
970             $tr_status = $this->eth_getTransactionReceipt($contrarc_deploy_tr);
971              
972             =cut
973              
974             sub eth_getTransactionReceipt()
975             {
976 0     0 1   my ($this, $transaction_hash) = @_;
977 0           my $rq = { jsonrpc => "2.0", method => "eth_getTransactionReceipt", params => [ $transaction_hash ], id => 1 };
978              
979             #print "eth_getTransactionReceipt: ", Dumper($rq);
980              
981 0           my $rc = $this->_node_request($rq);
982              
983             #print "eth_getTransactionReceipt rc: ", Dumper($rc);
984              
985 0           return $rc;
986             # return $this->_node_request($rq);
987             }
988              
989             =pod
990              
991             =head2 wei2ether
992              
993             Convert wei to Ether, returns Ether
994              
995             my $price_in_eth = $node->wei2ether($contract_deploy_price);
996              
997             =cut
998              
999             sub wei2ether()
1000             {
1001 0     0 1   my ($this, $wei) = @_;
1002 0           my $eth_in_wei = Math::BigFloat->new(1000000000000000000);
1003 0           my $wei_bigfloat = Math::BigFloat->new($wei);
1004 0           my $ether = $wei_bigfloat / $eth_in_wei;
1005 0           return $ether;
1006             }
1007              
1008              
1009             =pod
1010              
1011             =head2 sendTransaction
1012              
1013             Send message to contract.
1014             Returns transaction id
1015              
1016             my $function_params={};
1017             $function_params->{ newString } = "+= test =+";
1018             my $tr = $node->sendTransaction($src_account, $node->get_contract_id(), 'setString', $function_params);
1019              
1020             $src_account - account;
1021             $contract_id - contract id to send message;
1022             $function_name - function name;
1023             $function_params - function params
1024              
1025             =cut
1026              
1027             sub sendTransaction()
1028             {
1029 0     0 1   my ($this, $src_account, $contract_id, $function_name, $function_params, $gas) = @_;
1030 0           my $raw_params=$this->_marshal($function_name, $function_params);
1031              
1032 0           my $params = {};
1033 0           $params-> {from} = $src_account;
1034 0           $params-> {to} = $contract_id;
1035 0           $params-> {data} = $raw_params;
1036             # $params-> {gas} = "0xd312";
1037 0           $params-> {gas} = sprintf('0x%x', $gas);
1038              
1039 0           my $rc = $this->eth_sendTransaction($params);
1040 0           return $rc;
1041             }
1042              
1043              
1044             =pod
1045              
1046             =head2 deploy_contract_estimate_gas
1047              
1048             Estimate used gas for deployed contract.
1049             Makes a call or transaction, which won't be added to the blockchain and returns the used gas, which can be used for estimating the used gas.
1050             Returns the amount of gas used.
1051              
1052             my $contract_used_gas = $node->deploy_contract_estimate_gas($src_account, $contract_binary);
1053             print 'Estimate GAS: ', Dumper($contract_used_gas);
1054              
1055             $src_account - account
1056             $contract_binary - contract binary code
1057              
1058             =cut
1059              
1060             sub deploy_contract_estimate_gas()
1061             {
1062 0     0 1   my ($this, $src_account, $contract_binary, $constructor_params) = @_;
1063              
1064 0           my $params = {};
1065 0           $params-> {from} = $src_account;
1066              
1067 0 0         if($constructor_params)
1068             {
1069 0           my $raw_params=$this->_marshal('constructor', $constructor_params);
1070 0           $params-> {data} = $contract_binary.$raw_params;
1071             }
1072             else
1073             {
1074 0           $params-> {data} = $contract_binary;
1075             }
1076              
1077             #$params-> {data} = $contract_binary;
1078 0           return hex($this->eth_estimateGas($params));
1079             }
1080              
1081              
1082             =pod
1083              
1084             =head2 deploy_contract
1085              
1086             Deploy contract
1087             Returns transaction id
1088              
1089             my $rc = $node->personal_unlockAccount($src_account, 'ptktysq', 600);
1090             print 'Unlock account '.$src_account.'. Result: '.$rc, "\n";
1091             my $contrarc_deploy_tr = $node->deploy_contract($src_account, $contract_binary);
1092              
1093             $src_account - account
1094             $contract_binary - contract binary code
1095              
1096             =cut
1097              
1098             sub deploy_contract()
1099             {
1100 0     0 1   my ($this, $src_account, $contract_binary, $constructor_params, $gas) = @_;
1101              
1102 0           my $params = {};
1103 0           $params-> {from} = $src_account;
1104 0 0         if($constructor_params)
1105             {
1106 0           my $raw_params=$this->_marshal('constructor', $constructor_params);
1107 0           $params-> {data} = $contract_binary.$raw_params;
1108             }
1109             else
1110             {
1111 0           $params-> {data} = $contract_binary;
1112             }
1113              
1114 0           $params-> {gas} = sprintf('0x%x', $gas);
1115 0           my $rc = $this->eth_sendTransaction($params);
1116 0           return $rc;
1117             }
1118              
1119             =pod
1120              
1121             =head2 wait_for_contract
1122              
1123             Wait for contract deployment/
1124             Store contract address into this object.
1125             Returns the transction status:
1126              
1127             $VAR1 = {
1128             'transactionIndex' => '0x0',
1129             'logs' => [],
1130             'contractAddress' => '0xa5e4b4aa28b79891f12ffa985b660ff222157659',
1131             'cumulativeGasUsed' => '0xb64b3',
1132             'to' => undef,
1133             'blockNumber' => '0x36a6',
1134             'blockHash' => '0x71b75f5eae70c532f94aeee91df0fef0df6208c451f6c007fe9a2a462fb23fc0',
1135             'transactionHash' => '0xfa71027cb3ae4ed05ec7a71d1c0cdad7d0dc501679e976caa0bf665b7309b97b',
1136             'from' => '0x0f687ab2be314d311a714adde92fd9055df18b48',
1137             'logsBloom' => '0x0000000...00',
1138             'gasUsed' => '0xb64b3',
1139             'root' => '0x9a32416741eb3192eae9197fc20acf7e5436fce7e6d92153aca91f92d373d41b'
1140             };
1141              
1142              
1143             my $contract_status = $node->wait_for_contract($contrarc_deploy_tr);
1144              
1145             $contrarc_deploy_tr - waiting contract transaction;
1146             $iterations - number of wait iterations;
1147             $show_progress - show progress on console (1 or 0)
1148              
1149             =cut
1150              
1151             sub wait_for_contract()
1152             {
1153 0     0 1   my ($this, $contrarc_deploy_tr, $iterations, $show_progress) = @_;
1154 0           my $new_contract_id;
1155             my $tr_status;
1156              
1157 0 0         if($show_progress) { $| = 1; }
  0            
1158 0           for(my $i=0; $i<$iterations;$i++)
1159             {
1160 0           my $rc = $this->eth_getTransactionByHash($contrarc_deploy_tr);
1161 0 0         if($rc->{blockNumber}) # do not pending
1162             {
1163 0           $tr_status = $this->eth_getTransactionReceipt($contrarc_deploy_tr);
1164 0 0         if($tr_status->{result})
1165             {
1166 0           $new_contract_id = $tr_status->{result}->{contractAddress};
1167 0           $this->set_contract_id($new_contract_id);
1168 0 0         if($show_progress) { print "\n"; }
  0            
1169 0           last;
1170             }
1171             }
1172             else
1173             {
1174 0           sleep(5);
1175 0 0         if($show_progress) { print '.'.$i; }
  0            
1176             }
1177             }
1178 0           return $tr_status->{result};
1179             }
1180              
1181             =pod
1182              
1183             =head2 wait_for_transaction
1184              
1185             Wait for wait_for_transaction
1186             Returns the transction status:
1187              
1188             $VAR1 = {
1189             'cumulativeGasUsed' => '0xabcd',
1190             'transactionHash' => '0x237569eeae8f8f3da05d7bbd68066c18921406441dac8de13092c850addcb15b',
1191             'logs' => [],
1192             'gasUsed' => '0xabcd',
1193             'transactionIndex' => '0x0',
1194             'blockHash' => '0xdb7f3748658abdb60859e1097823630d7eb140448b40c8e1ac89170a76fc797e',
1195             'from' => '0x0f687ab2be314d311a714adde92fd9055df18b48',
1196             'logsBloom' => '0x000000000000000000...0000',
1197             'to' => '0x6f059b63aee6af50920d2a0fbd287cec94117826',
1198             'root' => '0x3fe46002e1c71876474a8b460222adb2309ab8f36b5750a6408a1f921f54ab4c',
1199             'blockNumber' => '0x36e2',
1200             'contractAddress' => undef
1201             };
1202              
1203              
1204             my $tr_status = $node->wait_for_transaction($tr);
1205              
1206             $contrarc_deploy_tr - waiting transaction;
1207             $iterations - number of wait iterations;
1208             $show_progress - show progress on console (1 or 0)
1209              
1210             =cut
1211              
1212             sub wait_for_transaction()
1213             {
1214 0     0 1   my ($this, $contrarc_deploy_tr, $iterations, $show_progress) = @_;
1215 0           my $new_contract_id;
1216             my $tr_status;
1217              
1218 0 0         if($show_progress) { $| = 1; }
  0            
1219 0           for(my $i=0; $i<$iterations;$i++)
1220             {
1221 0           my $rc = $this->eth_getTransactionByHash($contrarc_deploy_tr);
1222 0 0         if($rc->{blockNumber}) # do not pending
1223             {
1224 0           $tr_status = $this->eth_getTransactionReceipt($contrarc_deploy_tr);
1225 0 0         if($tr_status->{result})
1226             {
1227 0 0         if($show_progress) { print "\n"; }
  0            
1228 0           last;
1229             }
1230             }
1231             else
1232             {
1233 0           sleep(5);
1234 0 0         if($show_progress) { print '.'.$i; }
  0            
1235             }
1236             }
1237 0           return $tr_status->{result};
1238             }
1239              
1240              
1241             =pod
1242              
1243             =head2 contract_method_call_estimate_gas
1244              
1245             Estimate used gas for contract method call.
1246             Makes a call or transaction, which won't be added to the blockchain and returns the used gas, which can be used for estimating the used gas.
1247             Returns the amount of gas used.
1248              
1249             my $function_params={};
1250             $function_params->{ newString } = '+= test GAS ok =+';
1251              
1252             my $used_gas = $node->contract_method_call_estimate_gas('setString', $function_params);
1253             print 'Estimate GAS: ', Dumper($used_gas);
1254              
1255             =cut
1256              
1257             sub contract_method_call_estimate_gas()
1258             {
1259 0     0 1   my ($this, $function_name, $function_params) = @_;
1260              
1261 0           my $raw_params=$this->_marshal($function_name, $function_params);
1262 0           my $params = {};
1263 0           $params-> {to} = $this->{contract_id};
1264 0           $params-> {data} = $raw_params;
1265 0           return hex($this->eth_estimateGas($params));
1266             }
1267              
1268              
1269             =pod
1270              
1271             =head2 contract_method_call
1272              
1273             Call contract method without transaction
1274             Returns unmarshalled data
1275              
1276             $function_params={};
1277             $function_params->{ pBool } = 1;
1278             $function_params->{ pAddress } = "0xa3a514070f3768e657e2e574910d8b58708cdb82";
1279             $function_params->{ pVal1 } = 11;
1280             $function_params->{ pStr1 } = "str1 This is string 1";
1281             $function_params->{ pVal2 } = 22;
1282             $function_params->{ pStr2 } = "str2 And this is String 2, very long string +++++++++++++++++== smart!";
1283             $function_params->{ pVal3 } = 33;
1284             $function_params->{ pVal4 } = 44;
1285              
1286             my $rc = $node->contract_method_call('repiter', $function_params);
1287              
1288             =cut
1289              
1290             sub contract_method_call()
1291             {
1292 0     0 1   my ($this, $function_name, $function_params) = @_;
1293 0           my $raw_params=$this->_marshal($function_name, $function_params);
1294              
1295 0           my $params = {};
1296 0           $params-> {to} = $this->{contract_id};
1297 0           $params-> {data} = $raw_params;
1298              
1299 0 0         if($this->{debug})
1300             {
1301 0           print 'Function name: '.$function_name. "\n";
1302 0           print 'Function params: ', Dumper($function_params);
1303 0           print 'Function raw params: ', Dumper($params);
1304             }
1305              
1306 0           my $rc = $this->eth_call($params);
1307              
1308 0 0         if($this->{debug})
1309             {
1310 0           print 'eth_call return data: '.$rc. "\n";
1311             }
1312 0           my $raw_data = substr($rc, 2);
1313 0           return $this->_unmarshal($function_name, $raw_data);
1314             }
1315              
1316              
1317             =pod
1318              
1319             =head2 compile_and_deploy_contract
1320              
1321             Compile and deploy contract
1322             Returns contract id
1323              
1324             my $constructor_params={};
1325             $constructor_params->{ initString } = '+ from constructor +';
1326             $constructor_params->{ initValue } = 101;
1327              
1328             my $contract_status = $node->compile_and_deploy_contract($contract_name, $constructor_params, $src_account, $password);
1329             my $new_contract_id = $contract_status->{contractAddress};
1330              
1331             =cut
1332              
1333             sub compile_and_deploy_contract()
1334             {
1335 0     0 1   my ($this, $contract_name, $constructor_params, $src_account, $password) = @_;
1336              
1337 0           my $contract_src_path = $contract_name.'.sol';
1338 0           my $bin_solc = '/usr/bin/solc';
1339 0           my $cmd = "$bin_solc --bin --abi $contract_src_path -o build --overwrite";
1340 0 0         if(system($cmd))
1341             {
1342 0           die sprintf("Failed to compile $contract_name with value %d\n", $? >> 8);
1343             }
1344              
1345 0           my $abi = $this->_read_file('build/'.$contract_name.'.abi');
1346 0           $this->set_contract_abi($abi);
1347 0           my $bin = $this->_read_file('build/'.$contract_name.'.bin');
1348 0           $bin = '0x'.$bin;
1349              
1350 0           $this->personal_unlockAccount($src_account, $password, 600);
1351              
1352 0           my $contract_used_gas = $this->deploy_contract_estimate_gas($src_account, $bin, $constructor_params);
1353 0           my $gas_price=$this->eth_gasPrice();
1354 0           my $contract_deploy_price = $contract_used_gas * $gas_price;
1355 0           my $price_in_eth = $this->wei2ether($contract_deploy_price);
1356              
1357             # print 'Estimate Contract GAS: '.$contract_used_gas.' wei ('.sprintf('0x%x', $contract_used_gas).' wei), $price_in_eth: '.$price_in_eth.' ether', "\n";
1358              
1359 0           my $contrarc_deploy_tr = $this->deploy_contract($src_account, $bin, $constructor_params, $contract_used_gas);
1360 0           my $contract_status = $this->wait_for_contract($contrarc_deploy_tr, 25, $this->{show_progress});
1361              
1362 0           my $contract_code=$this->eth_getCode($contract_status->{contractAddress}, "latest");
1363 0 0         if($contract_code eq '0x')
1364             {
1365 0           die 'Error: no contract code from network'.$contract_code;
1366             }
1367 0           return $contract_status;
1368             }
1369              
1370              
1371              
1372             # ==========================
1373              
1374              
1375             =pod
1376              
1377             =head2 _read_file
1378              
1379             Read file into variable.
1380              
1381             $file_path - path to file.
1382              
1383             my $abi = $this->_read_file('build/'.$contract_name.'.abi');
1384             $this->set_contract_abi($abi);
1385             my $bin = $this->_read_file('build/'.$contract_name.'.bin');
1386              
1387             =cut
1388              
1389             sub _read_file()
1390             {
1391 0     0     my ($this, $file_path) = @_;
1392 0           my $content = read_text($file_path);
1393              
1394             # open my $FILE, "<", $file_path or die "Can't open file: $!\n";
1395             # local $/ = undef;
1396             # my $content = <$FILE>;
1397             # close $FILE;
1398 0           return $content;
1399             }
1400              
1401             =pod
1402              
1403             =head2 _marshal
1404              
1405             nternal method.
1406             Marshaling data from from function params/
1407             Returns raw marshalled data
1408              
1409             my $raw_params=$this->_marshal($function_name, $function_params);
1410              
1411             $function_name - method name to get ABI
1412             $function_params - function params
1413              
1414             =cut
1415              
1416             sub _marshal()
1417             {
1418 0     0     my ($this, $function_name, $function_params) = @_;
1419              
1420 0           my $function_abi;
1421              
1422 0 0         if($function_name eq 'constructor')
1423             {
1424 0           $function_abi = $this->_get_constructor_abi($function_name);
1425             }
1426             else
1427             {
1428 0           $function_abi = $this->_get_function_abi($function_name);
1429             }
1430              
1431 0           my $function_inputs = $function_abi-> { inputs };
1432              
1433 0           my $current_out_param_position=0;
1434 0           my $param_types_list="";
1435 0           my $encoded_arguments="";
1436              
1437 0           my $out_param_array=[];
1438 0           my $out_param_array_counter=0;
1439              
1440             # First pass
1441              
1442 0           foreach my $out_param (@$function_inputs)
1443             {
1444 0           my $cur_param_name = $out_param->{name};
1445 0           my $cur_param_type = $out_param->{type};
1446 0           $param_types_list .= $cur_param_type.',';
1447              
1448 0 0 0       if($cur_param_type eq 'bool' || $cur_param_type =~ m/^uint/i)
    0          
    0          
    0          
1449             {
1450 0           my $add_hunk=$function_params->{ $cur_param_name };
1451 0           $add_hunk=$this->_marshal_int($add_hunk);
1452 0           $out_param_array->[$out_param_array_counter] = $add_hunk;
1453 0           $out_param_array_counter++;
1454 0           $current_out_param_position += 64;
1455             }
1456             elsif($cur_param_type =~ m/^int/i)
1457             {
1458 0           my $add_hunk=$function_params->{ $cur_param_name };
1459 0           $add_hunk=$this->_marshal_int($add_hunk);
1460 0           $out_param_array->[$out_param_array_counter] = $add_hunk;
1461 0           $out_param_array_counter++;
1462 0           $current_out_param_position += 64;
1463             }
1464             elsif($cur_param_type eq 'address')
1465             {
1466 0           my $add_hunk=$function_params->{ $cur_param_name };
1467 0           $add_hunk=substr($add_hunk, 2);
1468 0           $add_hunk = sprintf('%064s', $add_hunk);
1469 0           $out_param_array->[$out_param_array_counter] = $add_hunk;
1470 0           $out_param_array_counter++;
1471 0           $current_out_param_position += 64;
1472             }
1473             elsif($cur_param_type eq 'string')
1474             {
1475 0           $current_out_param_position += 64;
1476 0           my $str=$function_params->{ $cur_param_name };
1477 0           my $str_length = length($str);
1478 0           $out_param_array->[$out_param_array_counter] = $str_length; # replace with data hunk offset into second pass
1479 0           $out_param_array_counter++;
1480 0           $current_out_param_position += 64;
1481             }
1482             else
1483             {
1484 0           die 'Net::Ethereum _marshal does not support type: '.$cur_param_type;
1485             }
1486             }
1487              
1488             # Second pass
1489              
1490 0           my $var_data_offset_index = $out_param_array_counter;
1491 0           my $var_data_offset = $var_data_offset_index * 32;
1492 0           my $array_index=0;
1493 0           my $second_pass_counter=0;
1494              
1495 0           foreach my $out_param (@$function_inputs)
1496             {
1497 0           my $cur_param_name = $out_param->{name};
1498 0           my $cur_param_type = $out_param->{type};
1499              
1500 0 0 0       if($cur_param_type eq 'bool' || $cur_param_type =~ m/^uint/i)
    0          
    0          
    0          
1501             {
1502 0           $array_index++;
1503             }
1504             elsif($cur_param_type =~ m/^int/i)
1505             {
1506 0           $array_index++;
1507             }
1508             elsif($cur_param_type eq 'address')
1509             {
1510 0           $array_index++;
1511             }
1512             elsif($cur_param_type eq 'string')
1513             {
1514 0           my $str=$function_params->{ $cur_param_name };
1515 0           my $hunk=$this->_string2hex($str);
1516 0           my $hunk_size = length($hunk);
1517 0           my $number_of_32b_blocks = int($hunk_size / 64);
1518 0           my $part = $hunk_size % 64;
1519 0           my $repeater = '0'x(64-$part + 2);
1520 0           my $hunk_appended = substr($hunk.$repeater, 2);
1521 0           my $str_length = length($str);
1522              
1523 0           $out_param_array->[$array_index] = sprintf('%064x', $var_data_offset);
1524 0           $var_data_offset += (($number_of_32b_blocks+1) * 32) + 32;
1525 0           $array_index++;
1526 0           $out_param_array->[$var_data_offset_index + $second_pass_counter] = sprintf('%064x', $str_length);
1527 0           $second_pass_counter++;
1528              
1529 0           my $hunk_position=0;
1530 0           while(1)
1531             {
1532 0           my $cur_str=substr($hunk_appended, $hunk_position, 64);
1533 0 0         if($cur_str eq "")
1534             {
1535 0           last;
1536             }
1537 0           $out_param_array->[$var_data_offset_index + $second_pass_counter] = $cur_str;
1538 0           $second_pass_counter++;
1539 0           $hunk_position += 64;
1540             }
1541             }
1542             }
1543              
1544 0           my $raw;
1545 0 0         if($function_name eq 'constructor')
1546             {
1547 0           $raw=join('', @$out_param_array);
1548             }
1549             else
1550             {
1551             # Get Contract Method Id
1552 0           my $function_selector = $function_name;
1553 0           chop($param_types_list);
1554 0           $function_selector = $function_selector.'('.$param_types_list.')';
1555 0           my $contract_method_id = $this->_getContractMethodId($function_selector);
1556 0           $raw=$contract_method_id.join('', @$out_param_array);
1557             }
1558 0           return $raw;
1559             }
1560              
1561              
1562             =pod
1563              
1564             =head2 _unmarshal
1565              
1566             Internal method.
1567             Unmarshal data from JSON RPC call
1568              
1569             return $this->_unmarshal($function_name, $raw_data);
1570              
1571             $function_name - method name to get ABI
1572             $raw_data - data, returned from method
1573              
1574             =cut
1575              
1576             sub _unmarshal()
1577             {
1578 0     0     my ($this, $function_name, $raw_data) = @_;
1579              
1580 0           my $function_abi = $this->_get_function_abi($function_name);
1581 0           my $function_outputs = $function_abi-> { outputs };
1582              
1583 0           my $return_value={};
1584 0 0         if($this->{return_raw_data})
1585             {
1586 0           $return_value-> { raw_data } = $raw_data;
1587             }
1588              
1589 0           my $current_out_param_position=0;
1590              
1591 0           foreach my $out_param (@$function_outputs)
1592             {
1593 0           my $cur_param_name = $out_param->{name};
1594 0           my $cur_param_type = $out_param->{type};
1595              
1596 0 0 0       if($cur_param_type eq 'bool' || $cur_param_type =~ m/^uint/i)
    0          
    0          
    0          
1597             {
1598 0           my $hunk='0x'.substr($raw_data, $current_out_param_position, 64);
1599 0           my $uint256 = $this->_unmarshal_int($hunk);
1600 0           $return_value->{ $cur_param_name } = $uint256;
1601 0           $current_out_param_position += 64;
1602             }
1603             elsif($cur_param_type =~ m/^int/i)
1604             {
1605 0           my $hunk='0x'.substr($raw_data, $current_out_param_position, 64);
1606 0           my $uint256 = $this->_unmarshal_int($hunk);
1607 0           $return_value->{ $cur_param_name } = $uint256;
1608 0           $current_out_param_position += 64;
1609              
1610             }
1611             elsif($cur_param_type eq 'address')
1612             {
1613 0           my $hunk='0x'.substr($raw_data, $current_out_param_position, 64);
1614 0           $hunk =~ s/00//g;
1615 0           $return_value->{ $cur_param_name } = $hunk;
1616 0           $current_out_param_position += 64;
1617             }
1618             elsif($cur_param_type eq 'string')
1619             {
1620 0           my $size_offset = hex('0x'.substr($raw_data, $current_out_param_position, 64));
1621 0           my $data_size = hex(substr($raw_data, $size_offset * 2, 64));
1622 0           my $data_chunk = substr($raw_data, 64 + $size_offset*2, $data_size * 2);
1623 0           my $str = $this->_hex2string('0x'.$data_chunk);
1624 0           $return_value->{ $cur_param_name } = $str;
1625 0           $current_out_param_position += 64;
1626             }
1627             else
1628             {
1629 0           die 'Net::Ethereum _marshal does not support type: '.$cur_param_type;
1630             }
1631             }
1632 0           return $return_value;
1633             }
1634              
1635             =pod
1636              
1637             =head2 _marshal_int
1638              
1639             Internal method.
1640             Marshal integer value
1641             Returns marshales string
1642              
1643             $add_hunk=$this->_marshal_int($add_hunk);
1644              
1645             int_to_marshal - int value to marshal
1646              
1647             =cut
1648              
1649             sub _marshal_int($$)
1650             {
1651 0     0     my ($this, $int_to_marshal) = @_;
1652 0           my $bint_hex;
1653             my $filler_size;
1654 0           my $filler_char;
1655              
1656 0           my $bint = Math::BigInt->new($int_to_marshal);
1657 0 0         if($bint->is_negative())
1658             {
1659 0           $bint->bneg();
1660 0           $bint->bxor('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
1661 0           $bint->binc();
1662 0           $filler_char = 'f';
1663             }
1664             else
1665             {
1666 0           $filler_char = '0';
1667             }
1668 0           $bint_hex = $bint->to_hex();
1669 0           $filler_size = 64 - length($bint_hex);
1670 0           for(1..$filler_size)
1671             {
1672 0           $bint_hex = $filler_char.$bint_hex;
1673             }
1674 0           return($bint_hex);
1675             }
1676              
1677             =pod
1678              
1679             =head2 _unmarshal_int
1680              
1681             Internal method.
1682             Unmarshal integer value
1683             Returns unmarshaled value
1684              
1685             my $uint256 = $this->_unmarshal_int($hunk);
1686              
1687             int_to_marshal - int value to marshal
1688              
1689             =cut
1690              
1691             sub _unmarshal_int($$)
1692             {
1693 0     0     my ($this, $str_to_unmarshal) = @_;
1694 0           my $unmarshalled_int = Math::BigInt->new($str_to_unmarshal);
1695 0           my $neg_test = Math::BigInt->new($str_to_unmarshal);
1696 0 0         if($neg_test->band('0x8000000000000000000000000000000000000000000000000000000000000000'))
1697             {
1698 0           $unmarshalled_int->bxor('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
1699 0           $unmarshalled_int->binc();
1700 0           $unmarshalled_int->bneg();
1701             }
1702 0           return $unmarshalled_int;
1703             }
1704              
1705             =pod
1706              
1707             =head2 _get_function_abi
1708              
1709             Internal method.
1710             Returns ABI for given method
1711              
1712             my $function_abi = $this->_get_function_abi($function_name);
1713              
1714             $function_name - method name to get ABI
1715              
1716             =cut
1717              
1718             sub _get_function_abi()
1719             {
1720 0     0     my ($this, $function_name) = @_;
1721 0           my $rec;
1722 0           my $abi = $this->{abi};
1723 0           foreach $rec (@$abi)
1724             {
1725 0 0 0       if($rec->{ type } eq 'function' and $rec->{ name } eq $function_name)
1726             {
1727 0           return $rec;
1728             }
1729             }
1730 0           return {};
1731             }
1732              
1733              
1734             =pod
1735              
1736             =head2 _get_constructor_abi
1737              
1738             Internal method.
1739             Returns ABI for contract constructor
1740              
1741             if($function_name eq 'constructor')
1742             {
1743             $function_abi = $this->_get_constructor_abi($function_name);
1744             }
1745              
1746             $function_name - method name to get ABI
1747              
1748             =cut
1749              
1750             sub _get_constructor_abi()
1751             {
1752 0     0     my ($this, $function_name) = @_;
1753 0           my $rec;
1754 0           my $abi = $this->{abi};
1755              
1756 0           foreach $rec (@$abi)
1757             {
1758 0 0         if($rec->{ type } eq 'constructor')
1759             {
1760 0           return $rec;
1761             }
1762             }
1763 0           return {};
1764             }
1765              
1766              
1767             =pod
1768              
1769             =head2 _getContractMethodId
1770              
1771             Internal method.
1772             Convert a method name into function selector (contract methos id).
1773             Returns the contract methos id.
1774              
1775             The first four bytes of the call data for a function call specifies the function to be called.
1776             It is the first (left, high-order in big-endian) four bytes of the Keccak (SHA-3) hash of the signature of the function.
1777             The signature is defined as the canonical expression of the basic prototype, i.e. the function name with the parenthesised list of parameter types.
1778             Parameter types are split by a single comma - no spaces are used.
1779              
1780             my $function_selector = $function_name;
1781             chop($param_types_list);
1782             $method_name = $function_selector.'('.$param_types_list.')';
1783             my $contract_method_id = $this->_getContractMethodId($method_name);
1784              
1785             $method_name - function name, include parameters
1786              
1787             =cut
1788              
1789             sub _getContractMethodId()
1790             {
1791 0     0     my ($this, $method_name) = @_;
1792 0           my $method_name_hex = $this->_string2hex($method_name);
1793 0           my $hash = $this->web3_sha3($method_name_hex);
1794 0           return substr($hash, 0, 10);
1795             }
1796              
1797             =pod
1798              
1799             =head2 _hex2string
1800              
1801             Internal method.
1802             Convert a hexadecimal value into string. Returns the string.
1803              
1804             my $str = $this->_hex2string('0x'.$data_chunk);
1805              
1806             $data_chunk - hexadecimal data to conv
1807              
1808             =cut
1809              
1810             sub _hex2string()
1811             {
1812 0     0     my ($this, $hex) = @_;
1813              
1814 0           my $n = 2; # $n is group size.
1815 0           my @groups = unpack "a$n" x (length( $hex ) /$n ), $hex;
1816 0           my $string = join ('', map { chr(hex($_)) } @groups );
  0            
1817 0           return($string);
1818             }
1819              
1820              
1821             =pod
1822              
1823             =head2 _string2hex
1824              
1825             Internal method.
1826             Convert a string to hexadecimal. Returns the converted string.
1827              
1828             my $hunk=$this->_string2hex($str);
1829              
1830             $string - source string to conv
1831              
1832             =cut
1833              
1834             sub _string2hex()
1835             {
1836 0     0     my ($this, $string) = @_;
1837 0           my @array = split('', $string);
1838 0           my @array_ascii = map { sprintf("%x", ord($_)) } @array;
  0            
1839 0           my $string_ascii = join('', @array_ascii);
1840 0           return '0x'.$string_ascii;
1841             }
1842              
1843              
1844             =pod
1845              
1846             =head2 _node_request
1847              
1848             Internal method.
1849             Send request to JSON RPC API
1850              
1851             my $rq = { jsonrpc => "2.0", method => "net_version", params => [], id => 67};
1852             my $num = $this->_node_request($rq)-> { result };
1853              
1854             =cut
1855              
1856             sub _node_request()
1857             {
1858 0     0     my ($this, $json_data) = @_;
1859              
1860 0           my $req = HTTP::Request->new(POST => $this->{api_url});
1861 0           $req->header('Content-Type' => 'application/json');
1862 0           my $ua = LWP::UserAgent->new;
1863 0           my $data = encode_json($json_data);
1864 0           $req->add_content_utf8($data);
1865              
1866             #print '_node_request req: ', Dumper($req);
1867              
1868 0           my $ua_rc = $ua->request($req)->{ _content };
1869              
1870             #print '_node_request ua_rc: ', Dumper($ua_rc);
1871              
1872 0           my $rc;
1873 0 0         if($ua_rc=~/"result":/)
1874             {
1875 0           $rc = JSON::decode_json($ua_rc);
1876             }
1877             else
1878             {
1879 0           die 'Died at Net::Ethereum _node_request() - '.$ua_rc;
1880             }
1881 0 0         if($rc->{error}) { die $rc; }
  0            
1882 0           else { return $rc; }
1883             }
1884              
1885              
1886              
1887             1;
1888             __END__