solidity面试题

solidity面试题(一)

  1. 私有、内部、公共和外部函数之间的区别?
    答:私有private只能当前合约内部使用,子合约继承也不能调用;内部internal可以当前合约和子合约调用;公共public合约内部外部和子合约都可以调用;外部external只提供给外部调用,合约内部不能调用,合约接口的函数都要声明成external。

  2. 智能合约大小大约可以有多大?
    答:上海升级后,实现EIP-3860是48KB,原来24KB,扩大代码量解决以前复杂合约拆分后合约之间互相调用的高gas费问题。

  3. create 和 create2 之间有什么区别?
    答:CREATE和CREATE2是Solidity中的两个重要的操作码,它们都可以用于部署智能合约,但是它们之间有一些区别. CREATE操作码通过对发送者地址和nonce值进行哈希运算来计算新合约的地址,而CREATE2操作码则使用了更复杂的公式来计算新合约的地址,这个公式包括发送者地址、随机数、字节码等参数. CREATE2操作码的主要优点是,它可以在部署合约之前预测合约的地址,这对于需要提前知道合约地址的场景非常有用

  4. Solidity 0.8.0 版本对算术运算有什么重大变化?
    答:从Solidity 0.8.0版本开始,算术运算会在下溢和上溢时回滚。这意味着,如果一个算术运算的结果超出了其数据类型的范围,那么这个运算将会失败并回滚。在Solidity 0.8.0之前,整数下溢和上溢是允许的,不会导致错误,为了避免这种情况,Solidity 0.8.0默认开启了算术运算检查,如果您需要使用旧的算术运算方式,可以使用unchecked{…}语句块

  5. 代理需要哪种特殊的 CALL 才能工作?
    答:Solidity中有三种调用函数可以实现合约间的函数调用,它们分别是call、delegatecall和callcode。其中,delegatecall是一种特殊的调用方式,它允许我们在主合约的上下文中加载和调用另一个合约的代码。被调用合约的代码被执行,但被调用合约所做的任何状态改变实际上是在主合约的存储中进行的,而不是在被调用合约的存储中

  6. 在 EIP-1559 之前,如何计算以太坊交易的成本?
    答:在EIP-1559之前,以太坊交易的成本由矿工通过拍卖机制来决定。矿工会选择最高出价的交易,并将其包含在下一个区块中。交易的成本由两个因素决定:Gas Price和Gas Limit。Gas Price是以太坊网络中的一种计量单位,用于衡量交易的复杂性。Gas Limit是指交易可以使用的最大Gas数量。交易的成本等于Gas Price乘以Gas Limit。因此,交易的成本取决于Gas Price和Gas Limit的值,这些值由交易的发送者设置。

  7. ETH转账的底层原理
    答:底层原理就是修改链上数据,可以回答详细一点就是交易过程发生了什么。
    以下是ETH转账的详细过程:

首先,您需要连接到以太坊网络并加载您的私钥。您可以使用go-ethereum客户端来完成此操作。
然后,您需要获取发送帐户的随机数(nonce)。每个新事务都需要一个nonce,这是一个仅使用一次的数字。如果是发送交易的新帐户,则该随机数将为“0”。
接下来,您需要将ETH转换为wei,因为这是以太坊区块链所使用的货币单位。以太网支持最多18个小数位,因此1个ETH为1加18个零。
然后,您需要设置您将要转移的ETH数量,以及燃气限额和燃气价格。燃气限额应设上限为“21000”单位,而燃气价格必须以wei为单位设定。
然后,您需要确定您要将ETH发送给哪个地址。这是接收地址。
接下来,您需要生成未签名的以太坊事务。这个函数需要接收nonce,地址,值,燃气上限值,燃气价格和可选发的数据。发送ETH的数据字段为“nil”(nil是go语言的null)。
然后,您需要使用发件人的私钥对事务进行签名。为此,您可以使用go-ethereum客户端提供的SignTx方法,该方法接受一个未签名的事务和您之前构造的私钥。
最后,您可以通过在客户端上调用“SendTransaction”将已签名的事务广播到整个网络。这将向网络中的所有节点发送交易,并等待矿工将其打包到区块中。
8. 在区块链上创建随机数的挑战是什么?
答:在区块链上创建随机数的挑战是确保生成的随机数是真正随机的。由于区块链是一个公开的分布式账本,因此任何人都可以查看和验证交易。这意味着,如果随机数生成算法不够随机,那么攻击者可能会通过分析交易来预测随机数的值,从而破坏系统的安全性。为了解决这个问题,一些技术已经被提出,例如使用区块链上的随机数生成器,或者使用多个随机数生成器来生成随机数,以确保生成的随机数是真正随机的 .

  1. solidity实现随机数生成器
    答:在这个示例中,我们使用了Solidity中的keccak256哈希函数来生成随机数。我们将当前时间戳、发送方地址和nonce值作为输入,然后对它们进行哈希运算,得到一个随机数。nonce值是一个递增的计数器,用于确保每次调用random函数时都会生成一个新的随机数。请注意,这种方法并不是完全随机的,因为它仍然依赖于输入参数的值。但是,它可以提供足够的随机性,以满足大多数应用程序的需求。

pragma solidity ^0.8.0;

contract RandomNumberGenerator {
uint256 private nonce = 0;

function random() public returns (uint256) {uint256 randomNumber = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, nonce)));nonce++;return randomNumber;
}

}

  1. 什么是EIP-1559
    答:Ethereum Improvement Proposal (EIP) 1559是以太坊的一个升级,旨在改变以太坊计算和处理网络交易费用(称为“gas费用”)的方式。该升级通过使用基于区块的基础费用和发送方指定的最大费用,而不是对gas价格进行竞价,来更加平衡地激励矿工在高或低网络拥塞期间进行挖矿,从而使以太坊交易更加高效。EIP-1559是一个提案,它改变了gas费用的结构和矿工的奖励方式。该提案于2021年8月5日作为以太坊伦敦硬分叉的一部分实施。
    gas费 = 基础费(Basefee)+ 矿工费(Tip),基础费会根据区块的Gas利用率动态调整,如每个区块的Gas费利用率低于50%,就降低手续费,高于50%,就提高手续费。

  2. 荷兰式拍卖和英式拍卖之间有什么区别?
    答:荷兰式拍卖和英式拍卖是两种不同的拍卖方式。在荷兰式拍卖中,拍卖师会先宣布一个高价,然后逐渐降低价格,直到有人愿意出价购买物品为止。在这种拍卖中,第一个出价的人通常会赢得拍卖,因为他们愿意支付的价格最高。荷兰式拍卖通常用于出售艺术品、古董和其他昂贵的物品.
    英式拍卖是一种更传统的拍卖方式。在这种拍卖中,拍卖师会宣布一个起始价,然后买家可以逐渐提高价格,直到没有人再愿意出价为止。在英式拍卖中,最后一个出价的人通常会赢得拍卖,因为他们愿意支付的价格最高。英式拍卖通常用于出售房地产、汽车和其他大型物品.
    总的来说,荷兰式拍卖和英式拍卖都是有效的拍卖方式,但它们的实现方式略有不同。荷兰式拍卖通常更适合出售昂贵的物品,而英式拍卖则更适合出售大型物品。

  3. ERC20 中的 transfer 和 transferFrom 之间有什么区别?
    答:transfer是从当前合约转账给目标账户,transferFrom是可设置发送账户和目标账户
    transfer(address recipient, uint256 amount)
    transferFrom(address sender, address recipient, uint256 amount)

  4. 对于地址 allowlist,使用映射还是数组更好?为什么?
    答:地址 allowlist 是一个存储地址的列表,用于限制只有在列表中的地址才能执行某些操作。在 Solidity 中,可以使用映射或数组来实现地址 allowlist。使用映射的优点是可以快速查找地址是否在 allowlist 中,而使用数组的优点是可以更容易地遍历整个 allowlist。
    如果您需要快速查找地址是否在 allowlist 中,那么使用映射可能更好。映射使用哈希表实现,可以在 O(1) 的时间内查找地址是否在 allowlist 中。这对于大型 allowlist 尤其有用,因为它可以避免遍历整个列表来查找地址。
    如果您需要遍历整个 allowlist,那么使用数组可能更好。数组可以更容易地遍历整个列表,因为它们是连续的内存块。这对于小型 allowlist 尤其有用,因为它可以避免使用哈希表的额外开销。

  5. 为什么不应该使用 tx.origin 进行身份验证?
    答:在 Solidity 中,tx.origin 是一个全局变量,它返回发送交易的账户地址。在合约代码中,最常用的是使用 msg.sender 来检查授权,但有时由于有些程序员不熟悉 tx.origin 和 msg.sender 的区别,如果使用了 tx.origin 可能导致合约的安全问题。黑客最典型的攻击场景是利用 tx.origin 的代码问题常与钓鱼攻击相结合的组合拳的方式进行攻击。因为 tx.origin 返回交易的原始发送者,因为攻击的调用链可能是原始发送者 -> 攻击合约 -> 受攻击合约。在受攻击合约中,tx.origin 是原始发送者。因此,通过调用 tx.origin 来检查授权可能会导致合约受到攻击。为了避免这种情况,建议使用 msg.sender 来检查授权

  6. 以太坊主要使用什么哈希函数?
    答:以太坊主要使用两种哈希函数,分别是 Keccak-256 和 SHA-3。Keccak-256 是以太坊最初采用的哈希函数,它是 SHA-3 标准的一个变种。在以太坊中,Keccak-256 用于计算地址、交易哈希和区块哈希等重要数据的哈希值。SHA-3 是一种新的哈希函数标准,它比 Keccak-256 更加安全和高效。在以太坊中,SHA-3 用于计算合约代码的哈希值。

  7. 1 个Ether 相当于 多少个 gwei ?
    答:1 ether =
    1
    0
    9
    10
    9
    gwei

  8. 1 个Ether 相当于 多少个 wei ?
    答:1 ether =
    1
    0
    18
    10
    18
    wei

  9. assert 和 require 之间有什么区别?
    答:在 Solidity 中,assert 和 require 都是用于检查条件的函数。当条件不满足时,它们会抛出错误或异常。它们之间的区别在于,require 用于在执行前验证输入和条件,而 assert 用于检查不应该为假的代码,失败的断言可能意味着代码层面存在错误。当我们想在执行之前验证输入和条件时,我们使用 require。当我们想要检查可以而且永远不应该为 false 的代码时,我们使用 assert。如果失败,则意味着存在错误,在这种情况下,我们应该使用 assert。
    当在选择 assert 和 require 之间做出决定时,需要考虑两个方面:
    Gas 优化的效率:如果 assert 返回一个 false 语句,它会编译为 0xfe,这是一个无效的操作码,它会用完所有剩余的 gas,从而完全恢复更改。如果 require 返回错误语句,它会编译为 0xfd,这是 REVERT 的操作码,这意味着它将返回剩余的气体。
    Bytecode 分析:如果你使用 assert,你的代码将会更小,因为它不会返回任何东西。如果你使用 require,你的代码将会更大,因为它需要返回错误信息。

  10. 什么是闪电贷?
    答:闪电贷是一种无抵押借贷的defi产品,主要给开发者提供的,它允许在一笔交易内进行借款还款,只要支付一点闪电贷设定的手续费。很多攻击者利用闪电贷进行攻击和套利。

  11. 什么是检查效果( check-effects )模式?
    答:检查效果(check-effects)模式是一种编程模式,用于确保函数在执行时不会对状态造成意外的影响。在这种模式下,函数应该只读取输入参数,并且不应该修改任何状态。如果函数需要修改状态,它应该返回一个包含所有修改的对象,而不是直接修改状态。这种模式可以帮助开发人员编写更安全、更可靠的代码,因为它可以减少由于状态修改而导致的错误。

  12. 运行独立验证节点所需的最小以太数量是多少?
    答:在以太坊网络中,要运行一个独立的验证节点,需要至少 32 个以太币作为抵押品。这是因为在以太坊的 Proof of Stake(PoS)共识机制中,验证节点需要抵押一定数量的以太币来证明他们对网络的贡献,同时也会获得一定的奖励

  13. fallback 和 receive 之间有什么区别?
    答:fallback和receive都是特殊的回调函数,用于处理合约中不存在的函数调用和接收以太币。它们之间的区别在于,fallback函数会在调用合约不存在的函数时被触发,而receive函数只用于处理接收以太币。

  14. 什么是重入?
    答:重入攻击是一种安全漏洞,攻击者利用合约的 fallback 函数和多余的 gas 将本不属于自己的以太币转走的攻击手段。攻击者通过在攻击合约中调用受攻击合约的函数,然后在受攻击合约中再次调用攻击合约的函数,从而实现重复调用,造成重入攻击。重入攻击的本质是递归调用,攻击者利用这种递归调用来绕过原代码中的限制条件,从而造成攻击。为了避免重入攻击,可以使用 Solidity 内置的 transfer() 函数,或者使用检查-生效-交互模式 (checks-effects-interactions) 来确保状态变量的修改要早于转账操作。
    被攻击合约取款方法:

