EVM虚拟机入门汇编入门(二)

系列文章目录

EVM虚拟机入门汇编入门(一)


文章目录

  • 系列文章目录
  • 前言
  • 一, TIMESTAMP
    • 1. TIMESTAMP是什么?
    • 2.TIMESTAMP作用
  • 二, KECCAK256
    • 1. KECCAK256是什么?
    • 2. KECCAK256举例?
  • 三 ,Mapping底层结构


前言

介绍时间戳以及sha指令,并详细解释mapping的底层结构

一, TIMESTAMP

1. TIMESTAMP是什么?

操作码TIMESTAMP是EVM中的一个操作码,用于获取当前区块的时间戳。以下是关于TIMESTAMP操作码的详细信息:

  • 功能TIMESTAMP操作码返回当前区块的Unix时间戳,即自1970年1月1日以来的秒数。

  • 历史:自以太坊创世区块(Frontier)以来,TIMESTAMP一直是EVM的一部分。

  • 栈操作

    • 输入:无输入参数。
    • 输出:栈顶(Index 1)将放置当前区块的Unix时间戳。
  • 示例

    • 如果当前区块的时间戳是1636704767,执行TIMESTAMP操作码后,栈上将输出1636704767
  • 错误情况

    • 如果执行TIMESTAMP操作码时gas不足,当前上下文所做的状态更改将被回滚。
    • 如果栈溢出(即栈大小超过了其最大容量),当前上下文的状态更改也会被回滚。
  • gas消耗:通常情况下,读取区块信息的操作码(如TIMESTAMP)的gas消耗较低,但具体的gas成本取决于以太坊网络的当前gas定价策略。

TIMESTAMP操作码在智能合约中非常有用,尤其是在需要根据当前时间执行特定逻辑时。例如,它可以用来实现时间锁定功能,确保某些操作只能在特定时间后执行。然而,开发者在使用TIMESTAMP时应该考虑到时间戳可能受到矿工时间设置的影响,因此可能存在一定的偏差。

2.TIMESTAMP作用

TIMESTAMP操作码返回的Unix时间戳是相对于1970年1月1日(UTC)的秒数,这是计算机编程中常用的时间表示方式。在以太坊中,这个时间戳是通过以下方式确定的:

  1. 区块时间戳:每个以太坊区块都包含了一个时间戳,这个时间戳是由矿工在创建区块时设置的。它代表了矿工认为的区块创建时的UTC时间。

  2. 网络时间:以太坊网络没有中心化的时间源,因此区块时间戳依赖于矿工的本地时间。虽然矿工通常会设置一个接近实际时间的时间戳,但这个时间戳可能受到矿工本地时间设置的影响,或者在某些情况下,矿工可能会故意设置一个不同的时间戳。

  3. 不可篡改性:一旦区块被加入到区块链中,它的所有信息,包括时间戳,就变得不可篡改。这意味着即使时间戳与实际时间有偏差,它也会被永久记录在区块链上。

  4. 智能合约使用:智能合约可以使用TIMESTAMP操作码来获取当前区块的时间戳,并根据这个时间戳来执行合约中的逻辑。例如,合约可以设置一个条件,只有在某个特定的时间戳之后才能执行某些操作。

  5. 偏差和限制:开发者在使用TIMESTAMP操作码时应该意识到,时间戳可能会有偏差,并且不能保证与实际时间完全一致。此外,时间戳的精度通常受限于区块的生成时间,这可能在几秒到几分钟不等。

总的来说,TIMESTAMP操作码提供了一种在智能合约中使用时间的方法,但它依赖于区块的时间戳,这个时间戳是由矿工设置的,并且一旦设置就无法更改。因此,虽然它可以用来实现基于时间的逻辑,但开发者应该考虑到可能存在的偏差和限制。

二, KECCAK256

1. KECCAK256是什么?

KECCAK256是EVM中的一个操作码,用于计算内存中给定数据的Keccak-256哈希值。以下是关于KECCAK256操作码的一些关键信息:

  • 功能:KECCAK256操作码接收两个输入参数,分别是内存中的字节偏移量(offset)和要哈希的字节大小(size),然后输出这个数据的Keccak-256哈希值。

  • 输入

    • offset:内存中的起始位置,从这个位置开始读取数据。
    • size:要读取并哈希的数据大小。
  • 输出hash,内存中指定数据的Keccak-256哈希值。

  • 示例

    • 如果内存地址0xFFFFFFFF处有一个值,输入1 0作为offsetsize,KECCAK256操作码将会计算从内存偏移量1开始的0个字节的哈希值,输出将是该数据的哈希值。
  • 错误情况

    • 如果gas不足,或者栈上没有足够的值,当前上下文所做的状态更改将被回滚。
  • gas消耗

    • static_gas:固定消耗30gas。
    • dynamic_gas:动态消耗,由6 * minimum_word_size + memory_expansion_cost组成,其中minimum_word_size是向上取整到最近的32字节倍数的size
  • 内存扩展成本:如果操作导致内存大小增加,还需要考虑内存扩展的成本。

  • 估算gas成本

    • 输入示例中,offset0size也为0,当前内存大小为0,那么静态gas加上动态gas的总和是30

