diff --git a/utils/vanitygen.php b/utils/vanitygen.php index fd3d5a28..6a6791d1 100644 --- a/utils/vanitygen.php +++ b/utils/vanitygen.php @@ -1,24 +1,42 @@ [-c] [-d] + * - : The desired prefix for the PHPCoin address. + * - -c: Case-sensitive matching. + * - -d: Enable debug output. + */ + const VANITYGEN_NAME = 'PHPCoin Vanity Address Generator'; -const VANITYGEN_VERSION = '0.0.1'; +const VANITYGEN_VERSION = '0.0.2'; const VANITYGEN_USAGE = 'Usage: php vanitygen.php prefix [-c] [-d]' . PHP_EOL . ' prefix Prefix for the PHPCoin address (e.g., "Php")' . PHP_EOL . ' -c Case sensitive matching' . PHP_EOL . ' -d Enable debug output' . PHP_EOL; -const VANITYGEN_URL = 'https://github.com/phpcoinn/node/blob/main/utils/vanitygen.php'; -const DEFAULT_CHAIN_ID = '00'; +const VANITYGEN_URL = 'https://github.com/phpcoinn/node/blob/main/utils/vanitygen.php'; +const CHAIN_PREFIX = "38"; $debug = false; -print VANITYGEN_NAME . ' v' . VANITYGEN_VERSION . PHP_EOL; +if (php_sapi_name() !== 'cli') { + exit('ERROR: This script must be run from the command line' . PHP_EOL); +}; -setupOrExit(); +print VANITYGEN_NAME . ' v' . VANITYGEN_VERSION . PHP_EOL; generateVanityAddress(getOptionsOrExit($argv)); print PHP_EOL . 'Exiting ' . VANITYGEN_NAME . PHP_EOL; +// +// Script Functions +// + /** * Generates a vanity PHPCoin address based on the provided options. * @@ -47,7 +65,7 @@ function generateVanityAddress(array $options): array $count = 0; while (true) { - $account = Account::generateAcccount(); + $account = generateAccount(); $address = $account['address']; $count++; _debug('Generation '. $count . ': ' . $address); @@ -136,26 +154,6 @@ function getOptionsOrExit(array $argv): array ]; } -/** - * Sets up the environment or exits if conditions are not met. - * - * Ensures the script is run from the command line and that the autoload file exists. - */ -function setupOrExit(): void -{ - if (php_sapi_name() !== 'cli') { - exit('ERROR: This script must be run from the command line' . PHP_EOL); - }; - $autoload = Phar::running() - ? 'vendor/autoload.php' - : dirname(__DIR__) . '/vendor/autoload.php'; - - if (! file_exists($autoload)) { - exit('ERROR: Autoload file not found. Please run "composer install".' . PHP_EOL); - } - require_once $autoload; -} - /** * Outputs debug messages if debugging is enabled. * @@ -168,3 +166,167 @@ function _debug(string $message): void print '[DEBUG] ' . $message . PHP_EOL; } } + +// +// Crypto Functions +// + +// +// Source: include/class/Account.php +// +function generateAccount() +{ + // using secp256k1 curve for ECDSA + $args = [ + "curve_name" => "secp256k1", + "private_key_type" => OPENSSL_KEYTYPE_EC, + ]; + + // generates a new key pair + $key1 = openssl_pkey_new($args); + + // exports the private key encoded as PEM + openssl_pkey_export($key1, $pvkey); + + if(PHP_VERSION_ID > 80000) { + $in = sys_get_temp_dir() . "/phpcoin.in.pem"; + $out = sys_get_temp_dir() . "/phpcoin.out.pem"; + file_put_contents($in, $pvkey); + $cmd = "openssl ec -in $in -out $out >/dev/null 2>&1"; + shell_exec($cmd); + $pvkey = file_get_contents($out); + unlink($in); + unlink($out); + } + + // converts the PEM to a base58 format + $private_key = pem2coin($pvkey); + + // exports the private key encoded as PEM + $pub = openssl_pkey_get_details($key1); + + // converts the PEM to a base58 format + $public_key = pem2coin($pub['key']); + + // generates the account's address based on the public key + $address = getAddress($public_key); + return ["address" => $address, "public_key" => $public_key, "private_key" => $private_key]; +} + +function getAddress($public_key) { + if(empty($public_key)) return null; + $hash1=hash('sha256', $public_key); + $hash2=hash('ripemd160',$hash1); + $baseAddress=CHAIN_PREFIX.$hash2; + $checksumCalc1=hash('sha256', $baseAddress); + $checksumCalc2=hash('sha256', $checksumCalc1); + $checksumCalc3=hash('sha256', $checksumCalc2); + $checksum=substr($checksumCalc3, 0, 8); + $addressHex = $baseAddress.$checksum; + $address = base58_encode(hex2bin($addressHex)); + return $address; +} + +// +// Source: include/functions.inc.php +// +function pem2coin($data) +{ + $data = str_replace("-----BEGIN PUBLIC KEY-----", "", $data); + $data = str_replace("-----END PUBLIC KEY-----", "", $data); + $data = str_replace("-----BEGIN EC PRIVATE KEY-----", "", $data); + $data = str_replace("-----END EC PRIVATE KEY-----", "", $data); + $data = str_replace("\n", "", $data); + $data = base64_decode($data); + return base58_encode($data); +} + +// +// Source: include/common.functions.php +// Base58 encoding/decoding functions - all credits go to https://github.com/stephen-hill/base58php +// +function base58_encode($string) +{ + $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; + $base = strlen($alphabet); + // Type validation + if (is_string($string) === false) { + return false; + } + // If the string is empty, then the encoded string is obviously empty + if (strlen($string) === 0) { + return ''; + } + // Now we need to convert the byte array into an arbitrary-precision decimal + // We basically do this by performing a base256 to base10 conversion + $hex = unpack('H*', $string); + $hex = reset($hex); + $decimal = gmp_init($hex, 16); + // This loop now performs base 10 to base 58 conversion + // The remainder or modulo on each loop becomes a base 58 character + $output = ''; + while (gmp_cmp($decimal, $base) >= 0) { + list($decimal, $mod) = gmp_div_qr($decimal, $base); + $output .= $alphabet[gmp_intval($mod)]; + } + // If there's still a remainder, append it + if (gmp_cmp($decimal, 0) > 0) { + $output .= $alphabet[gmp_intval($decimal)]; + } + // Now we need to reverse the encoded data + $output = strrev($output); + // Now we need to add leading zeros + $bytes = str_split($string); + foreach ($bytes as $byte) { + if ($byte === "\x00") { + $output = $alphabet[0].$output; + continue; + } + break; + } + return (string)$output; +} + +function base58_decode($base58) +{ + $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; + $base = strlen($alphabet); + + // Type Validation + if (is_string($base58) === false) { + return false; + } + // If the string is empty, then the decoded string is obviously empty + if (strlen($base58) === 0) { + return ''; + } + $indexes = array_flip(str_split($alphabet)); + $chars = str_split($base58); + // Check for invalid characters in the supplied base58 string + foreach ($chars as $char) { + if (isset($indexes[$char]) === false) { + return false; + } + } + // Convert from base58 to base10 + $decimal = gmp_init($indexes[$chars[0]], 10); + for ($i = 1, $l = count($chars); $i < $l; $i++) { + $decimal = gmp_mul($decimal, $base); + $decimal = gmp_add($decimal, $indexes[$chars[$i]]); + } + // Convert from base10 to base256 (8-bit byte array) + $output = ''; + while (gmp_cmp($decimal, 0) > 0) { + list($decimal, $byte) = gmp_div_qr($decimal, 256); + $output = pack('C', gmp_intval($byte)).$output; + } + // Now we need to add leading zeros + foreach ($chars as $char) { + if ($indexes[$char] === 0) { + $output = "\x00".$output; + continue; + } + break; + } + return $output; +}