• 以太坊:EVM的存储结构


    版权声明:本文系博主原创,未经博主许可,禁止转载。保留所有权利。

    引用网址:https://www.cnblogs.com/zhizaixingzou/p/10124209.html

     目录

    1. EVM的存储结构

    1.1. VM内存

    内存结构像堆栈一样,也提供了数据缓存的功能,但更作用的是提供了合约调用合约等过程中,子合约数据的临时存储。

    1)实现类

    public class Memory implements ProgramListenerAware

    2)结构

    private List<byte[]> chunks = new LinkedList<>();

    内存就是一个链表。

    链表的每个结点对应一个内存块,一个内存块1024字节,链块内存是可扩展的,扩展是以块为单位的。

    private int softSize;

    在内存中,有一个指针softSize,用于指示存储数据的末尾。

    3)外部接口

    public byte[] read(int address, int size) {

    从内存的address(块抹平了的字节偏移量,从0开始,如1025代表第1块第1个位置)开始读取size个字节的数据。

    address可以超过存储数据的末尾。

    读取时,如果被读数据超出了末尾指针的范围,则会以块为单位增加块数,也就是说读到的新增数据是0。而末尾指针则以字,即32字节为单位移动,最终移动到被读数据的末尾。

    public void write(int address, byte[] data, int dataSize, boolean limited)

    datadataSize字节写到address上。

    dataSize如果大于data长度,则最多写data长度个字节。

    limited表示是否根据末尾指针截断,如果不,即不限制,则扩展内存,写入dataSize个字节,否则写入从address到末尾的字节个数。

    public void extendAndWrite(int address, int allocSize, byte[] data) {

    扩展然后写入数据。

    public void extend(int address, int size) {

    扩展内存,扩展了后末尾指针就变化。

    public DataWord readWord(int address) {

    从指定位置读取32个字节的字。

    public byte readByte(int address) {

    从指定位置读取一个字节。

    public int size() {

    获取末尾指针的位置。

    public int internalSize() {

    获取内存以分配空间,即链表结点数乘以1024

    public List<byte[]> getChunks() {

    获取内存副本。

    public String toString() {

    以打印格式(含16进制和assii码形式)返回内存数据,softSize结尾。

    4)程序监听

    programListener.onMemoryExtend(toAllocate)

    programListener.onMemoryWrite(address, data, dataSize)

    在内存扩展(这里的扩展指应的字数)和写数据是会触发监听器。

    1.2. VM堆栈

    EVM的执行模型是基于栈结构的,像JVM一样,这里提供的堆栈就是用来存储字节码执行过程中的中间数据等。

    1)实现类

    public class Stack extends java.util.Stack<DataWord> implements ProgramListenerAware {

    2)结构

    直接继承自JavaStack

    protected Object[] elementData;

    只是itemDataWord,即32字节的字。

    可以看到这是一个定长数组,在需要扩展时会创建新数组,然后浅拷贝元素到新数组。

    以太坊栈深设置为1024

    3)外部接口

    public synchronized DataWord pop() {

    弹出数据。

    public DataWord push(DataWord item) {

    入栈数据,返回就是item

    public void swap(int from, int to) {

    交换两个位置的数据。

    4)程序监听

    programListener.onStackPop();

    programListener.onStackPush(item);

    programListener.onStackSwap(from, to);

    1.3. VM持久化存储

    持久化存储主要是独立于区块存储之外,存储以太坊的账户、合约代码以及合约的状态。

    1.3.1. 账户

    org.ethereum.core.AccountState

    可用来表示外部账户或合约账户。

    nonce、余额、状态根、代码哈希。

    private final BigInteger nonce

    对于外部账户,nonce表示从此账户发出的交易数量。

    对于合约账户,nonce表示此合约内创建的合约数量。

    设计nonce的目的是为了防止重放攻击,也就是防止一个交易被多次执行,因为每执行一个交易时,库中该交易发送者账户的nonce就会增加1

    默认值是从配置常量取出的0

    private final BigInteger balance

    账户的余额,以Wei为单位。

    默认为0

    private final byte[] stateRoot

    用于存储合约内容Trie结构的哈希根。

    默认是sha3(RLP.encodeElement(EMPTY_BYTE_ARRAY))

    private final byte[] codeHash;

    合约代码的哈希值。

    默认值是sha3(EMPTY_BYTE_ARRAY)

    提供了RLP编码和解码方法,以及编码结果缓存。

    只要代码哈希或nonce不是默认的,则认为合约是存在的。

    所谓空账户,是指代码哈希、nonce、余额都是默认的。空账户是需要被删除的。

    1.3.2. 数据源结构体系

    这里的Repository内部实现是比较复杂的,它封装了底层Key-Value数据库,并提供了批量提交和读、写缓存,并以此为基础提供了区块执行过程中的多级缓存、快照或者说事务提交。具体的讲解见另一篇文章。

  • 相关阅读:
    sql语句如何将多个空格字符替换成一个空格字符
    在 ServiceModel 客户端配置部分中,找不到引用协定“myservice.Service1Soap”的默认终结点元素。这可能是因为未找到应用程序的配置文件,或者是因为客户端元素中找不到与此协定匹配的终结点元素。
    sql语句查看表结构
    VxWorks 6.9 内核编程指导之读书笔记 -- VxWorks Small-Footprint Configuration
    C#学习笔记之线程
    C#学习笔记之线程
    C#学习笔记之线程
    C#学习笔记之线程
    C#学习笔记之线程安全
    WCF学习笔记 -- 如何用C#开发一个WebService
  • 原文地址:https://www.cnblogs.com/zhizaixingzou/p/10124209.html
Copyright © 2020-2023  润新知