function withdraw() public {
uint bal = balances[msg.sender];
require(bal > 0);
// 用call给攻击合约发送eth,
// 促发了攻击合约中定义了fallback函数,因为攻击合约中没有""方法,fallback中并实现调用withdraw方法
// call一直执行,一直触发fallback
(bool sent, ) = msg.sender.call{value: bal}("");
require(sent, "Failed to send Ether");
// 这里的修改余额应该放到发送eth步骤之前,这样反复攻击时前面的balance校验就会不通过
balances[msg.sender] = 0;
}

  1. 上海升级后,每个区块的 gas 限制是多少?
    答:在以太坊上,每个区块的 gas limit 是由矿工决定的。在上海升级后,每个区块的 gas limit 已经从 15,000,000 增加到了 30,000,000

  2. 什么阻止无限循环永远运行?
    答:这个问题原文是:What prevents infinite loops from running forever?
    如果理解成:是什么不让无限循环永远执行的话?答案就很明显,方法会受到gas费限制,栈深度的限制,所以循环不会一直永远运行,会报out of gas错误。
    如果题目理解成:做什么可以阻止无限循环。那就是代码实现,在循坏中使用计数器:require判断是否达到满足中断循坏次数。(感谢评论区指出错误)

  3. tx.origin 和 msg.sender 之间有什么区别?
    答:tx.orgin是这笔交易的发起者,msg.sender是前一个调用发起者,如交易流程:张三发起交易-合约A-合约B,在合约B中msg.sender = 合约A,tx.orgin = 张三

  4. 如何向没有payable 函数、receive 或回退的合约发送以太?
    答:这里是评论区给出的答案
    ①通过其他合约自毁将自毁的eth发送目标合约
    ②将挖矿获取的区块奖励费用的接收者设置为目标合约地址
    ③将Beacon信标链上质押后的提现地址设置为目标合约地址
    这里补充一个:在solidity0.4之前的合约,也可以直接接收ETH。

  5. view 和 pure 之间有什么区别?
    答:view和pure都是函数的修饰符,view可以访问合约中的状态变量,不能修改;pure不能访问也不能修改状态变量。

  6. ERC721 中的 transferFrom 和 safeTransferFrom 之间有什么区别?
    答:transferFrom和safeTransferFrom都是ERC721合约中的函数,用于将代币从一个地址转移到另一个地址。它们之间的区别在于,safeTransferFrom函数会检查接收合约是否实现了onERC721Received函数,并且该函数返回的值是否为特定的魔数。如果接收合约没有实现onERC721Received函数或者返回的值不是特定的魔数,safeTransferFrom函数将抛出异常并回滚所有更改,以确保代币不会永久丢失。而transferFrom函数则不会进行这种检查,因此可能会导致代币永久丢失。

  7. 如何将 ERC1155 代币转换为非同质化代币?
    答:ERC1155代币是一种可替代和不可替代的代币,可以在同一合约中管理多个资产。ERC721代币是一种非可替代代币,每个代币都是唯一的。因此,将ERC1155代币转换为ERC721代币需要将每个ERC1155代币转换为一个唯一的ERC721代币。
    要将ERC1155代币转换为ERC721代币,您需要执行以下步骤:
    1.创建一个新的ERC721合约。
    2.为每个ERC1155代币创建一个新的ERC721代币。
    3.将ERC1155代币的所有权转移到新的ERC721代币。
    4.销毁原始ERC1155代币

  8. 访问控制是什么,为什么重要?
    答:访问控制是一种重要的机制,用于限制对智能合约的访问。通过使用访问控制,您可以确保只有经过授权的用户才能执行特定操作或访问敏感信息。这可以帮助保护您的智能合约免受未经授权的访问和攻击。
    Solidity提供了几种访问控制修饰符,例如public、private、internal和external。这些修饰符用于控制函数和状态变量的可见性和访问权限。

  9. 修饰符(modifier)的作用是什么?
    答:modifier是一种函数修饰符,用于声明一个函数修改器。函数修改器的作用与Spring中的切面功能很相似,当它作用于一个函数上,可以在函数执行前或后(依赖于具体实现)预先执行modifier中的逻辑,以增强其功能。使用modifier可以将一些通用的操作提取出来,提高编码效率,降低代码耦合性。
    modifier可以用于控制函数的访问权限和行为。例如,如果您希望只有合约的所有者才能访问某个函数,您可以将该函数标记为private,并使用modifier来检查调用者是否为合约的所有者。如果调用者不是合约的所有者,则函数将停止执行并回滚所有更改。这可以帮助保护合约免受未经授权的访问和攻击。

contract MyContract {
address public owner;

modifier onlyOwner() {require(msg.sender == owner, "Only the contract owner can call this function.");_; // 这里才执行myFunction代码
}function myFunction() public onlyOwner {// do something
}

}

  1. uint256 可以存储的最大值是多少?
    答:最大2^256-1 。为什么减1?0占用了一个空间,256位的表示的数是从000...00到111...11 。例如:如果有uint2 , 那么2^2可存储的数可以是 00 = 0 ,01 = 1,10 = 2,11 = 3,这里最大是3,也就是2^-1,而不是4。

  2. 什么是浮动利率和固定利率?
    答:固定利率是指在贷款期间内,贷款利率保持不变。而浮动利率是指贷款利率会随着市场利率的变化而变化。浮动利率通常基于某个基准利率,例如央行基准利率或LIBOR(伦敦银行间同业拆借利率)。如果基准利率上升,贷款利率也会上升,反之亦然。
    send函数的gas限制也为2300 gas,但它返回一个布尔值,指示转账是否成功。如果转账失败,它将返回false。但是,如果接收方合约没有实现fallback函数,或者fallback函数消耗的gas超过了2300,那么转账将失败并回滚所有更改。

