The "UniversalAddress" structure

In Qtum (as well as Bitcoin and others) there are multiple "address" formats. These might look like QNPkSpiwVfvMoMK8wqpKN9TdyLgRwJTYKz for a standard pay-to-pubkeyhash address or the address might begin with the letter M indicating a pay-to-scripthash address. The prefix of the address (ie, the bit that determines if it starts with a Q or M) determines what format the address is, and thus how coins can be sent to that address. There is actually more data in address than what goes into the blockchain. An address is made like the following:

base58(version + data + checksum)

Version is a 1 byte prefix that determines what letter the base58 address begins with. Data is the actual data that will go on the blockchain. For a pubkeyhash address, this would be a ripemd160 hashed public key. The checksum is the bottom 4 bytes of sha256(sha256(version + data)). Satoshi himself designed this system to prevent mistakes where it is possible to send coins to a wrong address, etc.

Since Qtum was first thought of, the mutliple address formats has been difficult to reconcile with the design of the EVM. In the EVM, all addresses are assumed to be 160 bits. This does not allow for any simple method of disambiguating between a pubkeyhash or scripthash form of address, there is no room for the "version" field. Furthermore, it's always been rather confusing because Qtum uses base58 addresses everywhere like Bitcoin does, but the EVM and Solidity naturally uses hex addresses like Ethereum. It's easy to convert a base58 address to a hex address, but it strips away the version number and it's easy to make mistakes.

Our best workaround for this problem was basically to have soldiity contracts etc continue to use hex addresses. If the hex address exists in the EVM as a contract, it sends coins or whatever to that contract address. If the address does not exist, then it assumes that the address is a pubkeyhash address. With this system it's impossible to make use of pay-to-scripthash or multisig addresses.

With the design of the x86 VM in Qtum, this was one of the more important things to be fixed. It will be possible to use established and safe Bitcoin style multisig addresses to manage x86 contracts, and contracts can send and receive funds to/from these addresses. Exposing the base58 version number was not enough however. To avoid mistakes like sending mainnet Qtum to a testnet network address, the exact version number can vary per network. It would not be ideal to need to compile special versions of a contract that work only on testnet, or only on mainnet. So, instead the "UniversalAddress" structure was created. It is currently defined as the following:

#define ADDRESS_DATA_SIZE 32
typedef struct{
    //Do not modify this struct's fields
    //This is consensus critical!
    uint32_t version;
    uint8_t data[ADDRESS_DATA_SIZE];
} __attribute__((__packed__)) UniversalAddressABI;

//Note: we don't use Params().Base58Prefixes for these version numbers because otherwise
//contracts would need to use two different SDKs since the the base58 version for pubkeyhash etc changes
//between regtest, testnet, and mainnet
enum AddressVersion{
    UNKNOWN = 0,
    //legacy is either pubkeyhash or EVM, depending on if the address already exists
    LEGACYEVM = 1,
    PUBKEYHASH = 2,
    EVM = 3,
    X86 = 4,
    SCRIPTHASH = 5
};

The version number was made 4 bytes instead of 1 to allow for future flags and to keep with a 4 byte alignment. Allowing the structure to be 33 bytes might be rather inefficient due to unaligned memory access on some processors being more expensive. The address data is 32 bytes because it can fit a 256 bit hash. No current address format uses a full 256 bit hash though, so there has been some consideration of reducing that size to 20 bytes instead, to fit the standard ripemd160 bit hash.. but thus far we are leaning on the side of flexibility.

The version number used in UniversalAddresses are not the same as the base58 address version. Instead, these addresses are not intended to be used directly, but rather to be easy to write code around. Each value corresponds to pubkeyhash, scripthash, EVM, or x86 addresses. These version numbers are always the same regardless of network, and tools are provided to decode and map a base58 address into a UniversalAddress. Here is an example in a C contract:

UniversalAddressABI a;
if(qtumParseAddress("xCREYTgvdk3XzFNpdXkj35fZX4hJK2Ckpd", &a)){
    qtumError("Error parsing address");
}
Posted: 11/19/2018 5:46:12 AM

Comments

Posting comments is currently disabled(probably due to spam)