以太坊源码阅读01

正所谓区块链,怎能不熟悉区块的数据结构呢?区块的结构体被保存在core/types/block.go文件中,下面是我截取出来的:

type Block struct {header       *Headeruncles       []*Headertransactions Transactionswithdrawals  Withdrawals
​// cacheshash atomic.Valuesize atomic.Value
​// These fields are used by package eth to track// inter-peer block relay.ReceivedAt   time.TimeReceivedFrom interface{}
}

我之前主要使用C++语言,可以看出来上面的代码和C++语言还是有一定的差异的,C++结构体中先声明变量的类型,然后给出变量的名称,而上面的代码则是反过来了,先给出了成员变量的名称,然后给出了类型。下面是对各个字段的解释:

字段描述
header指向Header类型的指针,表示该区块的头部信息
uncles指向Header类型的数组,表示该区块的叔父区块
transactions区块所包含的交易列表
withdrawals区块中的提款列表
hash区块的哈希值
size区块的大小
ReceivedAt接收到该区块的时间
ReceivedFrom区块是被谁挖出来的

现在对于区块的内容已经有所了解了,再来看看区块头的数据结构:

type Header struct {ParentHash  common.Hash    `json:"parentHash"       gencodec:"required"`UncleHash   common.Hash    `json:"sha3Uncles"       gencodec:"required"`Coinbase    common.Address `json:"miner"`Root        common.Hash    `json:"stateRoot"        gencodec:"required"`TxHash      common.Hash    `json:"transactionsRoot" gencodec:"required"`ReceiptHash common.Hash    `json:"receiptsRoot"     gencodec:"required"`Bloom       Bloom          `json:"logsBloom"        gencodec:"required"`Difficulty  *big.Int       `json:"difficulty"       gencodec:"required"`Number      *big.Int       `json:"number"           gencodec:"required"`GasLimit    uint64         `json:"gasLimit"         gencodec:"required"`GasUsed     uint64         `json:"gasUsed"          gencodec:"required"`Time        uint64         `json:"timestamp"        gencodec:"required"`Extra       []byte         `json:"extraData"        gencodec:"required"`MixDigest   common.Hash    `json:"mixHash"`Nonce       BlockNonce     `json:"nonce"`
​// BaseFee was added by EIP-1559 and is ignored in legacy headers.BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
​// WithdrawalsHash was added by EIP-4895 and is ignored in legacy headers.WithdrawalsHash *common.Hash `json:"withdrawalsRoot" rlp:"optional"`
​// BlobGasUsed was added by EIP-4844 and is ignored in legacy headers.BlobGasUsed *uint64 `json:"blobGasUsed" rlp:"optional"`
​// ExcessBlobGas was added by EIP-4844 and is ignored in legacy headers.ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"`
​// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
}

下面是对于字段的解释:

字段描述
ParentHash父区块的哈希值
UncleHash叔父区块的哈希值
Coinbase挖矿成功的奖励地址
Root状态树的根哈希值
TxHash交易树的根哈希值
ReceiptHash收据树的根哈希值
Bloom布隆过滤器
Difficulty挖矿难度
Number区块的编号
GasLimit汽油的最大量限制
GasUsed汽油的使用量
Time区块的生成时间
Extra附加数据
MixDigest区块的混合哈希值
Nonce挖矿所需要不断尝试的随机数(4字节)
BaseFee基础手续费
WithdrawalsHash提款根的哈希值
BlobGasUsed新的燃气类型的使用量
ExcessBlobGas辅助识别处理异常情况,当恶意节点试图使用超额的新的燃气类型
ParentBeaconRoo跟踪对应的父分片,以太坊2.0引入了分片链的概念,每个分片都是相对独立的链,有一个对应的父分片,目的是提高网络的吞吐量和扩展性。

上面的字段中,最后五个字段是新提出来的,在传统的区块中会忽略对应的字段。接下来是区块体:

