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,$options) { 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"; } $redis = new Redis(); $redis->connect($options['redis']['ip'], $options['redis']['port']); if($detailInfo){ echo "[Rat $cid] Connecting Redis……\n"; } if(isset($options['redis']['password'])&&$options['redis']['password']!=false){ if($detailInfo){ echo "[Rat $cid] Redis Authing……\n"; } $redis->auth($options['redis']['password']); } if($redis->ping(1)&&$detailInfo){ echo "[Rat $cid] Redis Successfully Connected\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 "\033[33m[Rat $cid] PID={$pid} random_bytes failed: " . $e->getMessage() . ",thus jumped this loop\n This is an expected but extremely rare error. It should not affect normal operation. \n However, if this error persists, please be vigilant.\n This Rat will sleep for 1000 ms\033[0m\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} Found Address {$address} (ended with {$count} x '{$lastChar}')\n"; } $gd->scount=$gd->scount+1; $result = [ 'pid' => $pid, 'private_key' => $privHex, 'timestamp' => time(), 'count'=> $count, 'char'=> $lastChar, ]; $redis->sadd('goldRat_set', $address); $redis->hset('goldRat_hash', $address, json_encode($result)); } catch (Throwable $t) { if($detailInfo){ echo "\033[31m ❌[Rat $cid] PID={$pid} ERROR OCCURRED : " . $t->getMessage() . "\033[0m\n"; } } } } // 若想降低 CPU 占用可在这里加短暂 sleep(但会降低查找速度) // usleep(0); // 等同于 yield } if($detailInfo){ echo "\033[31m ⚠[Rat $cid] PID={$pid} Unexceptedly Exited The Loop \033[0m\n"; } }; $Rat->onWorkerStop = function($Rat) { $pid = posix_getpid(); $cid=$pid-$GLOBALS['mainid']; echo "[Rat $cid] PID={$pid} 已终止\n"; }; $Static = new Worker(); $Static->name='Static&Timer Service'; $Static->onWorkerStart = function($Static)use($staticSecond,$detailInfo,$apiKey) { $GLOBALS['starttime']=microtime(true); $GLOBALS['lc']=0; $GLOBALS['lsc']=0; $gda = new \GlobalData\Client('127.0.0.1:2207'); $gda->second_token=5; $gda->minute_token=60; $gda->lastToken='--'; $gda->lastTokenTime='will shown after 1min'; Timer::add(1, function()use($staticSecond,$detailInfo,$gda,$apiKey){ $gda->second_token= $gda->second_token+5; }); Timer::add(60, function()use($staticSecond,$detailInfo,$gda,$apiKey){ if($detailInfo){ echo "Go to ask EtherScan for API useage"; } $client = new Client([ 'base_uri' => 'https://api.etherscan.io/v2/', 'timeout' => 10.0, ]); $params = [ 'query' => [ 'module' => 'getapilimit', 'action' => 'getapilimit', 'apikey' => $apiKey ] ]; try { $response = $client->get('api', $params); $statusCode = $response->getStatusCode(); if ($statusCode == 200) { $data = json_decode($response->getBody(), true); if ($data['status'] === '1') { $lastminute=secondsUntilUtcDayEnd()*60; $addToken=$data['result']['creditsAvailable']/60; $gda->lastToken=$data['result']['creditsAvailable']; $gda->lastTokenTime=$data['result']['intervalExpiryTimespan']; if($detailInfo){ echo "✅[Timmer]EtherScan Got the credit successfully\n"; } } else { if($detailInfo){ echo "❌[Timmer]EtherScan Get Credit Failed\n"; print_r($data); } $addToken=0; } }else{ if($detailInfo){ if($detailInfo){ echo "\033[31m❌[Timmer]EtherScan Http Request Failed: HTTP" . $statusCode . "\033[0m\n"; } } $addToken=0; } } catch (RequestException $e) { echo "\033[31m❌[Timmer]EtherScan Http Request Failed: " . $e->getMessage() . "\033[0m\n"; $addToken=0; } $gda->minute_token= $gda->minute_token+$addToken; }); Timer::add($staticSecond, function()use($staticSecond,$detailInfo,$gda){ $staticCount=$gda->count; $avgCount=($staticCount-$GLOBALS['lc'])/$staticSecond; $successCount=$gda->scount; $creditAvailable=$gda->lastToken; $resetCountdown=$gda->lastTokenTime; $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 " \033[33m .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 \033[0m ==================================================== || 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 ----------------API CREDITS INFO-------------------- EtherScan.io API Available: $creditAvailable Credits Reset Countdown: $resetCountdown ==================================================== Enter Ctrl+C to Exit. ———————————————————————————————————————————————————— ©Gold Rat by LayFi.de \n"; $GLOBALS['lc']=$staticCount; $GLOBALS['lsc']=$successCount; }); }; require __DIR__ . '/scanner.php'; require __DIR__ . '/writeToDB.php'; $Scanner=new Worker(); $Scanner->name='Scanner'; $Scanner->count=1; $Scanner->onWorkerStart = function($Scanner)use($detailInfo,$options,$apiKey,$dbConfig){ usleep(1000);#延迟1秒启动,防止与Rat争抢redis并发 $pid = posix_getpid(); if($detailInfo){ echo "[Scanner] PID={$pid} Started\n"; } $redis = new Redis(); $redis->connect($options['redis']['ip'], $options['redis']['port']); if($detailInfo){ echo "[Scanner] Connecting Redis……\n"; } if(isset($options['redis']['password'])&&$options['redis']['password']!=false){ if($detailInfo){ echo "[Scanner] Redis Authing……\n"; } $redis->auth($options['redis']['password']); } if($redis->ping(1)&&$detailInfo){ echo "[Scanner] Redis Successfully Connected\n"; } $setKey = 'goldRat_set'; $hashKey = 'goldRat_hash'; while(true){ $address = $redis->spop($setKey); if ($address === false) { usleep(1000); #休眠1s }else{ $json = $redis->hget($hashKey, $address); if($detailInfo){ echo "[Scanner] Take out and start scanning: $address\n"; } if(scanwallet($address,$apiKey,$detailInfo)){ $active='true'; echo "\033[32m🎉[Scanner] $address found active!\033[0m\n"; writeToDB($address,$dbConfig,$json,true); }else{ $active='false'; if($detailInfo){ echo "[Scanner] $address found nothing \n"; } writeToDB($address,$dbConfig,$json,false); } $redis->hdel($hashKey, $address); } } }; Worker::runAll();