以太坊基本原理包括:以太坊账户、以太坊虚拟机、钥匙文件、交易、Gas、以太坊区块、以太币
1.以太坊基本原理 1.1 以太坊简介以太坊是一个开源的有智能合约功能的公共区块链平台,它提供了一个虚拟机来处理点对点的合约。以太坊概念最早是由Vitalik Buterin在2013年到2014年被提出的,2014年通过ICO众筹开始得以发展
以太坊的本质就是一个状态机(读取输入并且输出)
相比比特币的10分钟产生一个区块,以太坊每产生一个区块大约是15s,以太坊把30000个区块当做一个纪元,每个纪元的耗时为125h,这个也是通过难度调整算法来实现的
GHOST协议
以太坊区块产生的时间区间是15s,在此期间,如果不同节点都打包好了区块,那此时无法确定应该纳入哪个节点的区块,接着让接节点比赛谁先打包好下一个区块,谁先挖出谁获胜,但是以此类推,算力最强的节点将会控制整个链,因为在更长的时间内,它一定能够产生更长的链,算力小的节点的打包区块将不会被算力强的节点采用
为了解决上述问题,以太坊使用了GHOST协议
1.每个区块必须有一个父块,有0个或者多个叔块
2.叔块必须是当前区块的k代祖先的直系区块,2 <=k <=7
3.叔块不能是当前区块的祖先
4.叔块必须有合法的区块头,但是可以不经过验证,甚至不是合法的区块
5.叔块不能被重复包括
挖出一个叔块的奖励
(uncle.numer + 8 - header.number)/8*blockReward
挖出一个叔块最少可获得1/4*blockReward
引用n个叔块的奖励
n*1/32*blockReward
最多可以引用2个,也就是说可以拿到1/16分之一的奖励
打包一个有效区块的奖励
Gas费用 + 引用n个叔块的奖励 + 静态奖励
总结:GHOST协议本质上是促使挖到叔块的节点尽快把挖到的节点交出去让主链节点引用,并且接着最新的区块挖,这样在利益最大化的驱使下,节点会尽可能的在一条链上打包区块
1.2 以太坊账户以太坊有两种账户类型:合约账户和外部账户
外部账户功能上和比特币的账户模型是一样的:发送交易,查看余额等,但是产生的机制不同:具体而言就是:私钥-公钥-地址-取最后的20位
合约账户:合约账户在合约部署时生成,用来收取gas费用,调用者需要支付一定的Gas费来执行,这些交易费用到时候会奖励给产生区块的节点
账户状态
type Account struct {
Nonce uint64
Balance *big.Int
Root []byte
CodeHash []byte
}
nonce:代表外部账户的交易数量,代表合约账户创建的合约序号
balance:余额(单位wei)
storageRoot:Merkle Partricia树的根节点hash值
codeHash:外部账户是空字符串的哈希值,合约账户是EVM代码的哈希值(合约代码的哈希值)
每个区块都有一个header,header里包含三个树,状态树,交易树,收据树
节点类型:全节点和轻节点,下载全节点数据量比较大,截止此刻,区块高度是14628886,轻节点只包含区块头
1.3 Gas和费用以太坊中各种 *** 作都需要支付gas,如存储数据、创建合约以及执行哈希计算等
gas = gasPrice * gasLimit: *** 作发起方在某次 *** 作中愿意支付的最高手续费
gasPrice通常等于1gwei = 1亿分之一ETH(1ETH = 一亿亿分之一wei),当网络中的算力下降时,gasPrice就会上升,以吸引更多的节点参与区块打包。另外,gasPrice会直接影响成交速度
gasLimit:某次 *** 作中gas消耗上限
此机制主要是防止由未知错误带来的gas浪费,需要尽可能的减少损失。如某次合约调用过程中,未知错误导致了循环,导致合约一直执行,这将会消耗很多gas,设置gasLimit可以减少这种损失
以太坊中对于常见 *** 作需要的gas的规定
交易基本费用:500gas
创建合约:53000gas
交易每个字节:5gas
费用机制的核心在于维护网络的安全及繁荣
1.4 交易和消息两种交易类型:交易和消息
交易由外部账户发起,指向另一个账户发送存储消息的签名数据包(此处可理解为转账)
消息是内部合约发起的,本质上还是一个交易
交易和消息都包含:
nonce:此处的nonce和账户中的nonce(该账户已完成的交易数量)不同,它是一个累加整数,可选指定,它需要大于账户中的nonce值,会影响交易的被打包进区块的时间。例如,账户中的nonce是2,发送交易时,指定其为4,那此笔交易不会被马上打包进区块,而是会被放入交易池等待,直到缺失的nonce被提交到交易池中。在实际转账时可以通过提交相同nonce的交易来覆盖之前的那一笔
gasPrice:一般钱包会计算出一个均值
gasLimit:
to: 收款方地址
value: 转账金额
v,r,s:数据签名,验证交易的合法性
init:初始化新合约账户的EVM代码片段
data :对于外部账户交易来说,是可选项。对创建合约而言是编译后的合约字节码,对于合约方法调用来说,是合约方法签名和参数编码
合约与合约之间可以相互调用执行,并不需要Gas费用
有效交易
交易必须是正确格式化的RLP
签名有效
交易序号有效
gas limit 大于等于交易使用的intrinsic gas
1.5 区块包含区块头,交易信息以及其他信息
type Block struct {
header *Header
uncles []*Header
transactions Transactions
// caches
hash atomic.Value
size atomic.Value
// Td is used by package core to store the total difficulty
// of the chain up to and including the block.
td *big.Int
// These fields are used by package eth to track
// inter-peer block relay.
ReceivedAt time.Time
ReceivedFrom interface{}
}
区块头,区块头中包含了三棵树:状态树、交易树和收据树
// Header represents a block header in the Ethereum blockchain.
type Header struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"`
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"`
/*
TODO (MariusVanDerWijden) Add this field once needed
// Random was added during the merge and contains the BeaconState randomness
Random common.Hash `json:"random" rlp:"optional"`
*/
}
日志
日志可以跟踪各种交易和信息
合约可以通过定义事件来显示生成的日志
日志实体包含:
记录器的账户地址
代表本次交易的执行的各种事件的一些主题以及与这些事件相关的任何数据
日志被被保存在bloom过滤器中,过滤器能够高效的保存无尽的日志数据
交易收据
包括:区块号、区块哈希、交易哈希、当前交易使用了的gas、执行完当前交易之后该区块累计使用的gas,当前交易日志
区块难度
nonce <= 2的256次方 / 区块难度
区块难度算法
/*
Algorithm
block_diff = pdiff + pdiff / 2048 * (1 if time - ptime < 13 else -1) + int(2^((num // 100000) - 2))
Where:
- pdiff = parent.difficulty
- ptime = parent.time
- time = block.timestamp
- num = block.number
*/
1.6 执行模式
EVM:以太发虚拟机
是图灵完备的虚拟机
基于堆栈的架构
每个对栈顶的大小为256位,堆栈有一个最大的大小,为1024位
有一个存储器
有专用语言:”EVM字节码”
1.7 以太坊网络对于分布式的DApp来说,网络模式是P2P方式。以太坊也是一个分布式的系统,使用了分布式哈希表拓扑结构,具体的实现是类Kademlia算法
节点与节点之间需要进行数据交互
KAD算法与节点发现
以太坊中定义了节点发现协议,节点使用此协议标准来发现彼此
KAD算法
distance(A,B) = keccak256(NODEIDA) XOR keccak256(NODEIDB)
每一个节点都按照距离维护着一张路由表,路由表中维护着256个bucket,每个桶存放着k个Node(k=16,指定距离范围最多能存放16个节点,此举可以增加整个网络的稳定性),每个Node用NODEID作为唯一标识符
type Node struct {//节点的类似实现
NODEID int
IP string
Port string
}
任意一个节点B与当前节点A的公式
distance(A,B) = keccak256(NODEIDA) XOR keccak256(NODEIDB)
当前节点维护的路由表中的距离区间:
[2 ^ i, 2 ^(i+1))
注意:是左开右闭区间
[ bucket0 ] : [1]
[ bucket1 ] :[2,3]
[ bucket2 ] : [4,5,6,7]
[ bucket i ] : [2^i : 2 ^ (i+1))
[ bucket i+1 ] : [...]
[ bucket ... ] : [...]
[ bucket255 ] : [2^255: 2^256)
节点启动时怎么初始化路由表,以太坊在源码中包含了一些节点,当节点启动时,自动先同步这些节点的信息完成自己的路由表构造
1.8 Ethash算法以太坊区块的打包也是同比特币一样使用的是工作量证明的机制,但是过程有所区别,大致伪代码过程如下
比特币:hash(nonce + preBlockHash + … )< target
以太坊:hash (nonce + preBlockHash + DAG… )< target
解释:以太坊在计算满足条件的哈希值时,使用了nonce 、前一个区块的哈希以及dataset中的数据
datasset其实是一个DAG,并且DAG数据每3000个区块生成一次,并且只依赖区块号
DAG生成过程:blockNumber -> seedHash -> 16MB大小的 cache -> 1GB大小的DAG
具体过程:
一个seedHash为64字节,16MB =16777216byte / 64byte = 262144 ,也就是说一个cache中有16777216字节(262144段64字节的数据,后一段是前一段的哈希值)
DAG由cache生成:具体是从头到尾依次遍历cache,并将每一个字节进行fnvHash256然后再次哈希,得到64字节
并将其填充到DAG中,整个过程需要执行16777216 * 256次,16777216 * 64 = 1GB
注意:上述DAG是初始状态,DAG每3000区块生成一次,随着时间的推移会变大
DAG的作用:限制专业机器打包区块对网络的影响。因为每打包一个区块就需要从DAG中循环64次获取64*128字节的数据,而获取的速度取决于显卡的带宽。所以最终打包区块的速度就取决于算力以及带宽
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)