type Body struct {Transactions []*TransactionUncles       []*HeaderWithdrawals  []*Withdrawal `rlp:"optional"`
}
字段描述
Transactions保存当前区块的所有交易列表
Uncles保存当前区块的所有叔父区块
Withdrawals保存当前区块的所有提款列表
type headerMarshaling struct {Difficulty    *hexutil.BigNumber        *hexutil.BigGasLimit      hexutil.Uint64GasUsed       hexutil.Uint64Time          hexutil.Uint64Extra         hexutil.BytesBaseFee       *hexutil.BigHash          common.Hash `json:"hash"` // adds call to Hash() in MarshalJSONBlobGasUsed   *hexutil.Uint64ExcessBlobGas *hexutil.Uint64
}

如上面结构体所示,以太坊提供了一个将区块头编码的结构体,将区块头中的部分变量换成了十六进制。在以太坊中,还提供了类型覆盖结构体,用于进行编码和解码时指定特定字段的编码方式,以满足不同的需求或协议规范。这种方式允许开发者根据实际情况定制编码和解码的行为,使得数据的序列哈和反序列化更加灵活和可定制。

以太坊中的区块头包含了诸多重要的信息,例如区块号、难度、气体限制、时间戳等等。在不同的协议中,可能需要不同的方式对这些数据进行编码和解码,以满足不同的需求,因此,使用覆盖结构体可以灵活的指定特定字段的编码方式,使得以太坊在不同的环境中具有更强的适应性和可扩展性。

在文件中,还有行代码:

type BlockNonce [8]byte

在工作量证明中,用户打包交易后生成Merck树,然后将其根哈希值保存到区块头中,对整个区块头取哈希的到一个值,这个值需要满足足够小的条件,由于哈希正向容易反向难的特点,矿工很难反向计算得到Nonce值,因此会一个一个数字去尝试,由于不同的矿工打包的交易可能不一样,因此矿工需要找到的Nonce值也可能不一样。上面这行代码说明了Nonce是一个8字节64位的值。通过查询资料得知,由于全网算力的不断增大,64位的难度难以满足要求,因此将Coinbase字段的前4字节也作为取哈希的内容,这部分的内容可以用户自己设置,通常包含挖矿的奖励地址。

言归正传,先声明变量名,然后声明类型已经很奇怪了,后来发现还有更加怪异的,看下面的函数:

func (n BlockNonce) Uint64() uint64 {return binary.BigEndian.Uint64(n[:])
}

先不用管函数的意义,看看函数的声明是否很奇怪???通过查找资料发现:

func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {函数体
}

通过上网查询资料发现,接收者类型类似于C++中的this指针,而使用this指针时会修改实际的值,如果是传递一般的参数,则会拷贝一份副本,副本的改变不会导致实参的改变。接收者的声明规则如下:

  • 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。

  • 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。

  • 方法名、参数列表、返回参数:具体格式与函数定义相同。

在Go语言中,nil 是一个预定义的标识符,用于表示指针、切片、映射、通道、函数和接口类型的零值或空值。具体含义取决于它所用于的类型。

  • 对于指针类型,nil 表示一个空指针,即指针未指向任何有效的内存地址。

  • 对于切片、映射、通道、函数和接口类型,nil 表示这些数据结构是空的,即切片、映射、通道为空,函数和接口未指向任何具体的实现或值。

在条件语句中,可以使用 nil 来检查指针、切片、映射、通道、函数和接口是否为零值或空值。

下面是以太坊外部区块的结构体:

type extblock struct {Header      *HeaderTxs         []*TransactionUncles      []*HeaderWithdrawals []*Withdrawal `rlp:"optional"`
}
  • Header: 代表了区块的头部信息,包括区块的元数据,如区块号、时间戳、难度等。

  • Txs: 是一个包含了所有交易的列表,每个交易都是一个 Transaction 结构体的实例。

  • Uncles: 是一个包含了叔块(Uncle Block)的列表,每个叔块也是一个 Header 结构体的实例。

  • Withdrawals: 是一个包含了所有提现的列表,每个提现也是一个 Withdrawal 结构体的实例。

