您正在查看: 2019年6月

EOS节点数据备份

简介

最近在做eos数据落盘的项目,遇到了很多场景,都需要备份数据。所以,今天跟大家分享下备份节点数据的小工具。

前言

eos节点非正常退出,甚至节点升级,都会意外导致脏数据的产生,节点数据不得不重新同步。官方给的数据重新同步方法,是用下面的命令重启节点:

nodeos --hard-replay

不过,实际使用起来会发现replay数据执行的非常慢,普通的全节点也要两天时间才能完成重新同步。归根结底,还是eos节点数据的验证机制导致的慢。具体原因,大家可以看看这篇文章:痛苦的EOS数据同步,可能的EOS安全隐患

简而言之,eos之所有不能像以太坊那样做到快速同步,是因为eos没有将状态数据生成merkle root,存储上链。所以,eos重新同步数据时,节点不能信任其他节点,只能重新执行一遍交易,亲自验证交易状态,当然会极大影响同步速度。另外,节点的同步只能从创世块开始,不能按照时间分片,同步相当于重新计算一遍全网交易,速度可想而知。

数据同步

eos的节点数据是存储在data目录的,data目录里有blocks和state目录,分别存储区块信息和状态信息:

mac os:~/Library/Application\ Support/eosio/nodeos/data
Linux:~/.local/share/eosio/nodeos/data

因为eos数据是按文件形式存储的,常用的备份方法是将data目录整个拷贝一份,当数据坏了时,启用备用data目录,则节点会从备用data目录的高度开始,再同步之后的数据。这个方法简单,但是普通的全节点,不开history-plugin时数据30G+,开启history-plugin数据可达500G。简单的cp操作处理备份,显然需要数小时不等,还是太慢。

pitreos工具

pitreos是加拿大超级节点 eos canada 出品的节点数据备份工具,项目地址:pitreos。
它实现了大文件的快速备份和恢复。以下是30G大文件备份和恢复的实测效果,备份和恢复处理时间各约30s,极大的提升了效率。

  • 生成30G大文件
  • 备份30G大文件,时间约30s
  • 查看备份tag
  • 恢复备份,时间约30s

pitreos安装步骤

1. 需要安装golang、设置gopath
2. cd $GOPATH/src/github.com/eoscanada
3. git clone https://github.com/eoscanada/pitreos.git
4. cd $GOPATH/src/github.com/eoscanada/pitreos && go get -v ./... && go install -v

pitreos常用命令

备份:pitreos backup ./mydata -t <备份tag>。备份文件默认存在:$HOME/.pitreos/backups
恢复:pitreos -c restore <备份tag> ./mydata。
查询tag:pitreos list

转载自:https://www.jianshu.com/p/16f193b49a39

EOS合约内查询代币余额

#include <eosiolib/eosio.token.hpp>   // right path to eosio.token.hpp file
void getBalance(account_name owner){
    eosio::token t(N(eosio.token));
    const auto sym_name = eosio::symbol_type(S(4,EOS)).name();
    const auto my_balance = t.get_balance(N(owner), sym_name );
    eosio::print("My balance is ", my_balance);
}

https://eosio.stackexchange.com/questions/375/how-can-i-get-my-contract-currency-balance-with-c-code?rq=1

eos linkauth unlinkauth

https://github.com/EOSIO/eos/blob/3fddb727b8f3615917707281dfd3dd3cc5d3d66d/libraries/chain/eosio_contract.cpp#L301

void apply_eosio_linkauth(apply_context& context) {
//   context.require_write_lock( config::eosio_auth_scope );

   auto requirement = context.act.data_as<linkauth>();
   try {
      EOS_ASSERT(!requirement.requirement.empty(), action_validate_exception, "Required permission cannot be empty");

      context.require_authorization(requirement.account); // only here to mark the single authority on this action as used

      auto& db = context.db;
      const auto *account = db.find<account_object, by_name>(requirement.account);
      EOS_ASSERT(account != nullptr, account_query_exception,
                 "Failed to retrieve account: ${account}", ("account", requirement.account)); // Redundant?
      const auto *code = db.find<account_object, by_name>(requirement.code);
      EOS_ASSERT(code != nullptr, account_query_exception,
                 "Failed to retrieve code for account: ${account}", ("account", requirement.code));
      if( requirement.requirement != config::eosio_any_name ) {
         const auto *permission = db.find<permission_object, by_name>(requirement.requirement);
         EOS_ASSERT(permission != nullptr, permission_query_exception,
                    "Failed to retrieve permission: ${permission}", ("permission", requirement.requirement));
      }

      auto link_key = boost::make_tuple(requirement.account, requirement.code, requirement.type);
      auto link = db.find<permission_link_object, by_action_name>(link_key);

      if( link ) {
         EOS_ASSERT(link->required_permission != requirement.requirement, action_validate_exception,
                    "Attempting to update required authority, but new requirement is same as old");
         db.modify(*link, [requirement = requirement.requirement](permission_link_object& link) {
             link.required_permission = requirement;
         });
      } else {
         const auto& l =  db.create<permission_link_object>([&requirement](permission_link_object& link) {
            link.account = requirement.account;
            link.code = requirement.code;
            link.message_type = requirement.type;
            link.required_permission = requirement.requirement;
         });

         context.add_ram_usage(
            l.account,
            (int64_t)(config::billable_size_v<permission_link_object>)
         );
      }

  } FC_CAPTURE_AND_RETHROW((requirement))
}

void apply_eosio_unlinkauth(apply_context& context) {
//   context.require_write_lock( config::eosio_auth_scope );

   auto& db = context.db;
   auto unlink = context.act.data_as<unlinkauth>();

   context.require_authorization(unlink.account); // only here to mark the single authority on this action as used

   auto link_key = boost::make_tuple(unlink.account, unlink.code, unlink.type);
   auto link = db.find<permission_link_object, by_action_name>(link_key);
   EOS_ASSERT(link != nullptr, action_validate_exception, "Attempting to unlink authority, but no link found");
   context.add_ram_usage(
      link->account,
      -(int64_t)(config::billable_size_v<permission_link_object>)
   );

   db.remove(*link);
}
auto link_key = boost::make_tuple(requirement.account, requirement.code, requirement.type);
auto link = db.find<permission_link_object, by_action_name>(link_key);

父级可以把自己已有的action执行权限link给子级,同一子级只能有一个权限有同一action执行权限,赋值给同一级其他的,会把原先的删除掉。如果子级有某个action执行权限,那父级也有。如果子级unlink了某一action执行权限,将从此级追溯到顶父级active或者owner,顶父级之前的此action执行权限都会删除掉(因为active和owner为特殊账户,不会被删除)。
因为account,code,type三个值加起来是查询索引,所以只有一条记录,记录着当前action执行权限link到哪个子权限了,并且子级有的话,父级就有。
新创建的权限,没link的话,没有任何action执行权限。