建设进出口外贸网站,最新国际热点新闻事件,装饰公司资质等级,常州网站推广多少钱区块链 2.0
以太坊概述
相对于比特币的几点改进
缩短出块时间至10多秒ghost共识机制mining puzzle BTC:计算密集型ETH#xff1a;memory-hard(限制ASIC) proof of work-proof of stake对智能合约的支持 BTC#xff1a;decentralized currencyETH#xff1a;decentral…区块链 2.0
以太坊概述
相对于比特币的几点改进
缩短出块时间至10多秒ghost共识机制mining puzzle BTC:计算密集型ETHmemory-hard(限制ASIC) proof of work-proof of stake对智能合约的支持 BTCdecentralized currencyETHdecentralized contract(去中心化合约) 优势
以太坊账户
比特币是基于交易的账本模式(transaction-based ledger)每笔交易都是由输入和输出构成的。输入引用先前交易的输出以证明发送者有权使用这些比特币。而输出则指定接收方地址和转移给接收方的比特币数量。因此要想知道余额要去UTXO找关联账户 举个例子 假设Alice拥有10个比特币她想将其中3个比特币发送给Bob。这将产生一笔交易输入是10个比特币来自Alice之前的交易输出输出是3个比特币给Bob和7个比特币返回给Alice自己因为剩下的比特币必须归还给发送方。 当这笔交易被确认后系统中不再存在先前的10个比特币的“账户余额”概念。相反账本中将存在两个未使用交易输出一个是给Bob的3个比特币另一个是给Alice的7个比特币。这些未使用的交易输出可以用于构建下一笔交易。 以太坊系统是基于账户的模型(account-based ledger)
在基于账户的账本模式中区块链记录着各个账户及其相应的余额。每个账户都有一个地址类似于银行账户号码和与之相关的以太币余额。当以太坊网络上发生交易时涉及的是在不同账户之间转移以太币。 假设账户B想要向账户C发送以太币。一笔交易被创建指定了发送方账户B、接收方账户C以及要转移的以太币数量。交易被确认并添加到以太坊区块链的一个区块后账户余额相应地进行更新。以太币从账户B的余额中减少同时同样的数量被增加到账户C的余额中。 account based模式的优点
符合人的主观感受和现行银行交易类似可以防范double speeding attack高可扩展性基于账户的模式在处理大量交易时有较好的扩展性。由于不需要跟踪每个交易的UTXO交易处理速度相对较快从而提高了整个网络的吞吐量。
account based模式的缺点
1.replay attack(nonce计数器) 在账户模式下需要存储每个账户的余额和状态信息。随着账户数量和交易量的增加需要更大的存储空间来保存所有账户的信息这可能会导致存储成本的增加
账户类别
外部账户(externally owned account)
balancenonce 外部账户也被称为EOA账户或用户账户。它们是由用户生成和控制的标准账户通过私钥和公钥来实现身份认证和交易签名。 每个外部账户有一个对应的以太币ETH余额可以发送和接收以太币。外部账户可以用于参与普通的货币交易例如转账以太币给其他账户。外部账户通过私钥签署交易然后将交易广播到区块链网络。 Smart contract account(合约账户)
balancenonce(一个合约可以调用另外一个合约因此通过nonce记录调用的次数)codestorge(调用过程会改变) 合约账户是一种由智能合约代码控制的特殊账户它们不依赖于私钥和公钥进行身份认证。 智能合约是一种以编程方式定义的自动执行的计算机代码能够根据预设规则和条件执行交易或实现特定功能。智能合约代码被部署到以太坊上生成一个合约账户并分配一个以太币余额用于支付交易执行的成本称为“燃气”费用。合约账户可以处理复杂的业务逻辑和多方交互可以实现更为复杂的去中心化应用DApps。 智能合约要求有稳定的账户参与
以太坊中的状态树 思路一:使用hash表查询
存在问题:每次出现新交易打包进区块中从而改变Merkle tree但事实上只有一部分发生改变一部分改变要重写整个Merkle tree
(另外由于BTC)
思路二直接使用Merkle tree存取账户要改直接改merkle tree是否可行
merkle tree查找效率低不排序存在的问题当计算叶节点顺序不能达成一致(BTC的顺序是由拥有铸币权的节点决定的)
如果使用sorted merkle tree呢
插入账户时merkle tree的重构代价很大
MTP结构
Trie结构(Trie是一种用于存储和快速检索键值对的树状数据结构通常用于高效地存储大量的关联数据。)可以类比信号的状态转移 trie结构的特点
打乱顺序trie结构不变具有很好的更新操作局部性
trie缺点存储浪费部分内容效率低(键值的分布稀疏)
Patricia Tree(压缩前缀树)经过了路径压缩的树 树的高度明显减少(如果树的分布很稀疏较为明显) disintermediarion(去中介)
为了防止哈希碰撞所以地址设置为 2 160 2^{160} 2160这么大
Merkle Patricia Tree
把普通指针换成了哈希指针
根节点Root Node状态树的根节点是一个哈希值用于表示整个状态树的当前状态。所有账户状态数据都通过这个根节点的哈希值进行唯一标识。
分支节点Branch Node分支节点是状态树中的中间节点用于连接不同的叶子节点或其他分支节点。每个分支节点有17个子节点对应了16进制数字0-15和一个特殊节点NIL节点。每个子节点保存一个哈希值指向下一级的分支节点或叶子节点。
叶子节点Leaf Node叶子节点包含了账户的具体状态信息。每个叶子节点对应一个账户地址保存了与该地址相关的账户状态数据如余额、代码、存储等。
这种状态树的结构使得以太坊能够高效地跟踪和维护账户的状态同时也有助于实现轻客户端验证和状态证明等功能从而提高整个网络的可扩展性和安全性。
以太坊实际用的结构是modified MPT
根节点哈希值存在块头 每次发布区块时状态树中新节点的值会发生改变这些改变是局部的
共享分支 问题为什么保留历史状态不在原先数据上进行修改
答为了回滚在ETH中分叉是常态orphan block中的数据都要向前一状态回滚而由于ETH中有智能合约为了支持智能合约的回滚必须保持之前的状态
区块头结构 发布的信息 状态树保存的是(key,value),上面讲的都是key保存的地址那么Value呢
经过RLP(Recursive Length Prefix)序列化再存储
只做nested array of bytes
区块链的交易树和收据树
状态树是包含多个区块的状态
交易树和收据树(独立的区块的交易和交易结果)的作用 提供merkle proof 支持更复杂的查询操作-e.g.支持查找过去10天和某个智能合约相关的交易 解决方案引入了bloom filter数据结构(有可能出现false postive(误报)而不会出现false passive(漏报))
bloom filter数据结构
布隆过滤器Bloom Filter是一种概率型数据结构用于快速判断一个元素是否属于某个集合。它可以高效地检索元素同时占用较少的内存空间。布隆过滤器的主要应用场景是在大规模数据集中进行快速查找和过滤操作。 ETH有关bloom filter的具体操作
每个交易执行完后会形成一个收据收据中包含一个bloom filter记录这个交易的类型地址等其他信息
发布的区块在它的块头,也有一个总的blooom filter这个总的blooom filter是该区块中所有交易的bloom filter的并集
当我们需要查找过去10天的有关智能合约的所有交易的方法是
step1:找哪个区块的块头中的bloom filter有我们需要的交易的类型如果块头中没有那么这个区块不是我们所需要的区块
step2:如果块头有就去这个区块查找各个收据的bloom filter,如果有则去确定交易
ETH运行过程交易驱动的状态机(tx-driven state machine) Q:现行状态树的机制中包含全部的账户的信息的状态可否改成只包含区块中涉及的tx的账户的状态?
每个节点所涉及的交易可能会不完整查找某账号状态不方便
当前状态树的设计保证了每个区块的状态是完整和一致的。如果只包含区块中涉及的交易的账户状态那么可能会出现信息丢失或不一致的情况从而导致网络的不安全性。
新建账户的查找麻烦最差得找到genisis block才能发现是新建账户
GHOST协议
分叉在以太坊是常态
ETH将区块生成时间设置为10秒可能带来的危害 但对ETH来说出块时间太短如果继续使用最长合法链对于个人矿工尤其不公平因为大矿场有能力去制造最长合法链
mining pool
GHOST协议核心给挖到orphan blocks的节点“安慰奖” 考虑分叉区块在GHOST协议中当节点在选择最长链时不仅考虑最长链上的区块还会考虑其他分叉区块即不在最长链上的区块。具体来说节点会选择最重的分支链而不仅仅是最长链。
奖励叔块在GHOST协议中叔块Uncle Block也称叔节点是指那些没有被选择为最终区块的分叉区块(最多包含两个Uncle Block)。虽然叔块没有包含在最长链中但它们也对区块链的安全性和分叉问题有积极的贡献。GHOST协议会对叔块的挖矿者给予一定的奖励以激励挖矿者选择更广泛的区块增加网络的整体安全性。
Uncle Block可以一直延续下去最多能够延续7代奖励 state fork
问题把uncle block 的tx包括进来其中的tx是否执行
答不执行交易可能有重复并且也不检查uncle block的交易的合法性
问题uncle block 后面还跟着一串怎么办是否算叔父区块 ETH实际 可参考 etherscan网站上的tx 以太坊的挖矿算法
Ethash
Block chain is secured by mining
BTC mining饱受争议的地方在于要用专业的ASIC芯片这与去中心化理念背道而驰
ASIC resistence一个重要的方法是memory hard mining puzzle
例子Litecoin(基于Scrypt) ETH的改进2种数据集 Ethash是一种重量级的PoW算法许多最流行的加密货币都加以使用包括以太坊以及墨客MOAC、Expanse、Pirl等。该算法是不同的因为它使用的DAG文件是在矿工启动的那一刻被加载到GPU内存。每30000个区块就会发生一次纪元变化使得DAG文件增加8MB。 ETHASH改良了Dagger-Hashimoto,有效地解决了单纯内存依赖的算法诸如Scrypt算法加密难与解密同样难的困境也突破Dagger算法不抵抗内存共享硬件加速的困境从全区块链数据的生成改为固定的1GB的数据的生成支持了客户端预生成数据保障挖矿难度的平滑过度。 16M cache - 轻节点便于验证
1G dataset - 全节点
具体过程
16M cache生成方式与Scrypt类似
step0 计算一个种子Seed该种子的计算依赖于当本块及本块之前的所有块。
step1 在数组中首位填入随机数(nonce),之后的数用hash函数算出来并依次填充cache中的元素(256 bits) 从种子中计算得出一个缓存Cache该缓存仅仅由种子得出是一个16MB大小的数据集。轻客户端应存储下该缓存用于日后验证。 step2 得到256个的hash放入更大的数组当中(1G dataset) step3 找nonce通过nonce读取对应位置(以及循环)往复64次找到128个数取hash去解puzzle 挖矿过程即为哈希过程。哈希的输入是取得1GB数据集的128个子部分并将它们放在一起执行哈希。验证过程是可以在轻客户端通过Cache缓存生成被挖矿制定的数据碎片并执行哈希验证。故而轻客户端并不需要时刻保存1GB的DAG。
伪代码分析
step 1
#step1 生成16Mcache
def mkcache(cache_size,seed):o[hash(seed)]for i in range(cache_size):o.append(hash(o[-1]))#hash函数 o[-1]是上一个hash值return ostep 2
#step2 通过cache生成dataset中的第i个数据
def calc_dataset_item(cache,i):cache_sizecache.size#cache的大小mixhash(cache[i%cache_size]^i)#cache[i%cache_size]^i是一个随机数for j in range(256):cache_indexget_int_from_item(mix)mixmake_item(mix,cache[cache_index%cache_size])return hash(mix)step 3多次调用这个函数得到完整的dataset
puzzle算法实现 为什么矿工要保存所有的dataset而情节点只需要保存cache? 以太坊总供应量
https://etherscan.io/stat/supply 以太坊挖矿难度调整
ETH的挖矿难度调整操作很多有出入因此这里以官方代码为主
PART 1:主公式 PART 2
参数说明 x x x是调整的单位 ζ 2 \zeta_{2} ζ2为调整的系数 H s H_s Hs是当前的时间戳, P ( H ) H S P(H)_{H_S} P(H)HS是父区块的时间戳两者时间差为出块间隔
PART 3 参数说明 权益证明
POW饱受批评的一点在于浪费资源
问题挖矿消耗的能耗是否是必须得
挖矿的本质大家比拼算力算力越大挖矿机率越高因此本质为投入的钱越多挖矿机率越高
POS核心思想直接拼钱-virtual mining
POS vs. POW优点
节能POS是闭环生态而POW是开放生态因此POS天然防止51% attack(attacker必须购买足够多的加密货币(成为股东或者获取更多的投票权)才有发动attack的能力但此时对于币的开发者和早期矿工是受益的如同恶意收购)
noting at state:当出现下述分叉时pow会选择一条链而POS下两条链都可以下注(且没有损失) ETH准备采用的POS协议是Casper the Friendly Finality Gadget(FFG)
引入了验证者validator-投入一定数量的保证金
每挖出100个区块生成一个epoch然后开始投票(two-phase commit) 投票中需要有两轮投票(都要达到 2 3 \frac{2}{3} 32) 每个epoch只投票一次(validator投票)
对于validator来说积极参与投票有奖励行政不作为则会受到处罚(扣保证金)
而对于两边下注的行为没收全部保证金
以太坊虚拟机
在以太坊上最重要的活动除了转账以外就是编译、运行智能合约(Smart Contract)。
智能合约代表了一个以太坊世界里的独立管家。它按照自身代码指示进行以太币的收入支出活动也具有一定存储空间可以存储一些数据。
以太坊虚拟机执行分为两大类只读操作和写操作。 仅获取区块链状态的操作为只读操作。
只读操作并不修改区块链状态在链式调用合约的时候也不会触发任何状态变更所以较为迅速。
写操作则会改变区块链的状态。例如一个更改账户状态的操作。 写操作则是需要花费以太币的操作因为它更改了某一个或者数个账户的存储空间。 存储空间是区块链的一部分是要被全世界的计算机永久同步存储的这个更改的过程代价昂贵 例如将一个值从 0 变为非零值需要耗费 20000 单位的gas修改一个 非0 值需要消耗 5000 单位gas 将一个值从 非0 赋值为 0 可以回收 15000 单位的gas读取一个变量值需要 200 gas。而相对比从内存中读取变量值仅需 3 gas。
以太坊的虚拟机不包含正常CPU所具备的硬件寄存器 它的执行宽度都是 256bit 的固定长度值所以相对比较容易编程它包含了存储、堆栈、内存三大存储机构。 Storage存储
存储里的值都是永久记录在区块链上的。存储在写和读取上都代价昂贵如非必须则数据不要存储在存储区。
存储区的读写操作都是以 256bit 为单位的没有更小的操作空间。故而 uint8 和 uint256 在单值存储的情况下占用的空间相同耗费的 Gas 也相同。 将 unit8 包裹进入 struct 结构体以后可以通过优化来节约空间位置节省gas支出。 Stack堆栈
堆栈有且仅有 1024 层深度当我们执行递归调用过多的时候堆栈就会击穿 1024 层则代码执行失败。
堆栈仅有高处的 16 层是可以被快速访问的堆栈的宽度也是 256bit也就是 32byte一个 word 的长度任何读写操作都是 256bit 为一个单位进行的。 编译器往往会将代码执行中的临时变量、变量地址放到堆栈上临时保存变量地址可以进一步索引到内存 Memory 中。 Memory 内存 虚拟机的输入输出 Gas花费
发送交易的时候我们可以指定 gas 的花费上限以防止智能合约代码有bug而导致无限循环执行下去。
一旦 gas 过早耗尽则虚拟机抛出异常结束代码执行。
有一类情况很特殊就是Solidity智能合约代码的 assert() 函数与 require() 函数。
在执行时候这两个函数都是做真假条件判断的但是 assert 函数感情更加强烈往往判断的都是关键的安全性条件例如 SafeMath 中利用 assert 函数判断是否位数溢出 合约取款时判断调用方是否为合约所有者等。
require()函数则较为普通的条件判断例如判断合约调用者的余额是否足够等。
assert() 函数判断一旦失败则会扣完剩下所有的 gas 作为惩罚措施 require() 判断失败则仅仅停止目前的执行收取执行到当前步骤相应的 gas 费用再撤销发生的变更。 虚拟机指令集
EVM执行的是字节码。由于操作码被限制在一个字节以内所以EVM指令集最多只能容纳256条指令。
mem[a…b] 表示内存中a到b(不包含b)个字节 storage[p] 表示从p开始的32个字节 谨记evm虚拟机的word(字)是256位32字节
智能合约
什么是智能合约
智能合约是运行在区块链上的一段代码代码的逻辑定义了合约的内容 智能合约的帐户保存了合约当前的运行状态
balance:当前余额nonce:交易次数code:合约代码storage:存储数据结构是一棵MPTSolidity是智能合约最常用的语言语法上与JavaScript很接近 以太坊中凡是需要接受外部转账的函数都需要标记为payable
solidity语言不支持hash表的遍历
bidders.push(bidder)//添加竞拍人
bidders.length//大小调用智能合约
创建一个交易接收地址为要调用的那个智能合约的地址data域填写要调用的函数及其参数的编码值。 1.直接调用 3.代理调用
代理调用delegatecall()
使用方法与call()相同只是不能使用.value() 区别在于是否切换上下文
call0切换到被调用的智能合约上下文中delegatecall()只使用给定地址的代码其它属性存储余额等)都取自当前合约。delegatecall的目的是使用存储在另外一个合约中的库代码。
fallback()函数
function()public [payable]{
......
}匿名函数没有参数也没有返回值。在两种情况下会被调用 直接向一个合约地址转账而不加任何data被调用的函数不存在 如果转账金额不是0同样需要声明payable,否则会抛出异常。
智能合约的创建和运行
智能合约的代码写完后要编译成bytecode创建合约外部帐户发起一个转账交易到0x0的地址 转账的金额是0但是要支付汽油费合约的代码放在data域里 智能合约运行在EVM(Ethereum Virtual Machine)上 以太坊是一个交易驱动的状态机 调用智能合约的交易发布到区块链上后每个矿工都会执行这 个交易从当前状态确定性地转移到下一个状态
Gas fee ETH的错误处理
ETH交易具有原子性即一个交易要么不执行要么全执行不会执行一部分 智能合约不存在自定义的try-catch结构 一旦遇到异常除特殊情况外本次执行操作全部回滚 可以抛出错误的语句 assert(bool condition)如果条件不满足就抛出——用于内部错误。 require(bool condition)如果条件不满足就抛掉——用于输入或者外部组件引起的错误。 Function bid()pubiie payable{
//拍卖未结束
require(now auctionEnd);
}revert():终止运行并回滚状态变动(无条件)
嵌套调用 Block header 汽油费的扣除机制
ETH的三棵树都是在本地维护的数据结构全节点收到对智能合约的调用,在本地账户扣除余额然后将取得记账权的节点发布区块将它本地维护的三棵树上传到区块链
ETH挖矿过程
全节点打包交易执行对智能合约的调用调整智能合约的内容求得3棵树的根哈希值试nonce,取得记账权,发布区块别的没有取得记账权的区块要独立验证新发布区块及其包含的tx和智能合约的合法性
应该先执行再挖矿(需要先算三棵树的hash再写进区块中)
如果不验证无法更新本地三棵树的Hash值无法发布正确的三棵树信息无法挖矿
Receipt 问题智能合约是否支持多线程?
不支持状态机必须是完全确定而多线程如果对内存访问顺序不同造成的结果可能也会不同
出了多线程“产生随机数”
智能合约可以获得的区块信息
block.blockhash(unint blockNumber) returns (bytes)给定区块的哈希-仅对最近的256个区块有效而不包括当前区块 block.coinbase(address)挖出当前区块的矿工地址 block.difficulty(uint) 当前区块难度 block.gaslimit(uint):当前区块gas限额 block.number(uint):当前区块号 block.timestamp(uint):自unix epoch起始当前区块以秒计的时间戳
智能合约可以获得的调用信息 msg.data(bytes):完整的calldata msg.gas(uint):剩余gas msg.sender(address):消息发送者当前调用 msg.sig(bytes4):calldata的前4字节也就是函数标识符 msg.value(uint):上随消息发送的wei的数量 now(uint):目前区块时间戳(block.timestamp) tx.gasprice(uint):上交易的gas价格 tx.origin(address):上交易发起者完全的调用链)
地址类型
address.balance(uint256) 以Wei为单位的地址类型的余额。 address.transfer(uint256 amount): 向地址类型发送数量为amount的Wei,失败时抛出异常发送2300(转入账户的地址)gas的矿工费不可调节。
address.send(uint256 amount)returns(bool): 向地址类型发送数量为amount的Wei,失败时返回false,发送2300gas的矿工费用不可调节。
address.call(...)returns (bool): 发出底层CALL,失败时返回false,发送所有可用gas,不可调节。
address.callcode(...)returns(bool)日 发出底层CALLCODE,失败时返回false,发送所有可用gas,不可调节。
address.delegatecall(...)returns (bool): 发出底层DELEGATECALL,失败时返回false,发送所有可用gas,不可调节。
所有智能合约均可显式地转换成地址类型
三种发送ETH的方式
address.transfer(uint256 amount) address.send(uint256 amount)returns (bool) address.call.value(uint256 amount)()
简单案例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;contract SimpleAuction {// 拍卖的受益人address payable public beneficiary;// 最高出价者address public highestBidder;// 最高出价uint public highestBid;// 拍卖结束时间uint public auctionEndTime;// 竞标是否已结束bool public ended;// 变更出价时触发的事件event HighestBidIncreased(address bidder, uint amount);// 拍卖结束时触发的事件event AuctionEnded(address winner, uint amount);// 构造函数设置受益人和拍卖结束时间constructor(uint _biddingTime, address payable _beneficiary) {beneficiary _beneficiary;auctionEndTime block.timestamp _biddingTime;}// 竞标函数function bid() public payable {require(block.timestamp auctionEndTime, 拍卖已结束);require(msg.value highestBid, 已存在更高出价);if (highestBid ! 0) {// 返还之前最高出价者的金额address payable previousBidder payable(highestBidder);previousBidder.transfer(highestBid);}highestBidder msg.sender;highestBid msg.value;emit HighestBidIncreased(msg.sender, msg.value);}// 结束拍卖函数只有受益人可以调用function auctionEnd() public {require(block.timestamp auctionEndTime, 拍卖尚未结束);require(!ended, 拍卖已结束);ended true;emit AuctionEnded(highestBidder, highestBid);// 将最高出价转给受益人beneficiary.transfer(highestBid);}
}参考文献
ETHASH 挖矿算法 — 以太坊的指南针 1.0.0 documentation (abyteahead.com)
虚拟机的执行资源 — 以太坊的指南针 1.0.0 documentation (abyteahead.com)