【区块链安全 | 第十三篇】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;
    }
}
相关推荐
JXY_AI2 小时前
大模型越狱:技术漏洞与安全挑战——从原理到防御
安全
上海云盾-高防顾问2 小时前
SCDN如何有效防护网站免受CC攻击?——安全加速网络的实战解析
网络·安全
riusksk2 小时前
网络安全顶会——SP 2025 论文清单与摘要
安全·web安全
CryptoRzz2 小时前
印度尼西亚数据源对接技术指南
开发语言·python·websocket·金融·区块链
FreeBuf_13 小时前
GNU Screen 曝多漏洞:本地提权与终端劫持风险浮现
安全·web安全·gnu
安 当 加 密14 小时前
hashicorp vault机密管理系统的国产化替代:安当SMS凭据管理系统,量子安全赋能企业密钥管理
安全
python算法(魔法师版)18 小时前
API安全
网络·物联网·网络协议·安全·网络安全
GIS数据转换器18 小时前
当三维地理信息遇上气象预警:电网安全如何实现“先知先觉”?
人工智能·科技·安全·gis·智慧城市·交互
网易易盾18 小时前
AIGC时代的内容安全:AI检测技术如何应对新型风险挑战?
人工智能·安全·aigc
w236173460118 小时前
识别安全网站,上网不再踩坑
安全