Sway编程语言-更新中(The Sway Programming Language-Updatin
  • Sway编程语言(The Sway Programming Language)
  • 1. 导言(Introduction)
    • 1.1 安装(Installation)
    • 1.2 Sway快速入门 (Sway Quickstart)
    • 1.3 Fuel工具链 (The Fuel Toolchain)
    • 1.4 一个Forc项目 (A Forc Project)
    • 1.5 标准库 (Standard Library)
  • 2. 示例(Example)
    • 2.1计数器(Counter)
    • 2.2子货币(Subcurrency)
    • 2.3 FizzBuzz
    • 2.4 钱包智能合约(Wallet Smart Contract)
  • 3.Sway编程类型(Sway Program Types)
    • 3.1 合约(Contracts)
    • 3.2 库 (Libraries)
    • 3.3 脚本(Scripts)
    • 3.4 谓词 (Predicates)
  • 4. Sway语言基础 (Sway Language basics)
    • 4.1 变量 (Variables)
    • 4.2 内置类型(Built-in Types)
    • 4.3 常用库类型(Commonly Used Library Types)
    • 4.4 区块链类 (Blockchain Types)
    • 4.5 函数 (Functions)
    • 4.6 结构、元祖和穷举 (Structs, Tuples, and Enums)
    • 4.7 方法和关联函数 (Methods and Associated Functions)
    • 4.8 注释和日志 (Comments and Logging)
    • 4.9 控制流 (Control Flow)
  • 5. 用Sway部署区块链 (Blockchain Development with Sway)
    • 5.1 哈希和加密学 (Hashing and Cryptography)
    • 5.2 合约存储(Contract Storage)
    • 5.3 函数纯度 (Function Purity)
    • 5.4 标识符(Identifiers)
    • 5.5 原生资产(Native Assets)
    • 5.6 访问控制 (Access Control)
    • 5.7 调用合约(Calling Contracts)
  • 6. 高级概念 (Advanced Concepts)
    • 6.1 高级类型 (Advanced Types)
    • 6.2 通用类型 (Generic Types)
    • 6.3 特征 (Traits)
    • 6.4 集 (Assembly)
  • 7. 一般集聚 (Common Collections)
    • 7.1 堆上的向量(Vectors on the Heap)
    • 7.2 存储向量 (Storage Vectors)
    • 7.3 存储映射 (Storage Maps)
  • 8.测试(Testing)
    • 8.1 单元测试(Unit Testing)
    • 8.2 用Rust来测试 (Testing with Rust)
  • 9.应用前端开发 (Application Frontend Development)
    • 9.1 TypeScript SDK
  • 10.Sway应用(Sway Reference)
    • 10.1 编译器内部函数(Compiler Intrinsics)
    • 10.2 属性(Attributes)
    • 10.3 风格向导(Style Guide)
    • 10.4 已知各类问题(Known Issues and Workarounds)
    • 10.5 与Solidity的不同之处 (Differences From Solidity)
    • 10.6 与Rust的不同之处 (Differences From Rust)
    • 10.7 向Sway贡献 (Contributing To Sway)
  • 11. Forc引用 (Forc Reference)
    • 11.1清单参考 (Manifest Reference)
    • 11.2 工作区(Workspaces)
    • 11.3 依赖(Dependencies)
    • 11.4 命令(Commands)
      • 11.4.1 forc-addr2line
      • 11.4.2 forc-build
      • 11.4.3 forc-check
Powered by GitBook
On this page
  • 示例(Example)
  • 高级调用 (Advanced Calls)
  • 处理重入问题 (Handling Re-entrancy)
  • 与EVM的不同之处 (Differences from the EVM)
  1. 5. 用Sway部署区块链 (Blockchain Development with Sway)

5.7 调用合约(Calling Contracts)

Previous5.6 访问控制 (Access Control)Next6. 高级概念 (Advanced Concepts)

Last updated 1 year ago

Smart contracts can be called by other contracts or scripts. In the FuelVM, this is done primarily with the instruction.

智能合约可以被其他合约或脚本调用。在FuelVM中,这主要是通过指令完成的。

Sway provides a nice way to manage callable interfaces with its abi system. The Fuel ABI specification can be found .

Sway提供了一个很好的方法来管理其abi系统的可调用接口。Fuel ABI规范可以在找到。

示例(Example)

Here is an example of a contract calling another contract in Sway. A script can call a contract in the same way.

下面是一个合约调用另一个合约的例子,在Sway中。脚本也可以用同样的方式调用一个合约。

// ./contract_a.sw
contract;

abi ContractA {
    fn receive(field_1: bool, field_2: u64) -> u64;
}

impl ContractA for Contract {
    fn receive(field_1: bool, field_2: u64) -> u64 {
        assert(field_1 == true);
        assert(field_2 > 0);
        return_45()
    }
}

fn return_45() -> u64 {
  45
}
// ./contract_b.sw
contract;

use contract_a::ContractA;

abi ContractB {
    fn make_call();
}

const contract_id = 0x79fa8779bed2f36c3581d01c79df8da45eee09fac1fd76a5a656e16326317ef0;

impl ContractB for Contract {
    fn make_call() {
      let x = abi(ContractA, contract_id);
      let return_value = x.receive(true, 3); // will be 45
    }
}

Note: The ABI is for external calls only therefore you cannot define a method in the ABI and call it in the same contract. If you want to define a function for a contract, but keep it private so that only your contract can call it, you can define it outside of the impl and call it inside the contract, similar to the return_45() function above. 注意: ABI只适用于外部调用,因此你不能在ABI中定义一个方法并在同一个合约中调用它。如果你想为一个合约定义一个函数,但要保持它的私密性,以便只有你的合约可以调用它,你可以在impl之外定义它,并在合约中调用它,类似于上面的return_45()函数。

高级调用 (Advanced Calls)

All calls forward a gas stipend, and may additionally forward one native asset with the call.

所有的调用都会转发一个gas津贴,并且可以额外转发一个本地资产的调用。

Here is an example of how to specify the amount of gas (gas), the asset ID of the native asset (asset_id), and the amount of the native asset (coins) to forward:

下面是一个例子,说明如何指定气体的数量(gas),本地资产的资产ID(asset_id),以及要转发的本地资产的数量(coins):

script;

abi MyContract {
    fn foo(field_1: bool, field_2: u64);
}

fn main() {
    let x = abi(MyContract, 0x79fa8779bed2f36c3581d01c79df8da45eee09fac1fd76a5a656e16326317ef0);
    let asset_id = 0x7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777_7777;
    x.foo {
        gas: 5000, asset_id: asset_id, coins: 5000
    }
    (true, 3);
}

处理重入问题 (Handling Re-entrancy)

contract;

use reentrancy::reentrancy_guard;

abi MyContract {
    fn some_method();
}

impl ContractB for Contract {
    fn some_method() {
        reentrancy_guard();
        // do something
    }
}

CEI模式违反静态分析 (CEI pattern violation static analysis)

Another way of avoiding re-entrancy-related attacks is to follow the so-called CEI pattern. CEI stands for "Checks, Effects, Interactions", meaning that the contract code should first perform safety checks, also known as "pre-conditions", then perform effects, i.e. modify or read the contract storage and execute external contract calls (interaction) only at the very end of the function/method.

另一种避免重入相关攻击的方法是遵循所谓的 CEI 模式。CEI是 "检查、影响、交互 "的缩写,意思是说,合约代码应该首先执行安全检查,也称为 "前置条件",然后执行影响,即修改或读取合约存储,并且只在函数/方法的最末端执行外部合约调用(交互)。

The Sway compiler implements a check that the CEI pattern is not violated in the user contract and issues warnings if that's the case.

Sway编译器实现了对CEI模式在用户合约中是否被违反的检查,如果是这样就会发出警告。

For example, in the following contract the CEI pattern is violated, because an external contract call is executed before a storage write.

例如,在下面的合约中,CEI模式被违反了,因为一个外部合约调用在存储写入之前被执行。

contract;

mod other_contract;

use other_contract::*;

use std::auth::msg_sender;

abi MyContract {
    #[storage(read, write)]
    fn withdraw(external_contract_id: ContractId);
}

storage {
    balances: StorageMap<Identity, u64> = StorageMap {},
}

impl MyContract for Contract {
    #[storage(read, write)]
    fn withdraw(external_contract_id: ContractId) {
        let sender = msg_sender().unwrap();
        let bal = storage.balances.get(sender).try_read().unwrap_or(0);

        assert(bal > 0);

        // External call
        let caller = abi(OtherContract, external_contract_id.into());
        caller.external_call { coins: bal }();

        // Storage update _after_ external call
        storage.balances.insert(sender, 0);
    }
}

Here, other_contract is defined as follows:

library;

abi OtherContract {
    #[payable]
    fn external_call();
}

The CEI pattern analyzer issues a warning as follows, pointing to the interaction before a storage modification:

CEI模式分析器发出如下警告,指向存储修改前的交互:

warning
  --> /path/to/contract/main.sw:28:9
   |
26 |
27 |           let caller = abi(OtherContract, external_contract_id.into());
28 |           caller.external_call { coins: bal }();
   |  _________-
29 | |
30 | |         // Storage update _after_ external call
31 | |         storage.balances.insert(sender, 0);
   | |__________________________________________- Storage write after external contract interaction in function or method "withdraw". Consider making all storage writes before calling another contract
32 |       }
33 |   }
   |