solidity面试题(二)

stephenCheung stephenCheung 发布于 2024-01-16 08:30
这是根据一个面试题给出的问题总结 https://learnblockchain.cn/article/7076,我自己附上答案。另外还有问题或者答案里涉及到一些概念,在文中也会把它变成一个问题。这里是中级的答案,如有不正确的地方,请在评论区指正。

  1. transfer 和 send 之间有什么区别?为什么不应该使用它们?
    答:transfer和send函数都是用于将以太币从一个地址转移到另一个地址的函数。它们之间的区别在于它们的gas限制不同,这可能会导致一些安全问题。因此,您应该使用call函数来转移以太币,但是会有重入攻击风险。

详细:(transfer函数的gas限制为2300 gas,这意味着如果接收方合约没有实现fallback函数,或者fallback函数消耗的gas超过了2300,那么转账将失败并回滚所有更改。这可以防止重入攻击,但也可能导致一些问题,例如无法向某些合约发送以太币。
send函数的gas限制也为2300 gas,但它返回一个布尔值,指示转账是否成功。如果转账失败,它将返回false。但是,如果接收方合约没有实现fallback函数,或者fallback函数消耗的gas超过了2300,那么转账将失败并回滚所有更改。
由于这些限制,transfer和send函数已经被认为是不安全的,因此不应该使用它们。相反,您应该使用call函数来转移以太币。call函数没有gas限制,可以向任何地址发送以太币,并且可以指定要发送的gas数量。但是,您应该小心使用call函数,因为它可能会导致一些安全问题,例如重入攻击。)

  1. 如何在 Solidity 中编写节省gas的高效循环?
    答:
    1.避免无限循环。
    2.避免重复计算:多次计算相同的值,应该将该值存储在变量中,并在需要时重复使用该变量。
    3.避免使用昂贵的操作:例如除法和取模。尝试使用更便宜的操作来代替它们。
    4.避免使用大型数组:如果可能的话,您应该使用映射或其他数据结构来代替数组。
    5.避免使用复杂的嵌套循环
    6.使用constant和immutable关键字:如果您需要在循环中使用常量或不可变变量,请使用constant和immutable关键字来声明它们。

  2. 代理合约中的存储冲突是什么?
    答:存储冲突是指多个合约尝试访问同一存储位置时发生的问题。当多个合约同时尝试更新同一存储位置时,可能会发生存储冲突,导致数据不一致或合约无法正常工作。为了避免这种情况,使用代理合约来管理存储位置,并确保只有一个合约可以访问每个存储位置。代理合约可以充当存储位置的所有者,并使用访问控制机制来限制对存储位置的访问。这可以帮助确保数据的一致性,并提高智能合约的安全性和可靠性。

  3. abi.encode 和 abi.encodePacked 之间有什么区别?
    答:
    abi.encode会在每个参数之间添加填充,以确保每个参数占用32个字节。这可以确保编码后的字节数组具有固定的长度,并且可以正确地解码回原始参数。但是,由于填充的存在,编码后的字节数组可能会比实际需要的更大。
    abi.encodePacked不会添加填充,而是将所有参数拼接在一起。这可以确保编码后的字节数组尽可能小,但是可能会导致解码时出现问题,因为无法确定每个参数的确切位置。

  4. uint8、uint32、uint64、uint128、uint256 都是有效的 uint 大小。还有其他的吗?
    答:这些类型分别表示8位、32位、64位、128位和256位的无符号整数。除了这些类型之外,Solidity还支持其他整数类型,例如int8、int16、int32、int64、int128和int256。这些类型分别表示8位、16位、32位、64位、128位和256位的有符号整数。

  5. 在权益证明之前后,block.timestamp 发生了什么变化?
    答:在PoW协议中,block.timestamp表示矿工开始挖掘新块的时间戳。在PoS协议中,block.timestamp表示验证器开始验证新块的时间戳。因此,block.timestamp的含义在两种协议中都与块的创建时间有关,但在PoS协议中,它与验证器的行为有关,而不是矿工的行为有关。

  6. 什么是抢跑(frontrunning)?
    答:抢跑(frontrunning)是一种攻击行为,指在一笔正常交易等待打包的过程中,抢跑机器人通过设置更高 Gas 费用抢先完成攻击交易。在所有 Front-Running 中,最典型最具危害性的就是针对 AMM 交易的 Sandwich Attacks (三明治攻击)
    注意:夹子交易有时也成三明治攻击,但是它是有矿工或验证节点端完成的,能把被攻击的那笔交易夹在中间打包区块。

  7. 什么是提交-揭示方案,何时使用它?
    答:提交-揭示方案(Commit-Reveal Scheme)是一种用于在区块链上进行投票或竞标的协议。该协议的目的是防止参与者在提交投票或竞标之前查看其他参与者的提交,从而保护投票或竞标的公正性。
    具体:

提交-揭示方案的基本思想是将投票或竞标分为两个阶段:提交阶段和揭示阶段。在提交阶段,参与者将加密的投票或竞标提交到智能合约中。在揭示阶段,参与者揭示他们的加密投票或竞标,并将其与提交阶段中的哈希值进行比较。如果哈希值匹配,则投票或竞标被接受。否则,投票或竞标将被拒绝。
提交-揭示方案可以防止参与者在提交投票或竞标之前查看其他参与者的提交,从而保护投票或竞标的公正性。它还可以防止恶意参与者在提交阶段提交虚假的投票或竞标,因为他们无法预测其他参与者的投票或竞标。
提交-揭示方案通常用于加密货币中的投票或竞标,例如DAO(去中心化自治组织)的投票。它也可以用于其他需要保护公正性的场景,例如拍卖和投标。
9. 在什么情况下,abi.encodePacked 可能会产生漏洞?
答:abi.encodePacked可能会产生漏洞,因为它不会在参数之间添加填充,而是将所有参数拼接在一起。这可能会导致哈希碰撞,从而使攻击者能够伪造交易或执行其他恶意操作。例如,如果攻击者知道您使用abi.encodePacked来编码交易数据,他们可以构造一个具有相同哈希值的交易,从而欺骗您的智能合约。
为了避免这种情况,用abi.encode来编码交易数据,因为它会在每个参数之间添加填充,以确保每个参数占用32个字节。这可以防止哈希碰撞,并提高智能合约的安全性。

  1. 以太坊如何确定 EIP-1559 中的 BASEFEE?
    答:在以太坊的EIP-1559协议中,BASEFEE是由以太坊网络根据交易需求和区块大小动态调整的。BASEFEE的计算方式是通过一个名为“基础费用追踪器”的算法来实现的。该算法会根据当前区块的交易需求和区块大小来动态调整BASEFEE,以确保交易能够在合理的时间内得到确认。
    具体来说,基础费用追踪器会根据当前区块的交易需求和区块大小计算出一个目标基础费用。如果当前的BASEFEE高于目标基础费用,那么BASEFEE将会下降。如果当前的BASEFEE低于目标基础费用,那么BASEFEE将会上升。这种动态调整机制可以确保BASEFEE始终能够反映当前的交易需求和区块大小,从而提高以太坊的交易效率和可靠性。

  2. 冷读(cold read)和热读(warm read)之间有什么区别?
    答:冷读(cold read)和热读(warm read)是两种不同的访问存储变量的方式。冷读是指第一次读取存储变量时,需要从存储中读取变量的值,这需要较高的gas费用。而热读是指在第一次读取存储变量之后,再次读取存储变量时,可以从缓存中读取变量的值,这需要较低的gas费用。热读和冷读是由Ethereum虚拟机(EVM)自动处理的。

  3. AMM 如何定价资产?
    答:通过恒定乘积算法,a * b = k ,兑换 m个a 需要的b数量算法 = k/(a+m) - b ,这里没计算手续费。

  4. 代理中的函数选择器冲突是什么,它是如何发生的?
    答:函数选择器是一个用于标识函数的哈希值。在Solidity中,当您调用一个合约中的函数时,您需要提供该函数的选择器。函数选择器由函数名称和参数类型组成,并使用Keccak-256哈希算法进行哈希处理。当您在代理合约中调用另一个合约的函数时,您需要将该函数的选择器传递给代理合约。如果您在代理合约中定义了具有相同名称和参数类型的函数,则会发生函数选择器冲突。这意味着当您调用代理合约中的函数时,Solidity无法确定您要调用哪个函数,因为它们具有相同的函数选择器。为了避免函数选择器冲突,您可以使用不同的函数名称或参数类型来定义代理合约中的函数。或者使用管理员校验来调用,只有管理员才调用代理合约定义的函数。

  5. payable 函数对 gas 的影响是什么?
    答:payable函数是一种特殊类型的Solidity函数,它允许合约接受以太币作为支付。当您在Solidity中定义一个payable函数时,您可以在函数调用中包含以太币,并将其存储在合约的余额中。由于以太币是一种有价值的加密货币,因此在调用payable函数时需要支付一定的gas费用。这是因为在以太坊网络中,每个操作都需要消耗一定数量的gas,以保证网络的安全性和可靠性。
    当您在Solidity中使用payable函数时,需要注意以下几点:

