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

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

 

以太坊:EVM的存储结构

 

1. EVM的存储结构

1.1. VM内存

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

以太坊:EVM的存储结构

 

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一样,这里提供的堆栈就是用来存储字节码执行过程中的中间数据等。

以太坊:EVM的存储结构

 

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持久化存储

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

以太坊:EVM的存储结构

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数据库,并提供了批量提交和读、写缓存,并以此为基础提供了区块执行过程中的多级缓存、快照或者说事务提交。具体的讲解见另一篇文章。

分类:

技术点:

相关文章: