A. 简要解释为什么Rollup系统能将所有交易数据存储在链上?如果交易数据被丢弃且不存储在任何地方,会出现什么问题?

B. 看以下Solidity代码:

pragma solidity ^0.8.0;

contract ERC20 is IERC20 {

 mapping(address => uint256) private _balances;

  event Transfer(address indexed from, address indexed to, uint256 value);

  function _transfer(address sender, address recipient, uint256 amount) {

    emit Transfer(sender, recipient, amount);

}}

问1:假设这段代码部署在两个合约中:地址为X的合约和地址为Y的合约。下面哪个选项可以读取合约X中_balance的状态? 圈出正确的答案。

地址为X的ERC20合约中的transfer()函数代码地址为Y的ERC20合约中的transfer()函数代码终端用户使用etherscan.io

问2:继续(B)部分,下面哪一个可以读取调用函数_transfer()时发出的日志条目Transfer? 圈出正确的答案。

地址为X的ERC20合约中定义的getBalance()函数中的代码地址为Y的ERC20合约中定义的getBalance()函数中的代码终端用户使用etherscan.io

C. 两个以太坊交易,tx1和tx2,并把它们同时提交到网络上。交易tx1的maxPriorityFee设置为y, 交易tx2的maxPriorityFee设置为2y。tx2一定要在tx1之前在链上执行吗? 请进行证明。可以假设tx1和tx2的maxFee都大于baseFee + maxPriorityFee。

D. Alice想从经销商Bob那里买一辆车。她发送1个比特币到Bob的比特币地址。Bob会等待一个交易,其中(i)输入来自Alice的地址,(ii)其中一个输出是绑定到Bob地址的UTXO,值为1 BTC。只要Bob在比特币区块链上看到这笔交易,他就把钥匙给了Alice,然后她把车开走了。这是安全的吗?Alice可以免费得到那辆车吗?如果可以,请解释原因。如果不可以,解释一下Bob应该做什么来确保他得到了钱。

F. Alice拥有一辆全新的特斯拉,她现在可以在Compound系统中使用她的车作为抵押吗?(不卖她的车)如果可以,解释应该怎么做。如果不可以,解释原因。

拜占庭

考虑n个参与方,其中n≥3个,其中一个参与方被指定为发送方。

发送方的一个比特 b∈{0,1}。广播协议是一种协议,其中各方会彼此发送消息,最终每一方输出一个比特 bi,因为 i = 1,…, n,或什么都不输出。

协议具有一致性,如果有两个诚实方,如果一方输出b,另一方输出b",则b = b"。协议具有有效性,如果发送方是诚实的,诚实的各方的输出等于输入发送方的b。协议具有全面性,如果当某个诚实方输出一个比特时,那么最终所有诚实方都输出一个比特。

一个可靠的广播协议(RBC)是满足这三个特性的广播协议。

我们假设存在一个公钥基础设施(PKI),这意味着每一方都有一个秘密的签名密钥,并且每一方都知道另一方的正确的公开签名验证密钥。

在同步网络中,考虑以下广播协议:

步骤0:发送方将其输入位b (连同其签名) 发送给所有其他方。然后发送方输出它的b位并终止。

步骤1:每个非发送方 i 将它从发送方听到的内容反馈给所有其他非发送方 (添加了i的签名) 。如果没有从发送方那里听到任何消息,那么它在这一步中什么也不做。类似地,如果发送方的消息是不正常的:例如,如果发送方的签名是无效的,或者消息不是单个比特,则该方在这一步中不做任何事情。

步骤2:每个非发送方收集它收到的所有信息( 最多有n-1条消息,在步骤0中最多有一条来自发送方,在步骤1中最多有一条来自每个非发送方)。如果有两个由发送方收到的消息包含一个有效的签名,但是相反的位(即,在一个签名的消息中,比特为0,在另一个签名的消息中,比特为1),那么发送方是不诚实的,这一方输出0并进行终止。否则,来自发送方的所有正确签名的比特都是相同的,并且这一方输出该比特。如果非发送方没有收到任何消息,则不输出任何内容。

问:对于下面的每一个问题,描述攻击或解释为什么没有攻击。

如果最多只有一个不诚实方,协议是否具有一致性?如果最多只有一个不诚实方,协议是否有效?如果最多有两个不诚实方,表明协议没有一致性。如果最多有两个不诚实方,协议是否有效?协议是否具有全面性(对于任意数量的不诚实方)?

自动做市商

假设1个ETH值1000个DAI。你是Uniswap V2的流动性提供者,并为DAI/ETH池贡献5 ETH和5000 DAI。假设1个DAI值1美元,那么你的出资总额为1万美元。

几个月后,1个ETH的价格上升到相当于2000个 DAI。在DAI/ETH池稳定下来以适应这个新的汇率后,你决定撤回作为流动性提供者的全部头寸。假设系统不收费(φ = 1),你将收到多少个ETH和DAI ?如果你一直持有你的5 ETH和5000 DAI,你的资产现在将有1.5万美金的DAI,比起点多5000个DAI的利润。在这几个月里,作为Uniswap V2的流动性提供者,与“自己拿着”策略相比,你经历了什么损失?以美元的绝对值表示损失,假设1 DAI = 1 USD。这被称为无常损失,尽管在这种情况下,这种损失是相当永久性的。如果你作为Uniswap V2的流动性提供者损失了x美元,其中x是在(2)部分计算的,这些资金去了哪里?具体来说,谁在这个过程中获得了x美元?现在让我们转向使用Uniswap V2进行交易。假设Bob执行了一个大的交易,使用DAI/ETH池尾了ETH将DAI卖掉。交易完成后,DAI/ETH池中的DAI金额会比之前高,且ETH的金额有点低。因此,DAI/ETH池中的资产比例有点偏离了平衡点。套利者Alice发现了这个机会,并希望在发布一个反方向的交易,这将重新平衡池。她将从该交易中获利,并希望确保在Bob的交易之后立即执行她的交易。这种策略被称为 back-running。Alice该如何实施 back-running策略?说一种方法,使Alice的交易在Bob的交易之后立即执行。假设10个不同的套利者在同一时间执行相同的 back-running策略,以捕获Bob的交易创造的套利机会。它们都使用了你在(4)部分所描述的相同机制。10个人当中谁会赢呢?