确保您的合约具有足够的余额来处理以太币支付。
确保您的合约具有足够的gas来处理以太币支付。
确保您的合约具有足够的安全性来处理以太币支付。
15. 什么是签名重放攻击?
答:签名重放攻击是一种网络攻击,攻击者通过重复使用已经被验证的数字签名来欺骗系统。数字签名是一种用于验证数据完整性和身份验证的技术,它使用公钥密码学来生成和验证签名。在签名重放攻击中,攻击者截获了一个数字签名并将其重复使用,以便在未经授权的情况下执行某些操作。例如,攻击者可以使用重放攻击来多次执行某个交易,从而导致资金损失。为了防止签名重放攻击,您可以使用以下方法:
使用时间戳或随机数来确保每个数字签名只能使用一次。
使用序列号来确保数字签名按顺序使用。
使用加密协议来保护数字签名,例如TLS或SSL。

  1. 什么是 gas griefing ?
    答:Gas griefing是一种智能合约攻击,攻击者通过发送恰好足够的gas来执行主要智能合约,但未为其子调用或外部通信提供足够的gas,从而导致未受控制的行为并在某些情况下对合约的业务逻辑造成严重破坏1. 这种攻击可能会导致合约的不可预测行为,例如无限循环或拒绝服务攻击。为了防止gas griefing攻击,您可以使用以下方法:

在智能合约中检查子调用所需的gas量,并确保为其提供足够的gas。
使用安全的编程实践来编写智能合约,例如避免使用循环和递归等高消耗操作。
使用Solidity的require和assert语句来确保智能合约的正确性和安全性。
17. 自由内存指针是什么,它存储在哪里?
答:自由内存指针是指指向已经释放的内存地址的指针。当您释放内存时,内存中的数据并不会被删除,而是被标记为可用。如果您在稍后的时间内使用已经释放的内存地址,您可能会访问到已经被其他程序或数据覆盖的数据。这可能会导致程序崩溃或产生不可预测的行为。自由内存指针通常是由于编程错误或不正确的内存管理而引起的。为了避免自由内存指针,您可以使用以下方法:

在释放内存后,将指针设置为NULL或0。
使用动态内存分配函数(例如malloc和calloc)来分配内存,并使用free函数来释放内存。
使用智能指针或垃圾回收器等工具来管理内存。
18. 接口中有效的函数修饰符有哪些?
答:在Solidity中,接口是一种抽象合约,它定义了合约应该实现的函数。接口中的函数没有实现,只有函数签名。函数签名包括函数名称、参数类型和返回类型。接口中的函数可以使用以下修饰符:
external:指定函数只能从合约外部调用。
view:指定函数不会修改合约状态。
pure:指定函数既不会修改合约状态,也不会读取合约状态。
payable:指定函数可以接受以太币作为支付。

  1. 函数参数中的 memory 和 calldata 有什么区别?
    答:memory:用于声明函数参数将被存储在内存中。内存中的数据只在函数执行期间存在,执行完毕后就被销毁。在函数内部,您可以使用memory关键字来创建临时变量,但是不能在函数之外使用它们。在函数调用期间,函数参数的值将从调用方复制到内存中,并在函数执行完毕后被销毁。
    calldata:用于声明函数参数将被存储在调用数据区域中。调用数据区域是一个不可修改的区域,用于保存函数参数。在函数内部,您可以使用calldata关键字来访问函数参数,但是不能在函数之外使用它们。在函数调用期间,函数参数的值将从调用方复制到调用数据区域中,并在函数执行完毕后被销毁。

  2. 描述三种存储 gas 成本类型。
    答:
    内存变量:内存变量是指在函数执行期间分配的临时变量。它们的gas成本取决于它们的大小,通常比存储变量更便宜。在函数执行结束后,内存变量将被销毁。
    存储变量:存储变量是指在合约存储器中永久存储的变量。它们的gas成本取决于它们的大小,通常比内存变量更昂贵。存储变量的gas成本还取决于它们的位置,例如,如果它们位于映射中,则访问映射中的元素的成本更高。
    状态变量:状态变量是指在合约存储器中永久存储的变量,但它们是在合约创建时定义的。与存储变量相比,它们的gas成本更便宜,因为它们只需要在合约创建时初始化一次。

  3. 为什么可升级合约不应该使用构造函数?
    答:使用构造函数来初始化合约状态变量是一种常见的做法,但是这会导致合约的状态变量无法升级。因为在升级合约时,新的合约代码将会被部署到一个新的地址,而旧的合约状态变量将无法被传递到新的合约中。因此,为了使合约状态变量能够升级,应该使用初始化函数来初始化合约状态变量,而不是构造函数。初始化函数可以在合约部署后随时调用,因此可以在升级合约时重新初始化合约状态变量。

  4. UUPS 和 Transparent Upgradeable Proxy 模式之间有什么区别?
    答:在Transparent Upgradeable Proxy模式中,代理合约只负责将所有调用转发到实现合约,并将实现合约的地址存储在代理合约的状态变量中。在升级合约时,新的实现合约将被部署到新的地址,然后将新的实现合约的地址存储在代理合约的状态变量中。这种方法的缺点是,每次升级合约时都需要部署一个新的代理合约。
    相比之下,UUPS代理模式使用了更加智能的方法。在UUPS代理模式中,代理合约只负责将所有调用转发到实现合约,并将实现合约的地址存储在代理合约的状态变量中。在升级合约时,只需要将新的实现合约的代码上传到现有的代理合约地址,而无需部署新的代理合约。这种方法的优点是,可以在不更改代理合约地址的情况下升级合约,从而避免了每次升级合约时都需要部署一个新的代理合约的问题。

  5. 如果合约通过 delegatecall 调用一个空地址或之前已自毁的实现,会发生什么?如果是常规调用而不是 delegatecall 呢?
    答:如果合约通过delegatecall调用一个空地址或之前已自毁的实现,会导致delegatecall返回false,并且不会发生任何状态更改。如果是常规调用而不是delegatecall,则会导致交易失败并回滚,因为您不能调用一个不存在的合约或已自毁的合约。

  6. ERC777 代币存在什么危险?
    答:ERC777 代币是一种功能型代币,它在 ERC20 标准的基础上进行了改进,解决了一些 ERC20 标准存在的问题。ERC777 代币的主要优势是它支持发送代币时携带额外的信息,同时也支持代币的操作员功能。此外,ERC777 代币还可以通过 ERC1820 接口注册表合约来实现代币转账的监听,增强了代币的安全性.
    虽然 ERC777 代币有很多优点,但是它也存在一些潜在的危险。由于 ERC777 代币是一种相对较新的代币标准,因此它的生态系统相对较小,可能存在一些安全漏洞。此外,ERC777 代币的操作员功能也可能会被滥用,导致代币被盗或者其他安全问题。因此,在使用 ERC777 代币时,需要谨慎选择代币合约,同时也需要注意代币的安全性.

  7. 根据 Solidity 风格指南,函数应该如何排序?
    答:函数应根据其可见性和顺序进行分组:
    构造函数
    receive 函数(如果存在)
    fallback 函数(如果存在)
    外部函数(external)
    公共函数(public)
    内部函数(internal)
    私有函数(private)
    在一个分组中,把 view 和 pure 函数放在最后。

  8. 根据 Solidity 风格指南,函数修饰符应该如何排序?
    答:函数修改器的顺序应该是:
    可见性(Visibility)
    可变性(Mutability)
    虚拟(Virtual)
    重载(Override)
    自定义修改器(Custom modifiers)
    function balance(uint from) public view override onlyowner returns (uint) { // onlyowner是自定义修改器
    return balanceOf[from];
    }

  9. 什么是债券曲线(bonding curve)?
    答:债券曲线是指一种数学模型,用于描述债券价格与到期时间之间的关系。它通常是一个连续的曲线,横坐标表示债券的到期时间,纵坐标表示债券的价格。债券曲线可以用来计算债券的收益率、估算债券价格以及预测市场利率的变化等。在加密货币领域,债券曲线也被用于构建一些新型的金融工具,如预测市场价格的算法交易、代币发行等。

  10. OpenZeppelin ERC721 实现中的 safeMint 与 mint 有何不同?
    答:在OpenZeppelin ERC721实现中,safeMint和mint都是用于创建新的ERC721代币的函数,但它们之间有一些区别。mint函数只是简单地创建一个新的ERC721代币,并将其分配给指定的地址。而safeMint函数则会在创建新的ERC721代币之前检查目标地址是否支持ERC721转移。如果目标地址不支持ERC721转移,则safeMint函数会抛出异常并阻止创建新的ERC721代币。因此,safeMint函数比mint函数更安全,因为它可以防止ERC721代币被锁定在不支持ERC721转移的合约中。

  11. Solidity 提供哪些关键字来测量时间?
    答:

