【区块链安全 | 第十三篇】Solidity 合约结构

文章目录

Solidity 合约

Solidity 中的合约类似于面向对象语言中的类。每个合约可以包含状态变量、函数、函数修饰符、事件、错误、结构体类型和枚举类型。此外,合约可以继承自其他合约。

还有一些特殊类型的合约,称为库(libraries)和接口(interfaces)。

Solidity 合约结构

本文针对合约结构提供一个快速概览。

状态变量

状态变量是那些值被永久存储在合约存储中的变量,或者是临时存储在瞬态存储中的变量,后者在每次交易结束时会被清除。

solidity 复制代码
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;

contract SimpleStorage {
    uint storedData; // 状态变量
    // ...
}

函数

函数是代码的可执行单元。函数通常定义在合约内,但也可以在合约外部定义。

solidity 复制代码
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.1 <0.9.0;

contract SimpleAuction {
    function bid() public payable { // 函数
        // ...
    }
}

// 在合约外部定义的辅助函数
function helper(uint x) pure returns (uint) {
    return x * 2;
}

函数调用可以是内部的或外部的,并且具有不同的可见性级别。函数接受参数并返回变量,以便在它们之间传递数据和值。

函数修饰符

函数修饰符可用于以声明方式修改函数的语义。

特点

1.不支持重载,即不能使用相同名称但不同参数的修饰符。

2.与函数类似,修饰符可以被重写。

以下示例展示了 onlySeller 修饰符,该修饰符用于确保只有 seller(卖家)才能调用特定函数:

solidity 复制代码
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.22 <0.9.0;

contract Purchase {
    address public seller;

    modifier onlySeller() { // 定义修饰符
        require(
            msg.sender == seller,
            "Only seller can call this."
        );
        _; // 继续执行被修饰的函数
    }

    function abort() public view onlySeller { // 使用修饰符
        // 仅限卖家调用
    }
}

事件

Solidity 事件(Events)是 EVM(以太坊虚拟机)提供的日志机制,用于在链上记录特定操作,并供外部应用监听和使用。

特点

1.相比状态变量存储,事件日志存储在交易日志中,成本更低。

2.便于外部访问,前端应用(如 dApps)可以监听事件,实时获取区块链上的重要信息。

3.不可被合约内部读取,合约代码无法直接访问已触发的事件信息(但可由外部应用监听)。

以下示例展示了 HighestBidIncreased 事件,该事件在 bid() 函数被调用时触发,并记录投标人和投标金额。

solidity 复制代码
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.22;

event HighestBidIncreased(address bidder, uint amount); // 事件

contract SimpleAuction {
    function bid() public payable {
        // ...
        emit HighestBidIncreased(msg.sender, msg.value); // 触发事件
    }
}

错误(Errors)

错误(Errors)允许为失败情况定义描述性名称和数据,通常通过 revert 语句实现。

特点

1.与直接使用字符串描述相比,错误的 gas 成本更低,并且可以编码额外数据。

2.可以使用 NatSpec 为用户提供错误的详细说明。

示例代码如下:

solidity 复制代码
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

/// 转账资金不足。请求金额为 `requested`,但仅有 `available` 可用。
error NotEnoughFunds(uint requested, uint available);

contract Token {
    mapping(address => uint) balances;

    function transfer(address to, uint amount) public {
        uint balance = balances[msg.sender];
        if (balance < amount)
            revert NotEnoughFunds(amount, balance); // 触发错误
        balances[msg.sender] -= amount;
        balances[to] += amount;
        // ...
    }
}

结构体类型(Struct Types)

结构体(Structs)是自定义类型,可用于将多个变量组合在一起。

示例代码如下:

solidity 复制代码
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;

contract Ballot {
    // 定义一个名为 Voter(投票者)的结构体
    struct Voter { 
        uint weight;      // 该投票者的投票权重
        bool voted;       // 该投票者是否已经投票
        address delegate; // 该投票者委托的代理人地址
        uint vote;        // 该投票者所投的候选人编号
    }
}

枚举类型(Enum Types)

枚举(Enums)可用于创建一组预定义的 常量值,使代码更具可读性和可维护性。

特点:

1.用易理解的单词替代数字或布尔值,例如 State.Created 比 0 更直观。

2.枚举类型的变量只能取其定义的值,有助于减少错误。

3.Solidity 中的枚举本质上是 uint 类型,第一个成员默认值为 0,第二个成员 1,依此类推。

示例代码如下:

复制代码
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.0 <0.9.0;

contract Purchase {
    // 定义一个枚举类型 State,表示合约的不同状态
    enum State { Created, Locked, Inactive }

    // 声明一个状态变量,默认值为 `State.Created`
    State public currentState;

    // 修改合约状态的方法
    function setLocked() public {
        currentState = State.Locked;
    }

    function setInactive() public {
        currentState = State.Inactive;
    }
}
相关推荐
用户9623779544819 小时前
DVWA 靶场实验报告 (High Level)
安全
数据智能老司机1 天前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机1 天前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
用户962377954481 天前
DVWA 靶场实验报告 (Medium Level)
安全
red1giant_star1 天前
S2-067 漏洞复现:Struts2 S2-067 文件上传路径穿越漏洞
安全
用户962377954481 天前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
cipher3 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
kida_yuan4 天前
【以太来袭】4. Geth 原理与解析
区块链
一次旅行6 天前
网络安全总结
安全·web安全
red1giant_star6 天前
手把手教你用Vulhub复现ecshop collection_list-sqli漏洞(附完整POC)
安全