PHP WebShell
Текущая директория: /var/www/bitcardoApp/models/crypto
Просмотр файла: create_tron_wallet.php
<?php
// models/crypto/create_tron_wallet.php
namespace Models\Crypto;
require_once __DIR__ . '/../../vendor/autoload.php';
use mysqli;
use Elliptic\EC;
use kornrunner\Keccak;
use Exception;
class CreateTronWallet
{
/** @var mysqli */
private $db;
public function __construct(mysqli $conn)
{
$this->db = $conn;
}
/**
* Create TRX + USDT-TRC20 wallets for a user.
*
* @param int $userId
* @return array ['success' => bool, 'message' => string, 'address' => string|null]
*/
public function create(int $userId): array
{
try {
// Block duplicates: only 1 TRX wallet per user
if ($this->userHasTrxWallet($userId)) {
return [
'success' => false,
'message' => 'TRX wallet already exists for this user.',
'address' => null,
];
}
// 1) Generate keypair
$privateKeyHex = $this->generatePrivateKey();
$address = $this->tronAddressFromPrivateKey($privateKeyHex); // Base58, matches TronLink
// 2) Extra safety check (same logic as check_tron_wallet_keys.php)
if (!$this->verifyKeyAndAddress($privateKeyHex, $address)) {
// Do NOT touch DB if derivation is not stable
return [
'success' => false,
'message' => 'TRX wallet cannot be created at this time. Please try again later.',
'address' => null,
];
}
// 3) Start DB transaction
$this->db->begin_transaction();
// Save private key
$this->insertWalletKey($userId, $address, $privateKeyHex);
// Save TRX + USDT wallets (same address)
$this->insertUserWallets($userId, $address);
$this->db->commit();
return [
'success' => true,
'message' => 'TRX and USDT-TRC20 wallets created successfully.',
'address' => $address,
];
} catch (Exception $e) {
// Rollback if transaction started
@ $this->db->rollback();
return [
'success' => false,
'message' => 'Error creating TRX wallet: ' . $e->getMessage(),
'address' => null,
];
}
}
/**
* Check if user already has a TRX wallet.
*/
private function userHasTrxWallet(int $userId): bool
{
$sql = "SELECT COUNT(*) AS cnt
FROM user_wallets
WHERE user_id = ? AND coin = 'TRX' AND wallet_status = 'active'";
$stmt = $this->db->prepare($sql);
if (!$stmt) {
throw new Exception("DB error preparing userHasTrxWallet: " . $this->db->error);
}
$stmt->bind_param("i", $userId);
$stmt->execute();
$res = $stmt->get_result();
$row = $res->fetch_assoc();
$stmt->close();
return ((int)($row['cnt'] ?? 0)) > 0;
}
/**
* Secure random 32-byte private key (hex).
*/
private function generatePrivateKey(): string
{
$bytes = random_bytes(32);
$hex = bin2hex($bytes);
if (strlen($hex) !== 64) {
throw new Exception("Generated private key has invalid length.");
}
return $hex;
}
/**
* Extra safety: verify that privateKey → derived address matches the one we plan to save.
* This mirrors the logic used in check_tron_wallet_keys.php.
*/
private function verifyKeyAndAddress(string $privateKeyHex, string $address): bool
{
$derived = $this->tronAddressFromPrivateKey($privateKeyHex);
// hash_equals protects against timing attacks, but here it's mostly just a clean comparison
return hash_equals($derived, $address);
}
/**
* Correct TRON address derivation from private key (matches TronLink/TronWeb).
*/
private function tronAddressFromPrivateKey(string $privateKeyHex): string
{
$ec = new EC('secp256k1');
$key = $ec->keyFromPrivate($privateKeyHex, 'hex');
// 1) Uncompressed public key: 0x04 + X(32) + Y(32)
$pubHex = $key->getPublic(false, 'hex'); // "04...."
$pubHex = substr($pubHex, 2); // drop "04"
// 2) Keccak-256 on the *binary* public key (Ethereum/Tron style)
// -> keccak256( hex2bin(pubkey_without_04) )
$hashHex = Keccak::hash(hex2bin($pubHex), 256);
// 3) Last 20 bytes (40 hex chars)
$ethPart = substr($hashHex, -40);
// 4) Tron mainnet prefix 0x41
$tronHex = '41' . $ethPart;
$addrBin = hex2bin($tronHex);
// 5) Base58Check: address + 4-byte checksum (double SHA256)
$checksum = substr(
hash('sha256', hash('sha256', $addrBin, true), true),
0,
4
);
$payload = $addrBin . $checksum;
return $this->base58encode($payload);
}
/**
* Base58 encoding with Bitcoin alphabet (TRON uses same).
*/
private function base58encode(string $data): string
{
$alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
$num = gmp_init(0, 10);
$len = strlen($data);
for ($i = 0; $i < $len; $i++) {
$num = gmp_add(
gmp_mul($num, 256),
ord($data[$i])
);
}
$encoded = '';
while (gmp_cmp($num, 0) > 0) {
[$num, $rem] = [
gmp_div_q($num, 58),
gmp_intval(gmp_mod($num, 58))
];
$encoded = $alphabet[$rem] . $encoded;
}
// Preserve leading zeros as '1'
$i = 0;
while ($i < $len && $data[$i] === "\x00") {
$encoded = '1' . $encoded;
$i++;
}
return $encoded;
}
/**
* Insert private key row.
*/
private function insertWalletKey(int $userId, string $address, string $privateKeyHex): void
{
$sql = "INSERT INTO wallet_keys (user_id, wallet_add, private_key, created_at)
VALUES (?, ?, ?, NOW())";
$stmt = $this->db->prepare($sql);
if (!$stmt) {
throw new Exception("DB error preparing insertWalletKey: " . $this->db->error);
}
$stmt->bind_param("iss", $userId, $address, $privateKeyHex);
if (!$stmt->execute()) {
$err = $stmt->error;
$stmt->close();
throw new Exception("DB error executing insertWalletKey: " . $err);
}
$stmt->close();
}
/**
* Insert TRX and USDT-TRC20 wallets using the same address.
*/
private function insertUserWallets(int $userId, string $address): void
{
$icon = 'trx.png';
$type = 'crypto';
$status = 'active';
$balance = '0.000000'; // 6 decimals for TRX/USDT
// TRX wallet
$sql = "INSERT INTO user_wallets
(user_id, wallet_add, coin, label, balance, type, icon, wallet_status, created_at)
VALUES (?, ?, 'TRX', 'TRX Wallet', ?, ?, ?, ?, NOW())";
$stmt = $this->db->prepare($sql);
if (!$stmt) {
throw new Exception("DB error preparing insert TRX wallet: " . $this->db->error);
}
// user_id (i), wallet_add (s), balance (s), type (s), icon (s), status (s)
$stmt->bind_param("isssss", $userId, $address, $balance, $type, $icon, $status);
if (!$stmt->execute()) {
$err = $stmt->error;
$stmt->close();
throw new Exception("DB error executing insert TRX wallet: " . $err);
}
$stmt->close();
// USDT-TRC20 wallet
$icon = 'usdt.png';
$sql = "INSERT INTO user_wallets
(user_id, wallet_add, coin, label, balance, type, icon, wallet_status, created_at)
VALUES (?, ?, 'USDT-TRC20', 'USDT-TRC20 Wallet', ?, ?, ?, ?, NOW())";
$stmt = $this->db->prepare($sql);
if (!$stmt) {
throw new Exception("DB error preparing insert USDT wallet: " . $this->db->error);
}
$stmt->bind_param("isssss", $userId, $address, $balance, $type, $icon, $status);
if (!$stmt->execute()) {
$err = $stmt->error;
$stmt->close();
throw new Exception("DB error executing insert USDT wallet: " . $err);
}
$stmt->close();
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!