Tron地址靓号生成器(Objective-C++)
Tron地址靓号生成器(Objective-C++)
保存代码为main.mm
#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
#include <cmath>
#include <csignal>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
// Single-file Apple-Silicon TRON vanity address searcher.
// CPU code is deliberately independent from the Metal implementation and is
// used to verify every GPU hit before it is printed.
namespace cpu {
struct U256 { uint32_t v[8]; }; // little-endian 32-bit limbs
struct Affine { U256 x, y; };
struct Jacobian { U256 x, y, z; };
static constexpr U256 FIELD_P = {{
0xfffffc2fu, 0xfffffffeu, 0xffffffffu, 0xffffffffu,
0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu
}};
static constexpr U256 ORDER_N = {{
0xd0364141u, 0xbfd25e8cu, 0xaf48a03bu, 0xbaaedce6u,
0xfffffffeu, 0xffffffffu, 0xffffffffu, 0xffffffffu
}};
static constexpr U256 P_MINUS_2 = {{
0xfffffc2du, 0xfffffffeu, 0xffffffffu, 0xffffffffu,
0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu
}};
static bool is_zero(const U256 &a) {
uint32_t x = 0;
for (uint32_t w : a.v) x |= w;
return x == 0;
}
static int cmp(const U256 &a, const U256 &b) {
for (int i = 7; i >= 0; --i) {
if (a.v[i] < b.v[i]) return -1;
if (a.v[i] > b.v[i]) return 1;
}
return 0;
}
static U256 raw_sub(const U256 &a, const U256 &b) {
U256 r{};
uint64_t borrow = 0;
for (int i = 0; i < 8; ++i) {
const uint64_t sub = uint64_t(b.v[i]) + borrow;
r.v[i] = uint32_t(uint64_t(a.v[i]) - sub);
borrow = uint64_t(a.v[i]) < sub;
}
return r;
}
static void fold10(uint32_t x[10]) {
const uint32_t h0 = x[8], h1 = x[9];
uint32_t low[8];
std::memcpy(low, x, sizeof(low));
uint64_t carry = 0;
for (int k = 0; k < 10; ++k) {
uint64_t a = carry + (k < 8 ? low[k] : 0u);
if (k == 0) a += uint64_t(977) * h0;
if (k == 1) a += uint64_t(h0) + uint64_t(977) * h1;
if (k == 2) a += h1;
x[k] = uint32_t(a);
carry = a >> 32;
}
}
static U256 canonicalize(uint32_t x[10]) {
// p = 2^256 - 2^32 - 977. Three folds are enough after a 512-bit product.
fold10(x); fold10(x); fold10(x);
U256 r{};
std::memcpy(r.v, x, sizeof(r.v));
while (cmp(r, FIELD_P) >= 0) r = raw_sub(r, FIELD_P);
return r;
}
static U256 add(const U256 &a, const U256 &b) {
uint32_t x[10]{};
uint64_t carry = 0;
for (int i = 0; i < 8; ++i) {
uint64_t s = uint64_t(a.v[i]) + b.v[i] + carry;
x[i] = uint32_t(s);
carry = s >> 32;
}
x[8] = uint32_t(carry);
return canonicalize(x);
}
static U256 sub(const U256 &a, const U256 &b) {
U256 r{};
uint64_t borrow = 0;
for (int i = 0; i < 8; ++i) {
const uint64_t s = uint64_t(b.v[i]) + borrow;
r.v[i] = uint32_t(uint64_t(a.v[i]) - s);
borrow = uint64_t(a.v[i]) < s;
}
if (borrow) {
uint64_t carry = 0;
for (int i = 0; i < 8; ++i) {
uint64_t s = uint64_t(r.v[i]) + FIELD_P.v[i] + carry;
r.v[i] = uint32_t(s);
carry = s >> 32;
}
}
return r;
}
static U256 mul(const U256 &a, const U256 &b) {
uint32_t t[16]{};
for (int i = 0; i < 8; ++i) {
uint64_t carry = 0;
for (int j = 0; j < 8; ++j) {
const uint64_t cur = uint64_t(a.v[i]) * b.v[j] + t[i + j] + carry;
t[i + j] = uint32_t(cur);
carry = cur >> 32;
}
t[i + 8] = uint32_t(carry);
}
uint32_t x[10]{};
uint64_t carry = 0;
for (int k = 0; k < 10; ++k) {
uint64_t s = carry;
if (k < 8) s += t[k] + uint64_t(977) * t[k + 8];
if (k >= 1 && k <= 8) s += t[k + 7];
x[k] = uint32_t(s);
carry = s >> 32;
}
return canonicalize(x);
}
static U256 sqr(const U256 &a) { return mul(a, a); }
static U256 twice(const U256 &a) { return add(a, a); }
static U256 times3(const U256 &a) { return add(twice(a), a); }
static U256 times4(const U256 &a) { return twice(twice(a)); }
static U256 times8(const U256 &a) { return twice(times4(a)); }
static U256 inv(U256 a) {
U256 r{{1,0,0,0,0,0,0,0}};
for (int bit = 255; bit >= 0; --bit) {
r = sqr(r);
if ((P_MINUS_2.v[bit >> 5] >> (bit & 31)) & 1u) r = mul(r, a);
}
return r;
}
static Jacobian point_double(const Jacobian &p) {
if (is_zero(p.z) || is_zero(p.y)) return {};
const U256 yy = sqr(p.y);
const U256 s = times4(mul(p.x, yy));
const U256 m = times3(sqr(p.x));
const U256 nx = sub(sqr(m), twice(s));
const U256 yyyy = sqr(yy);
const U256 ny = sub(mul(m, sub(s, nx)), times8(yyyy));
const U256 nz = twice(mul(p.y, p.z));
return {nx, ny, nz};
}
static Jacobian point_add_affine(const Jacobian &p, const Affine &q) {
if (is_zero(p.z)) return {q.x, q.y, U256{{1,0,0,0,0,0,0,0}}};
const U256 z2 = sqr(p.z);
const U256 u2 = mul(q.x, z2);
const U256 s2 = mul(q.y, mul(p.z, z2));
const U256 h = sub(u2, p.x);
if (is_zero(h)) {
if (is_zero(sub(s2, p.y))) return point_double(p);
return {};
}
const U256 hh = sqr(h);
const U256 i = times4(hh);
const U256 j = mul(h, i);
const U256 rr = twice(sub(s2, p.y));
const U256 v = mul(p.x, i);
const U256 nx = sub(sub(sqr(rr), j), twice(v));
const U256 ny = sub(mul(rr, sub(v, nx)), twice(mul(p.y, j)));
const U256 nz = sub(sub(sqr(add(p.z, h)), z2), hh);
return {nx, ny, nz};
}
static Affine to_affine(const Jacobian &p) {
if (is_zero(p.z)) throw std::runtime_error("point at infinity");
const U256 zi = inv(p.z);
const U256 zi2 = sqr(zi);
return {mul(p.x, zi2), mul(p.y, mul(zi2, zi))};
}
static U256 from_hex(const char *s) {
U256 r{};
for (int i = 0; i < 64; ++i) {
char c = s[i];
uint32_t n = (c >= '0' && c <= '9') ? uint32_t(c - '0') :
(c >= 'a' && c <= 'f') ? uint32_t(c - 'a' + 10) :
uint32_t(c - 'A' + 10);
int bit = (63 - i) * 4;
r.v[bit >> 5] |= n << (bit & 31);
}
return r;
}
static const char *TABLE_HEX[15][2] = {
{"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"},
{"c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5","1ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a"},
{"f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9","388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672"},
{"e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13","51ed993ea0d455b75642e2098ea51448d967ae33bfbdfe40cfe97bdc47739922"},
{"2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4","d8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6"},
{"fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556","ae12777aacfbb620f3be96017f45c560de80f0f6518fe4a03c870c36b075f297"},
{"5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc","6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da"},
{"2f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01","5c4da8a741539949293d082a132d13b4c2e213d6ba5b7617b5da2cb76cbde904"},
{"acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe","cc338921b0a7d9fd64380971763b61e9add888a4375f8e0f05cc262ac64f9c37"},
{"a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7","893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7"},
{"774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb","d984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b"},
{"d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a","a9f34ffdc815e0d7a8b64537e17bd81579238c5dd9a86d526b051b13f4062327"},
{"f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8","0ab0902e8d880a89758212eb65cdaf473a1a06da521fa91f29b5cb52db03ed81"},
{"499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4","cac2f6c4b54e855190f044e4a7b3d464464279c27a3f95bcc65f40d403a13f5b"},
{"d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e","581e2872a86c72a683842ec228cc6defea40af2bd896d3a5c504dc9ff6a26b58"}
};
static std::array<Affine,15> make_table() {
std::array<Affine,15> t{};
for (int i = 0; i < 15; ++i) t[i] = {from_hex(TABLE_HEX[i][0]), from_hex(TABLE_HEX[i][1])};
return t;
}
static Affine scalar_mul(const U256 &k, const std::array<Affine,15> &table) {
Jacobian r{};
bool started = false;
for (int pos = 63; pos >= 0; --pos) {
const uint32_t d = (k.v[pos >> 3] >> ((pos & 7) * 4)) & 15u;
if (!started) {
if (!d) continue;
r = {table[d - 1].x, table[d - 1].y, U256{{1,0,0,0,0,0,0,0}}};
started = true;
continue;
}
r = point_double(point_double(point_double(point_double(r))));
if (d) r = point_add_affine(r, table[d - 1]);
}
return to_affine(r);
}
static inline uint32_t rotr32(uint32_t x, int n) { return (x >> n) | (x << (32 - n)); }
static const uint32_t SHA_K[64] = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
static std::array<uint8_t,32> sha256(const uint8_t *data, size_t len) {
std::vector<uint8_t> msg(data, data + len);
msg.push_back(0x80);
while ((msg.size() % 64) != 56) msg.push_back(0);
const uint64_t bits = uint64_t(len) * 8;
for (int i = 7; i >= 0; --i) msg.push_back(uint8_t(bits >> (i * 8)));
uint32_t h[8] = {0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19};
for (size_t off = 0; off < msg.size(); off += 64) {
uint32_t w[64];
for (int i = 0; i < 16; ++i) w[i] = (uint32_t(msg[off+4*i])<<24)|(uint32_t(msg[off+4*i+1])<<16)|(uint32_t(msg[off+4*i+2])<<8)|msg[off+4*i+3];
for (int i = 16; i < 64; ++i) {
uint32_t s0=rotr32(w[i-15],7)^rotr32(w[i-15],18)^(w[i-15]>>3);
uint32_t s1=rotr32(w[i-2],17)^rotr32(w[i-2],19)^(w[i-2]>>10);
w[i]=w[i-16]+s0+w[i-7]+s1;
}
uint32_t a=h[0],b=h[1],c=h[2],d=h[3],e=h[4],f=h[5],g=h[6],hh=h[7];
for(int i=0;i<64;++i){uint32_t S1=rotr32(e,6)^rotr32(e,11)^rotr32(e,25),ch=(e&f)^(~e&g),t1=hh+S1+ch+SHA_K[i]+w[i];uint32_t S0=rotr32(a,2)^rotr32(a,13)^rotr32(a,22),maj=(a&b)^(a&c)^(b&c),t2=S0+maj;hh=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;}
h[0]+=a;h[1]+=b;h[2]+=c;h[3]+=d;h[4]+=e;h[5]+=f;h[6]+=g;h[7]+=hh;
}
std::array<uint8_t,32> out{};
for(int i=0;i<8;++i){out[4*i]=uint8_t(h[i]>>24);out[4*i+1]=uint8_t(h[i]>>16);out[4*i+2]=uint8_t(h[i]>>8);out[4*i+3]=uint8_t(h[i]);}
return out;
}
static inline uint64_t rol64(uint64_t x, int n) { return n ? ((x << n) | (x >> (64 - n))) : x; }
static void keccak_f(uint64_t a[25]) {
static const uint64_t RC[24]={0x0000000000000001ULL,0x0000000000008082ULL,0x800000000000808aULL,0x8000000080008000ULL,0x000000000000808bULL,0x0000000080000001ULL,0x8000000080008081ULL,0x8000000000008009ULL,0x000000000000008aULL,0x0000000000000088ULL,0x0000000080008009ULL,0x000000008000000aULL,0x000000008000808bULL,0x800000000000008bULL,0x8000000000008089ULL,0x8000000000008003ULL,0x8000000000008002ULL,0x8000000000000080ULL,0x000000000000800aULL,0x800000008000000aULL,0x8000000080008081ULL,0x8000000000008080ULL,0x0000000080000001ULL,0x8000000080008008ULL};
static const int R[25]={0,1,62,28,27,36,44,6,55,20,3,10,43,25,39,41,45,15,21,8,18,2,61,56,14};
for(int round=0;round<24;++round){uint64_t c[5],d[5],b[25];for(int x=0;x<5;++x)c[x]=a[x]^a[x+5]^a[x+10]^a[x+15]^a[x+20];for(int x=0;x<5;++x)d[x]=c[(x+4)%5]^rol64(c[(x+1)%5],1);for(int y=0;y<5;++y)for(int x=0;x<5;++x)a[x+5*y]^=d[x];for(int y=0;y<5;++y)for(int x=0;x<5;++x)b[y+5*((2*x+3*y)%5)]=rol64(a[x+5*y],R[x+5*y]);for(int y=0;y<5;++y)for(int x=0;x<5;++x)a[x+5*y]=b[x+5*y]^((~b[(x+1)%5+5*y])&b[(x+2)%5+5*y]);a[0]^=RC[round];}
}
static std::array<uint8_t,32> keccak256_64(const uint8_t in[64]) {
uint8_t block[136]{};
std::memcpy(block,in,64); block[64]=0x01; block[135]=0x80;
uint64_t s[25]{};
for(int i=0;i<17;++i) for(int j=0;j<8;++j) s[i]|=uint64_t(block[8*i+j])<<(8*j);
keccak_f(s);
std::array<uint8_t,32> out{};
for(int i=0;i<32;++i) out[i]=uint8_t(s[i>>3]>>(8*(i&7)));
return out;
}
static U256 scalar_for_offset(const U256 &base, uint64_t offset) {
U256 r=base;
uint64_t s=uint64_t(r.v[0])+uint32_t(offset); r.v[0]=uint32_t(s); uint64_t carry=s>>32;
s=uint64_t(r.v[1])+uint32_t(offset>>32)+carry; r.v[1]=uint32_t(s); carry=s>>32;
for(int i=2;i<8&&carry;++i){s=uint64_t(r.v[i])+carry;r.v[i]=uint32_t(s);carry=s>>32;}
if(carry || cmp(r,ORDER_N)>=0) r=raw_sub(r,ORDER_N);
return r;
}
static std::array<uint8_t,25> address_bytes(const U256 &priv, const std::array<Affine,15> &table) {
const Affine p=scalar_mul(priv,table);
uint8_t pub[64];
for(int i=0;i<32;++i){int bit=(31-i)*8;pub[i]=uint8_t(p.x.v[bit>>5]>>(bit&31));pub[32+i]=uint8_t(p.y.v[bit>>5]>>(bit&31));}
auto kh=keccak256_64(pub);
std::array<uint8_t,25> out{};out[0]=0x41;std::memcpy(out.data()+1,kh.data()+12,20);
auto h1=sha256(out.data(),21);auto h2=sha256(h1.data(),h1.size());std::memcpy(out.data()+21,h2.data(),4);
return out;
}
static std::string base58(std::array<uint8_t,25> in) {
static const char *ABC="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
size_t leading=0;while(leading<in.size()&&in[leading]==0)++leading;
std::string rev;
while(true){uint32_t rem=0;bool any=false;for(uint8_t &b:in){uint32_t v=(rem<<8)|b;b=uint8_t(v/58);rem=v%58;any|=b!=0;}rev.push_back(ABC[rem]);if(!any)break;}
for(size_t i=0;i<leading;++i)rev.push_back('1');
std::reverse(rev.begin(),rev.end());return rev;
}
static std::string hex256(const U256 &x) {
std::ostringstream o;o<<std::hex<<std::setfill('0');for(int i=7;i>=0;--i)o<<std::setw(8)<<x.v[i];return o.str();
}
} // namespace cpu
static const char *kMetalSource = R"MSL(
#include <metal_stdlib>
using namespace metal;
struct U256 { uint v[8]; };
struct Affine { U256 x; U256 y; };
struct Jacobian { U256 x; U256 y; U256 z; };
struct Params { ulong startOffset; uint count; uint suffixLen; };
struct SearchResult { atomic_uint found; uint pad; ulong offset; };
constant U256 FIELD_P = {{0xfffffc2fu,0xfffffffeu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu}};
constant U256 ORDER_N = {{0xd0364141u,0xbfd25e8cu,0xaf48a03bu,0xbaaedce6u,0xfffffffeu,0xffffffffu,0xffffffffu,0xffffffffu}};
constant U256 P_MINUS_2 = {{0xfffffc2du,0xfffffffeu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu,0xffffffffu}};
inline bool uzero(U256 a) { uint x=0; for(int i=0;i<8;++i)x|=a.v[i]; return x==0; }
inline int ucmp(U256 a,U256 b){for(int i=7;i>=0;--i){if(a.v[i]<b.v[i])return -1;if(a.v[i]>b.v[i])return 1;}return 0;}
inline U256 raw_sub(U256 a,U256 b){U256 r;ulong borrow=0;for(int i=0;i<8;++i){ulong s=(ulong)b.v[i]+borrow;r.v[i]=(uint)((ulong)a.v[i]-s);borrow=(ulong)a.v[i]<s;}return r;}
inline void fold10(thread uint *x){
uint h0=x[8],h1=x[9],lo[8];for(int i=0;i<8;++i)lo[i]=x[i];ulong carry=0;
for(int k=0;k<10;++k){ulong a=carry+(k<8?(ulong)lo[k]:0ul);if(k==0)a+=(ulong)977*h0;if(k==1)a+=(ulong)h0+(ulong)977*h1;if(k==2)a+=h1;x[k]=(uint)a;carry=a>>32;}
}
inline U256 canonical(thread uint *x){fold10(x);fold10(x);fold10(x);U256 r;for(int i=0;i<8;++i)r.v[i]=x[i];for(int i=0;i<3&&ucmp(r,FIELD_P)>=0;++i)r=raw_sub(r,FIELD_P);return r;}
inline U256 fadd(U256 a,U256 b){uint x[10];for(int i=0;i<10;++i)x[i]=0;ulong c=0;for(int i=0;i<8;++i){ulong s=(ulong)a.v[i]+b.v[i]+c;x[i]=(uint)s;c=s>>32;}x[8]=(uint)c;return canonical(x);}
inline U256 fsub(U256 a,U256 b){U256 r;ulong br=0;for(int i=0;i<8;++i){ulong s=(ulong)b.v[i]+br;r.v[i]=(uint)((ulong)a.v[i]-s);br=(ulong)a.v[i]<s;}if(br){ulong c=0;for(int i=0;i<8;++i){ulong s=(ulong)r.v[i]+FIELD_P.v[i]+c;r.v[i]=(uint)s;c=s>>32;}}return r;}
inline U256 fmul(U256 a,U256 b){
uint t[16];for(int i=0;i<16;++i)t[i]=0;
for(int i=0;i<8;++i){ulong c=0;for(int j=0;j<8;++j){ulong z=(ulong)a.v[i]*b.v[j]+t[i+j]+c;t[i+j]=(uint)z;c=z>>32;}t[i+8]=(uint)c;}
uint x[10];for(int i=0;i<10;++i)x[i]=0;ulong c=0;
for(int k=0;k<10;++k){ulong s=c;if(k<8)s+=(ulong)t[k]+(ulong)977*t[k+8];if(k>=1&&k<=8)s+=t[k+7];x[k]=(uint)s;c=s>>32;}
return canonical(x);
}
inline U256 fsqr(U256 a){return fmul(a,a);} inline U256 f2(U256 a){return fadd(a,a);} inline U256 f3(U256 a){return fadd(f2(a),a);} inline U256 f4(U256 a){return f2(f2(a));} inline U256 f8(U256 a){return f2(f4(a));}
inline U256 finv(U256 a){U256 r={{1,0,0,0,0,0,0,0}};for(int bit=255;bit>=0;--bit){r=fsqr(r);if((P_MINUS_2.v[bit>>5]>>(bit&31))&1u)r=fmul(r,a);}return r;}
inline Jacobian pdouble(Jacobian p){
if(uzero(p.z)||uzero(p.y)){Jacobian q={};return q;}U256 yy=fsqr(p.y),s=f4(fmul(p.x,yy)),m=f3(fsqr(p.x));U256 nx=fsub(fsqr(m),f2(s));U256 ny=fsub(fmul(m,fsub(s,nx)),f8(fsqr(yy)));U256 nz=f2(fmul(p.y,p.z));return {nx,ny,nz};
}
inline Jacobian padd(Jacobian p,Affine q){
if(uzero(p.z)){U256 one={{1,0,0,0,0,0,0,0}};return {q.x,q.y,one};}
U256 z2=fsqr(p.z),u2=fmul(q.x,z2),s2=fmul(q.y,fmul(p.z,z2)),h=fsub(u2,p.x);
if(uzero(h)){if(uzero(fsub(s2,p.y)))return pdouble(p);Jacobian z={};return z;}
U256 hh=fsqr(h),ii=f4(hh),j=fmul(h,ii),rr=f2(fsub(s2,p.y)),v=fmul(p.x,ii);U256 nx=fsub(fsub(fsqr(rr),j),f2(v));U256 ny=fsub(fmul(rr,fsub(v,nx)),f2(fmul(p.y,j)));U256 nz=fsub(fsub(fsqr(fadd(p.z,h)),z2),hh);return {nx,ny,nz};
}
inline Jacobian scalar_jacobian(U256 k,constant Affine *table){
Jacobian r={};bool started=false;
for(int pos=63;pos>=0;--pos){uint d=(k.v[pos>>3]>>((pos&7)*4))&15u;if(!started){if(d==0)continue;U256 one={{1,0,0,0,0,0,0,0}};r={table[d-1].x,table[d-1].y,one};started=true;continue;}r=pdouble(pdouble(pdouble(pdouble(r))));if(d)r=padd(r,table[d-1]);}
return r;
}
inline Jacobian scalar_gid(uint k,constant Affine *table){
// gid is at most 2^20-1 with the host batch size. Avoid walking the 59
// guaranteed-zero high nibbles of a generic 256-bit scalar.
Jacobian r={};bool started=false;
for(int pos=4;pos>=0;--pos){uint d=(k>>(pos*4))&15u;if(!started){if(d==0)continue;U256 one={{1,0,0,0,0,0,0,0}};r={table[d-1].x,table[d-1].y,one};started=true;continue;}r=pdouble(pdouble(pdouble(pdouble(r))));if(d)r=padd(r,table[d-1]);}
return r;
}
inline Affine jac_affine(Jacobian r){U256 zi=finv(r.z),zi2=fsqr(zi);return {fmul(r.x,zi2),fmul(r.y,fmul(zi2,zi))};}
inline U256 scalar_offset(constant uint *base,ulong off){
U256 r;for(int i=0;i<8;++i)r.v[i]=base[i];ulong s=(ulong)r.v[0]+(uint)off;r.v[0]=(uint)s;ulong c=s>>32;s=(ulong)r.v[1]+(uint)(off>>32)+c;r.v[1]=(uint)s;c=s>>32;for(int i=2;i<8&&c;++i){s=(ulong)r.v[i]+c;r.v[i]=(uint)s;c=s>>32;}if(c||ucmp(r,ORDER_N)>=0)r=raw_sub(r,ORDER_N);return r;
}
constant ulong KECCAK_RC[24]={0x0000000000000001ul,0x0000000000008082ul,0x800000000000808aul,0x8000000080008000ul,0x000000000000808bul,0x0000000080000001ul,0x8000000080008081ul,0x8000000000008009ul,0x000000000000008aul,0x0000000000000088ul,0x0000000080008009ul,0x000000008000000aul,0x000000008000808bul,0x800000000000008bul,0x8000000000008089ul,0x8000000000008003ul,0x8000000000008002ul,0x8000000000000080ul,0x000000000000800aul,0x800000008000000aul,0x8000000080008081ul,0x8000000000008080ul,0x0000000080000001ul,0x8000000080008008ul};
constant uint KECCAK_ROTC[24]={1,3,6,10,15,21,28,36,45,55,2,14,27,41,56,8,25,43,62,18,39,61,20,44};
constant uint KECCAK_PILN[24]={10,7,11,17,18,3,5,16,8,21,24,4,15,23,19,13,12,2,20,14,22,9,6,1};
inline ulong rol64(ulong x,uint n){return n?((x<<n)|(x>>(64-n))):x;}
inline void keccakf(thread ulong *a){
// In-place rho/pi removes the old 25-lane temporary B array. Keeping only
// the state plus one five-lane row materially lowers per-thread registers.
for(int round=0;round<24;++round){
ulong row[5];
for(int x=0;x<5;++x)row[x]=a[x]^a[x+5]^a[x+10]^a[x+15]^a[x+20];
for(int x=0;x<5;++x){ulong d=row[(x+4)%5]^rol64(row[(x+1)%5],1);for(int y=0;y<5;++y)a[x+5*y]^=d;}
ulong t=a[1];
for(int i=0;i<24;++i){uint j=KECCAK_PILN[i];ulong saved=a[j];a[j]=rol64(t,KECCAK_ROTC[i]);t=saved;}
for(int y=0;y<5;++y){int j=5*y;for(int x=0;x<5;++x)row[x]=a[j+x];for(int x=0;x<5;++x)a[j+x]=row[x]^((~row[(x+1)%5])&row[(x+2)%5]);}
a[0]^=KECCAK_RC[round];
}
}
inline uint bswap32(uint x){return ((x&0x000000ffu)<<24)|((x&0x0000ff00u)<<8)|((x&0x00ff0000u)>>8)|((x&0xff000000u)>>24);}
inline ulong coordinate_lane(U256 c,uint pair){uint hi=7u-(pair<<1),lo=hi-1u;return (ulong)bswap32(c.v[hi])|((ulong)bswap32(c.v[lo])<<32);}
inline void tron_payload(Affine p,thread uchar *payload){
// The input is exactly x||y (64 big-endian bytes), so absorb it directly
// into Keccak lanes. This removes the old 64-byte pubkey and 136-byte block.
ulong s[25];for(int i=0;i<25;++i)s[i]=0;
for(uint i=0;i<4;++i)s[i]=coordinate_lane(p.x,i);
for(uint i=0;i<4;++i)s[4+i]=coordinate_lane(p.y,i);
s[8]=1ul;s[16]=0x8000000000000000ul;
keccakf(s);
payload[0]=0x41;
for(int i=0;i<20;++i){int byteIndex=i+12;payload[i+1]=(uchar)(s[byteIndex>>3]>>(8*(byteIndex&7)));}
}
constant uint SHA_K[64]={
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2};
inline uint rotr(uint x,uint n){return (x>>n)|(x<<(32-n));}
inline U256 sha_block(thread uint *w){
uint a=0x6a09e667,b=0xbb67ae85,c=0x3c6ef372,d=0xa54ff53a,e=0x510e527f,f=0x9b05688c,g=0x1f83d9ab,h=0x5be0cd19;
// A 16-word circular schedule replaces w[64]. Both TRON checksum hashes
// are one-block fixed-length messages, so no generic padding path is needed.
for(int i=0;i<64;++i){
uint wi;
if(i<16)wi=w[i];
else{uint s0=rotr(w[(i+1)&15],7)^rotr(w[(i+1)&15],18)^(w[(i+1)&15]>>3),s1=rotr(w[(i+14)&15],17)^rotr(w[(i+14)&15],19)^(w[(i+14)&15]>>10);wi=w[i&15]+s0+w[(i+9)&15]+s1;w[i&15]=wi;}
uint s1=rotr(e,6)^rotr(e,11)^rotr(e,25),ch=(e&f)^(~e&g),t1=h+s1+ch+SHA_K[i]+wi,s0=rotr(a,2)^rotr(a,13)^rotr(a,22),maj=(a&b)^(a&c)^(b&c),t2=s0+maj;h=g;g=f;f=e;e=d+t1;d=c;c=b;b=a;a=t1+t2;
}
U256 out;out.v[0]=a+0x6a09e667;out.v[1]=b+0xbb67ae85;out.v[2]=c+0x3c6ef372;out.v[3]=d+0xa54ff53a;out.v[4]=e+0x510e527f;out.v[5]=f+0x9b05688c;out.v[6]=g+0x1f83d9ab;out.v[7]=h+0x5be0cd19;return out;
}
inline uint tron_checksum(thread const uchar *payload){
uint w[16];for(int i=0;i<16;++i)w[i]=0;
for(int i=0;i<21;++i)w[i>>2]|=(uint)payload[i]<<(24-8*(i&3));
w[5]|=0x00800000u;w[15]=168u;
U256 first=sha_block(w);
for(int i=0;i<16;++i)w[i]=0;
for(int i=0;i<8;++i)w[i]=first.v[i];
w[8]=0x80000000u;w[15]=256u;
U256 second=sha_block(w);return second.v[0];
}
constant uchar MOD58_WEIGHT[25]={20,54,24,30,52,36,16,20,54,24,30,52,36,16,20,54,24,30,52,36,16,20,54,24,1};
inline bool suffix_match(thread uchar *full,constant uint *target,uint len){
// Almost all candidates fail on the final Base58 character. Obtain N%58
// with 25 multiply-adds and one constant division, without materialising
// the quotient. Only the ~1/58 survivors enter the long-division path.
uint weighted=0;for(int i=0;i<25;++i)weighted+=(uint)full[i]*MOD58_WEIGHT[i];
if(weighted%58!=target[len-1])return false;
if(len==1)return true;
uchar x[25];for(int i=0;i<25;++i)x[i]=full[i];
for(uint digit=1;digit<len;++digit){
uint rem=0;for(int j=0;j<25;++j){uint v=(rem<<8)|x[j];x[j]=(uchar)(v/58);rem=v%58;}
weighted=0;for(int j=0;j<25;++j)weighted+=(uint)x[j]*MOD58_WEIGHT[j];
if(weighted%58!=target[len-1-digit])return false;
}
return true;
}
// Invert 256 independent field elements with one exponentiation. A product
// tree needs 255 multiplies on the way up and 510 on the way down. The old
// two-sided Hillis-Steele scans needed about 4098 multiplies per threadgroup.
inline U256 batch_inverse_256(U256 value,threadgroup U256 *tree,uint tid){
tree[256+tid]=value;
threadgroup_barrier(mem_flags::mem_threadgroup);
for(uint nodes=128;nodes>=1;nodes>>=1){
if(tid<nodes){uint i=nodes+tid;tree[i]=fmul(tree[i<<1],tree[(i<<1)+1]);}
threadgroup_barrier(mem_flags::mem_threadgroup);
if(nodes==1)break;
}
if(tid==0)tree[1]=finv(tree[1]);
threadgroup_barrier(mem_flags::mem_threadgroup);
for(uint nodes=1;nodes<256;nodes<<=1){
if(tid<nodes){
uint i=nodes+tid,left=i<<1;
U256 parentInverse=tree[i],leftProduct=tree[left],rightProduct=tree[left+1];
tree[left]=fmul(parentInverse,rightProduct);
tree[left+1]=fmul(parentInverse,leftProduct);
}
threadgroup_barrier(mem_flags::mem_threadgroup);
}
return tree[256+tid];
}
kernel void precompute_points(device Affine *points [[buffer(0)]],constant Affine *table [[buffer(1)]],uint gid [[thread_position_in_grid]],uint tid [[thread_index_in_threadgroup]],uint tgSize [[threads_per_threadgroup]]){
// Build gid*G once at startup. The search kernel reuses this 64 MiB table
// for every batch instead of repeating a 20-bit scalar multiplication for
// every candidate key.
threadgroup U256 productTree[512];
const U256 one={{1,0,0,0,0,0,0,0}};
Jacobian r=scalar_gid(gid,table);
const bool valid=gid!=0&&!uzero(r.z);
const U256 z=valid?r.z:one;
U256 zi=batch_inverse_256(z,productTree,tid);
if(!valid){Affine zero={};points[gid]=zero;return;}
U256 zi2=fsqr(zi);
points[gid]={fmul(r.x,zi2),fmul(r.y,fmul(zi2,zi))};
}
kernel void vanity_points(constant Affine *batchStart [[buffer(0)]],device const Affine *points [[buffer(1)]],constant Params ¶ms [[buffer(2)]],device Affine *publicPoints [[buffer(3)]],uint gid [[thread_position_in_grid]],uint tid [[thread_index_in_threadgroup]],uint tgSize [[threads_per_threadgroup]]){
// Add batchStart to the precomputed gid*G table. All 256 affine additions
// in a threadgroup share one Montgomery inversion of their denominators.
threadgroup U256 productTree[512];
const U256 one={{1,0,0,0,0,0,0,0}};
const bool active=gid<params.count;
const bool direct=gid==0;
const Affine base=batchStart[0];
Affine q=base;
if(!direct)q=points[gid];
U256 denominator=one,numerator=one;
bool valid=active;
if(!direct){
denominator=fsub(q.x,base.x);
if(uzero(denominator)){
if(uzero(fsub(q.y,base.y))){
// q == base: use the tangent (point doubling) slope.
denominator=f2(base.y);
numerator=f3(fsqr(base.x));
if(uzero(denominator))valid=false;
}else{
// q == -base, so the resulting scalar is zero modulo n.
valid=false;
}
}else{
numerator=fsub(q.y,base.y);
}
}
const U256 z=(valid&&!direct)?denominator:one;
U256 denominatorInv=batch_inverse_256(z,productTree,tid);
if(!valid){Affine zero={};publicPoints[gid]=zero;return;}
Affine p=base;
if(!direct){
U256 slope=fmul(numerator,denominatorInv);
U256 nx=fsub(fsub(fsqr(slope),base.x),q.x);
U256 ny=fsub(fmul(slope,fsub(base.x,nx)),base.y);
p.x=nx;p.y=ny;
}
publicPoints[gid]=p;
}
kernel void vanity_hash(device const Affine *publicPoints [[buffer(0)]],constant uint *target [[buffer(1)]],constant Params ¶ms [[buffer(2)]],device SearchResult *result [[buffer(3)]],uint gid [[thread_position_in_grid]]){
// Do not atomically poll result->found for every candidate. A hit is rare;
// finishing the current dispatch is cheaper than a global atomic read/key.
if(gid>=params.count)return;
const Affine p=publicPoints[gid];
if(uzero(p.x)&&uzero(p.y))return;
uchar full[25];tron_payload(p,full);uint checksum=tron_checksum(full);full[21]=(uchar)(checksum>>24);full[22]=(uchar)(checksum>>16);full[23]=(uchar)(checksum>>8);full[24]=(uchar)checksum;
if(suffix_match(full,target,params.suffixLen)){ulong off=params.startOffset+(ulong)gid;if(atomic_exchange_explicit(&result->found,1u,memory_order_relaxed)==0u)result->offset=off;}
}
)MSL";
struct alignas(16) GpuParams {
uint64_t startOffset;
uint32_t count;
uint32_t suffixLen;
};
struct alignas(16) GpuResult {
uint32_t found;
uint32_t pad;
uint64_t offset;
};
static std::atomic<bool> gStop{false};
static void on_signal(int) { gStop.store(true, std::memory_order_relaxed); }
static std::string grouped_u64(uint64_t value) {
std::string s=std::to_string(value);
for(std::ptrdiff_t i=std::ptrdiff_t(s.size())-3;i>0;i-=3)s.insert(size_t(i),",");
return s;
}
static std::string human_number(double value) {
static const char *units[]={"","K","M","G","T","P","E"};
size_t unit=0;
constexpr size_t unitCount=sizeof(units)/sizeof(units[0]);
while(value>=1000.0&&unit+1<unitCount){value/=1000.0;++unit;}
std::ostringstream out;
const int precision=value>=100.0?0:(value>=10.0?1:2);
out<<std::fixed<<std::setprecision(precision)<<value;
std::string s=out.str();
if(s.find('.')!=std::string::npos){while(!s.empty()&&s.back()=='0')s.pop_back();if(!s.empty()&&s.back()=='.')s.pop_back();}
return s+units[unit];
}
static std::string human_duration(double seconds) {
if(!std::isfinite(seconds))return "不可估算";
if(seconds<0)seconds=0;
if(seconds>=31557600.0){
const double years=seconds/31557600.0;
return human_number(years)+"y";
}
const uint64_t total=uint64_t(seconds);
const uint64_t days=total/86400,hours=(total/3600)%24,minutes=(total/60)%60,secs=total%60;
std::ostringstream out;
if(days)out<<days<<"d ";
if(days||hours)out<<hours<<"h ";
if(hours||minutes)out<<minutes<<"m ";
out<<secs<<"s";
return out.str();
}
struct ProbabilityEstimate {
double expectedKeys;
double expectationProgress;
double cumulativeChance;
double expectedThresholdDeltaSeconds;
};
static ProbabilityEstimate estimate_search(size_t suffixLen,uint64_t searched,double rate) {
// Each fixed Base58 suffix has probability 1/58^len per independently
// distributed address. Delta is the live ETA to the expected-work marker;
// it becomes negative after that marker so the UI can show overtime.
const double expected=std::pow(58.0,double(suffixLen));
const double p=1.0/expected;
const double n=double(searched);
const double chance=-std::expm1(n*std::log1p(-p));
const double delta=rate>0?(expected-n)/rate:std::numeric_limits<double>::infinity();
return {expected,100.0*n/expected,std::clamp(chance*100.0,0.0,100.0),delta};
}
static std::string expected_time_text(const ProbabilityEstimate &estimate) {
if(!std::isfinite(estimate.expectedThresholdDeltaSeconds))return "期望倒计时 不可估算";
if(estimate.expectedThresholdDeltaSeconds>=0)return "期望倒计时 "+human_duration(estimate.expectedThresholdDeltaSeconds);
return "已超出期望 "+human_duration(-estimate.expectedThresholdDeltaSeconds);
}
static std::string percent_text(double value) {
if(!std::isfinite(value))return "--";
std::ostringstream out;
if(value>=100.0)out<<std::fixed<<std::setprecision(1)<<value;
else if(value>=10.0)out<<std::fixed<<std::setprecision(1)<<value;
else if(value>=1.0)out<<std::fixed<<std::setprecision(2)<<value;
else out<<std::fixed<<std::setprecision(3)<<value;
return out.str()+"%";
}
static void usage(const char *exe) {
std::cerr
<< "用法: " << exe << " <地址后缀> [可选:初始密码字符串]\n"
<< "示例: " << exe << " 888\n"
<< " " << exe << " 888 \"my strong password\"\n\n"
<< "说明:\n"
<< " - 后缀只能包含 Base58 字符: 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\n"
<< " - 未提供密码时 Base Key 为 64 个十六进制 0,并从 offset=1 开始。\n"
<< " - 提供密码时 Base Key=SHA-256(密码的 UTF-8/命令行字节),从 offset=0 开始。\n";
}
static cpu::U256 hash_to_u256(const std::array<uint8_t,32> &h) {
cpu::U256 x{};
for (int limb=0; limb<8; ++limb) {
int i=28-4*limb;
x.v[limb]=(uint32_t(h[i])<<24)|(uint32_t(h[i+1])<<16)|(uint32_t(h[i+2])<<8)|h[i+3];
}
return x;
}
int main(int argc, const char *argv[]) {
@autoreleasepool {
try {
if (argc < 2 || argc > 3) { usage(argv[0]); return argc < 2 ? 1 : 2; }
const std::string suffix=argv[1];
static const std::string alphabet="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
if (suffix.empty() || suffix.size()>34) {
std::cerr << "错误: 地址后缀长度必须为 1..34。\n"; return 2;
}
std::array<uint32_t,34> target{};
for(size_t i=0;i<suffix.size();++i){size_t p=alphabet.find(suffix[i]);if(p==std::string::npos){std::cerr<<"错误: 后缀含有非 Base58 字符 '"<<suffix[i]<<"'。\n";return 2;}target[i]=uint32_t(p);}
const auto table=cpu::make_table();
// A known secp256k1/Keccak/Base58Check vector protects against silent
// CPU verification errors and accidental compiler regressions.
cpu::U256 one{{1,0,0,0,0,0,0,0}};
const std::string vectorAddress=cpu::base58(cpu::address_bytes(one,table));
if(vectorAddress!="TMVQGm1qAQYVdetCeGRRkTWYYrLXuHK2HC")
throw std::runtime_error("CPU self-test failed: privkey=1 produced "+vectorAddress);
cpu::U256 base{};
uint64_t nextOffset=1;
if(argc==3){const uint8_t *p=reinterpret_cast<const uint8_t*>(argv[2]);base=hash_to_u256(cpu::sha256(p,std::strlen(argv[2])));nextOffset=0;}
std::cout << "[+] Base Key: " << cpu::hex256(base) << "\n"
<< "[+] 目标后缀: " << suffix << "\n";
id<MTLDevice> device=MTLCreateSystemDefaultDevice();
if(!device) throw std::runtime_error("未检测到可用的 Metal GPU");
std::cout << "[+] Metal GPU: " << [[device name] UTF8String] << "\n"
<< "[+] 正在运行时编译 Metal Shader..." << std::flush;
NSError *error=nil;
NSString *source=[[NSString alloc] initWithUTF8String:kMetalSource];
id<MTLLibrary> library=[device newLibraryWithSource:source options:nil error:&error];
if(!library){std::string e=error?[[error localizedDescription] UTF8String]:"未知错误";throw std::runtime_error("Metal Shader 编译失败:\n"+e);}
id<MTLFunction> pointsFunction=[library newFunctionWithName:@"vanity_points"];
id<MTLFunction> hashFunction=[library newFunctionWithName:@"vanity_hash"];
id<MTLFunction> precomputeFunction=[library newFunctionWithName:@"precompute_points"];
if(!pointsFunction||!hashFunction||!precomputeFunction) throw std::runtime_error("Metal kernel 不完整");
id<MTLComputePipelineState> pointsPipeline=[device newComputePipelineStateWithFunction:pointsFunction error:&error];
if(!pointsPipeline){std::string e=error?[[error localizedDescription] UTF8String]:"未知错误";throw std::runtime_error("创建公钥 pipeline 失败: "+e);}
id<MTLComputePipelineState> hashPipeline=[device newComputePipelineStateWithFunction:hashFunction error:&error];
if(!hashPipeline){std::string e=error?[[error localizedDescription] UTF8String]:"未知错误";throw std::runtime_error("创建哈希 pipeline 失败: "+e);}
id<MTLComputePipelineState> precomputePipeline=[device newComputePipelineStateWithFunction:precomputeFunction error:&error];
if(!precomputePipeline){std::string e=error?[[error localizedDescription] UTF8String]:"未知错误";throw std::runtime_error("创建预计算 pipeline 失败: "+e);}
id<MTLCommandQueue> queue=[device newCommandQueue];
if(!queue) throw std::runtime_error("创建 Metal command queue 失败");
std::cout << " 完成\n";
constexpr uint32_t BATCH=1u<<20;
static_assert(sizeof(cpu::Affine)==64,"Affine layout must match Metal");
id<MTLBuffer> batchPointBuffer=[device newBufferWithLength:sizeof(cpu::Affine) options:MTLResourceStorageModeShared];
id<MTLBuffer> tableBuffer=[device newBufferWithBytes:table.data() length:sizeof(table) options:MTLResourceStorageModeShared];
id<MTLBuffer> pointTableBuffer=[device newBufferWithLength:size_t(BATCH)*sizeof(cpu::Affine) options:MTLResourceStorageModePrivate];
id<MTLBuffer> publicPointBuffer=[device newBufferWithLength:size_t(BATCH)*sizeof(cpu::Affine) options:MTLResourceStorageModePrivate];
id<MTLBuffer> targetBuffer=[device newBufferWithBytes:target.data() length:sizeof(target) options:MTLResourceStorageModeShared];
id<MTLBuffer> paramsBuffer=[device newBufferWithLength:sizeof(GpuParams) options:MTLResourceStorageModeShared];
id<MTLBuffer> resultBuffer=[device newBufferWithLength:sizeof(GpuResult) options:MTLResourceStorageModeShared];
if(!batchPointBuffer||!tableBuffer||!pointTableBuffer||!publicPointBuffer||!targetBuffer||!paramsBuffer||!resultBuffer)throw std::runtime_error("Metal buffer 分配失败(需要约 128 MiB GPU 工作区)");
if(pointsPipeline.maxTotalThreadsPerThreadgroup<256||hashPipeline.maxTotalThreadsPerThreadgroup<256||precomputePipeline.maxTotalThreadsPerThreadgroup<256)
throw std::runtime_error("当前 Metal GPU 不支持本程序需要的 256 线程组");
constexpr NSUInteger groupSize=256;
std::cout << "[+] 正在 GPU 预计算 " << human_number(BATCH) << " 个固定曲线点(64 MiB,一次性)..." << std::flush;
{
const auto preBegin=std::chrono::steady_clock::now();
id<MTLCommandBuffer> command=[queue commandBuffer];
id<MTLComputeCommandEncoder> enc=[command computeCommandEncoder];
[enc setComputePipelineState:precomputePipeline];
[enc setBuffer:pointTableBuffer offset:0 atIndex:0];
[enc setBuffer:tableBuffer offset:0 atIndex:1];
[enc dispatchThreadgroups:MTLSizeMake(BATCH/groupSize,1,1) threadsPerThreadgroup:MTLSizeMake(groupSize,1,1)];
[enc endEncoding];[command commit];[command waitUntilCompleted];
if(command.status==MTLCommandBufferStatusError){std::string e=command.error?[[command.error localizedDescription] UTF8String]:"未知错误";throw std::runtime_error("GPU 点表预计算失败: "+e);}
const double preSeconds=std::chrono::duration<double>(std::chrono::steady_clock::now()-preBegin).count();
std::cout << " 完成(" << human_duration(preSeconds) << ")\n";
}
// Exercise the complete GPU path before a long search. Starting at
// private key 1, gid 1 must produce private key 2 and its exact
// 34-character Base58Check address.
std::cout << "[+] 正在执行 GPU 全链路自检..." << std::flush;
{
cpu::U256 two{{2,0,0,0,0,0,0,0}};
const std::string testAddress=cpu::base58(cpu::address_bytes(two,table));
std::array<uint32_t,34> testTarget{};
for(size_t i=0;i<testAddress.size();++i)testTarget[i]=uint32_t(alphabet.find(testAddress[i]));
std::memcpy([targetBuffer contents],testTarget.data(),sizeof(testTarget));
const cpu::Affine generator=table[0];
std::memcpy([batchPointBuffer contents],&generator,sizeof(generator));
auto *testParams=static_cast<GpuParams*>([paramsBuffer contents]);
*testParams={1,2,uint32_t(testAddress.size())};
auto *testResult=static_cast<GpuResult*>([resultBuffer contents]);
std::memset(testResult,0,sizeof(*testResult));
id<MTLCommandBuffer> command=[queue commandBuffer];
id<MTLComputeCommandEncoder> pointsEnc=[command computeCommandEncoder];
[pointsEnc setComputePipelineState:pointsPipeline];
[pointsEnc setBuffer:batchPointBuffer offset:0 atIndex:0];
[pointsEnc setBuffer:pointTableBuffer offset:0 atIndex:1];
[pointsEnc setBuffer:paramsBuffer offset:0 atIndex:2];
[pointsEnc setBuffer:publicPointBuffer offset:0 atIndex:3];
[pointsEnc dispatchThreadgroups:MTLSizeMake(1,1,1) threadsPerThreadgroup:MTLSizeMake(groupSize,1,1)];
[pointsEnc endEncoding];
id<MTLComputeCommandEncoder> hashEnc=[command computeCommandEncoder];
[hashEnc setComputePipelineState:hashPipeline];
[hashEnc setBuffer:publicPointBuffer offset:0 atIndex:0];
[hashEnc setBuffer:targetBuffer offset:0 atIndex:1];
[hashEnc setBuffer:paramsBuffer offset:0 atIndex:2];
[hashEnc setBuffer:resultBuffer offset:0 atIndex:3];
[hashEnc dispatchThreadgroups:MTLSizeMake(1,1,1) threadsPerThreadgroup:MTLSizeMake(groupSize,1,1)];
[hashEnc endEncoding];
[command commit];[command waitUntilCompleted];
if(command.status==MTLCommandBufferStatusError){std::string e=command.error?[[command.error localizedDescription] UTF8String]:"未知错误";throw std::runtime_error("GPU 自检执行失败: "+e);}
if(testResult->found!=1||testResult->offset!=2)throw std::runtime_error("GPU 全链路自检失败(privkey=2 未精确命中)");
std::memcpy([targetBuffer contents],target.data(),sizeof(target));
std::cout << " 通过\n";
}
// A larger dispatch amortizes command-buffer submission, CPU public-key
// setup and shared-memory synchronization without making Ctrl+C sluggish.
std::signal(SIGINT,on_signal);std::signal(SIGTERM,on_signal);
const auto begin=std::chrono::steady_clock::now();
auto lastRefresh=begin-std::chrono::milliseconds(100);
uint64_t dispatched=0;
bool done=false;
std::cout << "[+] 开始搜索,线程组=" << groupSize << ",每批=" << human_number(BATCH)
<< " keys(界面每 0.1 秒最多刷新一次,Ctrl+C 停止)\n"
<< "[i] 概率搜索没有固定终点;期望搜索量=" << human_number(std::pow(58.0,double(suffix.size())))
<< " keys,进度与期望倒计时均为统计估算。\n";
while(!gStop.load(std::memory_order_relaxed)&&!done){
uint32_t count=BATCH;
if(nextOffset>UINT64_MAX-(uint64_t(count)-1))count=uint32_t(UINT64_MAX-nextOffset+1);
cpu::U256 batchPrivate=cpu::scalar_for_offset(base,nextOffset);
if(cpu::is_zero(batchPrivate)){if(nextOffset==UINT64_MAX)break;++nextOffset;continue;}
cpu::Affine batchPoint=cpu::scalar_mul(batchPrivate,table);
std::memcpy([batchPointBuffer contents],&batchPoint,sizeof(batchPoint));
auto *params=static_cast<GpuParams*>([paramsBuffer contents]);
*params={nextOffset,count,uint32_t(suffix.size())};
auto *result=static_cast<GpuResult*>([resultBuffer contents]);
std::memset(result,0,sizeof(*result));
@autoreleasepool {
id<MTLCommandBuffer> command=[queue commandBuffer];
const NSUInteger groups=(NSUInteger(count)+groupSize-1)/groupSize;
id<MTLComputeCommandEncoder> pointsEnc=[command computeCommandEncoder];
[pointsEnc setComputePipelineState:pointsPipeline];
[pointsEnc setBuffer:batchPointBuffer offset:0 atIndex:0];
[pointsEnc setBuffer:pointTableBuffer offset:0 atIndex:1];
[pointsEnc setBuffer:paramsBuffer offset:0 atIndex:2];
[pointsEnc setBuffer:publicPointBuffer offset:0 atIndex:3];
[pointsEnc dispatchThreadgroups:MTLSizeMake(groups,1,1) threadsPerThreadgroup:MTLSizeMake(groupSize,1,1)];
[pointsEnc endEncoding];
id<MTLComputeCommandEncoder> hashEnc=[command computeCommandEncoder];
[hashEnc setComputePipelineState:hashPipeline];
[hashEnc setBuffer:publicPointBuffer offset:0 atIndex:0];
[hashEnc setBuffer:targetBuffer offset:0 atIndex:1];
[hashEnc setBuffer:paramsBuffer offset:0 atIndex:2];
[hashEnc setBuffer:resultBuffer offset:0 atIndex:3];
[hashEnc dispatchThreadgroups:MTLSizeMake(groups,1,1) threadsPerThreadgroup:MTLSizeMake(groupSize,1,1)];
[hashEnc endEncoding];
[command commit];[command waitUntilCompleted];
if(command.status==MTLCommandBufferStatusError){std::string e=command.error?[[command.error localizedDescription] UTF8String]:"未知错误";throw std::runtime_error("GPU 执行失败: "+e);}
}
dispatched+=count;
const auto now=std::chrono::steady_clock::now();
const double seconds=std::chrono::duration<double>(now-begin).count();
const double rate=seconds>0?double(dispatched)/seconds:0;
const ProbabilityEstimate estimate=estimate_search(suffix.size(),dispatched,rate);
if(result->found){
const uint64_t hitOffset=result->offset;
const cpu::U256 priv=cpu::scalar_for_offset(base,hitOffset);
if(cpu::is_zero(priv))throw std::runtime_error("GPU 返回了无效的零私钥");
const std::string address=cpu::base58(cpu::address_bytes(priv,table));
if(address.size()<suffix.size()||address.compare(address.size()-suffix.size(),suffix.size(),suffix)!=0)
throw std::runtime_error("GPU 命中未通过 CPU 独立验证(地址="+address+")");
std::cout << "\r\033[K"
<< "[✓] Tron 地址: " << address << "\n"
<< "[✓] 私钥: " << cpu::hex256(priv) << "\n"
<< "[✓] Offset: " << human_number(double(hitOffset)) << " (" << grouped_u64(hitOffset) << ")\n"
<< "[✓] 已搜索: " << human_number(double(dispatched)) << " keys (" << grouped_u64(dispatched) << ")\n"
<< "[✓] 搜索算力: " << human_number(rate) << " Keys/s\n"
<< "[✓] 命中时的期望进度: " << percent_text(estimate.expectationProgress)
<< "(此前累计命中概率 " << percent_text(estimate.cumulativeChance) << ")\n"
<< "[✓] 用时: " << human_duration(seconds) << "\n";
done=true;
}else{
if(now-lastRefresh>=std::chrono::milliseconds(100)){
const uint64_t currentOffset=nextOffset+uint64_t(count)-1;
std::cout << "\r\033[K[>] 已搜索 " << human_number(double(dispatched))
<< " | " << human_number(rate) << " Keys/s | 期望进度 "
<< percent_text(estimate.expectationProgress) << " | 命中概率 "
<< percent_text(estimate.cumulativeChance) << " | "
<< expected_time_text(estimate) << " | offset "
<< human_number(double(currentOffset)) << std::flush;
lastRefresh=now;
}
if(count<BATCH||nextOffset>UINT64_MAX-count)break;
nextOffset+=count;
}
}
if(!done){std::cout << "\n[-] 搜索已停止,未找到匹配地址。\n";return 130;}
return 0;
} catch(const std::exception &e) {
std::cerr << "\n错误: " << e.what() << "\n"; return 1;
}
}
}
编译
clang++ -O3 -std=c++17 -framework Metal -framework Foundation main.mm -o gpu_vanity_search
用法:./gpu_vanity_search <地址后缀> [可选:初始密码字符串]
参数 1(必选):要匹配的 Tron 地址结尾(例如:888)。如果不传此参数,输出帮助提示并退出。
参数 2(可选):初始密码字符串。如果提供,程序先对其进行 SHA-256 计算,得到的 256 位 Hash 作为基础私钥(Base Key)
如果不提供,默认基础私钥为 0000000000000000000000000000000000000000000000000000000000000000
本文来自博客园,作者:项希盛,转载请注明原文链接:https://www.cnblogs.com/xiangxisheng/p/22354575
浙公网安备 33010602011771号