区块链 2.0笔记

区块链 2.0

以太坊概述

相对于比特币的几点改进

  1. 缩短出块时间至10多秒
  2. ghost共识机制
  3. mining puzzle
    • BTC:计算密集型
    • ETH:memory-hard(限制ASIC)
  4. proof of work->proof of stake
  5. 对智能合约的支持
    • BTC:decentralized currency
    • ETH:decentralized 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模式的优点

  1. 符合人的主观感受和现行银行交易类似
  2. 可以防范double speeding attack
  3. 高可扩展性:基于账户的模式在处理大量交易时有较好的扩展性。由于不需要跟踪每个交易的UTXO,交易处理速度相对较快,从而提高了整个网络的吞吐量。

account based模式的缺点

1.replay attack(nonce计数器)

在这里插入图片描述

  1. 在账户模式下,需要存储每个账户的余额和状态信息。随着账户数量和交易量的增加,需要更大的存储空间来保存所有账户的信息,这可能会导致存储成本的增加

账户类别

外部账户(externally owned account)

  • balance
  • nonce

外部账户也被称为EOA账户或用户账户。它们是由用户生成和控制的标准账户,通过私钥和公钥来实现身份认证和交易签名。

  • 每个外部账户有一个对应的以太币(ETH)余额,可以发送和接收以太币。
  • 外部账户可以用于参与普通的货币交易,例如转账以太币给其他账户。
  • 外部账户通过私钥签署交易,然后将交易广播到区块链网络。

Smart contract account(合约账户)

  • balance
  • nonce(一个合约可以调用另外一个合约,因此通过nonce记录调用的次数)
  • code
  • storge(调用过程会改变)

合约账户是一种由智能合约代码控制的特殊账户,它们不依赖于私钥和公钥进行身份认证。

  • 智能合约是一种以编程方式定义的自动执行的计算机代码,能够根据预设规则和条件执行交易或实现特定功能。
  • 智能合约代码被部署到以太坊上,生成一个合约账户,并分配一个以太币余额用于支付交易执行的成本(称为“燃气”费用)。
  • 合约账户可以处理复杂的业务逻辑和多方交互,可以实现更为复杂的去中心化应用(DApps)。

智能合约要求有稳定的账户参与

以太坊中的状态树

在这里插入图片描述

思路一:使用hash表查询

存在问题:每次出现新交易打包进区块中,从而改变Merkle tree,但事实上只有一部分发生改变,一部分改变要重写整个Merkle tree

(另外由于BTC)

思路二:直接使用Merkle tree存取账户,要改直接改merkle tree,是否可行?

  • merkle tree查找效率低
  • 不排序,存在的问题:当计算叶节点顺序不能达成一致(BTC的顺序是由拥有铸币权的节点决定的)

如果使用sorted merkle tree呢?

插入账户时,merkle tree的重构代价很大

MTP结构

Trie结构(Trie是一种用于存储和快速检索键值对的树状数据结构,通常用于高效地存储大量的关联数据。)可以类比信号的状态转移

在这里插入图片描述

trie结构的特点:

  1. 打乱顺序,trie结构不变
  2. 具有很好的更新操作局部性

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

区块链的交易树和收据树

状态树:是包含多个区块的状态

交易树和收据树(独立的区块的交易和交易结果)的作用:

  1. 提供merkle proof

  2. 支持更复杂的查询操作->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的账户的状态?

  1. 每个节点所涉及的交易可能会不完整,查找某账号状态不方便

当前状态树的设计保证了每个区块的状态是完整和一致的。如果只包含区块中涉及的交易的账户状态,那么可能会出现信息丢失或不一致的情况,从而导致网络的不安全性。

  1. 新建账户的查找麻烦,最差得找到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 o

在这里插入图片描述

step 2

#step2 通过cache生成dataset中的第i个数据
def calc_dataset_item(cache,i):cache_size=cache.size#cache的大小mix=hash(cache[i%cache_size]^i)#cache[i%cache_size]^i是一个随机数for j in range(256):cache_index=get_int_from_item(mix)mix=make_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优点:

  1. 节能
  2. 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:存储,数据结构是一棵MPT
  • Solidity是智能合约最常用的语言,语法上与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

在这里插入图片描述

