The internal organization of NEO wallet and account
By: Neo Saint Petersburg Competence Center, Neo Spcc
Tags: Wallet, Blockchain, Golang, Articles, Neo

Author: Anastasia Prasolova

This is a note about the internal organization of NEO wallet and account.

When I started with development for NEO ecosystem, my first challenge was to build raw transactions to invoke smart contracts and send assets. Golang was selected to implement a transactions processor. Go part of NEO community is pretty small, and there are no already written wallets or clients or transaction builders, so I spent some time on investigating how NEO identities work under the hood. The conclusions of my research are below with Go code snippets.

All identities mentioned in the note are common-used keys from NEO privnet wallet.

What is WIF and how to get it?

WIF stands for “Wallet Import Format”. It is a 32-byte long private key hashed and encoded into base58. What for? — to be more human-readable. Let’s say you have a well-known private key 1dd37fba80fec4e6a6f13fd708d8dcb3b29def768017052f6c930fa1c5d90bbb. The corresponding WIF is KxDgvEKzgSBPPfuVfw67oPQBSjidEiqTHURKSDL1R7yGaGYAeYnr. Looks slightly nicer, isn’t it? The private key to WIF conversion algorithm is:

take a private key. prepend a version byte. It is a “0x80” by default and represents a version of import format. append a compression flag. It is a “0x01” (true) by default. encode the byte slice you got to Base58 using Bitcoin symbol chart.

What is a public key signature and what is it useful for?

A public key signature is equivalent to an address script hash (as mentioned here, every wallet address is a two-instruction smart contract). Steps to get an address script hash:

Prepend ‘0x21’ byte to the public key; append ‘0xac’ byte to the public key. What do these magic numbers mean? — you calculate the signature to be pushed into the NEO-VM. These bytes are virtual machine opcodes. ‘0x21’ is ‘PUSHBYTES33’ opcode, 32 bytes of the public key and ‘0xac’ byte, which is ‘CHECKSIG’ opcode. Note: this byte slice is also known as verification script. Every transaction in NEO must have ‘Witness’ field. This field is composed of two scripts: verification script and invocation script. Take SHA256 hash of byte slice from step (1) Take RIPEMD160 hash of byte slice from step (2). Here you have an address script hash but in reverse order. In NEO-VM, this number is stored in UInt160 data type (which is, obviously, 20-bytes length).

This signature is used in operations with wallet address described below.

How to get a wallet address?

Your primary identity for transactions in NEO network is a private/public key pair. However, when we talk about asset transfer, we use “wallet” and “address” notions. How do keys transform into a wallet address? The algorithm is below. First, calculate a checksum for address:

Take the aforementioned public key signature Prepend ‘0x17’ byte to this signature. This byte is an “address version”, which is usually specified in protocol.json: C#,neo-go. 17 is a hex representation of decimal 23. Take sha256 hash twice Take first 4 bytes of hash from step (3)

When the checksum is obtained, calculate the wallet address:

Take a public key signature. Note that it should be in reversed order. Prepend ‘0x17’ byte to this signature Append a checksum Apply Base58 encoding to byte slice from step (3)

How to get smart contract hash by its address?

Now let’s look at opposite conversion: how to get script hash from Base58-encoded wallet address.

Decode wallet address from Base58 to byte slice. Cut off first byte ‘0x17’. It is an aforementioned “address version” byte. The result is a reverted script hash.

Hope this topic shed some light on NEO wallet internals.