Nano Deterministic Account Addresses

Ben Kray
Coinmonks
3 min readJan 10, 2018

--

After getting into Nano (RaiBlocks at the time), I decided to work on my own iOS light wallet. One of the first features I worked on was deriving the same Nano account address from a seed generated by raiwallet.com. When writing my own Swift implementation, I referenced raiwallet.com’s source code as well as a light Python wallet.

Going from a seed to Nano address:

  • Generate secret key from seed
  • Derive public key from secret key
  • Derive Nano account address from public key

I’ve noticed at least a few times, some confusion on the difference between a public key and account address. For clarity, here are my definitions:

seed: A random 32-byte value. This can be any random data. Typically seen as a 64-character hex string. Keep it secret!

secret key: Also called a private key. Keyp this key secret as well since it’s responsible for signing blocks.

public key: The key from which your account address is derived from. The public key can be shared.

account address: An address derived from a public key. Base-32 encoded with a checksum and ‘xrb_’ prepended.

Libraries

Generating a Secret Key

A secret key in this context is just a Blake2b hash of a 32-byte hex value (your seed) along with a 4-byte seed index value. I spent a frustrating few seconds wondering why my secret key was coming out different after hashing the seed and index — turned out that my index byte count was 32 instead of 4 lol.

The seed index should start at 0, and should be incremented each time a user creates a new account address off of the same seed. With an unsigned 32-bit integer, you can generate 2³²-1 unique key pairs.

Note: swift-sodium’s genericHash class uses Blake2b under the hood.

Deriving Public from Secret

Modify Ed25519

Nano uses Ed25519 key pair/signing functions with Blake2b internal hashing, which is different than libsodium’s internal SHA512.

I added an extra function to libsodium’s keypair.c to be able to derive the public from a secret key:

Calling it in Swift:

XRB Account Address

The XRB account address is composed of 3 parts:

  • ‘xrb_’
  • encoded public key
  • encoded checksum

‘xrb_’<encoded public key><encoded checksum>

Encoding

Each 5-bit chunk in the integer is the index of a character in the 32 letter encoding alphabet ‘13456789abcdefghijkmnopqrstuwxyz’. You can create an encoding lookup table where numbers 00000–11111 (0–31) map to a letter in the alphabet:

encoding lookup table that was shared in the Nano discord (slight error with ‘00110’ and ‘00111’, they should be ‘8’ and ‘9’ respectively)

Encoding an integer (or bit array) requires parsing in 5-bit chunks:

Public Key

Create a bit array from the public key byte array, and zero pad it with 4 extra bits (256 -> 260 bits). Because the first 4 bits of a public key are zero padded, the first letter of an account address can only either be a ‘1’ or a ‘3' (00000 : ‘1’, 00001 : ‘3’).

Checksum

Take a Blake2b hash of the public key with a 5 byte digest size, and perform a byte swap on the resulting hash. Encode the result.

Concatenate all 3 and you’ve got an address similar to the one below:

xrb_1sf5f67nw56ts998bsrnjhp4po3y9ky57mg6c4534rqbz96sczotn4nufhf7

Get Best Software Deals Directly In Your Inbox

--

--