【区块链安全 | 第十三篇】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;
    }
}
相关推荐
llxxyy卢2 小时前
文件操作之文件下载读取全解
安全
友莘居士2 小时前
Hyperledger Fabric与 FISCO BCOS深度对比
区块链·fabric·fisco
公链开发3 小时前
区块链软件开发专家:达普韦伯的技术哲学与实战体系
区块链
虹科网络安全4 小时前
艾体宝洞察 | 利用“隐形字符”的钓鱼邮件:传统防御为何失效,AI安全意识培训如何补上最后一道防线
运维·网络·安全
Moonbeam Community6 小时前
走出围墙花园:Polkadot 2.0 如何在多链内卷中重整旗鼓
web3·区块链·solidity·polkadot
冰敷逆向7 小时前
苏宁滑块VMP深入剖析(一):解混淆篇
javascript·爬虫·安全·web
小五传输7 小时前
隔离网闸的作用是什么?新型网闸如何构筑“数字护城河”?
大数据·运维·安全
石像鬼₧魂石8 小时前
内网渗透是网络安全渗透测试
安全·web安全
Neolnfra10 小时前
渗透测试标准化流程
开发语言·安全·web安全·http·网络安全·https·系统安全
小明的小名叫小明11 小时前
Solidity入门(9)-合约间调用
区块链