<?php
// start.php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/helper.php';
require __DIR__ . '/config.php';
require __DIR__ . '/version.php';

use Workerman\Worker;
use Elliptic\EC;
use kornrunner\Keccak;
use Workerman\Timer;


// 你应该实现这个函数：发现地址后要做的事（扫描、转账、上报等）
// 这里只给出一个示例实现：异步-safe 的日志 + 简单输出。
// 请根据你实际需要替换或扩展该函数内容。
// 注意：这个函数在各个子进程中同步执行（阻塞该进程直至返回）。
function scanwallet(string $address, string $privateKey): void {
    // === 示例逻辑（请替换为真实逻辑） ===
    // 1) 记录到日志文件（追加）
    global $logFile;
    $entry = [
        'timestamp' => date('c'),
        'address' => $address,
        'private_key' => $privateKey,
    ];
    // 写入日志（追加，使用文件锁）
    $json = json_encode($entry, JSON_UNESCAPED_SLASHES) . PHP_EOL;
    file_put_contents($logFile, $json, FILE_APPEND | LOCK_EX);

    // 2) 打印到控制台（便于实时监控）
    echo "[" . date('Y-m-d H:i:s') . "] scanwallet called for {$address}\n";

    // 3) 你可以在这里调用 HTTP 接口、RPC、或本地扫描逻辑
    //    例如：调用一个外部脚本或执行 web3 RPC 检查余额 / token 等。
    //    如果操作可能耗时较长，考虑把耗时任务放到队列（Redis/Beanstalk/DB）或另起子进程处理。
    // =======================================
}

// CLI 参数解析（可选）
#$options = getopt('', ['workers::', 'repeat::', 'numeric-only::', 'logfile::']);
#echo($options['repeat']);
$workerCount = isset($options['workers']) ? (int)$options['workers'] : cpu_count()*4; 
$minRepeat = isset($options['repeat']) ? (int)$options['repeat'] : 4; // 默认至少 4 个相同
$requireNumeric = isset($options['numeric-only']) ? (bool)$options['numeric-only'] : false; 
$logFile = isset($options['logfile']) ? $options['logfile'] : (__DIR__ . '/found.log');
$detailInfo = isset($options['detailInfo']) ? (bool)$options['detailInfo'] : false;
$staticSecond = isset($options['staticSecond']) ? (int)$options['staticSecond'] : 10; 
echo "配置: 详细输出={$detailInfo}, 进程:{$workerCount}, 相同数量={$minRepeat}, 仅数字=" . ($requireNumeric ? 'true' : 'false') . ", logfile={$logFile}\n";

