ERC-5564 Stealth Addresses

I’m returning to this (after quite some time), just because I’m thinking about a similar problem for another project. Here’s some (hopefully correct) high-level analysis of three approaches. I’m seeking fast discovery of addresses, over billions (or maybe trillions) of trial attempts, where the cost of performing repeated hashes would be non-negligible. I’m not sure how many trial attempts this EIP expects (will there be thousands / millions / billions of stealth addresses?).

I tweaked the notation to help my brain. I also had to be a little lax with notation, because this forum doesn’t support latex.

(The . is a scalar multiplication. Capital letters are points. Lower-case letters are scalars).

Original approach (1 byte tag)

K = k.G - ethereum keypair.

V = v.G - viewing keypair

E = e.G - ephemeral keypair. E is published.

S = e.V = v.E - shared secret

h = hash(S) - secret hash

tag = h.slice(0, 1) - tag. tag is published.

P = K + h.G - stealth public key

stealth_addr = hash(P).slice(-20)

Operations to discover a match:

1 mul, 1 hash to derive the tag. Or mul + hash if you’ll forgive the disgusting notation.

False positives will occur roughly once every 2^8 = 256 trials, on average, due to a collision with the 1-byte tag. With each false-positive, you need to derive the stealth address to be sure you own it, resulting in some extra operations: 1 mul, 1 add, 1 hash to derive the stealth address, and then a check to see whether this address has a nonzero balance.

So the expected discovery time, on average, allowing for false positives, is:

mul + hash + (1 / 256) * (mul + add + hash + "an ethereum balance lookup")


Double-hash approach

K = k.G - ethereum keypair

V = v.G - viewing keypair

E = e.G - ephemeral keypair. E is published.

S = e.V = v.E - shared secret

h = hash(S) - secret hash

tag = hash(h) - tag. tag is published.

P = K + h.G - stealth public key

stealth_addr = hash(P).slice(-20)

Operations to discover a match:

1 mul, 2 hash to derive the tag. Or, if you’ll again forgive the disgusting notation:

mul + 2 * hash

False positives are infeasible.


Tag keypair approach

K = k.G - ethereum keypair

V = v.G - viewing keypair

T = t.G - tagging keypair

E = e.G - ephemeral keypair. E is published.

tag = (e.T).serialise() = (t.E).serialise() - tag. tag is published. serialise() takes the x-coord and the sign, to compress the tag.

S = e.V = v.E - shared secret

h = hash(S) - secret hash

P = K + h.G - stealth public key

stealth_addr = hash(P).slice(-20)

Operations to discover a match:

1 mul to derive the tag. Or, if you’ll again forgive the disgusting notation:

mul

False positives are infeasible.


I don’t have concrete figures for the relative speeds of mul, hash and add. Nor do I know how many stealth addresses are expected to exist. If it’s only thousands of addresses, then the asymptotics aren’t worth exploring. If it’s billions, then they probably are.