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 &params [[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 &params [[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

posted on 2026-08-09 19:28  项希盛  阅读(2)  评论(0)    收藏  举报