重入漏洞

在这个问题中,我们将看一个有趣的现实例子。考虑以下在16384个NFT中使用的Solidity代码片段。通过调用mintNFT()函数,用户可以一次领取多达20个NFT。可以假设所有的内部变量都被构造函数正确地初始化了(没有显示)。

让我们证明_safeMint根本就不安全 (尽管它的名字挺安全的)。

假设已经铸造了16370个NFT,那么totalSupply() == 16370。解释一个恶意合约是如何铸造超过16384个NFT的。攻击者可以铸造NFT的最大数量是多少? 提示:如果在调用地址收到的onERC721Received是恶意的,会发生什么?注意铸造循环,并考虑重入漏洞。假设totalSupply()的当前值为16370,为一个恶意的Solidity合约编写代码,实现从(1)部分发起的攻击。你会在前一页Solidity的代码中添加或更改哪一行来防止你的攻击?请注意,单个交易不应该创建超过20个NFT。

比特币问题

闪电网络提议的好处是无需向比特币网络发布交易就可以执行支付。闪电网络支付最终会完全取代所有的比特币交易,使区块链变得不必要吗?回想一下,比特币交易有一组输入地址和一组输出地址。通常,每个输入地址签署整个交易(不包括签名)来授权支付。这种签名类型称为SIGHASH_ALL。相反,假设使用每个输入地址的密钥来签名整个Txin(交易的输入部分,不包括签名),而不签名其他任何内容。也就是说,Txout(交易的输出部分)没有签名(这种签名类型称为SIGHASH_NONE)。一旦交易提交给比特币网络,矿工可以使用SIGHASH_NONE方法从交易的输入地址窃取资金吗?如果可以,解释如何;如果没有,解释原因。如果有人发现了一种方法,在给了一个ECDSA公钥之后就能伪造ECDSA签名的任意消息,比特币会受到怎样的影响? 假设伪造一个签名需要30分钟的计算时间,而且不能加快速度。

Tornado Cash

Tornado Cash合约需要存储一个大的无效符,从树中每次都要提取一个无效符。在提款期间,合约需要确定,被提款的的票据的无效符不在已经提款的无效符集合中。如果是这样的,合约会将这个无效符添加到集合中。Tornado Cash合约将其完成为一个映射:

mapping(bytes32 => bool) public nullifierHashes;

在提款过程中,合约对所提供的zk-SNARK证据进行验证,如果有效,合约则:

假设从树中成功提取了k个。考虑一个正在验证以太坊交易的矿工。作为k的函数,这个矿工需要分配多少存储空间来存储nullifierhash映射? 可以假设Tornado合约除了这个nullifierhash映射之外不需要其他长期存储。如果我们能将提取的无效符Sk链下存储起来就更好了,比如说储存在云的某个地方。Tornado合约将只存储对当前无效符设置Sk的一个简短承诺。当调用提款函数时,用户将向该函数提供所有当前参数,此外,用户还将提供

足够的信息使Tornado合约能够计算承诺到更新的无效符设置Sk+1:= Sk∪{nf},π的证明被提取的币的无效符nf不在提交的无效符的集合,即 nf 6不属于Sk。

该合约将验证π证明,即nf 6不属于Sk,计算Sk+1的承诺,并用更新后的Sk+1的承诺替换当前对Sk的承诺。有几种数据结构提供了这些功能,其中Sk的承诺是一个32字节的哈希值,而π的证明只包含 2[log2 k]个32字节的哈希值。此外,这个π证明使Tornado Cash能够计算到Sk+1的短承诺。虽然这种方法将大大减少合约存储阵列的大小,但只有当它减少调用提款函数所需的gas时,才值得实现。考虑以下的gas成本:calldata(包含函数参数的字符串):每字节16 gas 。写入存储阵列的非零条目:5K gas,在存储阵列中写入一个零条目:20K gas假设我们只计算上面三个项目所消耗的gas。如果想在当前实现调用提款函数,那么k是多少的时候,这一改变将节省gas?Tornado cash提供了一个合规工具,可以让用户将自己的币去匿名化:该工具生成一个文件,将用户的存款与特定的提款联系起来。该文档可能需要提交给中心化交易所(如Coinbase),然后该交易所才能接受资金。假设n个人将一枚币存入一个Tornado池,那么这个池的匿名性设置为n(假设n = 1000)。之后,所有的n个人将他们的币取出到n个新的以太坊地址中(每个新地址都有一个币)。观察者无法判断哪个新的以太坊地址对应于这n个人中的哪一个,因此匿名集的大小为n。但是,假设有n - 1人使用合规工具并发送结果文档到Coinbase。这对最后剩下的那些想要私人地址的用户的隐私意味着什么?

Source:https://cs251.stanford.edu/