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;
  }