Enclave-EVM 源码分析

1、Enclave
Enclave EVM(eEVM)是以太坊虚拟机的开源,独立,可嵌入的C实现。 它最初构建为在Open Enclave SDK之上的TEE(即SGX enclave)内运行,使用Microsoft的机密联盟区块链框架。
1.1 机密计算是一项持续的工作,旨在保护数据在其休息,运输和使用中的整个生命周期。 通过使用信任执行环境,客户可以构建应用程序,以便在使用时保护数据免受外部访问。 Open Enclave SDK是一个开源SDK,旨在为开发人员创建单一的统一enclave抽象,以构建基于可信执行环境(TEE)的应用程序。 随着TEE技术的成熟和不同的实现,Open Enclave SDK致力于支持API集,允许开发人员构建一次并部署在多个技术平台,从云到混合到边缘,以及Linux和Windows的不同环境。
1.2 安全区应用程序将自身划分为两个组件(1)不可信组件(称为主机)和(2)可信组件(称为安全区)。 主机组件在不受信任的操作系统上未经修改地运行,而受信任组件在安全区内运行,即由TEE实现提供的受保护容器。 这些保护允许enclave执行安全计算,并保证不会泄露机密。
1.3 核心原则
普遍:概括飞地应用程序模型以最小化硬件/软件特定概念
可插拔:组件化以支持所需的运行环境和加密库
标准化:删除硬件供应商特定的签名和验证要求
多平台:考虑到所有软件平台,Windows和Linux的设计
兼容:更轻松地启用可再发行的应用程序
打开:基于安全飞地的应用程序开发的开源和标准
1.4 支持的SDK功能
✔Enclave的创建和管理:函数调用来管理应用程序中enclave的生命周期
✔Enclave测量和身份:Enclave测量和身份的表达
✔Communication:定义调用和被调用以及与之相关的数据编组的机制
✔系统原语:由安全区运行时公开的系统原语,例如线程和内存管理
✔Sealing:支持秘密持久性的功能
✔Attestation:支持身份验证的功能
✔运行时和加密库:可插拔库,在enclave内提供必要的语言和加密支持
2、Microsoft的机密联盟区块链框架
在过去几年中,企业已经意识到区块链(比特币和以太坊等公共网络的技术)可以用来简化自己的业务流程。 然而,他们还发现大多数现有的区块链协议无法满足几个关键的企业要求 - 包括可接受的交易吞吐量和延迟,机密性,有效治理和计算效率(即采矿/工作证明的能源成本)。 努力适应现有的公共区块链协议或创建新协议以满足这些需求通常将一个所需的企业属性换成另一个 - 例如以更高的复杂性或更低的性能为代价来改善机密性。
Confidential Consortium Blockchain Framework是一个开源系统,可实现满足所有关键企业要求的高规模,机密区块链网络 - 提供加速生产企业采用区块链技术的手段。我们通过在机密联盟的背景下重新评估公共区块链协议的现有假设来实现这一目标,其中节点和参与者(包括投票成员和其他非投票参与者)被明确声明和控制。基于这一新的要求,该框架将现有区块链协议,可信执行环境,分布式系统和加密技术的强大功能汇集在一起
企业就绪的区块链网络,提供:
•吞吐量和延迟接近数据库速度。
•更丰富,更灵活,针对特定业务的机密性模型。
•通过分布式治理实现网络策略管理
•支持非确定性交易。
•降低能耗。
值得注意的是,该框架不是一个独立的区块链协议;相反,它提供了一个可信赖的基础,通过它可以集成现有的区块链协议,如以太坊,Quorum,Corda或Hyperledger Sawtooth,以提供完整的企业级分类帐解决方案。
Confidential Consortium Blockchain Framework旨在开放并与任何区块链协议兼容。
本文重新审视了机密联盟背景下区块链网络的要求。然后介绍了Confidential Consortium Blockchain Framework,包括网络拓扑和系统体系结构,网络创建和治理,事务流和体系结构变体。它还研究了如何实现可伸缩性、机密性和分布式治理,以及安全考虑因素以及如何减轻安全风险。最后,讨论了框架的当前工作实现,包括初始性能结果。
3、eEVm
主要入口点是evm :: Processor :: run()。需要为evm :: Processor提供evm :: GlobalState的实现来处理与永久状态的所有交互。源包括evm :: SimpleGlobalState作为std :: map支持的示例,但其他实例可能需要一个提供永久存储的实现 - 可能是从以太坊区块链读/写数据的包装器。eEVM支持所有来自Ethereum的Homestead版本的操作码,如opcode.h中所列。不包括更新的操作码,例如来自EIP#211的RETURNDATACOPY或RETURNDATASIZE。该实施忽略了所有的gas成本 - 在执行过程中不会消耗、跟踪或更新天然气,执行将永远不会引发gas异常。但是,如果字节码计算或验证气体预算本身,可能仍需要将合理的初始gas值传递给evm :: Processor :: run()。
3.1 evm::Processor::run()
ExecResult Processor::run(
Transaction& tx,
const Address& caller,
AccountState callee,
const vector& input,
uint64_t call_value,
Trace* tr)
{
return _Processor(gs, tx, tr).run(caller, callee, input, call_value);
}
3.2 Transaction 类
struct Transaction
{
const Address origin; //地址
const uint64_t value; //值
const uint64_t gas_price; //gas 价格
const uint64_t gas_limit; //gas限制

LogHandler& log_handler;   //log处理器
std::vector
destroy_list; // 地址vector Transaction( const Address origin, // 原始地址 LogHandler& lh, // log处理器 uint64_t value = 0, //值 uint64_t gas_price = 0, //gas 价格 uint64_t gas_limit = 0) : //gas限制 origin(origin), value(value), gas_price(gas_price), gas_limit(gas_limit), log_handler(lh) {}

};
3.3 Address类:uint256
3.4 AccountState类:
账户状态:账户和存储
struct AccountState
{
Account& acc;
Storage& st;
template <
typename T,
typename = std::enable_if_t::value>>
AccountState(std::pair& p) : acc(p.first), st(p.second)
{}
AccountState(Account& acc, Storage& st) : acc(acc), st(st) {}
};
3.5 Account账户类
struct Account
{
Address address; //地址
uint64_t nonce; // 防止重放攻击
uint256_t balance; //余额
Code code;

Account() = default;
Account(const Address& address, const uint256_t& balance, Code code) :
  address(address),
  nonce(0),
  balance(balance),
  code(code)
{}

};
3.6 storage 类:
struct Storage
{
//存储key-value
virtual void store(uint256_t key, uint256_t value) = 0;
//加载key
virtual uint256_t load(const uint256_t& key) = 0;
//是否存在key
virtual bool exists(const uint256_t& key) = 0;
//根据key移除
virtual bool remove(const uint256_t& key) = 0;
//判断两个存储是否相等
virtual bool operator==(const Storage&) const = 0;
virtual ~Storage() {}
};
3.7 Trace跟踪类:
//智能合约的运行跟踪
struct Trace
{
std::vector events;

template 
TraceEvent& add(Args&&... args)
{
  events.emplace_back(std::forward(args)...);
  auto& e = events.back();
  return e;
}

void reset()
{
  events.clear();
}

void print_last_n(std::ostream& os, size_t n) const
{
  auto first = n < events.size() ? events.size() - n : 0;
  for (auto i = first; i < events.size(); ++i)
    os << events[i] << std::endl;
  os << std::endl;
}

};
3.8 TraceEvent 跟踪事件类:
struct TraceEvent
{
const uint64_t pc; //pc 计数
const Opcode op; //操作码
const uint16_t call_depth; //调用深度
std::unique_ptr s; //堆栈

TraceEvent(
  const uint64_t pc,
  const Opcode op,
  const uint16_t call_depth,
  const Stack s) :
  pc(pc),
  op(op),
  call_depth(call_depth),
  s(std::make_unique(s))
{}

};
3.9 GlobalState全局状态类:
//与EVM世界状态交互的抽象接口
struct GlobalState
{
//根据地址判断是否存在全局状态
virtual bool exists(const Address& addr) = 0;
//根据地址删除全局状态
virtual void remove(const Address& addr)
/*如果不存在,在给定地址下创建一个新的0初始化账户/
//根据地址取得账户状态
virtual AccountState get(const Address& addr) = 0;
//根据地址、余额、code创建账户状态
virtual AccountState create(
const Address& addr, uint256_t balance, Code code) = 0;

virtual size_t num_accounts() = 0;
//返回当前的block
virtual const Block& get_current_block() = 0;
//返回块的哈希值
virtual uint256_t get_block_hash(uint64_t idx) = 0;

};
3.10 ExecResult 合约执行结果类:
struct ExecResult
{
ExitReason er; //退出原因
Exception::Type ex; //异常类型
std::string exmsg; //执行信息
std::vector output; //运行结果
};
enum class ExitReason : uint8_t
{
returned = 0, //返回
halted, //终止
threw //抛出异常
};
3.11 推送上下文
void push_context(
const Address& caller, //调用者地址
AccountState as, //账户状态
vector&& input, //输入值
Program&& prog, //
uint64_t call_value,
Context::ReturnHandler&& rh,
Context::HaltHandler&& hh,
Context::ExceptionHandler&& eh)
3.12 字节码程序
class Program
{
public:
const vector& code;
const set jump_dests;

Program(const vector& code) :
  code(code),
  jump_dests(compute_jump_dests(code))
{}

3.13 推送上下文构造函数
private:
void push_context(
const Address& caller,
AccountState as,
vector&& input,
Program&& prog,
uint64_t call_value,
Context::ReturnHandler&& rh,
Context::HaltHandler&& hh,
Context::ExceptionHandler&& eh)
{
if (get_call_depth() >= Consts::MAX_CALL_DEPTH) //如果调用深度>=最大调用深度,返回异常
throw Exception(
ET::outOfBounds,
“Reached max call depth (” + to_string(Consts::MAX_CALL_DEPTH) + “)”);
//产生唯一上下文
auto c = make_unique(
caller,
as,
move(input),
call_value,
move(prog),
move(rh),
move(hh),
move(eh));
ctxts.emplace_back(move©); //向ctxts添加元素
ctxt = ctxts.back().get(); // 返回当前vector容器中末尾元素的引用对应的值
}
3.14 处理类
class _Processor
{
private:
/// 全局状态接口
GlobalState& gs;
/// 交易
Transaction& tx;
/// 指向跟踪器的指针(用于debug)
Trace* const tr;
///上下文的堆栈 (每个嵌套调用)
vector ctxts;
///指向当前上下文的指针
Context* ctxt;

using ET = Exception::Type;

public:
_Processor(GlobalState& gs, Transaction& tx, Trace* tr) :
gs(gs),
tx(tx),
tr(tr)
{}
3.15 上下文
Context(
const Address& caller, //调用者的地址
AccountState as, //账户状态
vector&& input, //输入值数组
uint64_t call_value,//调用值
Program&& prog,//程序prog
ReturnHandler&& rh, //返回处理器
HaltHandler&& hh, //暂停处理器
ExceptionHandler&& eh) ?/异常处理器
as(as),
acc(as.acc),
st(as.st),
caller(caller),
input(input),
call_value(call_value),
prog(prog),
rh(rh),
hh(hh),
eh(eh)
{}
3.15 dispatch调度程序
void dispatch()
{
//获取操作码
const auto op = get_op();
if (tr) // 来自关键路径则移除
tr->add(ctxt->get_pc(), op, get_call_depth(), ctxt->s);

  switch (op)
  {
    case Opcode::PUSH1:
    case Opcode::PUSH2:
              push();
            break;
    case Opcode::POP:
      		pop();
      		break;
   case Opcode::SWAP16:
      		swap();
     		break;
    case Opcode::STOP:
      stop();
      break;
    default:
      stringstream err; //字符串流err
      //未知/不支持的操作码
      err << "unknown/unsupported Opcode: 0x" << hex << int{get_op()}
          << endl;
      // 看位置:pc   账户状态的账户地址  调用深度 调用者
      err << dec << " seen at position " << ctxt->get_pc() << " in "
          << to_hex_str(ctxt->as.acc.address) << ", at call-depth "
          << get_call_depth() << " called by " << to_hex_str(ctxt->caller);
      //抛出异常:非法指令,错误字符串
      throw Exception(Exception::Type::illegalInstruction, err.str());
  };
}

3.16 run运行过程:
Processor(gs, tx, tr).run(caller, callee, input, call_value)
//run 过程
ExecResult run(
const Address& caller, //调用者地址
AccountState callee, //被调用者账户状态
vector input, // Take a copy here, then move it into context
uint64_t call_value) //调用值
{
// 创建第一个上下文
ExecResult result; //执行结果
//退出原因、执行结果
auto rh = [&result](vector output
) {
result.er = ExitReason::returned;
result.output = move(output_);
};
//返回退出原因: 挂掉
auto hh = &result { result.er = ExitReason::halted; };
//根据异常返回结果,包括类型、执行信息、退出原因
auto eh = [&result](const Exception& ex_) {
result.er = ExitReason::threw;
result.ex = ex_.type;
result.exmsg = ex_.what();
};
// 推送上下文:调用者、被调用者、
push_context(
caller, callee, move(input), callee.acc.code, call_value, rh, hh, eh);
/**
当上下文的pc值大于当前上下文程序code的大小时,执行调度
*/
while (ctxt->get_pc() < ctxt->prog.code.size())
{
try
{
dispatch();
}
catch (Exception& ex)
{
//异常处理
ctxt->eh(ex);
//上下文出栈
pop_context();
}
//如果不是上下文,跳出循环
if (!ctxt)
break;
//如果pc值没有改变,增加pc值
ctxt->step();
}

