Wallet   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 78
dl 0
loc 194
rs 10
c 0
b 0
f 0
wmc 24

15 Methods

Rating   Name   Duplication   Size   Complexity  
A getTransactionById() 0 8 1
A createTransaction() 0 11 1
A getAddressHex() 0 9 2
A validateAddress() 0 11 2
A genKeyPair() 0 5 1
A signTransaction() 0 13 1
A getAccountNet() 0 12 2
A getBlockById() 0 8 1
A generateAddress() 0 30 4
A __construct() 0 3 1
A getBase58CheckAddress() 0 8 1
A broadcastTransaction() 0 10 3
A getAccount() 0 12 2
A getNowBlock() 0 5 1
A easyTransferByPrivate() 0 3 1
1
<?php
2
3
namespace mattvb91\TronTrx;
4
5
use kornrunner\Keccak;
6
use mattvb91\TronTrx\Exceptions\TransactionException;
7
use mattvb91\TronTrx\Exceptions\TronErrorException;
8
use mattvb91\TronTrx\Interfaces\WalletInterface;
9
use mattvb91\TronTrx\Support\Base58;
10
use mattvb91\TronTrx\Support\Base58Check;
11
use mattvb91\TronTrx\Support\Crypto;
12
use mattvb91\TronTrx\Support\Hash;
13
use mattvb91\TronTrx\Traits\TronAwareTrait;
14
use Phactor\Key;
15
16
/**
17
 * Class Wallet
18
 * @package mattvb91\TronTrx
19
 */