now:返回当前区块的时间戳(以秒为单位)。
时间单位:Solidity提供了几个时间单位,包括seconds、minutes、hours、days、weeks和years。这些单位可以与数字一起使用,例如5 minutes或1 hours。
block.timestamp:与now关键字类似,返回当前区块的时间戳。
block.number:返回当前区块的块号。
30. 什么是三明治(sandwich)攻击?
答:三明治攻击是去中心化的一种攻击手段,主要出现在swap交易中。黑客节点把用户的交易夹在中间,前后是攻击者的交易,利用用户的交易改变价格,获取价差套利。例如用户买入eth, 黑客会在创建一个eth买单,和卖单,然后打包交易,区块内的交易顺序是:黑客买单 - 用户买单 - 黑客卖单。导致用户真正的买入价格偏高。解决:可设置低滑点,如果价格超出预算范围交易失败。

  1. 如果向一个会回滚的函数进行 delegatecall,delegatecall 会怎么做?
    答:如果向一个会回滚的函数进行 delegatecall,delegatecall 会返回 false,并且不会发生任何状态更改。如果是常规调用而不是 delegatecall,则会导致交易失败并回滚,因为您不能调用一个不存在的合约或已自毁的合约。

  2. 乘以和除以二的倍数的 gas 高效替代方法是什么?
    答:在Solidity中,将一个数乘以或除以2的倍数可以通过移位运算来实现,这比使用乘法或除法运算更高效。具体来说,将一个数左移n位相当于将它乘以2的n次方,而将一个数右移n位相当于将它除以2的n次方。例如,将一个数除以8可以通过将它右移3位来实现,而将一个数乘以16可以通过将它左移4位来实现。在Solidity 0.8.3及更高版本中,编译器会自动将乘法和除法运算转换为移位运算,从而提高代码的效率。

  3. 多大 uint 可以与一个地址在一个槽中?
    答:一个地址是20个字节(160位),而一个uint256是32个字节(256位), 256-160 = 96。uint96及以下的都可以

  4. 哪些操作会部分退还 gas?
    答:
    SSTORE 操作:如果将一个存储槽从非零值更改为零值,则会退还一部分gas。
    SLOAD 操作:如果从存储中读取一个非零值,则会退还一部分gas。
    CALL 操作:如果调用的合约执行成功,则会退还一部分gas。
    RETURN 操作:如果从函数中返回,则会退还一部分gas。
    SELFDESTRUCT 操作:如果自毁合约,则会退还其余的gas。
    需要注意的是,这些操作的退还gas的数量是固定的,具体取决于操作的类型和执行的环境。

  5. ERC165 作用于什么?
    答:ERC165是一个标准,用于检测和发布智能合约实现的接口。它标准化了如何识别接口,如何检测它们是否实现了ERC165或其他接口,以及合约将如何发布它们实现的接口。它可以帮助您查询合约实现的特定接口,以及更重要的是,该智能合约实现的版本。

  6. 如果代理对 A 进行 delegatecall,而 A 执行 address(this).balance,返回的是代理的余额还是 A 的余额?
    答:返回的是代理的余额。因为在 delegatecall 中,调用的A会共享代理合约的存储空间,因此 address(this) 将返回代理合约的地址,而不是被调用合约的地址。

  7. 滑点参数有什么用?
    答:滑点参数是指在交易时允许的价格波动范围。在去中心化交易所(DEX)中,滑点参数通常用于保护交易免受价格波动的影响。如果价格波动超过了滑点参数的范围,交易将被取消。滑点参数可以帮助交易者在不受过度波动的影响下进行交易,并减少交易失败的风险。

  8. ERC721A 如何减少铸造成本?有什么权衡?
    答:ERC721A是一个改进的ERC721标准,旨在通过减少铸造成本来提高NFT的可扩展性。ERC721A通过引入批量铸造API来实现这一点,从而将铸造成本降低到O(1)的时间复杂度。与OZ的单独铸造方式不同,ERC721A的批量铸造API可以同时铸造多个NFT,而不需要循环调用单独的铸造方法。这种方法可以显著减少铸造成本,但需要权衡的是,它可能会降低NFT的安全性。

  9. 为什么 Solidity 不支持浮点数运算?
    答:Solidity不支持浮点数运算是因为浮点数运算需要大量的计算资源,而以太坊虚拟机的计算资源是有限的。此外,浮点数运算可能会导致精度丢失和舍入错误,这可能会影响智能合约的正确性和安全性。为了避免这些问题,Solidity使用整数运算来代替浮点数运算。如果您需要进行浮点数运算,可以使用一些库,如FixedPoint.sol,来模拟浮点数运算。这些库使用整数运算来实现浮点数运算,从而避免了精度丢失和舍入错误的问题。

  10. 什么是 TWAP?
    答:TWAP(Time Weighted Average Price)时间加权平均价格是一种最简单的传统算法交易策略之一。该算法将交易时间均匀分割,并在每个分割节点上将均匀拆分的订单进行提交。其目的是使交易对市场影响减小的同时提供一个较低的平均成交价格,从而达到减小交易成本的目的。在分时成交量无法准确估计的情况下,该模型可以较好地实现算法交易的基本目的。

  11. Compound Finance 如何计算利用率?
    答:Compound Finance 是一个算法化的、自治的利率协议,旨在为开发者解锁一系列开放式金融应用。该协议的利用率是指借款人从 Compound 借入资产的数量与抵押品价值的比率。具体地,它是通过将所有借款人的借款总额除以所有抵押品的总价值来计算的。这个比率越高,代表 Compound 的借款人越多,市场上的资金也越紧张。(从compound中借款,可通过增加抵押品价值降低借款利率)。