现在对于区块的相关结构体,我们已经有了一定的了解,那么来看看以太坊是如何产生新区块的。

func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher) *Block {        // 创建一个block对象,拷贝一个header对象来初始化block的header字段b := &Block{header: CopyHeader(header)}
​// 设置交易列表,当交易列表不为空时,根据交易列表计算出哈希值并赋值给TxHash字段并设置block的交易列表if len(txs) == 0 {b.header.TxHash = EmptyTxsHash} else {b.header.TxHash = DeriveSha(Transactions(txs), hasher)b.transactions = make(Transactions, len(txs))copy(b.transactions, txs)}// 设置收据树以及布隆过滤器if len(receipts) == 0 {b.header.ReceiptHash = EmptyReceiptsHash} else {b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)b.header.Bloom = CreateBloom(receipts)}// 设置叔节点if len(uncles) == 0 {b.header.UncleHash = EmptyUncleHash} else {b.header.UncleHash = CalcUncleHash(uncles)b.uncles = make([]*Header, len(uncles))for i := range uncles {b.uncles[i] = CopyHeader(uncles[i])}}
​return b
}
​
// 下面的函数允许区块指定额外的体现信息
func NewBlockWithWithdrawals(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, withdrawals []*Withdrawal, hasher TrieHasher) *Block {b := NewBlock(header, txs, uncles, receipts, hasher)
​if withdrawals == nil {b.header.WithdrawalsHash = nil} else if len(withdrawals) == 0 {b.header.WithdrawalsHash = &EmptyWithdrawalsHash} else {h := DeriveSha(Withdrawals(withdrawals), hasher)b.header.WithdrawalsHash = &h}
​return b.WithWithdrawals(withdrawals)
}
​
// 实现对区块头的深拷贝
func CopyHeader(h *Header) *Header {cpy := *hif cpy.Difficulty = new(big.Int); h.Difficulty != nil {cpy.Difficulty.Set(h.Difficulty)}if cpy.Number = new(big.Int); h.Number != nil {cpy.Number.Set(h.Number)}if h.BaseFee != nil {cpy.BaseFee = new(big.Int).Set(h.BaseFee)}if len(h.Extra) > 0 {cpy.Extra = make([]byte, len(h.Extra))copy(cpy.Extra, h.Extra)}if h.WithdrawalsHash != nil {cpy.WithdrawalsHash = new(common.Hash)*cpy.WithdrawalsHash = *h.WithdrawalsHash}if h.ExcessBlobGas != nil {cpy.ExcessBlobGas = new(uint64)*cpy.ExcessBlobGas = *h.ExcessBlobGas}if h.BlobGasUsed != nil {cpy.BlobGasUsed = new(uint64)*cpy.BlobGasUsed = *h.BlobGasUsed}if h.ParentBeaconRoot != nil {cpy.ParentBeaconRoot = new(common.Hash)*cpy.ParentBeaconRoot = *h.ParentBeaconRoot}return &cpy
}

下面是一些函数的作用:

函数功能
func EncodeNonce(i uint64) BlockNonce将输入的64位无符号整数转换成字节数组,转换成BlockNorce类型(大端序)
func (n BlockNonce) Uint64() uint64将BlockNonce按照大端序转换成无符号64位整数
func (n BlockNonce) MarshalText() ([]byte, error)将BlockNonce类型的值编码为带有0x前缀的十六进制字符串
func (n *BlockNonce) UnmarshalText(input []byte) error从带有0x前缀的十六进制字符串解码并填充BlockNonce类型的值
func (h *Header) Hash() common.Hash计算区块头的哈希值
func (h *Header) Size() common.StorageSize区块头所有内容所占内存大小
func (h *Header) SanityCheck() error判断区块头部字段是否在合理范围内
func (h *Header) EmptyBody() bool检查头部信息的主体部分是否是空
func (h *Header) EmptyReceipts() bool检查头部的收据是否为空
func (b *Block) DecodeRLP(s *rlp.Stream) error从流中解码区块数据
func (b *Block) EncodeRLP(w io.Writer) error将区块数据序列格式化RLP格式,并写入流中

