深入了解 Solidity 错误 #1 - 编译器错误

  • Tiny熊
  • 更新于 2023-08-01 12:06
  • 阅读 1706

深入了解 Solidity 错误第二篇, 了解编译器错误。

img

上一篇我们开启了"深入Solidity 错误"系列, 继续更新。

现在,我们将详细介绍 Solidity 中的第一大类错误:与 Solidity 编译器有关的错误,称为 "编译时错误 "或 "编译器错误"。

Solidity 编译器 solc 在将 Solidity 代码编译成字节码时,会产生不同类型的错误。各种不同的错误原因都会导致编译器中止运行。

编译时错误简介

编译时错误(Compiler Errors),顾名思义,是指 Solidity 合约被 solc 编译器编译时发生的错误。

如果你的集成开发环境使用了某些插件(如 Solidity Visual Developer for VS Code),编译时错误会出现在集成了插件的开发环境(Remix、VS Code...)中,或者运行编译命令时出现在终端中。

当使用solc编译Solidity智能合约时,solc编译器会将.sol文件及其内容作为输入,生成输出:智能合约的EVM字节码。但在编译时,可能会错误。我们将这种类型的错误称为编译时错误

Solidity 编译时错误可分为两大类:

  • Solc CLI 命令错误
  • 编译器错误

Solc CLI 错误

这些错误与通过命令行使用 solc 编译器有关。

  1. JSONError:JSON 输入不符合所需的格式,例如输入不是 JSON 对象、不支持语言等。

  2. IOError:IO 和导入处理错误,如无法解决的 URL 或所提供源代码的哈希值不匹配等。

编译器错误

当 Solidity 代码被编译成可执行字节码时,就会出现这类编译时错误。

这包括无效语法、类型转换和不符合 Solidity 语法的声明等方面的错误。

以下是不同类型的 Solidity 编译器错误摘要

编译器错误 描述
Warning 关于合约中可能发生的潜在错误或安全的警告
ParserError Solidity代码不符合Solidity语言规则
DocstringParsingError 无法解析 Natspec 注释或 Natspec 块
SyntaxError 无效的Solidity语法,例如在错误的位置使用内置关键字
DeclarationError 无法解析为变量、函数或内置标志符
TypeError 将值赋给变量或将变量赋给函数或返回参数时,类型无效
UnimplementedFeatureError 当使用Solidity编译器尚未支持但计划在将来支持的语法时出现

Warning (警告)

有时你会使用 solc 编译合约,编译成功完成会生成合约字节码 + ABI。

不过,CLI 的输出会标出一些警告错误。这些警告通常以橙色显示,如下图所示。

让我们用一个常见的例子来说明 solc 编译器何时会生成警告:变量遮蔽。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract MyToken {

    string private _name;
    string private _symbol;

    constructor(string memory _name, string memory _symbol) {
        // ...
    }

}

solidity-编译器警告

<p align="center"> 由 solc 编译器生成的警告会在终端或 Remix 中显示为橙色。</p>

在这段代码,参数变量被传递给了 MyToken 合约的 constructor ,与定义的状态变量有相同的名称 (称为 变量遮蔽(variable shadowing) 。虽然 solc 编译器由于变量声明的范围(在构造函数内)会尝试解决这些赋值问题,但它仍然会感到困惑,并警告开发者可能发生了错误。是因为,由于状态变量与构造函数参数之间的命名冲突,可能会出现错误的赋值。

Solc 编译器器出现 warning 依旧允许你编译合约,但可能导致合约中的安全问题。

有多种情况会发生 warning, 包括:

  • 遮蔽(变量或内置标志符);
  • 语句无效果;
  • 无法到达的代码
  • 函数定义了返回类型,但函数体内部没有明确的 return 语句。
  • 函数状态可变性受到限制(例如:从没有修饰符到viewpure)。
  • 未使用低级 .call.staticcall.delegatecall的返回值。

无法访问的代码通常是由于合约代码中的一条(或几条)语句永远不会被运行。它们就像死代码。这是因为合约中的逻辑流要么提前return了, 要么在这条(这些)语句之前停止(revert或其他情况)。下面有一些示例。

关于函数的状态可变性,如果可以建议将其向下锁定为 viewpure 。尤其是内部函数。“向下锁定”可以确保这些函数不会对状态产生不必要的副作用,而只是负责读取合约状态执行纯计算

下面是一些warnings 示例:

// SPDX-License-Identifier: GPL-3.0  
pragma solidity ^ 0.8 .0;

contract Warnings {

  // all the lines below will return a warning  
  // ---  
  // Warning: This declaration shadows a builtin symbol.  

  string gasleft;
  uint tx;
  uint selfdestruct;

  // function keccak256() public pure;  

  function warningExample1() pure internal {
    // Warning: Statement has no effect.  
    5;
  }

  // Warning: Unnamed return variable can remain unassigned.  
  // Add an explicit return with value to all non-reverting code paths or
  name the variable.
  function warningExample2(uint _value) public pure returns(bool) {

    if (_value &lt; 10) {
      revert();
      // Warning: Unreachable code.  
      _value = 12;
    }

  }

  uint256 a;

  // Warning: Function state mutability can be restricted to pure  
  function warningExample3() public {
    // Warning: Unused local variable.  
    uint256 a = 32;
  }

}

另一种需要牢记的重要warnings类型是 "未使用的低级调用的返回值(return value of low-level calls not used)"。

solidity-编译器警告

// SPDX-License-Identifier: GPL-3.0  
pragma solidity ^ 0.8 .0;

contract DeployedContract {
  uint public result = 0;

  function add(uint256 input) public {
    result = result + input;
  }
}

contract Proxy {

  address deployed_contract = 0x1212121212121212121212121212121212121212;

  function lowLevelCall(uint256 lucky_number) public {
    bytes memory _calldata = abi.encode(bytes4(keccak256("add(uint256)")),
      lucky_number);
    deployed_contract.call(_calldata);
  }

}

建议始终检查低级调用(如 .call.staticcall.delegatecall)的第一个 bool 类型的返回值。以确保外部调用是成功。

上面的函数 lowLevelCall(uint256) 可以修改为如下代码:

function LowLevelCall(uint256 lucky_number) public { 
        bytes memory _calldata = abi.encode(bytes4(keccak256("add(uint256)")), lucky_number);
        (bool lowLevelCallSucceeded, ) = deployed_contract.call(_calldata);
    }

注意:我给这个 bool 参数起了一个很长的名字,目的是明确描述这个 bool 代表什么,以及低级调用函数的第一个返回值是什么。

编译器 warnings 并不会阻止 solc 编译器生成合约字节码。但是,强烈建议重构你的 Solidity 代码,以处理和修复编译器警告

solidity - 最佳实践

<p align=center>来源:https://ethereum.org/en/developers/docs/smart-contracts/security/&lt;/p>

Parser Error(解析器错误)

当 Solidity 源代码不符合 Solidity 语言规则时,就会出现Parser Error(解析器错误)。

例如:

  • 当一行没有以...

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Tiny熊
Tiny熊
0xD682...E8AB
登链社区发起人 通过区块链技术让世界变得更好而尽一份力。