$Rat = new Worker();
$GLOBALS['mainid']=posix_getpid(); #父进程PID
$Rat->name="Glod Rat";
$Rat->count = max(1, $workerCount);
$GDS = new GlobalData\Server('127.0.0.1', 2207);
$Rat->onWorkerStart = function($Rat) use ($minRepeat, $requireNumeric,$detailInfo) {
        global $gd;
        $gd = new \GlobalData\Client('127.0.0.1:2207');
        $gd->count=0;
        $pid = posix_getpid();

    $cid=$pid-$GLOBALS['mainid'];
    if($detailInfo){
    echo "[Rat $cid]启动  PID={$pid}\n";
    }
    $ec = new EC('secp256k1');
    $start_time = microtime(true);
            $staticCount=0;

    // 无限循环，持续生成地址并检查
    while (true) {
        // 生成随机私钥（32 字节）
        try {
            $privBin = random_bytes(32);
        } catch (Exception $e) {
            // random_bytes 失败（极少见），跳过一次循环
            echo "PID={$pid} random_bytes failed: " . $e->getMessage() . "\n";
            usleep(1000);
            continue;
        }
        $privHex = bin2hex($privBin);

        // 由私钥生成公钥（未压缩）
        $keyPair = $ec->keyFromPrivate($privHex);
        $pubHex = $keyPair->getPublic(false, 'hex'); // '04' + x + y
        $pubHexNoPrefix = (strpos($pubHex, '04') === 0) ? substr($pubHex, 2) : $pubHex;

        // keccak256 公钥并取地址
        $hash = Keccak::hash(hex2bin($pubHexNoPrefix), 256); // 返回 hex
        $address = '0x' . substr($hash, -40);

        $staticCount=$staticCount+1;
        if (microtime(true) - $start_time >= 5) {
            if($detailInfo){
            echo "[Rat $cid] digged $staticCount address in past 5s \n";
            }
            $gd->count=$gd->count+$staticCount;
            $staticCount=0;
            $start_time = microtime(true);
        }


        // 检查末尾连续相同字符（只检查尾部连续段）
        $addrRaw = strtolower(substr($address, 2)); // 纯 hex（40 chars）
        $len = strlen($addrRaw);
        $lastChar = $addrRaw[$len - 1];
        $count = 1;
        for ($i = $len - 2; $i >= 0; $i--) {
            if ($addrRaw[$i] === $lastChar) $count++;
            else break;
        }

        // 如果满足重复数量，并且（可选）最后字符为数字
        if ($count >= $minRepeat) {
            if ($requireNumeric && !ctype_digit($lastChar)) {
                // 不满足数字要求，继续
            } else {
                // 匹配成功 —— 调用 scanwallet 并记录（但不退出，继续寻找）
                try {
                    // 记录到控制台（便于监控）
                    if($detailInfo){
                    echo " [Rat $cid] PID={$pid} 找到匹配地址 {$address} (尾部 {$count} 个 '{$lastChar}'), 调用 scanwallet\n";
                    }
                    $gd->scount=$gd->scount+1;

                    // 可选：把结果也写入一个单独的结果文件（便于批处理）
                    $result = [
                        'pid' => $pid,
                        'private_key' => $privHex,
                        'address' => $address,
                        'repeat_char' => $lastChar,
                        'repeat_count' => $count,
                        'timestamp' => date('c'),
                        'count'=> $count,
                        'char'=> $lastChar,
                    ];

                    // 调用用户实现的扫描函数（可能阻塞当前进程）
                   # toCache($result);
                    /** 
                    // 追加到 found.log（同 scanwallet 中的 global logfile）
                    global $logFile;
                    file_put_contents($logFile, json_encode($result, JSON_UNESCAPED_SLASHES) . PHP_EOL, FILE_APPEND | LOCK_EX);
                    */
                    // 继续寻找（不要退出）
                } catch (Throwable $t) {
                    // 避免 scanwallet 中的异常导致进程崩溃
                    if($detailInfo){
                    echo "PID={$pid} scanwallet 抛出异常: " . $t->getMessage() . "\n";
                    }
                }
            }
        }

        // 若想降低 CPU 占用可在这里加短暂 sleep（但会降低查找速度）
        // usleep(0); // 等同于 yield
    }
    if($detailInfo){
    echo "PID={$pid} 退出循环（理论上不应到达）\n";
    }
};
$Rat->onWorkerStop = function($Rat) {
    $pid = posix_getpid();
    $cid=$pid-$GLOBALS['mainid'];
    echo "[Rat $cid]启动  PID={$pid} 已终止\n";
};
$Static = new Worker();
$Static->name='Static Server';
$Static->onWorkerStart = function($Static)use($staticSecond,$detailInfo) {
    $GLOBALS['starttime']=microtime(true);
    $GLOBALS['lc']=0;
    $GLOBALS['lsc']=0;
    Timer::add($staticSecond, function()use($staticSecond,$detailInfo){
    $gda = new \GlobalData\Client('127.0.0.1:2207');
    $staticCount=$gda->count;
    $avgCount=($staticCount-$GLOBALS['lc'])/$staticSecond;
    $successCount=$gda->scount;
    $avgsCount=($successCount-$GLOBALS['lsc']);
    $totalSpeed= ($successCount / (microtime(true)-$GLOBALS['starttime'])) * 3600;
    $running=formatSeconds(microtime(true)-$GLOBALS['starttime']);
     if(!$detailInfo){
         echo "\033[H";  // 光标回到左上角
    echo "\033[J";
    echo " 
 .88888.           dP       dP     888888ba             dP   
d8'   `88          88       88     88    `8b            88   
88        .d8888b. 88 .d888b88    a88aaaa8P' .d8888b. d8888P 
88   YP88 88'  `88 88 88'  `88     88   `8b. 88'  `88   88   
Y8.   .88 88.  .88 88 88.  .88     88     88 88.  .88   88   
 `88888'  `88888P' dP `88888P8     dP     dP `88888P8   dP   
    ====================================================
    ||      Gold Rat   v".GR_VERSION."   Build ".GR_BUILD."      || 
    ||Dig out wallet address with the beautiful number||
    ||        As well as a CPU Benchmark tool         ||
    ====================================================
    ";
    }
    echo "[STATIC] in last $staticSecond s:
    Avg: $avgCount adds/s, Total: $staticCount adds
    Succeed: $avgsCount adds, Total: $successCount adds
    Total Diging Speed: $totalSpeed adds/hour
    Total Runing Time: $running
    ====================================================
    Enter Ctrl+C to Exit.
    ————————————————————————————————————————————————————
    ©Gold Rat  by LayFi.de
    \n";
   
     $GLOBALS['lc']=$staticCount;
    $GLOBALS['lsc']=$successCount;

    });
};





Worker::runAll();