  // 如果本身不这么做,则停止外部合约
  if (ctxt)
    stop();
  // 清理工作:从全局状态中 清除交易的销毁清单的地址
  for (const auto& addr : tx.destroy_list)
    gs.remove(addr);
  return result;
}

3.17 拷贝内存

   void copy_mem(
     vector& dst, const vector& src, const uint8_t pad)
   {
     const auto offDst = ctxt->s.pop64();
     const auto offSrc = ctxt->s.pop64();
     const auto size = ctxt->s.pop64();
     copy_mem_raw(offDst, offSrc, size, dst, src, pad);
   }
   // 原生的拷贝内存
   static void copy_mem_raw(
     const uint64_t offDst, //结束位置
     const uint64_t offSrc, 
     const uint64_t size, //大小
     vector& dst,
     const vector& src,
     const uint8_t pad = 0) 
   {
     if (!size)
       return;
   //最后一个地址下标
     const auto lastDst = offDst + size;
     //整数移除
     if (lastDst < offDst)
       throw Exception(
         ET::outOfBounds,
         "Integer overflow in copy_mem (" + to_string(lastDst) + " < " +
           to_string(offDst) + ")");
     //内存超过限制
     if (lastDst > Consts::MAX_MEM_SIZE)
       throw Exception(
         ET::outOfBounds,
         "Memory limit exceeded (" + to_string(lastDst) + " > " +
           to_string(Consts::MAX_MEM_SIZE) + ")");
     //大小超过预先设定大小,则再次分配一定量的空间
     if (lastDst > dst.size())
       dst.resize(lastDst);
   //源码的最后位置
     const auto lastSrc = offSrc + size;
   //源码的结束位置=最后位置和src大小两者中的最小值
     const auto endSrc =
       min(lastSrc, static_cast(src.size()));
     uint64_t remaining;
     //源码的结束位置 > 源码的离开位置
     if (endSrc > offSrc)
     {
     //拷贝src的开始到离开位置、src的开始到结束位置到目标范围的开始到结束位置
       copy(src.begin() + offSrc, src.begin() + endSrc, dst.begin() + offDst);
       //其余=最后的src-结束的src
       remaining = lastSrc - endSrc;
     }
     // //源码的结束位置 <= 源码的离开位置
     else
     {
       remaining = size;
     }
     //如果要复制的字节数多于可用字节数,则添加填充.
     //dst开始+结束-保持,目标地址的开始+结束填充到pad
     fill(dst.begin() + lastDst - remaining, dst.begin() + lastDst, pad);
   }
   ```
3.18  日志
  void log()
        {
          const uint8_t n = get_op() - LOG0;
          const auto offset = ctxt->s.pop64();
          const auto size = ctxt->s.pop64();
          vector topics(n);
          for (int i = 0; i < n; i++)
            topics[i] = ctxt->s.pop();
            // Log处理器的处理
          tx.log_handler.handle(
            {ctxt->acc.address, copy_from_mem(offset, size), topics});
        }
       //从内存中拷贝
        vector copy_from_mem(const uint64_t offset, const uint64_t size)
        {
          prepare_mem_access(offset, size);
          return {ctxt->mem.begin() + offset, ctxt->mem.begin() + offset + size};
        }
       //准备内存读取:离开位置,大小     
      void prepare_mem_access(const uint64_t offset, const uint64_t size)
        {
        //结束= 离开位置+大小
          const auto end = offset + size;
          //结束小于离开位置,抛出超出边界异常。内存读取的整数溢出
          if (end < offset)
            throw Exception(
              ET::outOfBounds,
              "Integer overflow in memory access (" + to_string(end) + " < " +
                to_string(offset) + ")");
    	//结束大于上下文最大内存大小,抛出边界异常。超过内存限制
          if (end > Consts::MAX_MEM_SIZE)
            throw Exception(
              ET::outOfBounds,
              "Memory limit exceeded (" + to_string(end) + " > " +
                to_string(Consts::MAX_MEM_SIZE) + ")")
         //如果结束位置大于内存大小,则重新分配内存
          if (end > ctxt->mem.size())
            ctxt->mem.resize(end);
        }

你可能感兴趣的:(Ethereum)