Generating.php
1 <?php 2 3 namespace BitcoindRPC\Generating; 4 5 use BitcoindRPC\RpcClient; 6 use BitcoindRPC\RpcResponse; 7 8 class Generating 9 { 10 private RpcClient $client; 11 12 public function __construct(RpcClient $client) 13 { 14 $this->client = $client; 15 } 16 17 public function generateBlock(string $output, array $transactions, int $maxTries = 1000000): GeneratingResult 18 { 19 if (empty($transactions)) { 20 return new GeneratingResult(new RpcResponse( 21 data: null, 22 error: "transactions list cannot be empty", 23 statusCode: 400 24 )); 25 } 26 27 $params = [$output, $transactions, $maxTries]; 28 $response = $this->client->call('generateblock', $params); 29 30 return new GeneratingResult($response); 31 } 32 33 public function generateToAddress(int $nblocks, string $address, int $maxTries = 1000000): GeneratingResult 34 { 35 if ($nblocks <= 0) { 36 return new GeneratingResult(new RpcResponse( 37 data: null, 38 error: "nblocks must be greater than 0", 39 statusCode: 400 40 )); 41 } 42 43 $params = [$nblocks, $address]; 44 if ($maxTries !== 1000000) { 45 $params[] = $maxTries; 46 } 47 48 $response = $this->client->call('generatetoaddress', $params); 49 return new GeneratingResult($response); 50 } 51 52 public function generateToDescriptor(int $numBlocks, string $descriptor, int $maxTries = 1000000): GeneratingResult 53 { 54 if ($numBlocks <= 0) { 55 return new GeneratingResult(new RpcResponse( 56 data: null, 57 error: "num_blocks must be greater than 0", 58 statusCode: 400 59 )); 60 } 61 62 $params = [$numBlocks, $descriptor, $maxTries]; 63 $response = $this->client->call('generatetodescriptor', $params); 64 return new GeneratingResult($response); 65 } 66 67 public function generate(int $nblocks, int $maxTries = 1000000): GeneratingResult 68 { 69 if ($nblocks <= 0) { 70 return new GeneratingResult(new RpcResponse( 71 data: null, 72 error: "nblocks must be greater than 0", 73 statusCode: 400 74 )); 75 } 76 77 $params = [$nblocks]; 78 if ($maxTries !== 1000000) { 79 $params[] = $maxTries; 80 } 81 82 $response = $this->client->call('generate', $params); 83 return new GeneratingResult($response); 84 } 85 86 // Utility methods 87 public function generateBlocksWithStats( 88 int $numBlocks, 89 string $output, 90 string $outputType = "address", 91 int $maxTries = 1000000 92 ): array { 93 $startTime = microtime(true); 94 95 if ($outputType === "address") { 96 $result = $this->generateToAddress($numBlocks, $output, $maxTries); 97 } elseif ($outputType === "descriptor") { 98 $result = $this->generateToDescriptor($numBlocks, $output, $maxTries); 99 } else { 100 return [ 101 'result' => new GeneratingResult(new RpcResponse( 102 data: null, 103 error: "output_type must be 'address' or 'descriptor'", 104 statusCode: 400 105 )), 106 'stats' => null 107 ]; 108 } 109 110 $endTime = microtime(true); 111 $totalTime = $endTime - $startTime; 112 113 if ($result->isSuccess() && $result->getBlockHashes()) { 114 $averageTime = $numBlocks > 0 ? $totalTime / $numBlocks : 0; 115 $hashesPerSecond = $totalTime > 0 ? $numBlocks / $totalTime : 0; 116 117 $stats = [ 118 'total_blocks' => $numBlocks, 119 'average_time' => $averageTime, 120 'total_time' => $totalTime, 121 'hashes_per_second' => $hashesPerSecond, 122 'first_block_hash' => $result->getBlockHashes()[0] ?? null, 123 'last_block_hash' => $result->getBlockHashes()[count($result->getBlockHashes()) - 1] ?? null 124 ]; 125 126 return [ 127 'result' => $result, 128 'stats' => $stats 129 ]; 130 } else { 131 return [ 132 'result' => $result, 133 'stats' => null 134 ]; 135 } 136 } 137 138 public function validateOutput(string $output, string $outputType = "address"): GeneratingResult 139 { 140 if ($outputType === "address") { 141 $response = $this->client->call('validateaddress', [$output]); 142 } elseif ($outputType === "descriptor") { 143 $response = $this->client->call('getdescriptorinfo', [$output]); 144 } else { 145 return new GeneratingResult(new RpcResponse( 146 data: null, 147 error: "output_type must be 'address' or 'descriptor'", 148 statusCode: 400 149 )); 150 } 151 152 return new GeneratingResult($response); 153 } 154 155 public function estimateGenerationTime(int $numBlocks, ?float $networkHashRate = null): GeneratingResult 156 { 157 if ($numBlocks <= 0) { 158 return new GeneratingResult(new RpcResponse( 159 data: null, 160 error: "num_blocks must be greater than 0", 161 statusCode: 400 162 )); 163 } 164 165 // Get current network hashrate if not provided 166 if ($networkHashRate === null) { 167 $hashResponse = $this->client->call('getnetworkhashps', [120]); 168 if ($hashResponse->error) { 169 return new GeneratingResult(new RpcResponse( 170 data: null, 171 error: "Failed to get network hashrate: " . $hashResponse->error, 172 statusCode: 500 173 )); 174 } 175 $networkHashRate = (float) $hashResponse->data; 176 } 177 178 // Get current difficulty 179 $miningInfoResponse = $this->client->call('getmininginfo'); 180 if ($miningInfoResponse->error) { 181 return new GeneratingResult(new RpcResponse( 182 data: null, 183 error: "Failed to get mining info: " . $miningInfoResponse->error, 184 statusCode: 500 185 )); 186 } 187 188 $miningInfo = $miningInfoResponse->data; 189 $difficulty = (float) ($miningInfo['difficulty'] ?? 1.0); 190 191 // Simple estimation: time = (difficulty * 2^32) / hashrate * num_blocks 192 $singleBlockTime = ($difficulty * 2**32) / $networkHashRate; 193 $totalTime = $singleBlockTime * $numBlocks; 194 195 $estimationData = [ 196 'num_blocks' => $numBlocks, 197 'network_hash_rate' => $networkHashRate, 198 'difficulty' => $difficulty, 199 'estimated_time_per_block' => $singleBlockTime, 200 'estimated_total_time' => $totalTime, 201 'estimated_time_per_block_human' => number_format($singleBlockTime, 2) . " seconds", 202 'estimated_total_time_human' => number_format($totalTime, 2) . " seconds" 203 ]; 204 205 return new GeneratingResult(new RpcResponse(data: $estimationData)); 206 } 207 208 public function createAndGenerate( 209 string $output, 210 string $outputType = "address", 211 int $numBlocks = 1, 212 int $maxTries = 1000000 213 ): GeneratingResult { 214 // First validate the output 215 $validation = $this->validateOutput($output, $outputType); 216 if (!$validation->isSuccess()) { 217 return $validation; 218 } 219 220 // Then generate blocks 221 if ($outputType === "address") { 222 return $this->generateToAddress($numBlocks, $output, $maxTries); 223 } else { 224 return $this->generateToDescriptor($numBlocks, $output, $maxTries); 225 } 226 } 227 228 public function getGenerationStatus(): GeneratingResult 229 { 230 $response = $this->client->call('getmininginfo'); 231 return new GeneratingResult($response); 232 } 233 }