EOS区块链帐户名称始未

引子

从上一节中,我们知道在EOS系统中帐户必需遵循以两个准则

  • 必须短于13个字符
  • 仅能包含以下字符:.12345abcdefghijklmnopqrstuvwxyz
    EOS为什么要这样做呢,这样做有什么好处,在EOS源码中又是如何实现的,下面我们从EOS源码中一步步分析,并解答这些疑问。

帐户别名

  在EOS源码中,帐户的类型是account_name,但account_name在C++代码中又是什么类型呢,追踪代码发现,EOS好多类型都用了别名机制,在源码文件types.hpp中的133行,其代码所示

  using action_name      = name; //合约操作名称
  using scope_name       = name; //作用范围名称
  using account_name     = name; //帐户名称
  using permission_name  = name; //权限名称
  using table_name       = name; //数据库表名称

  这些名称都指向同一个name,也就是说帐户等其它名称限制都由name类来完成的,下面我们看一下name类的实现

struct name {
     uint64_t value = 0;
     bool empty()const { return 0 == value; }
     bool good()const { return !empty();   }

     name( const char* str )   { set(str);           }
     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;
    }
     name& operator=( const char* 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, const name& b ) { return a.value <= b.value; }
     friend bool operator > ( const name& a, const name& b ) { return a.value > b.value; }
     friend bool operator >=( const name& a, const name& b ) { return a.value >= b.value; }
     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; }
     friend bool operator != ( const name& a, uint64_t b ) { return a.value != b; }

     friend bool operator != ( const name& a, const name& b ) { return a.value != b.value; }

     operator bool()const           { return value; }
     operator uint64_t()const       { return value; }
     operator unsigned __int128()const       { return value; }
  };

  从name类中只有一个属性value且类型为uint64_t,即无符号64位整型,这是如何存储13位字符的呢?它们之间是如何转换的?等一系列疑问又产生了。

  在name类重载了所有比较运算符用于名称之间的比较,还写了赋值构造和拷贝构造函数用于name类型的构造与赋值。比较重要的函数是set函数和to_string函数。这两个函数应该是实现了字符到整型及整型到字符串的转化。

设置帐户名称set

void name::set( const char* str ) {
     const auto len = strnlen(str, 14);
     EOS_ASSERT(len <= 13, name_type_exception, "Name is longer than 13 characters (${name}) ", ("name", string(str)));
     value = string_to_name(str);
     EOS_ASSERT(to_string() == string(str), name_type_exception,
                "Name not properly normalized (name: ${name}, normalized: ${normalized}) ",
                ("name", string(str))("normalized", to_string()));
  }

  从代码中可以看帐户的名称长度必须最大长度为13位,大于13位会导致代码断言错误并向系统报告名称超过13个字符的异常。然后调用了函数string_to_name将字符转化为整型,其函数string_to_name如下所示

static constexpr uint64_t string_to_name( const char* str )
  {
     uint64_t name = 0;
     int i = 0;
     for ( ; str[i] && i < 12; ++i) {
         // NOTE: char_to_symbol() returns char type, and without this explicit
         // expansion to uint64 type, the compilation fails at the point of usage
         // of string_to_name(), where the usage requires constant (compile time) expression.
          name |= (char_to_symbol(str[i]) & 0x1f) << (64 - 5 * (i + 1));
      }

     // The for-loop encoded up to 60 high bits into uint64 'name' variable,
     // if (strlen(str) > 12) then encode str[12] into the low (remaining)
     // 4 bits of 'name'
     if (i == 12)
         name |= char_to_symbol(str[12]) & 0x0F;
     return name;
  }
static constexpr uint64_t char_to_symbol( char c ) {
     if( c >= 'a' && c <= 'z' )
        return (c - 'a') + 6;
     if( c >= '1' && c <= '5' )
        return (c - '1') + 1;
     return 0;
  }

  从代码中可以看出char_to_symbol是将一个字符转换为一个64位的无符号整型,在此判断判断了字符必须为a-z和1-5的字符。并将1-5的字符与数值1-5所对应,而字符a-z字符与数值6-31,对于字符.则对应数值为0.从而将所有帐户合法字符都转化为一个数值。 在string_to_name函数中,采用循环遍历帐户中的所有字符,并调用char_to_symbol函数将帐户中的字符转化所对应的数值,然后只取低5位,左移64- 5 * (i + 1)位,并与name做|操作。最后将所有字符所对应的数值都存储到name类型中。从中可以计算一下帐户总共有32个字符,所对应的数值为0到31,在计算机中采用二进制表示数据,0到31只需要5位就可以存储。一个64位的无符号整型最多可以存储64/5=12,余4位,也就是说最多13个字符且不能以字符z结尾,否则可能会出现问题。当不够13字符时则以字符.补充。

将name转化可见帐户

name::operator string()const {
    static const char* charmap = ".12345abcdefghijklmnopqrstuvwxyz";

     string str(13,'.');

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

     boost::algorithm::trim_right_if( str, []( char c ){ return c == '.'; } );
     return str;
  }

  将name变量转化为字符串,首先将合法字符按其所对应的数值大小排列并赋值给charmap变量,并作为查找表,然后在name变量的属性value中依次取5位(第一次取4位),并按其值在查找表中找出对应的字符,从而还原出对应可见帐户名称,在帐户规则中小于13位字符时高位用字符.来补充,所以帐户最高位不能是字符.,将补充的字符.移除,从而完成帐户还原。

总结

  综上所述,这就是EOS帐户名称有所限制的原因,它需要将13个字符装入一个64位的无符号整型,且能将整型转化为所对应的字符帐户的过程。

优点:

1、减少内存使用,帐户13个字符存储需要14个字节,而采用name变量存储只需要8个字节。

2、程序运行速度快,在做帐户查找与比较时,整型比较的效率会大大高于字符串比较。

缺点:

1、减少了帐户名称取名的最大范围,比较不能有特殊字符和67890等字符。

2、对帐户名称的长度有所限制。
EOS区块链帐户名称始未

链接

星河公链
了解最新更多区块链相关文章
敬请关注星链微信公众号,星链与你共成长
EOS区块链帐户名称始未

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值