20
class Wallet implements WalletInterface
21
{
22
    use TronAwareTrait;
23
24
    private $_api;
25
26
    public function __construct(Api $_api)
27
    {
28
        $this->_api = $_api;
29
    }
30
31
    public function genKeyPair(): array
32
    {
33
        $key = new Key();
34
35
        return $key->GenerateKeypair();
36
    }
37
38
    public function getAddressHex(string $pubKeyBin): string
39
    {
40
        if (strlen($pubKeyBin) == 65) {
41
            $pubKeyBin = substr($pubKeyBin, 1);
42
        }
43
44
        $hash = Keccak::hash($pubKeyBin, 256);
45
46
        return Address::ADDRESS_PREFIX . substr($hash, 24);
47
    }
48
49
    public function getBase58CheckAddress(string $addressBin): string
50
    {
51
        $hash0 = Hash::SHA256($addressBin);
52
        $hash1 = Hash::SHA256($hash0);
53
        $checksum = substr($hash1, 0, 4);
54
        $checksum = $addressBin . $checksum;
55
56
        return Base58::encode(Crypto::bin2bc($checksum));
57
    }
58
59
    public function generateAddress(): Address
60
    {
61
        $attempts = 0;
62
        $validAddress = false;
63
64
        do {
65
            if ($attempts++ === 5) {
66
                throw new TronErrorException('Could not generate valid key');
67
            }
68
69
            $keyPair = $this->genKeyPair();
70
            $privateKeyHex = $keyPair['private_key_hex'];
71
            $pubKeyHex = $keyPair['public_key'];
72
73
            //We cant use hex2bin unless the string length is even.
74
            if (strlen($pubKeyHex) % 2 !== 0) {
75
                continue;
76
            }
77
78
            $pubKeyBin = hex2bin($pubKeyHex);
79
            $addressHex = $this->getAddressHex($pubKeyBin);
80
            $addressBin = hex2bin($addressHex);
81
            $addressBase58 = $this->getBase58CheckAddress($addressBin);
82
83
            $address = new Address($addressBase58, $privateKeyHex, $addressHex);
84
            $validAddress = $this->validateAddress($address);
85
86
        } while (!$validAddress);
87
88
        return $address;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $address does not seem to be defined for all execution paths leading up to this point.
Loading history...
89
    }
90
91
    public function validateAddress(Address $address): bool
92
    {
93
        if (!$address->isValid()) {
94
            return false;
95
        }
96
97
        $body = $this->_api->post('/wallet/validateaddress', [
98
            'address' => $address->address,
99
        ]);
100
101
        return $body->result;
102
    }
103
104
    public function getAccount(Address $address): ?Account
105
    {
106
        $body = $this->_api->post('/wallet/getaccount', [
107
            'address' => $address->hexAddress,
108
        ]);
109
110
        if (isset($body->address)) {
111
            $address = new Address($body->address);
112
            return new Account($address, $body->balance, $body->create_time);
113
        }
114
115
        return null;
116
    }
117
118
    public function getAccountNet(Address $address): ?array
119
    {
120
        $data = $this->_api->post('/wallet/getaccountnet',
121
            ['address' => $address->hexAddress],
122
            true
123
        );
124
125
        if (sizeof($data)) {
126
            return $data;
127
        }
128
129
        return null;
130
    }
131
132
    /**
133
     * This is open to attacks. Instead use createTransaction(),
134
     * then sign it locally,
135
     * then broadcastTransaction()
136
     *
137
     * @deprecated See exception
138
     */
139
    final public function easyTransferByPrivate(string $private, Address $address, float $amount)
140
    {
141
        throw new \Exception('This is vulnerable to MiTM attacks. Do not use');
142
    }
143
144
    public function createTransaction(Address $toAddress, Address $ownerAddress, float $amount = 0): Transaction
145
    {
146
        $body = $this->_api->post('/wallet/createtransaction', [
147
            'to_address'    => $toAddress->hexAddress,
148
            'owner_address' => $ownerAddress->hexAddress,
149
            'amount'        => $amount,
150
        ]);
151
152
        return new Transaction(
153
            $body->txID,
154
            $body->raw_data
155
        );
156
    }
157
158
    /**
159
     * TODO sign locally instead of over api as this is man in the middle attack
160
     * waiting to happen by posting privateKey
161
     */
162
    public function signTransaction(Transaction &$transaction, string $privateKey): Transaction
163
    {
164
        unset($transaction->signature);
165
        $transactionArray = json_decode(json_encode($transaction), true);
166
167
        $body = $this->_api->post('/wallet/gettransactionsign', [
168
            'transaction' => $transactionArray,
169
            'privateKey'  => $privateKey,
170
        ]);
171
172
        $transaction->signature = $body->signature;
173
174
        return $transaction;
175
    }
176
177
    public function broadcastTransaction(Transaction $transaction): bool
178
    {
179
        if (!$transaction->isSigned()) {
180
            throw new TransactionException('Transaction is not signed');
181
        }
182
183
        $transactionArray = json_decode(json_encode($transaction), true);
184
        $body = $this->_api->post('/wallet/broadcasttransaction', $transactionArray);
185
186
        return $body->result ? $body->result : false;
187
    }
188
189
    public function getTransactionById(string $transactionID): Transaction
190
    {
191
        $body = $this->_api->post('/wallet/gettransactionbyid', [
192
                'value' => $transactionID,
193
            ]
194
        );
195
196
        return new Transaction($body->txID, $body->raw_data);
197
    }
198
199
    public function getNowBlock(): Block
200
    {
201
        $body = $this->_api->post('/wallet/getnowblock');
202
203
        return new Block($body->blockID, $body->block_header);
204
    }
205
206
    public function getBlockById(string $blockId): Block
207
    {
208
        $body = $this->_api->post('/wallet/getblockbyid', [
209
                'value' => $blockId,
210
            ]
211
        );
212
213
        return new Block($body->blockID, $body->block_header);
214
    }
215
216
//    public function freezeBalance(Address $ownerAddress, float $balanceToFreeze, int $durationDays, string $resource = 'BANDWIDTH')
217
//    {
218
//        $body = $this->_api->post('/wallet/freezebalance', [
219
//                'owner_address'   => $ownerAddress->hexAddress,
220
//                'frozen_balance'  => $balanceToFreeze,
221
//                'frozen_duration' => $durationDays,
222
//                'resource'        => $resource,
223
//            ]
224
//        );
225
//
226
//        return $body;
227
//    }
228
}
229