EOSIO源码分析 - 账号及权限体系

什么是EOSIO的账号

账号是访问EOSIO的基础,相当于一般系统中的用户名账号,它的账号是按照如下规则建立的

static const char* charmap = ".12345abcdefghijklmnopqrstuvwxyz";

EOSIO的账号是按照严格按照上面的字母进行自由组合的一个字符串,最大长度为13,两头不能是’.'字符
在EOSIO中有几个缺省的账号, 是系统使用的

const static name system_account_name    {
    
     "eosio"_n };
const static name null_account_name      {
    
     "eosio.null"_n };
const static name producers_account_name {
    
     "eosio.prods"_n };

注意:

  • 其中eosio是系统超级账号,相当于管理员,它是在创始节点随节点启动创建的,创建使用的公私钥,也是创始节点的公私钥
  • 以eosio.为开头创建的账户,其创建者必须是eosio,这是系统保留的私有规则
  • 具体的实现逻辑可以看name类

ESOIO的账号作用

了解了什么是EOSIO的账号,那它在整个系统时有何作用,纵观整个系统的各个功能,账号总结起来有如下作用

  • 交易的权限鉴定
  • 内链Action的权限鉴定
  • 智能合约,ABI的载体
  • 生产者名称

账户权限的数据结构

// 最基础的账户表,记录账户名,创建时间,还有ABI数据块
class account_object : public chainbase::object<account_object_type, account_object> {
    
    
   OBJECT_CTOR(account_object,(abi))

   id_type              id;
   account_name         name; //< name should not be changed within a chainbase modifier lambda
   block_timestamp_type creation_date;
   shared_blob          abi;
};

// 账户元数据表,记录关于账号易变信息,或者一些统计信息
class account_metadata_object : public chainbase::object<account_metadata_object_type, account_metadata_object>
{
    
    
   OBJECT_CTOR(account_metadata_object);
   enum class flags_fields : uint32_t {
    
    
      privileged = 1
   };

   id_type               id;
   account_name          name; //< name should not be changed within a chainbase modifier lambda
   uint64_t              recv_sequence = 0;
   uint64_t              auth_sequence = 0;
   uint64_t              code_sequence = 0;
   uint64_t              abi_sequence  = 0;
   digest_type           code_hash;
   time_point            last_code_update;
   uint32_t              flags = 0;
   uint8_t               vm_type = 0;
   uint8_t               vm_version = 0;
};

// 合约数据表,保存用户的部署的合约
class code_object : public chainbase::object<code_object_type, code_object> {
    
    
   OBJECT_CTOR(code_object, (code))

   id_type      id;
   digest_type  code_hash; //< code_hash should not be changed within a chainbase modifier lambda
   shared_blob  code;
   uint64_t     code_ref_count;
   uint32_t     first_block_used;
   uint8_t      vm_type = 0; //< vm_type should not be changed within a chainbase modifier lambda
   uint8_t      vm_version = 0; //< vm_version should not be changed within a chainbase modifier lambda
};

// 权限基础表
class permission_object : public chainbase::object<permission_object_type, permission_object> {
    
    
   OBJECT_CTOR(permission_object, (auth) )

   id_type                           id;
   permission_usage_object::id_type  usage_id;
   id_type                           parent; ///< parent permission
   account_name                      owner; ///< the account this permission belongs to (should not be changed within a chainbase modifier lambda)
   permission_name                   name; ///< human-readable name for the permission (should not be changed within a chainbase modifier lambda)
   time_point                        last_updated; ///< the last time this authority was updated
   shared_authority                  auth; ///< authority required to execute this permission
};

// 权限链接表,记录和action进行绑定关系
class permission_link_object : public chainbase::object<permission_link_object_type, permission_link_object> {
    
    
   OBJECT_CTOR(permission_link_object)

   id_type        id;
   /// The account which is defining its permission requirements
   account_name    account;
   /// The contract which account requires @ref required_permission to invoke
   account_name    code; /// TODO: rename to scope
   /// The message type which account requires @ref required_permission to invoke
   /// May be empty; if so, it sets a default @ref required_permission for all messages to @ref code
   action_name       message_type;
   /// The permission level which @ref account requires for the specified message types
   /// all of the above fields should not be changed within a chainbase modifier lambda
   permission_name required_permission;
};
/****************************************************************************
在EOSIO中,账户的权限是作为整个数据块进行存储的
在authority结构中,主要记录了一下核心数据
threshold:阈值,所有权限之权重值之和, 最后的比较值
keys:记录公钥与权重数值
accounts:记录管理账户与权重值,新创建的账户一般没有这个数值
waits:延迟执行相关权限
****************************************************************************/
struct permission_level {
    
    
   account_name    actor;
   permission_name permission;
};

struct permission_level_weight {
    
    
   permission_level  permission;
   weight_type       weight;
};

struct key_weight {
    
    
   public_key_type key;
   weight_type     weight;
};

