1. 智能合约介绍

150 阅读5分钟

今天是手把手教你写智能合约的第一天,今天会介绍一下开发智能合约使用到的编程语言,还有以太坊虚拟机EVM的底层结构。

之后的课程会教大家开发智能合约的细节,包括编程语言的语法细节,常见的智能合约漏洞,编译,部署的到以太坊公链上,还有gas费的消耗机制,还有常见的智能合约协议,ERC20、ERC721、ERC1155...,以及如何使用nodejs开发一个智能合约应用。

1. 使用的语言

其实开发智能合约的编程语言有很多种,不过大部分都使用solidity来进行开发,我就给大家重点介绍一下solidity语言。

下面给大家分享一些solidity的在线文档,还有线上编译器

solidity中文文档:learnblockchain.cn/docs/solidi…
solidity在线编译器:remix.ethereum.org/
github: github.com/ethereum/so…

1.1 solidity介绍

Solidity 是一门面向合约的、为实现智能合约而创建的高级编程语言。智能合约是管理以太坊状态里账户行为的程序。

Solidity是一种针对Ethereum虚拟机(EVM)设计的 花括号语言 。 它受到了C++、Python和JavaScript的影响。你可以在 语言影响 部分找到更多关于Solidity受到哪些语言启发的细节。

Solidity 是静态类型语言,支持继承、库和复杂的用户定义类型等特性。

在部署合约时,应该尽量使用最新版本,因为新版本会有一些重大的新特性以及bug修复(除特殊情况)。

1.2 代码示例

// SPDX-License-Identifier: GPL-3.0
// 使用的solidity 版本
pragma solidity >=0.7.0 <0.9.0;

/// 合约对象
contract Ballot {
    
    // 状态变量 会永久存储在智能合约中
    address public manager;
    

    // 合约的构造函数
    constructor(){
        manager = msg.sender;
    }
    
     // 获取合约余额
    function getBalance() public view returns (uint256){
        return address(this).balance;
    }

  }

2.以太坊虚拟机 EVM

evm在线文档:takenobu-hs.github.io/downloads/e…
eth开发者文档: ethereum.org/zh/develope…

EVM 作为一个堆栈机运行,其栈的深度为 1024 个项。 每个项目都是 256 位字,为了便于使用,选择了 256 位加密技术(如 Keccak-256 哈希或 secp256k1 签名)。

在执行期间,EVM 会维护一个瞬态_内存_(作为字可寻址的字节数组),该内存不会在交易之间持久存在。

然而,合约确实包含一个 Merkle Patricia 存储 trie(作为可字寻址的字数组),该 trie 与帐户和部分全局状态关联。

image.png

image.png

3.存储结构

以太坊虚拟机(Ethereum Virtual Machine,EVM)的存储方式可以分为四种:栈(Stack)、状态存储(Storage)、虚拟机内存(Memory)和只读内存。

3.1 栈

EVM是基于栈的虚拟机,栈中的每一个元素的长度是256位,基本的算数运算和逻辑运算都是使用栈完成。

image.png

3.2 虚拟机内存

虚拟机内存实际上是一个连续的数组空间,用于存放如字符串等较复杂的数据结构。 即数据在内存中,因此数据仅在其生命周期内(函数调用期间)有效 memory/内存

* 内存是一个字节数组,槽大小位256位(32字节)
* 数据仅在函数执行期间存在,执行完毕后就被销毁
* 读或写一个内存槽都会消耗少量的gas 如:3gas
* 为了避免矿工的工作量过大,22个操作之后的单操作成本会上涨

3.3 只读内存

只读内存是EVM最特殊的一种存储结构,主要用于存放参数和返回值。 calldata/调用数据

* 调用数据是不可修改、非持久化的区域,用来保存函数参数,其行为类似于内存
* 外部函数的参数必须使用calldata,但是也可用于其他变量
* 调用数据避免了数据拷贝,并确保数据不被修改
* 函数也可以返回使用calldata声明的数组和结果,但是不可能分配这些类型

image.png

3.4 状态存储

*态存储时key-value的存储结构,用于持久化数据。与栈和虚拟机内存不同,状态存储的值会被记录到以太坊的状态树当中。 storage/存储

* 存储中的数据是永久存在的。存储是一个key/value库- 存储中的数据写入区块链,因此会修改状态,这也是存储使用成本高的原因。
* 占用一个256位的槽需要消耗大量的gas 如:20000 gas
* 修改一个已经使用的存储槽的值,需要消耗大量的gas 如:5000 gas
* 当清零一个存储槽时,会返还一定数量的gas
* 存储按256位的槽位分配,即使没有完全使用一个槽位,也需要支付其开销

image.png

3.5 代码示例

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract StorageTest{

    string public name; //状态变量会默认声明为 storage

    function stackFun() public pure returns(uint256) {
        return 10  * 10;
    }

    function memoryFun(string memory _name) public returns(string memory){
        _name = "test";
        name = _name;
        return name;
    }

    function calldataFun(string calldata _name) public returns(string calldata result){
        //_name = "test";
        name = _name;
        return _name;
    }

}

4.调用方式

在以太坊成功部署的智能合约,可以通过如下的三种方式调用合约中的公共函数(External/Public):

  1. 通过客户端发送消息调用交易(Message Call Transaction),其中包含了数据参数以及目标函数签名的哈希值。

​ 这种函数调用方式必须在交易得到确认后才能生效。并且,矿工会对该交易收取Gas来作为执行函数时所需要的代价,因此,该方式是一种写操作,即会对消息调用者的账户余额以及合约的状态进行更改

  1. 通过另一个合约来间接的调用。

​ 这种函数调用方式最终可以被追溯成另一笔消息调用交易。

  1. 通过客户端调用view(或pure)函数。

​ 这种函数调用方式并不会改变合约的状态,也不需要耗费Gas。