solidity面试题(三)

  1. 以太坊预编译合约的地址是什么?
    答:由于 EVM 是一个基于堆栈的虚拟机,它根据交易所要执行的操作指令内容来计算 gas 消耗,如果计算非常复杂,在 EVM 中执行相关操作指令就会非常低效,而且会消耗大量的 gas。 例如,在 zk-snark 中,需要对椭圆曲线进行加减运算和配对运算。 在 EVM 中执行这些操作是非常复杂和不现实的。所幸以太坊还支持预编译合约。
    预编译合约是 EVM 中用于提供更复杂库函数(通常用于加密、散列等复杂操作)的一种折衷方法,这些函数不适合编写操作码。 它们适用于简单但经常调用的合约,或逻辑上固定但计算量很大的合约。 预编译合约是在使用节点客户端代码实现的,因为它们不需要 EVM,所以运行速度很快。 与使用直接在 EVM 中运行的函数相比,它对开发人员来说成本也更低。
    在代码层面,所谓的地址实际上是合约数组的索引,每一个索引唯一对应一个预编一个合约。

  2. 当函数数量超过 4 个时,Solidity 如何管理函数选择器?
    答:函数选择器是用来标识函数的 4 字节哈希值。当函数数量超过 4 个时,Solidity 会使用函数的哈希值来管理函数选择器。具体来说,Solidity 会将函数的哈希值与函数的签名进行映射,以便在编译时生成正确的函数选择器。在运行时,Solidity 会使用函数的哈希值来查找函数选择器,以便正确地调用函数。

  3. 如果对一个合约进行委托调用,而该合约又对另一个合约进行委托调用,那么在代理合约、第一个合约和第二个合约中,msg.sender 是谁?
    答:是调用代理合约的人,也就是代理合约中的msg.sender.

  4. 如果有的话,ABI 编码在 calldata 和 memory 之间有何不同?
    答:在 Solidity 中,ABI 编码是一种用于序列化和反序列化函数参数和返回值的标准格式。在函数调用时,函数参数被编码为 ABI 编码格式,并存储在 calldata 中。在函数内部,可以使用 msg.data 访问 calldata 中的数据。与之相反,memory 是 Solidity 中的一种数据位置,用于存储动态分配的内存。在函数内部,可以使用 memory 来存储和操作临时变量和数组。因此,ABI 编码和 memory 是两个不同的概念,它们之间没有直接的关系。

  5. uint64 和 uint256 在 calldata 中的 ABI 编码有何不同?
    答:在 Solidity 中,uint64 和 uint256 在 calldata 中的 ABI 编码有所不同。 当使用 uint64 时,编码器会将其填充到 32 字节,以与 EVM 的字长对齐。这样可以确保数据布局的协调,简化 EVM 中的操作。当使用 uint256 时,编码器不需要额外的填充,因为它的字长与 EVM 的字长完全相同。可用abi.encodePacked (number),看一下uint64 number和 uint256 number的输出结果

  6. 什么是只读重入?
    答:只读重入是一种攻击方式,它利用了智能合约中的函数重入漏洞。攻击者会在调用一个只读函数时,通过在函数执行期间调用另一个函数来重入合约。由于只读函数不会修改合约状态,因此它们可以在调用期间被重入,而不会引起任何问题。然而,如果攻击者在重入期间调用了一个写入函数,那么他们就可以修改合约状态并窃取资金。

  7. 为了防止只读重入攻击,开发者可以采取以下措施:
    避免在只读函数中调用写入函数,以防止重入攻击。
    使用 modifier 来限制只读函数的访问权限,以确保只有授权用户才能调用它们。
    使用最新版本的 Solidity 编译器,因为它们包含了针对函数重入漏洞的修复。

  8. 从不受信任的智能合约调用中读取(内存)字节数组的安全考虑是什么?
    答:从不受信任的智能合约中读取(内存)字节数组可能会导致安全问题。攻击者可以通过构造恶意合约来读取合约中的敏感数据,例如私钥、密码等。为了防止这种情况发生:
    使用 Solidity 中的 view 和 pure 函数来限制合约的访问权限。这些函数不会修改合约的状态,因此不会对合约中的数据造成任何影响。
    使用 require 和 assert 函数来确保输入的数据符合预期,从而防止攻击者利用缺陷来读取合约中的数据。
    使用加密技术来保护合约中的敏感数据,例如使用对称加密或非对称加密算法来加密数据。

  9. 如果部署一个空的 Solidity 合约,在区块链上会有什么字节码?
    答:部署一个空的 Solidity 合约,它将在区块链上占用一些空间,但不会有任何字节码。这是因为空合约不包含任何代码,因此它不需要在区块链上存储任何字节码。但是,即使是空合约也需要在区块链上分配地址,以便其他合约可以与它进行交互。因此,空合约将在区块链上占用一些空间,但这个空间非常小,可以忽略不计。

  10. 以太虚拟机如何定价内存使用?
    答:在以太坊中,内存使用的定价是动态的,它取决于当前内存使用情况。内存使用的定价是通过一种称为“内存燃料”的机制来实现的。内存燃料是一种虚拟的资源,它用于衡量内存使用的成本。在每个交易中,内存燃料的数量都是有限的,这意味着在使用大量内存时,交易的成本会相应地增加。内存燃料的价格是由矿工设置的,他们可以根据当前的内存使用情况来调整价格。EIP1559之后,价格由以太坊网络动态决定。

  11. 智能合约的元数据部分存储了什么?
    答:智能合约的元数据部分存储了一些关于合约的信息,例如合约的名称、版本、作者、编译器版本、编译时间等。这些信息可以帮助开发人员更好地理解和使用合约。此外,元数据还包括合约的ABI(应用程序二进制接口),它定义了与合约交互的方法和参数。具体可以看编译产生的_meta.json文件

  12. 从 MEV 的角度来看,什么是叔块攻击?
    答:从MEV(最大可提取价值)的角度来看,叔块攻击是指重新挖掘先前已经挖掘过的区块,以获取最大的套利机会并从协议的激励中获益,这可能会导致网络的显着不稳定性。攻击者可以通过在叔块中看到某些交易并将其前置来优化其收益,从而利用MEV。这种攻击可能会导致交易的顺序发生变化,从而影响交易的结果

  13. 如何进行签名篡改攻击(malleability attack)?
    答:签名篡改攻击是指攻击者通过更改交易的签名,从而生成一个新的交易,而不改变交易的有效性和语义。这种攻击可以导致交易的重放、取消或其他不良后果。攻击者可以通过多种方式进行签名篡改攻击,例如更改签名的字节、添加或删除签名的字节、更改签名的哈希值等。攻击者可以利用这些漏洞来欺骗合约,从而获得不当利益。
    要进行签名篡改攻击,攻击者需要知道交易的签名,并且需要对签名进行修改。攻击者可以使用一些工具来执行这些操作,例如Bitcoin Core中的signrawtransaction命令。为了防止签名篡改攻击,开发人员可以使用一些技术,例如使用随机数生成签名、使用哈希函数对交易进行哈希、使用公钥加密等。这些技术可以增强交易的安全性,从而防止签名篡改攻击。

  14. 在什么情况下,具有前导零的地址可以节省 gas,以及为什么?
    答:在以太坊中,具有前导零的地址可以节省gas费用。这是因为在以太坊中,地址是由20个字节的哈希值表示的。如果地址的前几个字节为零,则可以省略这些零,从而减少交易的大小,进而减少gas费用。
    例如,假设我们有一个地址为0x0000000000000000000000000000000000000000。如果我们将其转换为十六进制字符串,我们可以看到它的前12个字符都是零。在以太坊中,我们可以省略这些零,只使用0x0来表示该地址。这样,我们就可以减少交易的大小,从而减少gas费用。
    需要注意的是,具有前导零的地址只能在特定情况下节省gas费用。如果地址的前几个字节不是零,那么省略这些字节将不会节省任何gas费用。此外,省略前导零可能会降低地址的可读性,因此需要在可读性和gas费用之间进行权衡。

  15. payable(msg.sender).call{value: value}("")和 msg.sender.call{value: value}("")之间有什么区别?
    答:payable(msg.sender).call{value: value}("")和msg.sender.call{value: value}("")之间的区别在于前者将msg.sender转换为address payable类型,而后者不会。在Solidity 0.8.0及更高版本中,msg.sender和tx.origin的类型都是address,而不是address payable。因此,如果您想使用msg.sender或tx.origin来接收以太币,您需要将它们转换为address payable类型,例如:payable(msg.sender).transfer(amount)或payable(tx.origin).transfer(amount)。

  16. 一个字符串占用多少个存储槽?
    答:字符串是动态大小的,因此其存储空间也是动态分配的。字符串的存储空间由其长度和一个额外的 32 字节的存储槽组成,用于存储字符串的长度。因此,字符串的存储空间可以计算为:
    32 + (string_length + 31) / 32 * 32
    其中 string_length 是字符串的长度。例如,如果字符串的长度为 10,则其存储空间为 64 字节。请注意,这个公式只适用于字符串存储在状态变量中的情况。如果字符串存储在内存或 calldata 中,则不需要额外的存储槽。
    字符串不超31个字节的占一个存储槽,最低位存储字符串长度,所以一个槽最多存31个字节。

  17. Solidity 编译器中的--via-ir 功能是如何工作的?
    答:--via-ir 是 Solidity 编译器的一个选项,它可以启用基于中间代码(IR)的代码生成器。使用 IR 代码生成器,Solidity 可以生成 Yul 中间代码,然后再将其转换为 EVM 字节码。这种方法的优点是,它可以实现更强大的跨函数优化通道,同时也使代码生成更加透明和可审计。
    您可以在命令行中使用 --via-ir 或在 standard-json 中使用 {"viaIR": true} 选项来启用基于 IR 的编码。在 Hardhat 中,可以在 hardhat.config.js 的 settings 字段下加入配置,如:

solidity: {
version: "0.8.17",
settings: {
"viaIR": true, //配置启用IR
optimizer: {
enabled: true,
runs: 1000,
},
},
},

  1. 函数修饰符是从右到左调用还是从左到右调用,还是不确定的?
    答:Solidity中的函数修饰符是从右到左调用的,这是编译器决定的。

如果对一个合约进行委托调用,而执行了指令 CODESIZE,将返回哪个合约的大小?
答:如果您对一个合约进行委托调用,并执行指令 CODESIZE,则返回的是被委托的合约的大小。CODESIZE指令返回的是指定合约的代码大小,而不是当前合约的大小。因此,如果您在一个合约中进行委托调用,CODESIZE指令将返回被委托的合约的大小,而不是当前合约的大小。

  1. 为什么 ECDSA 对哈希而不是任意 bytes32 进行签名很重要
    答:ECDSA是一种数字签名算法,它使用椭圆曲线加密来生成公钥和私钥。在以太坊中,ECDSA被用于验证交易的签名。ECDSA对哈希进行签名十分重要,而不是对任意的bytes32签名,因为这样可以避免安全漏洞。
    如果ECDSA对任意的bytes32进行签名,那么攻击者可以构造一个新的bytes32,使得它的哈希值与原始bytes32的哈希值相同。这样,攻击者就可以使用原始签名来验证新的bytes32,从而导致安全漏洞。因此,ECDSA只对bytes32的哈希进行签名,以确保签名的安全性

  2. 符号操作测试( symbolic manipulation testing)是如何工作的。
    答:符号操作测试是一种基于符号计算的技术,用于在不实际执行程序的情况下探索程序的所有可能执行路径。在 Solidity 中,符号执行可以用于检测智能合约中的漏洞和错误。符号执行器将程序的输入参数表示为符号变量,然后通过解析程序的控制流图来生成程序的符号执行路径。这些路径可以用于检测程序中的漏洞和错误,例如整数溢出、未初始化的变量、未处理的异常等。

  3. 复制内存区域的最有效方式是什么?
    答:复制内存区域的最有效方式是使用 memcpy 函数。memcpy 函数可以将一个内存区域的内容复制到另一个内存区域。它的语法如下:

function memcpy(uint dest, uint src, uint len) internal {
// Copy word-length chunks while possible
for(; len >= 32; len -= 32) {
assembly {
mstore(dest, mload(src))
}
dest += 32;
src += 32;
}

// Copy remaining bytes
uint mask = 256 ** (32 - len) - 1;
assembly {let srcpart := and(mload(src), not(mask))let destpart := and(mload(dest), mask)mstore(dest, or(destpart, srcpart))
}

}

  1. 如何在链上验证另一个智能合约是否触发了一个事件,而不使用预言机?
    答:
    产生事件合约:

pragma solidity ^0.8.0;

