Skip to content

Instantly share code, notes, and snippets.

@jhbertra
Created June 12, 2024 13:15
Show Gist options
  • Save jhbertra/f21b81bca3765bbd13135104ac2a4900 to your computer and use it in GitHub Desktop.
Save jhbertra/f21b81bca3765bbd13135104ac2a4900 to your computer and use it in GitHub Desktop.
Byron Address Structure

Let's create a Byron address:

$ cardano-cli byron key keygen --secret test.skey
$ cardano-cli byron key signing-key-public --secret test.skey
    public key hash: 7e5078fa6e2a8308
public key (base64): RNbpyp7dquTkVJJ5hXxs4hLjtVCynfQvuVTi5gicU55McTshDzVSGQdNfJHN995WVWsPYpaAyIQ8omBBR8uagA==
   public key (hex): 44d6e9ca9eddaae4e4549279857c6ce212e3b550b29df42fb954e2e6089c539e4c713b210f355219074d7c91cdf7de56556b0f629680c8843ca2604147cb9a80
$ cardano-cli byron key signing-key-address --mainnet --secret test.skey
Ae2tdPwUPEZHvh7P2QsxENNDeHZ9K4vw4Pfk3LY9gxkrAVVLSTmXaCvxxFf
VerKey address with root d3a912bbc69d18f9f8754c4f79adcbaed373aa6703444d465fd31047, attributes: AddrAttributes { derivation path: {} }

To start with, let's state some facts about the address:

  1. The address is Ae2tdPwUPEZHvh7P2QsxENNDeHZ9K4vw4Pfk3LY9gxkrAVVLSTmXaCvxxFf in base58 encoding
  2. The address root hash in hex is d3a912bbc69d18f9f8754c4f79adcbaed373aa6703444d465fd31047
  3. The public key in hex is 44d6e9ca9eddaae4e4549279857c6ce212e3b550b29df42fb954e2e6089c539e4c713b210f355219074d7c91cdf7de56556b0f629680c8843ca2604147cb9a80 a. The first 32-bytes are the Ed25519 public key 44d6e9ca9eddaae4e4549279857c6ce212e3b550b29df42fb954e2e6089c539e b. The second 32-bytes are the BIP-32 chain code 4c713b210f355219074d7c91cdf7de56556b0f629680c8843ca2604147cb9a80

Now, suppose all we have is the address root hash. Given:

  • the 64-byte public key
  • the address attributes as a CBOR blob
  • a serialized message
  • a signature over the message We want to verify:
  1. The signature was signed by the owner of the public key
  2. The public key is associated with the address root hash we have record of

Verifying the signature is relatively straightforward:

ed25519PubKey <- pubKey[0..32]
verifyEd25519Signature ed25519PubKey message signature

Checking the address root hash is a bit more involved. The goal is to reconstruct the address root CBOR bytes, hash it, and check it against the hash we have record of. The CDDL for the address root is here https://raw.githubusercontent.com/cardano-foundation/CIPs/master/CIP-0019/CIP-0019-byron-addresses.cddl. Unfortunately, the schema is somewhat ambiguous and trying to use a tool like https://cbor.me will not produce the correct sequence of bytes. Here is how to serialize the address root with the given information:

writeByte(buffer, 0x83) // array(3) - ADDRESS_ROOT
writeByte(buffer, 0x00) //   byte(0) - BYRON_ADDRESS_TYPE = Public-key
writeByte(buffer, 0x82) //   array(2) - BYRON_ADDRESS_SPENDING_DATA
writeByte(buffer, 0x00) //     byte(0) - indicates public-key spending data
writeByte(buffer, 0x58) //     bytes
writeByte(buffer, 0x40) //       length 64
write(buffer, pubKey)   //       Ed25519 public key + BIP-32 chain code
write(buffer, attributes) // map(?) - BYRON_ADDRESS_ATTRIBUTES

For our example address, this results in the following CBOR bytes:

83
  00
  82
    00
    58 40
      44d6e9ca9eddaae4e4549279857c6ce212e3b550b29df42fb954e2e6089c539e4c713b210f355219074d7c91cdf7de56556b0f629680c8843ca2604147cb9a80
  a0

Now we have our address root. We now need to double-hash it - first with SHA3-256, then with Blake2b-224:

blake2b_224(sha3_256(buffer))

which results in d3a912bbc69d18f9f8754c4f79adcbaed373aa6703444d465fd31047, our original root hash. The hashes match, the message was signed by the owner of the address.

Herer is the top Byron address on Mainnet, taken from https://cardanoscan.io/topAddresses:

Ae2tdPwUPEYwFx4dmJheyNPPYXtvHbJLeCaA96o6Y2iiUL18cAt7AizN2zG

First, we need to base58-decode it:

82d818582183581c04865e42d2373addbebd5d2acf81c760c848970142889f7ee763091ba0001af01916d5

Now we have CBOR binary that conforms to the BYRON_ADDRESS schema defined here.

Let's decode it:

82                                      # array(2)
   D8 18                                # tag(24)
      58 21                             # bytes(33)
         83581C04865E42D2373ADDBEBD5D2ACF81C760C848970142889F7EE763091BA000 # BYRON_ADDRESS_PAYLOAD CBOR bytes
   1A F01916D5                          # unsigned(4028176085)

It starts with the byte 82. The first nibble 8 indicates the structure is an array and the second nibble 2 indicates the array is 2 elements long. The first element is tagged data (as indicated by D8) with the tag 18 or 24 - which indicates nested CBOR encoded as bytes. The payload of the tagged data is a 33-byte blob that consists of the actual address data. The second array element is a 32-bit CRC checksum.

We can discard all the outer stuff and focus on the inner CBOR. The parsing logic for this is roughly:

consumeBytes [0x82, 0xD8, 0x18, 0x58] // expect this sequence of bytes, all byron addresses start with it.
uint8 length <- consumeUint // read the next byte(s) as a unsigned int with variable length and store as a 64-bit uint
addressPayload <- readBytes length

However, I highly recommend using a CBOR library to do the initial parsing so you're working with structured data and not raw bytes.

Now that we have the address payload, let's examine its structure:

83                                      # array(3)
   58 1C                                # bytes(28)
      04865E42D2373ADDBEBD5D2ACF81C760C848970142889F7EE763091B # Address root hash bytes
   A0                                   # map(0)
   00                                   # unsigned(0)

Again, we have a 3-element long array of which the first element is a 28-byte blob. This is the hash of the address root. The second element is the address attributes map, which for this address is empty. The third element is an enum value indicating the type of address, in this case it is 0 for a "standard" Byron address. The other possible value is 2 for "redemption".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment