Solana Vanity Address using GPUs
Today I looked into using GPUs to generate Solana vanity addresses. I found an Ed25519 GPU vanity address generator, modified it a bit, and did some experimenting.
What even is an address?
Solana uses public/private keys to enforce ownership of funds. If you have the private key for an address, you can sign a transaction proving you own the funds at that address, and so transfer them to another address. And an address is just a string derived transparently from the public key. There are various schemes for different crypto currency networks, but in Solana an address is simply the public key encoded in base58.
As far as I know, all crypto networks use public/private keys in this way to control funds (if anyone does it some other way, please let me know), the important feature is that going from a private key to a public key is not reversible. You can NEVER go from a public key to a private key. Additionally, you want different private keys to map to different public keys; you don’t want collisions where different private keys map to the same public key. This private to public key mapping mechanism differs per crypto network. Most use elliptic curves, but different ones; Bitcoin and Ethereum use Secp256k1, NEO uses secp256r1, Cardano and Solana Ed25519.
In any case, the overall process goes like this:
Private-key → ed25519-elliptic-curve-magic → toBase58 → Address
That entire process is mathematical, it doesn’t require the internet. If you had a lot of time to spare, you could do it with a pen and paper.
The private key is just 32 bytes of random data; typically, entropy from the operating system, but it can be anything that is random. At the end of the process you have an address, which is just a string of random base58 characters, something like this:
Awes4Tr6TX8JDzEhCZY2QVNimT6iD1zWHzf1vNyGvpLM
The Base58 alphabet is a subset of ASCII alphanumeric characters designed to avoid confusion; for example, there is no upper case ‘o’ as that looks too much like a zero ‘0’. The encoding scheme is reversible, you can go from bytes to Base58 and back again.
Now we get to the ‘vanity’ aspect of it all. Clearly different private keys will result in a different final base58 address. If you tried really a lot of different private keys, you would find ones with words or names in them, this for example:
ChorusmmK7i1AxXeiTtQgQZhQNiXYU84ULeaYF1EH15n
It has “Chorus” at the beginning, that is a vanity address. Functionally it is not different from any other address, but it is distinctive, probably the owner likes singing or is called Chorus, or something like that.
The “Chorus” part of the address is known as “the vanity”, the vanity could be at the front, end or middle of the address — but over time, the preference seems to be for the front.
Remember, the elliptic curve mapping between private and public key is one way only, implied in that is, there is no relationship visually between the private key and the address. To find that vanity address is a brute search. You pick a random private key, work out the address, and in almost every case throw it away.
How many private keys did Chorus have to try before chancing on the above address? Very many indeed.
Solana CPU Vanity Gen
A computer can try many private keys a second, matching the output address against various prefixes to find a match. The traditional name for a program that does this is a “vanity gen”. There is a vanity gen that comes with the Solana CLI tools. You can run like this:
solana-keygen grind --starts-with AAA:1 --starts-with BBB:1
This will search for addresses starting with AAA or BBB, in each case stopping after it finds 1 address. When it finds a public/private keypair that results in an address that matches, it writes the keypair to a file. If you look at the file you will see one line, json formatted array containing 64 decimal numbers, each one between 0 and 255. These are the bytes; the first 32 bytes as decimal numbers is the private key, the last 32 the public key.
The Solana provided Vanity Gen uses the CPU to do the work. It is pretty fast, I ran some tests and an EPYC 7502P 32-Core CPU can do half a million “attempts” a second.
Solana GPU Vanity Gen
It is also possible to do the process on a GPU rather than the CPU. These things are not exclusive, you could do both at the same time. For this experiment I used leaderGPU.com to get a machine with 8 x 2080Ti GPUs.
I found a working Ed25519 vanity gen (the only one I think) that was mostly complete. I cloned the github repo and noted the author’s warning — the code (23rd Dec 2020) is not random it is deterministic, that means if you run the process twice, you will find the same private keys and addresses. I made various changes, you can look at my code, the important change is I use entropy to randomly seed the state of the GPUs.
You can run this too. You need CUDA installed (obvs). Clone my repo, then add CUDA libs to the path and compile:
export PATH=/usr/local/cuda/bin:$PATH
. mk
The first thing I will say is that CUDA code takes a long while to compile — about 3 minutes — whatever ptxas is, it’s too slow. Then you can run like this:
. run
The prefixes it is looking for are in ./src/config.h it should find a couple within the first few seconds. If you want to understand what is going on, vanity.cu is the main file.
By my reckoning, with eight 2080Ti GPUs, it does 2.5 million attempts a second — which is better than 0.5 million attempts by the CPU search on a 32-Core 7502P.
If you run it in the background:
export LD_LIBRARY_PATH=./src/release
nohup ./src/release/cuda_ed25519_vanity > log.out &
The nvidia-smi command should show all your GPUs running the program.
You want to be looking for MATCH lines in the output, the line following the match is the Solana keypair, paste the single line into a file with nothing else and that is your keypair.
How much time?
If you play with it a bit, you will quickly realise longer prefixes take much longer to find. This is because the difficult is not linear.
The alphabet size is 58, assuming things are evenly distributed, a prefix of:
1 character will occur 1 in 58 addresses
2 characters will occur 1 in 58² addresses (1 in 3364)
3 characters will occur 1 in 58³ addresses (1 in 195112)
4 characters will occur 1 in 58⁴ addresses (1 in 11316496)
5 characters will occur 1 in 656356768
6 characters will occur 1 in 38068692544
7 characters will occur 1 in 2.20798E+12
If we are using the CPU search running at 500,000 attempts per second, and we are looking for 4 leading characters, which occur once in 11,316,496 addresses, we would expect to find a match (on average!) by looking through half that number i.e. 5,658,248 which would take about 11 seconds.
The GPU search, running at 2,500,000 looking for the same 4 letter prefix, should take around 2–3 seconds.
5 characters would (on average!) require searching 328,178,384 taking around 2 minutes.
6 characters, just over 2 hours.
7 characters, about 10 days.
At least I think that’s right ;)
Of course, in many situations more than one prefix is acceptable, “Monkey” and “monkey” are probably both ok, so you can divide the time in half.
But unless you have oodles of time and money, anything over 6 letters is probably out.
Here ends my exploration into Solana vanity address searches and CUDA.
Delegations to our Solana Validator are always appreciated.