contract MyContract {
event MyEvent(uint256 indexed id, string data);

function myFunction(uint256 id, string memory data) public {emit MyEvent(id, data);
}

}
验证事件合约:

pragma solidity ^0.8.0;

contract MyVerifier {
function verifyEvent(address contractAddress, uint256 id, string memory data) public view returns (bool) {
MyContract myContract = MyContract(contractAddress);
uint256 fromBlock = block.number - 100;
uint256 toBlock = block.number;
bytes32 eventHash = keccak256(abi.encodePacked("MyEvent(uint256,string)"));
bool eventFound = false;
for (uint256 i = fromBlock; i <= toBlock; i++) {
bytes32[] memory topics = new bytes32;
topics[0] = bytes32(eventHash);
topics[1] = bytes32(id);
bytes memory data = abi.encodePacked(contractAddress);
uint256[] memory eventIds = myContract.getPastEvents("MyEvent", i, i, data, topics);
if (eventIds.length > 0) {
eventFound = true;
break;
}
}
return eventFound;
}
}
在上面的代码中,MyVerifier 合约包含一个名为 verifyEvent 的函数,该函数用于验证另一个智能合约是否触发了 MyEvent 事件。该函数接受三个参数:contractAddress 是要验证的智能合约地址,id 是要验证的事件 id 参数,data 是要验证的事件 data 参数。该函数使用 getPastEvents 函数来检索事件日志,并检查是否存在与指定参数匹配的事件。

  1. 当调用 selfdestruct 时,以太何时转移?智能合约的字节码何时被擦除?
    答:当调用 selfdestruct 函数时,合约账户上剩余的以太币会发送给指定的目标地址.、合约的存储和代码从状态中被移除,但是合约的地址仍然存在.。合约的字节码不会被擦除,但是合约的地址不再被使用,因此合约的代码也不再被执行。

  2. 自由内存指针是什么?
    答:自由内存指针是 Solidity 中的一个指针,它指向当前合约的内存空间中的下一个可用位置。在 Solidity 中,内存是一种临时存储空间,用于存储临时变量和函数参数。当您在 Solidity 中声明一个变量时,它将被分配到内存中,并且自由内存指针将被更新以指向下一个可用位置。在 Solidity 中,您可以使用 mload 和 mstore 操作码来读取和写入内存中的数据。自由内存指针是 Solidity 中的一个重要概念,因为它允许您在内存中动态分配空间,从而更有效地使用内存。

  3. 在什么条件下,Openzeppelin 的 Proxy.sol 会覆盖自由内存指针?为什么这样做是安全的?
    答:在 Proxy.sol 中,如果使用 delegatecall 调用另一个合约,那么在调用结束后,自由内存指针将被覆盖。这是因为 delegatecall 会将调用的合约的代码复制到当前合约的内存中,然后执行它。在执行期间,自由内存指针将被覆盖,因为它指向当前合约的内存空间。这是安全的,因为在 delegatecall 结束后,当前合约的状态不会被修改,而只会修改被调用的合约的状态。

  4. 为什么 Solidity 废弃了"years"关键字?
    答:在 Solidity 0.5.0 版本之前,Solidity 中有一个名为 “years” 的时间单位关键字。然而,由于闰年的存在,使用 “years” 作为时间单位会导致一些混淆和问题。因此,在 Solidity 0.5.0 版本中,Solidity 废弃了 “years” 关键字,建议使用 “days” 代替 “years”,并将时间单位转换为天数。例如,如果您想要表示一年的时间,您可以使用 “365 days” 来代替 “1 year”。这样做可以避免由于闰年而导致的时间计算错误。

  5. verbatim 关键字的作用是什么,以及它可以在哪里使用?
    答:智能合约中的 verbatim 关键字是 Solidity 语言的一部分。它的作用是将代码中的字符串字面量原样输出,而不进行转义或解析。这在编写智能合约时非常有用,因为它可以确保字符串的内容不会被意外地更改或破坏。例如,如果您需要在智能合约中编写一个包含 HTML 标记的字符串,那么使用 verbatim 关键字可以确保标记不会被解析或更改。verbatim 关键字可以在 Solidity 语言的任何地方使用,包括函数定义、变量声明和注释等。

  6. 在调用另一个智能合约时可以转发多少 gas?
    答:在调用另一个智能合约时,可以转发的 gas 数量取决于您的智能合约的 gas 限制和 gas 价格。如果您的智能合约的 gas 限制为 1000000,gas 价格为 20 Gwei,那么您可以转发的 gas 数量为 1000000。

存储 -1 的 int256 变量在十六进制中是什么样子的?
答:0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

  1. signextend 操作码有什么用?
    答:用于将有符号整数的位数扩展到更高的位数。在 Solidity 中,有符号整数使用补码表示法,其中最高位表示符号位。当您使用 signextend 操作码时,它将检查有符号整数的符号位,并将其扩展到更高的位数。如果符号位为 1,则 signextend 操作码将在高位添加 1;否则,它将在高位添加 0。这样做可以确保有符号整数的符号位在扩展后保持不变。

  2. 为什么 calldata 中的负数会消耗更多的 gas?
    答:当您在 Solidity 中使用 calldata 传递负数时,Solidity 会将其转换为补码形式,并将其存储在 calldata 中。由于补码形式需要更多的位来表示负数,因此在 calldata 中存储负数需要更多的空间,从而消耗更多的 gas。这是因为在 Solidity 中,gas 的消耗是与数据大小成正比的。因此,存储更大的数据将导致更多的 gas 消耗。

  3. 什么是 zk-friendly 哈希函数,它与非 zk-friendly 哈希函数有何不同?
    答:zk-friendly 哈希函数是一种特殊的哈希函数,它具有一些特殊的性质,使其适用于零知识证明场景。与传统的哈希函数不同,zk-friendly 哈希函数通常具有以下特点:

低计算复杂度:zk-friendly 哈希函数通常具有较低的计算复杂度。
低内存占用:zk-friendly 哈希函数通常具有较低的内存占用。
低交互性:zk-friendly 哈希函数通常具有较低的交互性。
与传统的哈希函数相比,zk-friendly 哈希函数通常具有更好的性能和更好的安全性。这使得它们在零知识证明中更容易使用,并且可以提供更好的隐私保护。
32. 在零知识的背景下,什么是 nullifier,它的用途是什么?
答:在零知识证明中,nullifier 是一个用于防止交易被重复使用的值。当一个交易被执行时,它会生成一个 nullifier 值,该值与交易相关联。如果交易被重复使用,nullifier 值将被公开,从而使交易无效。这种方法可以确保数字货币只能被使用一次,从而防止数字货币被重复支付。

solidity面试题(四)
智能合约常见的攻击方式,这也是面试经常问到的。以下是一些常见的黑客攻击方式和解决方法。

  1. 重入攻击
    攻击合约实现fallback方法,在fallback内调用被攻击合约的取eth方法,被攻击合约通过call发送eth,会触发攻击合约的fallback方法,fallback又调用被攻击合约的发送eth方法。如此反复,直到被攻击合约eth被耗尽。
    解决:用transfer或者send发送eth,先做余额判断 - 修改用户余额 - 发送eth,这样多次重入余额会一直减少,不满条件不会到发送eth流程。

  2. 拒绝服务攻击(DOS)
    被攻击合约的功能被破坏,拒绝正常服务使用。原理跟重入攻击类似。当被攻击合约通过call给黑客合约发送eth时,触发了fallback,黑客合约的fallback直接就说revert回滚。使得流程无法正常往下走,这就是拒绝服务攻击。例如:权限转让,如果黑客是管理员,转让管理权限要给黑客返还eth,如果黑客合约的fallback里实现revert(false),eth永远不能返还成功,黑客一直霸占管理员位置。
    解决:改成用户自己提取eth

  3. 带有tx.origin的网络钓鱼
    如果被攻击合约中利用tx.origin校验所有者,然后通过call发送eth。黑客可通过钓鱼让用户调用黑客合约,黑客合约再调用被攻击合约,被攻击合约判断tx.origin是用户地址,call发送eth到msg.sender中,这时eth就发送给黑客合约了。
    解决:用msg.sender判断真正用户。

  4. 算术溢出
    在solidity0.8以前,数字溢出不会抛出错误。例如,黑客利用被攻击合约的增加时间方法,使得数字溢出,让质押时间从一周变成0,提前取出质押token。原理: 2^256最大加1会变成0。如果传入的时间+原有锁仓时间,发生了溢出,结果会变成0,可到期取出质押。
    解决:0.8以前用openzepplin的safemath。0.8之后数字溢出会报错自动回滚。

  5. 访问私有数据
    如果在合约中保存敏感数据,即使是private修饰也会被读取。web3.eth.getStorageAt可以读取合约某个交易时期的存储状态,根据数据类型,推算在哪个存储插槽中。
    解决:不要在合约中存储敏感信息,密码之类的。

  6. 签名重播
    合约中的方法可以多次使用相同的签名来执行一个函数。
    解决:获取hash签名时,加入nonce值。

gas优化
gas优化有哪些手段,也是面试中常被问到的。这里我不详细给出123这样的列表。大家可以参考这个gas优化手册。

在evm中,对存储的操作都是要消耗gas的,例如读取,写入,计算等。不同的操作对应有不同的操作码,每个操作码对应的gas也是不一样的。