此外还提供了一个更换区块区块头、更换区块体、更换交易列表等函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/811735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

干货分享|TensorFlow构建神经网络

MNIST数据集前面章节已经多次遇到过,这里直接引用,并使用TensorFlow构建神经网络模型进行训练。下面举例说明如何构建简单的神经网络并训练。 【例15-33】 TensorFlow构建神经网络训练MNIST数据集。 输入如下代码: # 构建简单模型&#xf…

蓝桥杯单片机超声波示例通常涉及使用超声波模块进行测距。下面是一个基于51单片机的超声波测距示例代码:

蓝桥杯单片机超声波示例通常涉及使用超声波模块进行测距。下面是一个基于51单片机的超声波测距示例代码&#xff1a; #include <reg52.h> #include <intrins.h>sbit Trig P1^0; // 定义超声波发送引脚 sbit Echo P1^1; // 定义超声波接收引脚unsigned int Tim…

进程线程的关系

举个例子 滑稽老师吃100只鸡 如何加快滑稽老师吃鸡的效率&#xff1f;&#xff1f; 有一个方案&#xff0c;搞两个房间&#xff0c;两个滑稽老师 一个滑稽吃50只鸡&#xff0c;速度一定会大幅度增加 多进程的方案 创建新的进程 就需要申请更多的资源&#xff08;房间和…

Python编写一个抽奖小程序,新手入门案例,简单易上手!

“ 本篇文章将以简明易懂的方式引导小白通过Python编写一个简单的抽奖小程序&#xff0c;无需太多的编程经验。通过本文&#xff0c;将学习如何使用Python内置的随机模块实现随机抽奖&#xff0c;以及如何利用列表等基本数据结构来管理和操作参与抽奖的人员名单。无论你是Pytho…

贪心算法:柠檬水找零

题目链接&#xff1a;860. 柠檬水找零 - 力扣&#xff08;LeetCode&#xff09; 收的钱只能是5、10、20美元&#xff0c;分类讨论&#xff1a;收5美元无需找零&#xff1b;收10美元找零5元&#xff1b;收20美元找零15美元。其中对于找零15美元的方案有两种&#xff0c;此处涉及…

mysqlySQL中启用慢查询日志并设置阈值

要在MySQL中启用慢查询日志并设置阈值&#xff0c;可以按照以下步骤进行&#xff1a; 编辑MySQL配置文件&#xff1a;打开MySQL的配置文件&#xff08;通常是my.cnf或my.ini&#xff09;&#xff0c;在[mysqld]部分添加或修改以下行来启用慢查询日志并设置阈值&#xff1a; s…

一种基于镜像指示位办法的RingBuffer实现,解决Mirror和2的幂个数限制

简介 在嵌入式开发中&#xff0c;经常有需要用到RingBuffer的概念&#xff0c;在RingBuffer中经常遇到一个Buffer满和Buffer空的判断的问题&#xff0c;一般的做法是留一个单位的buffer不用&#xff0c;这样做最省事&#xff0c;但是当RingBuffer单位是一个结构体时&#xff0…

设计模式-外观模式(Facade)

1. 概念 外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它提供了一个统一的接口&#xff0c;用于访问子系统中的一群接口。外观模式的主要目的是隐藏系统的复杂性&#xff0c;通过定义一个高层级的接口&#xff0c;使得子系统更容易被使用。…

房屋鉴定研究院报告系统

一、项目背景与意义 随着城市化进程的加速和房地产市场的蓬勃发展&#xff0c;房屋安全问题日益受到社会各界的广泛关注。房屋鉴定作为确保房屋安全的重要手段&#xff0c;对于保障人民群众生命财产安全、维护社会稳定具有重要意义。然而&#xff0c;传统的房屋鉴定方式存在诸…