____

In case there is a storage read after an interaction, the CEI analyzer will issue a similar warning.

如果在交互之后有一个存储读取,CEI分析器将发出类似的警告。

In addition to storage reads and writes after an interaction, the CEI analyzer reports analogous warnings about:

除了交互后的存储读和写,CEI分析器还报告类似的警告:

  • balance tree updates, i.e. balance tree reads with subsequent writes, which may be produced by the tr and tro asm instructions or library functions using them under the hood; 平衡树更新,即平衡树的读和随后的写,这可能是由tr和tro asm指令或,在当时条件下下使用它们的库函数产生的;

  • balance trees reads with bal instruction; 用bal指令读取平衡树;

  • changes to the output messages that can be produced by the __smo intrinsic function or the smo asm instruction. 对输出信息的改变,可以由__smo内在函数或smo asm指令产生。

与EVM的不同之处 (Differences from the EVM)

While the Fuel contract calling paradigm is similar to the EVM's (using an ABI, forwarding gas and data), it differs in two key ways:

虽然Fuel合约的调用范式与EVM的相似(使用ABI,转发气体和数据),但它在_两个关键方面有所不同:

  1. No data serialization: Contract calls in the FuelVM do not need to serialize data to pass it between contracts; instead they simply pass a pointer to the data. This is because the FuelVM has a shared global memory which all call frames can read from. 无数据序列化: FuelVM中的合约调用不需要将数据序列化以在合约之间传递;相反,他们只是传递一个数据指针。这是因为FuelVM有一个共享的全局内存,所有的调用框架都可以从中读取。

A common attack vector for smart contracts is . Similar to the EVM, the FuelVM allows for re-entrancy.

智能合约的一个常见攻击媒介是。与EVM类似,FuelVM允许重入。

A stateless re-entrancy guard is included in the library. The guard will panic (revert) at run time if re-entrancy is detected.

在库中包含了一个_无状态_ 重入防护。如果检测到重入,该防护措施将在运行时恐慌(恢复)。

Please see this for more detail on some vulnerabilities in case of storage modification after interaction and this for more information on storage reads after interaction.

请看这篇,了解在交互后修改存储的情况下的一些漏洞,以及这篇,了解关于交互后读取存储的更多信息。

: FuelVM calls can forward any native asset not just base asset. : FuelVM调用可以转发任何本地资产,而不仅仅是基础资产。

call
call
here
这里
re-entrancy
重入
sway-libs
sway-libs
blog post
blog post
博文
博文
Native assets
本地资产