在智能合约中使用KECCAK256操作码时,开发者需要确保提供足够的gas来覆盖操作的gas成本,并且要注意内存的使用情况,以避免栈溢出或其他错误。此外,由于KECCAK256操作码会消耗较多的gas,因此它通常用于关键的哈希计算,而不是频繁的计算。

2. KECCAK256举例?

// Put the required value in memory
PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000
PUSH1 0
MSTORE

// Call the opcode
PUSH1 4
PUSH1 0
KECCAK256

这段代码是一系列以太坊虚拟机(EVM)的操作码,用于将一个32字节的值放入内存,然后计算这个值的Keccak-256哈希。下面是对这些操作的详细解释:

PUSH32 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000:
    这个操作码将32字节的十六进制值0xFFFFFFFF00000000000000000000000000000000000000000000000000000000000压入栈。PUSH32表示接下来的32字节将作为数据压入栈。

PUSH1 0:
    这个操作码将值0压入栈。PUSH1表示接下来的1字节将作为数据压入栈。

MSTORE:
    MSTORE操作码将栈顶的两个元素(在这个例子中是0和上面PUSH32操作压入的值)存储到内存中。第一个元素(0)作为内存的偏移量,第二个元素是要存储的值。因此,这将把0xFFFFFFFF...这个值存储到内存的起始位置(偏移量0)。

PUSH1 4:
    将值4压入栈。这个值将作为KECCAK256操作码的size参数,表示要从内存中读取4个字节的数据来计算哈希。

PUSH1 0:
    将值0压入栈。这个值将作为KECCAK256操作码的offset参数,表示要从内存的偏移量0开始读取数据。

KECCAK256:
    KECCAK256操作码计算内存中从偏移量0开始的4个字节的数据的Keccak-256哈希,并把结果哈希值压入栈。

在这个例子中,由于我们存储的是一个32字节的值,但只计算了前4个字节的哈希,所以最终的哈希值将只反映这4个字节0xffffffff的数据。
输出如下

29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238

那么这个生成可以用来,作为mapping储存的键。

三 ,Mapping底层结构

在以太坊智能合约中,mapping是一种用于存储键值对的数据结构,其中键可以是任何类型的数据(除了映射和动态数组)。然而,由于存储(storage)只能存储具有固定大小(256位)的数据,因此不能直接将映射的键作为存储的键使用。为了解决这个问题,以太坊使用SHA3哈希函数来生成映射键的唯一标识符,这个过程通常称为“slot计算”。

以下是详细解释:

  1. 映射键的哈希:首先,需要对映射的键进行SHA3哈希处理。例如,如果映射的键是一个地址或者一个字节串,那么直接对这个键进行SHA3哈希。

  2. 位置信息的结合:在某些情况下,例如映射用作数组的一部分,可能还需要结合位置信息(例如数组索引)。这个位置信息也需要被编码并结合到映射键中,以确保每个元素的slot是唯一的。

  3. 生成slot:将映射键的哈希值与位置信息结合后,再次进行SHA3哈希处理,得到的哈希值将用作存储中的slot。这个slot是一个256位的数值,它在存储中是唯一的,对应于映射中的一个特定元素。

  4. 存储值:使用计算得到的slot,就可以在存储中设置或检索映射元素的值了。例如,如果你想设置映射中的一个值,你可以使用SSTORE操作码:

push2 0x1234
push1 0x27
push0
mstore
PUSH1 0x2
PUSH0
KECCAK256
sstore

这里是将key也就是0x27储存在memory的0偏移,然后将其取出0x27的部分sha,作为solt的key,储存0x1234作为value

  1. 检索值:当需要检索映射中的值时,可以使用相同的过程计算slot,然后使用SLOAD操作码来读取存储中的值。

  2. 碰撞避免:由于SHA3哈希函数的属性,即使两个不同的键和位置信息产生了相同的哈希值,再次哈希也会得到不同的结果,这确保了映射的每个元素在存储中都有一个唯一的slot,从而避免了碰撞。

  3. 内存和存储:在实际的Solidity编程中,通常会使用内置的mapping语法,编译器会自动处理这些哈希和存储的操作。开发者通常不需要手动进行这些计算。

通过这种方式,以太坊智能合约能够高效地使用存储空间,同时保证了映射类型数据的灵活性和安全性。

你可能感兴趣的:(区块链一些,汇编,区块链,智能合约)