struct authority {
    
    
   authority( public_key_type k, uint32_t delay_sec = 0 )
   :threshold(1),keys({
    
    {
    
    k,1}})
   {
    
    
      if( delay_sec > 0 ) {
    
    
         threshold = 2;
         waits.push_back(wait_weight{
    
    delay_sec, 1});
      }
   }

   authority( permission_level p, uint32_t delay_sec = 0 )
   :threshold(1),accounts({
    
    {
    
    p,1}})
   {
    
    
      if( delay_sec > 0 ) {
    
    
         threshold = 2;
         waits.push_back(wait_weight{
    
    delay_sec, 1});
      }
   }

   authority( uint32_t t, vector<key_weight> k, vector<permission_level_weight> p = {
    
    }, vector<wait_weight> w = {
    
    } )
   :threshold(t),keys(move(k)),accounts(move(p)),waits(move(w)){
    
    }
   authority(){
    
    }

   uint32_t                          threshold = 0;
   vector<key_weight>                keys;
   vector<permission_level_weight>   accounts;
   vector<wait_weight>               waits;
};

从上面的代码结构,可以直接的出以下结论

  • 账户表值记录了最核心的账户名
  • 账户和权限做了深度绑定,整个权限数据作为整体数据块存储
  • 同一个权限可以对应多个key_weight, permission_level_weight,wait_weight,可以是权限更加灵活,扩展性更好
  • 权限直接作用于单一账号,控制的力度更细
  • 权限数据块整个存在一起,存取快速,但是同时也带来了操作麻烦,查询也不方便
  • code,abi分开存储,因为abi的查询频次要远远大于code,这个后面详细说

账户权限的操作

EOSIO中,账户权限的操作都是在系统合约eosio_contract来完成的,eosio_contract合约内置部署于eosio账户上,它是eosio中最基础,也是最重要的合约,直接编译部署在节点中,不能更新。
eosio_contract的action列表如下

  • newaccount: 创建账户,创建账户时默认建立owner,active两个权限
    动作附带参数如下:

    struct newaccount {
          
          
       account_name                     creator;
       account_name                     name;
       authority                        owner;
       authority                        active;
    };
    
  • updateauth:权限更新

    struct updateauth {
          
          
       account_name                      account;
       permission_name                   permission;
       permission_name                   parent;
       authority                         auth;
    };
    
  • deleteauth:删除账户权限

    struct deleteauth {
          
          
       account_name                      account;
       permission_name                   permission;
    };
    
  • linkauth:权限与合约动作的绑定

    struct linkauth {
          
          
       account_name                      account;
       account_name                      code;
       action_name                       type;
       permission_name                   requirement;
    };
    
  • unlinkauth:接触合约与动作的绑定关系

    struct unlinkauth {
          
          
       account_name                      account;
       account_name                      code;
       action_name                       type;
    };
    
  • setcode:部署合约

    struct setcode {
          
          
       account_name                     account;
       uint8_t                          vmtype = 0;
       uint8_t                          vmversion = 0;
       bytes                            code;
    };
    
  • setabi:部署ABI

    struct setabi {
          
          
       account_name                     account;
       bytes                            abi;
    };
    
  • canceldelay:取消延迟

    struct canceldelay {
          
          
       permission_level      canceling_auth;
       transaction_id_type   trx_id;
    };
    

关于cleos中权限账户的命令行

前面我们说过eosio提供给外部访问的接口都是http接口,cleos只是对这些接口的封装,一般在我们的项目中,直接使用的是http接口,但是在系统部署的初期阶段,我们还是免不了会使用cleos命令行,当然你自己开发一个命令行另说,在笔者的项目中,我们曾开发了java版本的命令行工具,还有钱包,这样做主要是为了后台及在android使用,非常的方便。

具体的命令使用规则大家可参考这里,EOSIO命令行大全

总结

相对来说,在EOSIO中,关于权限的代码比较统一集中,功能相对独立,可以封装城独立的模块,也可以建立一个独立的服务,只负责CA相关的业务逻辑。
但是,同时我们又观察到在公链中,权限的设计和一般的系统中权限又大大的不同,那么我们接着会问

  • 这样设计的权限又有什么好处?方便操作与访问么?如果不好,可以从哪些方面做出优化?
  • 在权限的数据结构中,account,key是个什么关系,是一对一的关系么?
  • 在交易中,权限是怎么鉴定的,进而引申在合约中又是怎么鉴定的?
  • 我们观察到,权限相关的表的定义,为什么继承chainbase::object,他到底是咋样存储的?

后记

账户权限这一块,是EOSIO中相对独立,但是又特别绕口难以理解的功能模块,特别是它引入的一些概念,如阈值,权重,多签,link绑定,code权限必须要牢记,才可以理解它进行权限验证的过程和原理。
所以理解了账户权限的相关数据结构,我们才能更好的去理解它的鉴权过程。

猜你喜欢

转载自blog.csdn.net/whg1016/article/details/128669267
今日推荐