主页 > imtoken官方下载 > 智能合约的核心思想、语法重点、编程模式、示例、规范和架构

智能合约的核心思想、语法重点、编程模式、示例、规范和架构

imtoken官方下载 2024-01-03 05:13:03

目录[ ](

demo#%E4%BB%80%E4%B9%88%E6%98%AF%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6) 什么是智能合约

智能合约是以数字形式定义的一组承诺,包括合约参与者可以实现这些承诺的协议。 合约由一组代码(合约的函数)和数据(合约的状态)组成,并在以太坊虚拟机上运行。

以太坊虚拟机(EVM)使用 256 位机器代码,是一个基于堆栈的虚拟机,用于执行以太坊智能合约。 由于 EVM 是为以太坊系统设计的,因此使用以太坊账户模型(Account Model)进行价值传递。

[ ](

演示#%E5%90%88%E7%BA%A6%E7%9A%84%E4%BB%A3%E7%A0%81%E5%85%B7%E6%9C%89%E4%BB%80 %E4%B9%88%E8%83%BD%E5%8A%9B%EF%BC%9A) 合约代码有哪些能力:

读取交易数据。读取或写入合约自己的存储空间。读取环境变量【块高,哈希值,gas】向另一个合约发送一个“内部交易”。

[ ](

演示#%E5%9C%A8%E5%8C%BA%E5%9D%97%E9%93%BE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%9E%B6 %E6%9E%84) 关于区块链平台的架构

区块链平台架构

[ ](

demo#1-%E4%BB%80%E4%B9%88%E6%98%AFsolidity) 1.什么是solidity

Solidity 是一种运行在以太坊虚拟机 (EVM) 之上的智能合约高级语言。

solidity 语言特性

它的语法接近Javascript,是一种面向对象的语言。 但作为真正运行在网络上的去中心化合约,它有很多不同之处:

[ ](

demo#2-%E5%BC%80%E5%8F%91%E7%9A%84%E5%B7%A5%E5%85%B7) 2.开发工具

[ ](

演示#3-%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8) 3 快速入门

[ ](

demo#31-%E4%B8%BE%E4%B8%AA%E4%BE%8B%E5%AD%90) 3.1 举个例子

完成步骤:

编写合约 编译合约 部署合约 测试合约

一个例子获取演示

    pragma solidity ^0.4.2;        contract SimpleStartDemo {        int256 storedData;        event AddMsg(address indexed sender, bytes32 msg);            modifier only_with_at_least(int x) {           if (x >= 5) {             x = x+10;              _;           }        }         function SimpleStartDemo() {            storedData = 2;        }            function setData(int256 x) public only_with_at_least(x){            storedData = x;            AddMsg(msg.sender, "[in the set() method]");        }            function getData() constant public returns (int256 _ret) {            AddMsg(msg.sender, "[in the get() method]");            return _ret = storedData;        }    }

[ ](

demo#32-%E9%83%A8%E7%BD%B2%E5%90%88%E7%BA%A6) 3.2 部署合约

例如获取演示

$ babel-node index.js

[ ](

demo#1-%E7%BC%96%E8%AF%91%E5%90%88%E7%BA%A6) 1. 编译合约

    execSync("solc --abi  --bin   --overwrite -o " + config.Ouputpath + "  " + filename + ".sol");

[ ](

演示#2-%E9%83%A8%E7%BD%B2%E5%90%88%E7%BA%A6%E5%88%B0%E5%8C%BA%E5%9D%97%E9%93 %BE%E4%B8%8A)2。 将合约部署到区块链

   var Contract = await web3sync.rawDeploy(config.account, config.privKey, filename);

[ ](

演示#3-%E5%AF%B9%E5%90%88%E7%BA%A6%E8%BF%9B%E8%A1%8C%E8%AF%BB%E5%86%99) 3. 是合约读写

    var address = fs.readFileSync(config.Ouputpath + filename + '.address', 'utf-8');    var abi = JSON.parse(fs.readFileSync(config.Ouputpath /*+filename+".sol:"*/ + filename + '.abi', 'utf-8'));    var contract = web3.eth.contract(abi);    var instance = contract.at(address);    //获取链上数据    var data = instance.getData();    //修改链上数据    var func = "setData(int256)";    var params = [10];    var receipt = await web3sync.sendRawTransaction(config.account, config.privKey, address, func, params);

[ ](

demo#321-%E5%BC%95%E5%85%A5%E6%A6%82%E5%BF%B5%EF%BC%9A) 3.2.1 概念介绍:

address:以太坊地址的长度比特币合约例子,大小为20字节,160位,所以可以用uint160编码。 地址是所有合约的基础,所有合约都会继承地址对象,随时可以使用一个地址字符串来获取对应的代码来调用。合约的地址是根据账户随机的哈希值计算出来的数量和交易数据

ABI:是以太坊中合约之间调用或发送消息时的一种消息格式。 就是定义操作函数签名、参数编码、返回结果编码等。

交易:以太坊中的“交易”是指存储从外部账户发送的消息的签名数据包。

简单的理解就是:只要写了区块链,就一定会发生交易。

交易收据:

交易发生后的返回值

[ ](

demo#322-%E6%89%A9%E5%B1%95%E9%98%85%E8%AF%BB%EF%BC%9A) 3.2.2 延伸阅读与学习:

3.3 合约文件结构介绍

[ ](

demo#1-%E7%89%88%E6%9C%AC%E5%A3%B0%E6%98%8E) 1.版本说明

pragma solidity ^0.4.10;

[ ](

演示#1-%E5%BC%95%E7%94%A8%E5%85%B6%E5%AE%83%E6%BA%90%E6%96%87%E4%BB%B6) 1. 报价其他源文件

import “filename”;//全局引入

[ ](

demo#1-%E7%8A%B6%E6%80%81%E5%8F%98%E9%87%8Fstate-variables) 1.状态变量(StateVariables)

    int256 storedData;

详情见下文

[ ](

demo#2-%E5%87%BD%E6%95%B0functions) 2. 函数

   function setData(int256 x) public {         storedData = x;         AddMsg(msg.sender, "[in the set() method]");     }       function getData() constant public returns (int256 _ret) {        return _ret = storedData;     }

[ ](

演示#3-%E4%BA%8B%E4%BB%B6%EF%BC%88events%EF%BC%89) 3. 事件

   //事件的声明   event AddMsg(address indexed sender, bytes32 msg);   //事件的使用   function setData(int256 x) public {         storedData = x;         AddMsg(msg.sender, "in the set() method");     }

[ ](

demo#4-%E7%BB%93%E6%9E%84%E7%B1%BB%E5%9E%8Bstructs-types) 4.结构类型(StructsTypes)

   contract Contract {     struct Data {       uint deadline;       uint amount;     }     Data data;     function set(uint id, uint deadline, uint amount) {       data.deadline = deadline;       data.amount = amount;     }   }

[ ](

演示#5-%E5%87%BD%E6%95%B0%E4%BF%AE%E9%A5%B0%E7%AC%A6%EF%BC%88函数修饰符%EF%BC%89) 5 . 函数修饰符

类似于钩子修饰符 only_with_at_least(int x) { if (x >= 5) { x = x+10; _; } }

[ ](

demo#4-%E5%90%88%E7%BA%A6%E7%BC%96%E7%A8%8B%E6%A8%A1%E5%BC%8Fcop) 4.合约编程模式COP

面向条件编程 (COP) 是面向契约编程的一个子领域,是面向函数和命令式编程的混合体。 COP 通过要求程序员显式枚举所有条件来解决这个问题。 逻辑变得平坦,没有条件状态变化。 可以从需求和实现中正确记录、重用和推断条件片段。 重要的是,COP 将前提条件视为编程中的一等公民。 这样的模型规范可以保证合约的安全性。

4.1

特征

例子:

contract Token {    // The balance of everyone    mapping (address => uint) public balances;    // Constructor - we're a millionaire!    function Token() {        balances[msg.sender] = 1000000;    }    // Transfer `_amount` tokens of ours to `_dest`.    function transfer(uint _amount, address _dest) {        balances[msg.sender] -= _amount;        balances[_dest] += _amount;    }}

改进后:

function transfer(uint _amount, address _dest) {    if (balances[msg.sender] < _amount)        return;    balances[msg.sender] -= _amount;    balances[_dest] += _amount;}

缔约方会议风格

modifier only_with_at_least(uint x) {    if (balances[msg.sender] >= x) _;}function transfer(uint _amount, address _dest)only_with_at_least(_amount) {    balances[msg.sender] -= _amount;    balances[_dest] += _amount;}

[ ](

demo#%E6%89%A9%E5%B1%95%E9%98%85%E8%AF%BB%EF%BC%9A) 扩展阅读和学习:

[ ](

demo#5-%E8%AF%AD%E6%B3%95%E4%BB%8B%E7%BB%8D) 五、语法介绍

基本语法见官方API

[ ](

demo#51-%E5%80%BC%E7%B1%BB%E5%9E%8B) 5.1 值类型

! 逻辑非 && 逻辑与 || 逻辑或 == 等于! = 不等于

[ ](

demo#52-%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8Breference-types) 5.2 引用类型

[ ](

demo#6-%E9%87%8D%E8%A6%81%E6%A6%82%E5%BF%B5) 6. 重要概念

[ ](

demo#61-solidity%E7%9A%84%E6%95%B0%E6%8D%AE%E4%BD%8D%E7%BD%AE) 6.1 Solidity数据位置

[ ](

demo#%E6%95%B0%E6%8D%AE%E4%BD%8D%E7%BD%AE%E7%9A%84%E7%B1%BB%E5%9E%8B) 数据位置类型

变量的存储位置属性。 分为三种,memory,storage,calldata。

存储 - 状态变量的存储模型

固定大小的变量(除map和变长数组外的所有类型)在存储中从位置0开始顺序排列。 如果多个变量占用小于 32 字节比特币合约例子,则尽可能将它们打包到一个存储槽中。 具体规则如下:

[ ](

demo#%E4%BC%98%E5%8C%96%E5%BB%BA%E8%AE%AE%EF%BC%9A)优化建议:

为了便于EVM优化,尽量有意识地对存储变量和结构体成员进行排序,这样可以将它们打包得更紧。 比如按照这个顺序定义,uint128,uint128,uint256,不是uint128,uint256,uint128。 因为后者会占用三个槽位。

内存 - 内存中的布局

Solidity 保留三个 32 字节的槽:

0-64:哈希方法的暂存空间

64-96:当前分配的内存大小(也叫空闲内存指针)

语句之间有可用的暂存空间(例如内联编译时)

Solidity 总是在空闲内存指针所在的位置创建一个新对象,并且相应的内存永远不会被释放(也许这在未来会改变)。

Solidity 中的某些操作需要超过 64 字节的暂存空间,这将超过保留的暂存空间。 它们会被分配到空闲内存指针所在的地方,但是由于它们自身的特性,生命周期比较短,而且指针本身不能更新,内存可能清零也可能不清零。 因此,您不应认为空闲内存一定已清零。

例子

6.2

地址

以太坊地址的长度是20字节160位,所以可以用uint160编码。地址是所有合约的基础,所有合约都会继承地址对象,随时可以得到一个地址串来获取对应的代码调用

6.3

事件

事件 AddMsg(地址索引发件人,bytes32 消息);

Event是使用EVM日志内置功能的便捷工具。 在DAPP的接口中,可以依次调用Javascript的回调来监听事件。

var event = instance.AddMsg({}, function(error, result) {        if (!error) {            var msg = "AddMsg: " + utils.hex2a(result.args.msg) + " from "            console.log(msg);            return;        } else {            console.log('it error')        }    });

[ ](

演示#64-%E6%95%B0%E7%BB%84) 6.4 数组

数组是固定长度或可变长度的数组。 有一个length属性,表示当前数组长度。

bytes:类似于byte[],一个动态长度的字节数组

string:类似于bytes,UTF-8编码的动态长度的字符类型

bytes1~bytes32

一般使用定长bytes1~bytes32。 当字符串的长度已知时,在指定长度时更节省空间。

[ ](

demo#641-%E5%88%9B%E5%BB%BA%E6%95%B0%E7%BB%84) 6.4.1 创建数组

文字 uint[] 内存 a = []

新的 uint[] 内存 a = 新的 uint ;

pragma solidity ^0.4.0;contract SimpleStartDemo{  uint[] stateVar;  function f(){   //定义一个变长数组    uint[] memory memVar;    //不能在使用new初始化以前使用    //VM Exception: invalid opcode    //memVar [0] = 100;    //通过new初始化一个memory的变长数组    memVar = new uint[](2);        //不能在使用new初始化以前使用    //VM Exception: invalid opcode    //stateVar[0] = 1;        //通过new初始化一个storage的变长数组    stateVar = new uint[](2);    stateVar[0] = 1;  }}

[ ](

演示#642-%E6%95%B0%E7%BB%84%E7%9A%84%E5%B1%9E%E6%80%A7%E5%92%8C%E6%96%B9%E6%B3 %95)6.4.2 数组的属性和方法

长度属性

storage变长数组是可以修改lengthmemory变长数组是不可以修改length

推送方法

storage变长数组可以使用push方法bytes可以使用push方法

pragma solidity ^0.4.2;contract SimpleStartDemo {  uint[] stateVar;  function f() returns (uint){    //在元素初始化前使用    stateVar.push(1);    stateVar = new uint[](1);    stateVar[0] = 0;    //自动扩充长度     uint pusharr = stateVar.push(1);     uint len = stateVar.length;    //不支持memory    //Member "push" is not available in uint256[] memory outside of storage.    //uint[] memory memVar = new uint[](1);    //memVar.push(1);    return len;  }}

下标:与其他语言类似

[ ](

demo#643-memory%E6%95%B0%E7%BB%84) 6.4.3 内存阵列

如果将 Memory 数组作为函数参数传递,则只能支持 ABI 支持的类型。

内存数组是一个属性,不能修改修改数组的大小

pragma solidity ^0.4.2;

contract SimpleStartDemo { function f() { //创建内存数组 uint[] memory a =new uint;

         //不能修改长度     //Error: Expression has to be an lvalue.     //a.length = 100; }  //storage uint[] b;  function g(){     b = new uint[](7);     //可以修改storage的数组     b.length = 10;     b[9] = 100; }

}

EVM 的局限性

由于EVM的限制,外部函数不能直接返回动态数组和多维数组

stroage数组不能直接返回,需要转成内存类型返回

  //Data层数据      struct Rate {              int key1;            int unit;            uint[3] exDataArr;            bytes32[3] exDataStr;        }            mapping(int =>Rate) Rates;     function getRate(int key1) public constant returns(int,uint[3],bytes32[3]) {            uint[3] memory exDataInt = Rates[key1].exDataArr;            bytes32[3] memory exDataStr = Rates[key1].exDataStr;            return (Rates[key1].unit,exDataInt,exDataStr);        }

商业场景