汽油费的扣除机制

  1. ETH的三棵树都是在本地维护的数据结构
  2. 全节点收到对智能合约的调用,在本地账户扣除余额
  3. 然后将取得记账权的节点发布区块,将它本地维护的三棵树上传到区块链

ETH挖矿过程

  1. 全节点打包交易,执行对智能合约的调用,调整智能合约的内容
  2. 求得3棵树的根哈希值
  3. 试nonce,取得记账权,发布区块
  4. 别的没有取得记账权的区块,要独立验证新发布区块及其包含的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)

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

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

相关文章

怎么查看gcc的安装路径

2023年7月29日 很简单&#xff0c;通过在命令行输入如下命令就可以了&#xff1a; gcc -print-search-dirs在Windows中 在Linux中 ​​​

力扣刷题记录---利用python实现链表的基本操作

文章目录 前言一、利用python实现链表的基本操作1.节点的定义使用类实现&#xff1a;1.链表的定义使用类实现&#xff1a;3.判断是否为空函数实现&#xff1a;4.链表长度函数实现&#xff1a;5.遍历链表函数实现&#xff1a;6.头插法函数实现&#xff1a;7.尾插法函数实现&…

js原型以及原型链

目录 原型隐式原型显式原型constructornew操作符 重写原型对象原型链继承原型链继承借用构造函数继承组合构造继承 原型继承寄生继承组合寄生继承 原型继承关系 原型 在JavaScript中&#xff0c;每个对象都有一个内置属性[[prototype]]&#xff0c;这个属性指向一个另一个对象…

【SpringCloud Alibaba】(六)使用 Sentinel 实现服务限流与容错

今天&#xff0c;我们就使用 Sentinel 实现接口的限流&#xff0c;并使用 Feign 整合 Sentinel 实现服务容错的功能&#xff0c;让我们体验下微服务使用了服务容错功能的效果。 因为内容仅仅围绕着 SpringCloud Alibaba技术栈展开&#xff0c;所以&#xff0c;这里我们使用的服…

【实践篇】推荐算法PaaS化探索与实践 | 京东云技术团队

作者&#xff1a;京东零售 崔宁 1. 背景说明 目前&#xff0c;推荐算法部支持了主站、企业业务、全渠道等20业务线的900推荐场景&#xff0c;通过梳理大促运营、各垂直业务线推荐场景的共性需求&#xff0c;对现有推荐算法能力进行沉淀和积累&#xff0c;并通过算法PaaS化打造…

存储重启后,ceph挂载信息没了,手动定位osd序号并挂载到对应磁盘操作流程、ceph查看不到osd信息处理方法

文章目录 故障说明处理流程定位硬盘中的osd序号挂载osd到ceph上验证并拉起osd重复上面操作故障说明 我们的一个存储节点莫名其妙的重启了,不知道咋回事 但这样的问题就是,所有osd都down了 因为挂载信息没有写到fstab里面,所以不会自动up,并且没有挂载信息,并且也看不到o…

Ubuntu—vi编辑器的使用一

vi编辑器 vi是Linux中最基本的编辑器。但vi编辑器在系统管理、服务器配置工作中永远都是无可替代的。 vi编辑器的使用 vi有以下三种模式 命令行模式 用户在用vi编辑文件时&#xff0c; 最初进入的是该模式。可以进行复制、粘贴等操作 插入模式 进行文件编辑&#xff0c; 按…

七大经典比较排序算法

1. 插入排序 (⭐️⭐️) &#x1f31f; 思想&#xff1a; 直接插入排序是一种简单的插入排序法&#xff0c;思想是是把待排序的数据按照下标从小到大&#xff0c;依次插入到一个已经排好的序列中&#xff0c;直至全部插入&#xff0c;得到一个新的有序序列。例如&#xff1a;…

【C++】开源:Boost库常用组件配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍Boost库常用组件配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c…

想学Python高级编程?必须了解这个小技巧:match-case!

大家好&#xff0c;这里是程序员晚枫&#xff0c;小破站/知乎/小红书/抖音都叫这个名字。 上次给大家分享了Python高级编程第一讲&#xff1a;从使用类型提示开始 &#xff1b;今天分享Python高级编程第二讲&#xff1a;深入解析Python中switch case的使用方法。 写在前面 分…

