FIO Account Name Hash Function
Overview
In FIO Chain, unlike EOSIO, FIO Account Name is created automatically from the supplied FIO Public Key using a custom hash function. In other words, a single FIO Public Key always "hashes down" to single FIO Account Name.
The FIO Account Name is also referred to as actor
ans is supplied on every signed call.
Getting FIO Account Name
Using the API
You can use /get_actor getter to convert FIO Public Key to actor.
Using clio
The following clio command will convert the FIO Public Key to actor:
clio convert fiokey_to_account FIO8VHb3sQ52KqjjLfGAyy31E6dAZv9Y8Mo4KsJTmyNEhXZGvG7Zg
Custom code
The following custom code describes how exactly FIO Public Key is converted to account.
Step 1
Remove FIO prefix from FIO Public Key.
- Example:
- FIO6cDpi7vPnvRwMEdXtLnAmFwygaQ8CzD7vqKLBJ2GfgtHBQ4PPy
- becomes 6cDpi7vPnvRwMEdXtLnAmFwygaQ8CzD7vqKLBJ2GfgtHBQ4PPy
Step 2
Perform Base58 decode operation on trimmed FIO Public Key from Step 1 and run through the shorten_key function:
#include <iostream>
#include <string>
#include <sstream>
#include "assert.h"
// Alphabet map for Base58 encoding example
const char * const ALPHABET =
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
const char ALPHABET_MAP[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1,
-1, 9, 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, 19, 20, 21, -1,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, -1,
-1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, -1, -1, -1, -1, -1
};
// result must be declared (for the worst case): char result[len * 2];
int DecodeBase58(const char *str, int len, unsigned char *result) {
result[0] = 0;
int resultlen = 1;
for (int i = 0; i < len; i++) {
unsigned int carry = (unsigned int) ALPHABET_MAP[(unsigned char)str[i]];
for (int j = 0; j < resultlen; j++) {
carry += (unsigned int) (result[j]) * 58;
result[j] = (unsigned char) (carry & 0xff);
carry >>= 8;
}
while (carry > 0) {
result[resultlen++] = (unsigned int) (carry & 0xff);
carry >>= 8;
}
}
for (int i = 0; i < len && str[i] == '1'; i++)
result[resultlen++] = 0;
for (int i = resultlen - 1, z = (resultlen >> 1) + (resultlen & 1);
i >= z; i--) {
int k = result[i];
result[i] = result[resultlen - i - 1];
result[resultlen - i - 1] = k;
}
return resultlen;
}
uint64_t shorten_key(const unsigned char* key) {
uint64_t res = 0;
int done = 0;
int i = 1; // Ignore key head
int len = 0;
while (len <= 12) {
assert(i<33); // Means the key has > 20 bytes with trailing zeroes...
auto trimmed_char = uint64_t(key[i] & (len == 12 ? 0x0f : 0x1f));
if (trimmed_char == 0) { i++; continue; } // Skip a zero and move to next
auto shuffle = len == 12 ? 0 : 5*(12-len)-1;
res |= trimmed_char << shuffle;
len++; i++;
}
return res;
}
int main() {
std::string pubkey("6cDpi7vPnvRwMEdXtLnAmFwygaQ8CzD7vqKLBJ2GfgtHBQ4PPy");
unsigned char* pub_key_bytes = new unsigned char[37];
DecodeBase58(pubkey.c_str(), pubkey.length(), pub_key_bytes);
uint64_t result = shorten_key(pub_key_bytes);
std::cout<<result<<std::endl;
return 0;
}
- Example:
- 6cDpi7vPnvRwMEdXtLnAmFwygaQ8CzD7vqKLBJ2GfgtHBQ4PPy
- becomes 1518832697283783336
Step 3
Convert unsigned long int from Step 2 and use it to instantiate a name object, similar to the one defined below:
#include <iostream>
#include <string>
#include <sstream>
#include <cstdlib>
using namespace std;
struct name {
uint64_t value = 0;
bool empty()const { return 0 == value; }
bool good()const { return !empty(); }
name( const string& str ) { set( str.c_str() ); }
void set( const char* str );
template<typename T>
name( T v ):value(v){}
name(){}
explicit operator string()const;
string to_string() const { return string(*this); }
name& operator=( uint64_t v ) {
value = v;
return *this;
}
name& operator=( const string& n ) {
value = name(n).value;
return *this;
}
friend std::ostream& operator << ( std::ostream& out, const name& n ) {
return out << string(n);
}
friend bool operator == ( const name& a, const name& b ) { return a.value == b.value; }
friend bool operator == ( const name& a, uint64_t b ) { return a.value == b; }
operator bool()const { return value; }
operator uint64_t()const { return value; }
};
name::operator string()const {
static const char* charmap = ".12345abcdefghijklmnopqrstuvwxyz";
string str(13,'.'); //We are forcing the string to be 13 characters
uint64_t tmp = value;
for( uint32_t i = 0; i <= 12; i++ ) {
char c = charmap[tmp & (i == 0 ? 0x0f : 0x1f)];
str[12-i] = c;
tmp >>= (i == 0 ? 4 : 5);
}
return str;
}
int main(int argc, char *argv[]) {
uint64_t hash = 1518832697283783336U; //Unsigned long long integer from Step 2
std::stringstream fulladdress;
//Important step, instantiate the name object using a hash, result is stored in fulladdress stringstream in this example
fulladdress << name(hash);
//Output resulting address without the 13th character
std::cout << fulladdress.str() << std::endl;
return 0;
}
- Example:
- 1518832697283783336
- becomes 2odzomo2v4pec
Step 4
The result is 13 characters in length, FIO requires an address of 12 characters. Shorten the address as needed:
#include <iostream>
#include <string>
int main() {
std::string shortaddress= "2odzomo2v4pec";
//Output address without the 13th character
std::cout << shortaddress.erase(12,13) << std::endl;
return 0;
}
Updated over 1 year ago