为了节省gas,我们可以减少对状态变量的操作,例如利用常量constant和immutable修饰不修改的变量,原因大家在gas优化手册这里找。

尽量使用少的slot存储槽。例如,uint80 ,uint80,uint256,顺序只用了两个存储槽,而uint80 , uint256 , uint80 使用了3个存储槽。

在前面的面试题有些问题也直接或间接的涉及到gas的,也可以当做这类问题来回答。

部分Solidity操作码的gas列表:
操作码 操作 Gas消耗
0x00 STOP 0
0x01 ADD 3
0x02 MUL 5
0x03 SUB 3
0x04 DIV 5
0x05 SDIV 5
0x06 MOD 5
0x07 SMOD 5
0x08 ADDMOD 8
0x09 MULMOD 8
0x0a EXP 20
0x0b SIGNEXTEND 5
0x10 LT 3
0x11 GT 3
0x12 SLT 3
0x13 SGT 3
0x14 EQ 3
0x15 ISZERO 3
0x16 AND 3
0x17 OR 3
0x18 XOR 3
0x19 NOT 3
0x1a BYTE 3
0x20 SHA3 30
0x30 ADDRESS 2
0x31 BALANCE 400
0x32 ORIGIN 2
0x33 CALLER 2
0x34 CALLVALUE 2
0x35 CALLDATALOAD 3
0x36 CALLDATASIZE 2
0x37 CALLDATACOPY 3
0x38 CODESIZE 2
0x39 CODECOPY 3
0x3a GASPRICE 2
0x3b EXTCODESIZE 700
0x3c EXTCODECOPY 700
0x3d RETURNDATASIZE 2
0x3e RETURNDATACOPY 3
0x40 BLOCKHASH 20
0x41 COINBASE 2
0x42 TIMESTAMP 2
0x43 NUMBER 2
0x44 DIFFICULTY 2
0x45 GASLIMIT 2
0x50 POP 2
0x51 MLOAD 3
0x52 MSTORE 3
0x53 MSTORE8 3
0x54 SLOAD 200
0x55 SSTORE 20000
0x56 JUMP 8
0x57 JUMPI 10
0x58 PC 2
0x59 MSIZE 2
0x5a GAS 2
0x5b JUMPDEST 1
0xa0 LOG0 375
0xa1 LOG1 750
0xa2 LOG2 1125
0xa3 LOG3 1500
0xa4 LOG4 1875
0xf0 CREATE 32000
0xf1 CALL 700
0xf2 CALLCODE 700
0xf3 RETURN 0
0xf4 DELEGATECALL 700
0xfa STATICCALL 700
0xfd REVERT 0
0xff SELFDESTRUCT 5000

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

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

相关文章

对象存储基本知识

1 存储桶 存储桶是对象存储的核心组织单元,相当于“云端文件夹”,所有对象(文件、数据)都必须存储在存储桶中,是管理对象的基础载体。 1.1 存储桶的核心定位 存储桶是对象存储的顶层命名空间,用于隔离和管理不同…

基于GF域的多进制QC-LDPC误码率matlab仿真,译码采用EMS算法

1.算法仿真效果 matlab2022a仿真结果如下(完整代码运行后无水印): 本课题实现的是四进制QC-LDPC2.算法涉及理论知识概要 多进制QC - LDPC码是一种基于GF域的线性分组码,它具有稀疏的校验矩阵。QC - LDPC码的校验矩…

AtCoder Beginner Contest 431

啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 [ABC431F] Almost Sorted 2给一个数组 \(A\) 和一个正整数 \(D(1 \le D \le 10^6)\),求有多少种重排数组 \(A\) 能得到的数组 \(B\)(显然多种完全相同的数组 \(B\) 只算一种)…

基于BPSK调制解调和LDPC编译码的单载波相干光传输系统matlab误码率仿真

1.算法仿真效果 matlab2022a仿真结果如下(完整代码运行后无水印):2.算法涉及理论知识概要 在单载波相干光传输系统中,光信号作为载波来传输信息。发送端将电信号调制到光载波上,通过光纤传输到接收端。接收端采用…

空间矢量脉宽调制(Space Vector Pulse Width Modulation)SVPWM基础

空间矢量脉宽调制(Space Vector Pulse Width Modulation)SVPWM基础 前面已经了解了如何将复杂的三相电流通过Clarke与park变换解耦为d-q旋转坐标系的控制,因此只要令\(i_d=0\),控制\(i_q\)来控制转矩大小。 下面需…

OI 笑传 #25

me me she感觉落下了好多东西要写,先写写 ABC431。被 E 吓跑了写了 F。 ABC431D 今年 T1 既视感。 首先贪心把幸福感更高的放进头和身子,这样一定最优但是不一定合法。 然后考虑从头里选出一些扔进身子,选的重量最少…

如何有效衡量开发者生产力:超越代码行数的思考

本文深入探讨了如何正确衡量软件开发者的生产力,批判了传统的代码行数指标,提出了基于产品价值输出的衡量方法,并针对不同开发角色给出了具体度量建议。衡量开发者生产力 几乎从我开始致力于改善软件工程师生活的那…

2025-11-blog

11/1/blog1 我的编程学习之旅:为什么开启这个博客 今天,我决定开始记录自己的编程学习之路。 这个想法的种子,来源于许多像「阮一峰的网络日志」那样优秀的个人博客。它们不仅分享了知识,更留下了一个人的思考轨迹…

科研项目申报

人工智能(工信部)2025年人工智能产业及赋能新型工业化创新任务揭榜挂帅隐私保护

关于apk安装包的解包与签名重新打包

关于apk安装包的解包与签名重新打包点击查看代码 #反编译并清理旧签名 apktool d mzt.apk -o f -f rmdir /s /q f\original\META-INF#新建强签名 keytool -genkeypair ^-v -keystore my-release-key.keystore ^-alias …

Mac 设置某类文件默认用某个软件打开

比如我想设置默认用 Cursor 打开所有 JSON 文件,操作步骤如下: 1、选中任意一个 JSON 文件(比如 data.json),右键选择 “显示简介”(或按 cmd + i); 2、在弹出的简介窗口中,找到 “打开方式” 栏目:点击下拉…

20232325 2025-2026-1 《网络与系统攻防技术》实验四实验报告

1.实验内容 1.1识别恶意代码的文件类型标识,进行脱壳与字符串提取。 1.2使用IDA Pro静态或动态分析所给exe文件,找到输出成功信息的方法。 1.3分析自制恶意代码样本并撰写报告。 1.4取证分析实践。2.实验目的 掌握恶…

题解:P11361 [NOIP2024] 编辑字符串

NOIP2024T1已严肃开坑真题题解合集,主要解决自己总是写完一题过一段时间又不会了的问题。 当时考这个的时候学 OI 的时间还没有我上高中以来放过的假多,不过好在没有爆零,共计得分 35pts,但是所有的分数都是这道题…

与某省代理商的合作,写一点感触吧

近几年,找了几个代理商合作,刚开始都挺好,但是后面都出现了问题。 大概过程 第一阶段(初期):双方达成合作,合作的挺好。 第二阶段(中期):对方变得难沟通。 1、找对方,微信不回,电话不接,说自己这几天忙什…

CSP-S 2025 解题报告

T1 社团招新 (club)考场思路 先是花了 20 分钟思考 DP 的可行性。然后我想到了反悔贪心,开始证明它的可能性或者找反例,接着我想到其实不用加一个人就反悔一次,只要先全部贪心,最后反悔即可。然而还是不会证,就直…

嵌入式面试中常见的一些编程题目 - 阿源

嵌入式面试中常见的一些编程题目注:本文只是代码实现,并没有深入讲解实现原理,大家可以看一下主要会考什么,然后再具体针对性了解原理,也更有利于理解。眼看26届秋招接近尾声,自己虽然很菜,但也在激烈的竞争中拿…

Makefile工程简单模板

一个简单的Makefile工程模板一个简单的Makefile工程模板 # 定义目标文件名 TARGET ?= bsp# 编译器选项 CROSS_COMPILE ?= arm-linux-gnueabihf- CC := $(CROSS_COMPILE)gcc LD := $(CROSS_COMPILE)ld OB…

实用指南:Visual Studio下载安装教程(非常详细)从零基础入门到精通,看完这一篇就够了

实用指南:Visual Studio下载安装教程(非常详细)从零基础入门到精通,看完这一篇就够了pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importan…

升鲜宝 供应链SCM 一体化自动化部署体系说明

升鲜宝 SCM 一体化自动化部署体系说明🚀 升鲜宝 SCM 一体化自动化部署体系说明 作者:杭州升鲜宝科技有限公司 版本:v1.0 日期:2025-11-09🧱 一、部署体系总览 本部署体系用于在服务器上一键完成: • ✅ 前端自…

折腾笔记[37]-使用ML.NET进行文本情感分类

使用.NET框架的ML.NET深度学习框架训练数据集并进行文本情感分类.摘要 使用.NET框架的ML.NET深度学习框架训练数据集并进行文本情感分类. 关键信息.net8原理简介 ML.NET简介 [https://www.nuget.org/packages?page=2&…