webpack-loader的使用

引入css后执行打包命令 "build": "npx webpack --config wk.config.js"发现报错&#xff1a; webpack默认只能处理js其他的像css,图片都需要借助loader来处理 css-loader loader可以用于对模块的源代码进行转换&#xff0c;可以把css看成一个模块&…

AWS被误扣费了,怎么解决?

有时在使用aws时&#xff0c;可能会无意中被AWS扣费&#xff0c;对于如何处理这个问题&#xff0c;作为aws的合作伙伴&#xff0c;接下来由九河云进行讲解&#xff1a; &#xff08;1&#xff09;审查账单&#xff1a;首先&#xff0c;您需要仔细审查AWS账单&#xff0c;了解具…

并发学习27--多线程 Tomcat 线程池

Tomcat连接器的线程池 socketProcessor也是个线程 Executor处理线程是按照JDK线程池方法处理&#xff0c;优先选用核心线程&#xff0c;再用救急线程&#xff0c;再去阻塞队列&#xff0c;最后采用拒绝策略。 Tomcat线程池与ThreadExecutorPool的区别 Tomcat中的配置 Tomcat …

kafka快速入门+应用

Kafka, 构建TB级异步消息系统 1.快速入门 1.1 阻塞队列 在生产线程 和 消费线程 之间起到了 &#xff0c; 缓冲作用&#xff0c;即避免CPU 资源被浪费掉 BlockingQueue 解决 线程通信 的问题阻塞方法 put 、 take生产者、消费者 模式 生产者&#xff1a;产生数据的线程…

登录加载动画

实现登录中 … 三个点的loading动画 <template><div><el-input type"password" placeholder"请填写密码" autocomplete"new-password"v-model"password" keyup.enter.native"login" show-password clearable…

Word中图表题注样式自动编号

需求 在写论文的时候&#xff0c;希望图表题注是下面的样子&#xff0c;其中图号表示为&#xff1a;章的编号-本章中图的序号&#xff0c;而且都是小写数字。 网上找的方法大多是使用 “插入题注” 来插入&#xff0c;此时章的编号是大写的&#xff0c;如“图一-1”。然后再通…

后台权限控制及动态路由

需求 后台系统需要能实现不同的用户权限可以看到不同的功能。 用户只能使用他的权限所允许使用的功能。 功能设计 之前在我的SpringSecurity的课程中就介绍过RBAC权限模型。没有学习过的可以去看下 RBAC权限模型 。这里我们就是在RBAC权限模型的基础上去实现这个功能。 表分…

Web前端-HTML

黑马程序员JavaWeb开发教程 一、初识web前端 1、 标准也称为网页标准&#xff0c;由一系列的标准组成&#xff0c;大部分由W3C负责指定 2、 三个部分组成 HTML&#xff1a;负责网页的结构&#xff08;页面元素和内容&#xff09;CSS&#xff1a;负责网页的表现&#xff08;页…

STL--pair 数对

pair 数对&#x1f357; pair是一个模板类,使用时需要引用文件 #include <utility>//通用工具pair可将两个value处理为一个元素。C标准库内多处用到了这个结构。尤其容器 map、unordered_map和unordered_multimap就是使用pair来管理其内部元素(key_value),任何函数如果…

ppt技巧:如何将Word文档大纲中导入到幻灯片中?

在PowerPoint中&#xff0c;将Word文档的大纲导入到新的幻灯片是一种非常实用的技巧。以下是详细的步骤&#xff1a; 首先&#xff0c;需要打开PowerPoint软件并打开原始的幻灯片文件。 在PowerPoint的顶部【开始】菜单栏中&#xff0c;找到并点击“新建幻灯片”按钮&#xff0…

【力扣】142. 环形链表 II

142. 环形链表 II 题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&am…