什么是智能合约
一个智能合约是一套以数字形式定义的承诺 (promises) , 包括合约参与方可以在上面执行这些承诺的协议一个合约由一组代码(合约的函数) 和数据 (合约的状态) 组成, 并且运行在以太坊虚拟机上.
以太坊虚拟机 (EVM) 使用了 256 比特长度的机器码, 是一种基于堆栈的虚拟机, 用于执行以太坊智能合约 由于 EVM 是针对以太坊体系设计的, 因此使用了以太坊账户模型 (Account Model) 进行价值传输
合约的代码具有什么能力:
读取交易数据
读取或写入合约自己的存储空间
读取环境变量块高, 哈希值, gas
向另一个合约发送一个内部交易
在区块链平台的架构
区块链平台的架构
1. 什么是 solidity
Solidity 是一种智能合约高级语言, 运行在 Ethereum 虚拟机 (EVM) 之上
solidity 语言特点
它的语法接近于 Javascript, 是一种面向对象的语言但作为一种真正意义上运行在网络上的去中心合约, 它有很多的不同点:
异常机制, 类似于事务的原子性一旦出现异常, 所有的执行都将会被回撤, 这主要是为了保证合约执行的原子性, 以避免中间状态出现的数据不一致
运行环境是在去中心化的网络上, 会比较强调合约或函数执行的调用的方式因为原来一个简单的函数调用变为了一个网络上的节点中的代码执行
存储是使用网络上的区块链, 数据的每一个状态都可以永久存储
2. 开发的工具
在线编译器 Remix
Visual Studio Code + soliidty 插件
3 快速入门
准备工作: 搭建区块链
一键部署区块链平台
3.1 举个例子
完整的步骤:
写合约
编译合约
部署合约
测试合约
一个例子 get demo
- 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;
- }
- }
3.2 部署合约
举个例子 get demo
$ babel-node index.js
1. 编译合约
execSync("solc --abi --bin --overwrite -o" + config.Ouputpath + "" + filename +".sol");
2. 部署合约到区块链上
var Contract = await web3sync.rawDeploy(config.account, config.privKey, filename);
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);
3.2.1 引入概念:
address: 以太坊地址的长度, 大小 20 个字节, 160 位, 所以可以用一个 uint160 编码地址是所有合约的基础, 所有的合约都会继承地址对象, 也可以随时将一个地址串, 得到对应的代码进行调用合约的地址是基于账号随机数和交易数据的哈希计算出来的
ABI: 是以太坊的一种合约间调用时或消息发送时的一个消息格式就是定义操作函数签名, 参数编码, 返回结果编码等
交易: 以太坊中交易是指存储从外部账户发出的消息的签名数据包
简单理解是: 只要对区块链进行写操作, 一定会发生交易
交易回执:
发生交易后的返回值
- Ethereum-Contract-ABI
- Solidity-Features
以太坊白皮书
3.3 合约文件结构简介
1. 版本声明
pragma solidity ^0.4.10;
1. 引用其它源文件
import filename;// 全局引入
1. 状态变量(State Variables)
int256 storedData;
详细说明见下文
2. 函数(Functions)
- 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. 事件(Events)
- // 事件的声明
- event AddMsg(address indexed sender, bytes32 msg);
- // 事件的使用
- function setData(int256 x) public {
- storedData = x;
- AddMsg(msg.sender, "in the set() method");
- }
4. 结构类型(Structs Types)
- 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. 函数修饰符(Function Modifiers)
类似于 hook modifier only_with_at_least(int x) { if (x >= 5) { x = x+10; _; } }
4. 合约编程模式 COP
面向条件的编程 (COP) 是面向合约编程的一个子域, 作为一种面向函数和命令式编程的混合模式 COP 解决了这个问题, 通过需要程序员显示地枚举所有的条件逻辑变得扁平, 没有条件的状态变化条件片段可以被正确的文档化, 复用, 可以根据需求和实现来推断重要的是, COP 在编程中把预先条件当作为一等公民这样的模式规范能保证合约的安全
4.1 FEATURES
函数主体没有条件判断
例子:
- 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;
- }
COP 的风格
- 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;
- }
- Condition-Orientated Programming
- Paper
- 6.3 event
- event AddMsg(address indexed sender, bytes32 msg);
- 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')
- }
- });
- 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;
- }
- }
- 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;
- }
- }
- pragma solidity ^ 0.4.2;
- contract SimpleStartDemo {
- function f() { // 创建一个 memory 的数组 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;
- }
- }
- //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);
- }
- pragma solidity ^0.4.5;
- contract FuntionTest{
- function internalFunc() internal{}
- function externalFunc() external{}
- function callFunc(){
- // 直接使用内部的方式调用
- internalFunc();
- // 不能在内部调用一个外部函数, 会报编译错误
- //Error: Undeclared identifier.
- //externalFunc();
- // 不能通过 `external` 的方式调用一个 `internal`
- //Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest
- //this.internalFunc();
- // 使用 `this` 以 `external` 的方式调用一个外部函数
- this.externalFunc();
- }
- }
- contract FunctionTest1{
- function externalCall(FuntionTest ft){
- // 调用另一个合约的外部函数
- ft.externalFunc();
- // 不能调用另一个合约的内部函数
- //Error: Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest
- //ft.internalFunc();
- }
- }
- pragma solidity ^0.4.2;
- contract SimpleStartDemo {
- uint public c = 10;
- function accessInternal() returns (uint){
- return c;
- }
- function accessExternal() returns (uint){
- return this.c();
- }
- }
- 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 setData(int256 x) public only_with_at_least(x){
- storedData = x;
- AddMsg(msg.sender, "[in the set() method]");
- }
- }
- pragma solidity ^0.4.4;
- contract Meta {
- string public name;
- string public abi;
- address metaAddress;
- function Meta(string n,string a){
- name=n;
- abi=a;
- }
- function getMeta()public constant returns(string,string,address){
- return (name,abi,metaAddress);
- }
- function setMetaAddress(address meta) public {
- metaAddress=meta;
- }
- }
- pragma solidity ^0.4.4;
- import "Meta.sol";
- contract Demo is Meta{
- bytes32 public orgID;
- function Demo (string n,string abi,bytes32 id) Meta(n,abi)
- {
- orgID = id;
- }
- }
- please keep in mind
- Fail as early and loudly as possible
- Favor pull over push payments
- Order your function code: conditions, actions, interactions
- Be aware of platform limits
- Write tests
- Fault tolerance and Automatic bug bounties
- Limit the amount of funds deposited
- Write simple and modular code
- Dont write all your code from scratch
- Timestamp dependency: Do not use timestamps in critical parts of the code, because miners can manipulate them
- Call stack depth limit: Dont use recursion, and be aware that any call can fail if stack depth limit is reached
- Reentrancy: Do not perform external calls in contracts. If you do, ensure that they are the very last thing you do
- bcos
- truffle
- trufflesuite
- upgrade smart contract
- upgradable
- solidity-proxy
- solution flow looks
- ContractFactory
- ether-router
- bcos
- blog.zeppelin.solutions
- solidity-workshop
- condition-orientated-programming
来源: https://juejin.im/post/5a9233926fb9a0634f40b502