巧用NGINX配置解决跨域问题

页面nginx配置 1&#xff0c;前端页面放在域名根目录&#xff0c;比如&#xff0c;http://www.xuecheng.com/ &#xff0c;对应的nginx配置&#xff1a; #门户location / {alias D:/Z_lhy/SpringCloud/xuecheng_online/www/xc-ui-pc-static-portal/;index index.html;} 页…

基于STM32设计的人体健康检测仪

一、项目介绍 当前文章介绍基于STM32设计的人体健康检测仪。设备采用STM32系列MCU作为主控芯片&#xff0c;配备血氧浓度传感器&#xff08;使用MAX30102血氧浓度检测传感器&#xff09;、OLED屏幕和电池供电等外设模块。设备可以广泛应用于医疗、健康等领域。可以帮助医生和病…

平板光波导中导模的(注意不是泄露模)传播常数β的matlab计算(验证了是对的)

参照的是导波光学_王建(清华大学)的公式(3-1-2、3-1-3)&#xff0c;算的参数是这本书的图3-3的。 function []PropagationConstantsMain() clear;clc;close all lambda01.55;%真空或空气中的入射波长&#xff0c;单位um k02*pi/lambda0; m3;%导模阶数(需要人为指定) n11.62;%芯…

网络知识点之-路由

本文章收录至《网络》专栏&#xff0c;点击右上角专栏图标可访问本专栏&#xff01; 路由&#xff08;routing&#xff09;是指分组从源到目的地时&#xff0c;决定端到端路径的网络范围的进程。路由工作在OSI参考模型第三层——网络层的数据包转发设备。路由器通过转发数据包来…

MySQL数据库配置及创建用户和授权

注意&#xff1a; 都是基于MySQL8.0以上版本 1、检查是否安装过sql [rootlocalhost ~]# rpm -[qa](https://so.csdn.net/so/search?qqa&spm1001.2101.3001.7020) | grep mysql[rootlocalhost ~]# rpm -qa | grep [mariadb](https://so.csdn.net/so/search?qmariadb&…

Spark(37):Streaming DataFrame 和 Streaming DataSet 创建

目录 0. 相关文章链接 1. 概述 2. socket source 3. file source 3.1. 读取普通文件夹内的文件 3.2. 读取自动分区的文件夹内的文件 4. kafka source 4.1. 导入依赖 4.2. 以 Streaming 模式创建 Kafka 工作流 4.3. 通过 Batch 模式创建 Kafka 工作流 5. Rate Source…

随笔03 考研笔记整理

图源&#xff1a;文心一言 上半年的博文整理&#xff0c;下半年依然会更新考研类的文章&#xff0c;有需要的小伙伴看向这里~~&#x1f9e9;&#x1f9e9; 另外&#xff0c;这篇文章可能是我上半年的努力成果之一&#xff0c;因此仅关注博主的小伙伴能够查看它~~&#x1f9e…

嵌入式系统中的GPIO控制:从理论到实践与高级应用

本文将探讨嵌入式系统中的GPIO(通用输入输出)控制,着重介绍GPIO的原理和基本用法。我们将使用一个实际的示例项目来演示如何通过编程配置和控制GPIO引脚。将基于ARM Cortex-M微控制器,并使用C语言进行编写。 GPIO是嵌入式系统中最常见且功能最强大的接口之一。它允许硬件工…

基于RK3588+FPGA+AI算法定制的智慧交通与智能安防解决方案

随着物联网、大数据、人工智能等技术的快速发展&#xff0c;边缘计算已成为当前信息技术领域的一个热门话题。在物联网领域&#xff0c;边缘计算被广泛应用于智慧交通、智能安防、工业等多个领域。因此&#xff0c;基于边缘计算技术的工业主板设计方案也受到越来越多人的关注。…

linux 指令 第3期

cat cat 指令&#xff1a; 首先我们知道一个文件内容属性 我们对文件操作就有两个方面&#xff1a;对文件内容和属性的操作 扩展&#xff1a;echo 指令 直接打印echo后面跟的字符串 看&#xff1a; 这其实是把它打印到了显示器上&#xff0c;我们也可以改变一下它的打印位置…