I tried to do that by base58 encoding ed255191 public key bytes. Consider a following code sample:

const seed = crypto.randomBytes(32);
const keyPair = ed25519.MakeKeypair(seed);
const base58encoded = base58.encode(keyPair.publicKey);

console.log('ed25519 pub key bytes len: ' + keyPair.publicKey.length);
console.log('base58 pub key: ' + base58encoded);
console.log('base58 pub key len: ' + base58encoded.length);
console.log('actual key: edpktx799pgw7M4z8551URER52VcENNCSZwE9f9cst4v6h5vCrQmJE');
console.log('actual key len: ' + 'edpktx799pgw7M4z8551URER52VcENNCSZwE9f9cst4v6h5vCrQmJE'.length);

It prints:

ed25519 pub key bytes len: 32
base58 pub key: 6jRoreKb7RXAQZBuKZs7WDzsFHHrg62GkMmkXuiWqtKm
base58 pub key len: 44
actual key: edpktx799pgw7M4z8551URER52VcENNCSZwE9f9cst4v6h5vCrQmJE
actual key len: 54

A base58 string that I got is 10 characters shorter than an actual Tezos public key. This, and also that topic suggests I need to prefix my ed25519 data with some magical bytes before encoding it with base58. What are those magical bytes? Is there a documentation about that?


EDIT after Arthur B's answer:

I tried appending prefix bytes: \013\015\037\217 to ed25519 bytes before base58 encoding them. Still, I got 49 characters when a real Tezos public key has 54 characters. After appending additional 4 random bytes, I got desired 54 chars and a value that starts with edpk. Clearly this means that prefix is correct - but where do I get remaining 4 bytes from?

EDIT after discussion in comments:

4 bytes were missing due to me using base58 instead of base58check that has additional 4 bytes as a checksum. After using base58check I got 55 bytes starting with 1edpk - so something looking like a valid public Tezos address prepended with a 1 .

Current problem is: when working with the key (specifically - computing its hash) I removed a leading 1 and got 54 characters. As a result, a library I'm using to hash a key threw an Invalid checksum error. Clearly, 1 is a part of base58check checksum. Still, if not removed the key is going to have an invalid prefix. An updated code sample looks like this:

const realKey = 'edpktx799pgw7M4z8551URER52VcENNCSZwE9f9cst4v6h5vCrQmJE';
const seed = crypto.randomBytes(32);
const keyPair = ed25519.MakeKeypair(seed);
const directPubKeyBytes = keyPair.publicKey;

const tezosPublicKeyPrefix = Buffer.from([13, 15, 37, 217]);
const prefixedPublicKey = Buffer.concat([tezosPublicKeyPrefix, directPubKeyBytes]);
const base58checkEncoded = base58check.encode(prefixedPublicKey).substr(1);

console.log('ed25519 pub key bytes len: ' + prefixedPublicKey.length);
console.log('base58 pub key: ' + base58checkEncoded);
console.log('base58 pub key len: ' + base58checkEncoded.length);
console.log('actual key len: ' + realKey.length);

const publicKey = new Key({ key: base58checkEncoded });
await publicKey.ready;
return publicKey.publicKeyHash();

It prints:

ed25519 pub key bytes len: 36
base58 pub key: edpkvCRxvpVG6FYAevL5Kqf4cQhaVAdyovGqrSCezHhNnav5BwjhuZ
base58 pub key len: 54
actual key len: 54
(node:12395) UnhandledPromiseRejectionWarning: Error: Invalid checksum
    at Object.decode (/home/kspisacki/coding/wallets/node_modules/bs58check/base.js:41:25)
    at Object.b58cdecode (/home/kspisacki/coding/wallets/node_modules/sotez/dist/node/index.js:1:12743)
    at z.<anonymous> (/home/kspisacki/coding/wallets/node_modules/sotez/dist/node/index.js:1:15122)
    at Generator.next (<anonymous>)
    at o (/home/kspisacki/coding/wallets/node_modules/sotez/dist/node/index.js:1:13324)

Any further help with that would be greatly appreciated as I don't understand how to get this to work despite reading up A LOT on the topic ;)

  • Does this answer your question? How to hash a Tezos public key – utdrmac Jan 17 '20 at 15:08
  • Not really. I am an author of the linked question as well ;) . The linked one is about creating a hash of a public key while this one is about creating a public key itself - it's a step before that. – K SS Jan 17 '20 at 15:25

You can find those bytes at the bottom of this file


In your case, "\013\015\037\217"

  • I tried that. I base58 encoded my 36 bytes and got 49 base58 characters. Then apart from the prefix, I also appended 4 random bytes. I got 54 base58 characters, starting from edpk. This means that prefix is clearly correct - but where do I get remaining 4 bytes to replace my random ones from? – K SS Jan 17 '20 at 14:59
  • You have to use base58Check which appends some bytes from a hash of the binary data as a checksum. – Arthur B Jan 17 '20 at 16:16
  • Nice. This gives me a 55 character string starting with 1edpk . I removed leading 1 (am I supposed to do that?) and got something that looks like a valid Tezos public key. However, after attempting to create a hash for this public key using sotez I got Error: Invalid checksum . Does this suggest that ed25519 bytes are wrong? Does Tezos use some custom ed25519 variant? Implementation that I'm using to generate ed25519 bytes is this one . – K SS Jan 17 '20 at 16:43
  • I'm not sure about that specific module, but Sotez uses libsodium-wrappers to perform its ed25519 crypto functions. You can reference this for example. – AKISH Jan 18 '20 at 18:32
  • Using libsodium to generate ed25519 bytes gave me the same error. It seems that leading 1 is a part of base58check checksum so it should not be removed. But if not removed, the key starts with 1edpk so, unsurprisingly I'm getting Error: Invalid prefix . I have no idea how to work that out O_O – K SS Jan 20 '20 at 12:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.