以太坊交互
区块链中级.以太坊开发
From:JamesZou & 传智播客研究院 & 传智播客区块链
一. 账户
1. 以太坊账户
账户分两种:外部账户 和 合约账户
1.1 外部账户
-
用户使用的账户,包含 地址和私钥
-
操作
- 查看余额
- 可以转账交易
- 调用合约
-
特点:
- 创建不需要网络,本地生成(钱包生成,自己生成,纸钱包等)
- 被私钥控制
- 仅有余额,没有代码
- 可以发送交易(transaction)
- 每个以太坊网络中都可以使用,但是金额互相独立,只要符合以太坊网络规则即可
1.2. 合约账户
-
由外部账户创建的智能合约实例,部署后返回一个地址,这个地址就是合约账户(有金额,有代码逻辑)
-
操作:
- 由外部账户发送消息**合约账户的代码
- 接收转账,向外转账
- 调用其它合约
-
特点
- 拥有 ether 余额
- 含有执行代码
- 代码仅在该合约地址发生交易或者收到其他合约发送的信息时才会被执行
- 拥有自己的独立存储状态,且可以调用其他合约
注意:合约账户由外部账户或合约代码逻辑进行创建,一旦部署成功,只能按照预先写好的合约逻辑进行业务交互,不存在其他方式直接操作合约账户或更改已部署的合约代码。
-
合约源码与实例
合约是“类”,可以创建很多“实例对象”
let a = new Dog();
let b = new Dog();
2.2 Ropsten
3.转账
3.1 对比
-
对比
- 传统银行:在不同的网络里面,同一个账户的金额可能不同
银行 | 开户人 | 金额 | 取款密码(存储在银行) |
---|---|---|---|
招商银行 | 华仔 | 10000元 | 123456 |
建设银行 | 华仔 | 20000元 | 654321 |
农业银行 | 华仔 | 15000元 | 235677 |
中国银行 | 华仔 | 30000元 | 876644 |
-
以太坊网络
- 不同的银行相当于不同的区块链网络, 同一个开户人在不同银行的存款是不一样的。
- 在传统中心化网络里面,密码是存储在银行的中心化的服务器里面的。
- 不同的区块链网络, 拥有相同的账户信息. 密码是存储在用户端(private key)。
- 不同之处是各个网络无法互相转账,而传统银行是可以的。
区块链网络 | 账户地址 | 金额 | 私钥(取款用,存储在用户端) |
---|---|---|---|
main network | 0xd5a2… | 10000 ether | 0x358b35069… |
Ropsten network | 0xd5a2… | 20000 ether | 0x358b35069… |
kovan network | 0xd5a2… | 15000 ether | 0x358b35069… |
rinkeby network | 0xd5a2… | 30000 ether | 0x358b35069… |
- 演示转账
3.2 几个概念
-
gas(汽油)- 油耗
由于以太坊是公链,所有人都可以自由的参与,为了防止垃圾数据充斥网络,所以以太坊上规定每一个操作都是要有成本的,这个成本由
gas
来体现,你要转账,部署智能合约,调用智能合约方法,都要消耗一定数量的gas。单位 : 1 eth = 10 ^18 wei
-
gasprice(汽油价格) - 油价
虽然操作消耗gas,但是最终真正花的还是eth,所以就有一个转换率的问题,gasprice就是起到一个汇率的作用,它代表的是一个gas值多少eth,gas*gasprice就是最终的手续费,也就是从你账户扣除的eth。
这种设计就可以保证用户在以太坊上的操作的手续费不会随着eth的价格而发生剧烈的变动(例如:如果eth涨,那么可以调低gasprice来降低手续费),
-
gaslimit(汽油上限)- 油箱
以太坊规定,每笔交易的gas最少21000,矿工可以调整这个值,所以最终的花费的gas是不确定的,所以以太坊就设置了gaslimit,这个代表的是最多给旷工这么多gas(防止自己写的合约里面有死循环),如果最终使用的gas少于这个gaslimit,剩余的还会返给你的,但是如果你的gaslimit不足以支付这次交易,那就是不会退回的,并且交易也就失败了,转账的额度也是回不来了,所以你转账设置的limit一定要大于21000。
3.3 转账流程
-
解释点下确定按钮的流程:
1.点击submit
2.地址通过get请求发给了服务器
3.web3.js 创造了一个转账 (translation) (什么是转账,转账包含什么信息)
nonce,to,value,gas,gaslimit,密码学相关 有privatekey生成
4.为什么需要等待, 后台server发送translation 到rinkeby网络
5.等待挖矿,30秒左右
6.后台服务器把成功信息反馈给前端
3.4 转账具体流程
- translation提交给一个node(如何修改node,选择node?)
- node可能在某个时间内收到很多个translation
- node对translation排序,挖矿
- node算好nonce就会广播给全网
- 最先算出nonce的node会得到挖矿奖励
4.区块时间
4.1 区块时间原理
- 区块时间 算 0000 开头是不科学的. 是简易算法.
- 实际算法是计算一个hash 小于某种特殊值.
- hash值的范围 0 ~ 1.15x10^77 跟摇色子一样.
4.2 区块时间详解
- blocktime = time to find a solution
-
难度系数
<1000 --> 20s
<10000 --> 5s
<5000 -->17s
<4000 -->15s
二. Solidity
1. Solidity简介
- 以太坊编写智能合约的语言,文件扩展名 .sol
- 强类型编程语言(变量类型有多种,例如int byte bool struct 等,类型之间转换时需要强转)
- 与javascript类似(但是JavaScript是弱类型的语言,一个var关键字自动识别数据类型)
- 一堆坑
- api手册 -》 Solidity中文手册
2.开发环境
-
Mist钱包 - 不好用,问题较多
-
Remix在线编程
- 特点:访问链接、方便调试、依赖网络
- 地址:remix.ethereum.org
-
Remix本地环境
-
特点:方便,相对稳定
-
问题:偶尔无法正常加载编译器,需要完全断网才能加载
-
安装
npm install remix-ide -g
-
启动服务:
remix-ide
-
3.第一个合约
- 智能合约概览
每个合约中可包含:
- 状态变量(State Variables) : 就是成员变量,这个数据是上链的。
- 函数(Functions)
- 函数修饰符(Function Modifiers)
- 事件(Events)
- 结构类型(Structs Types)
- 枚举类型(Enum Types)
- mapping(key => value) 如: map[1] = 2
- 便写一个 接收消息的方法
pragma solidity ^0.4.25;
contract Inbox{
string public message;
function getMessage() public constant returns(string){
return message;
}
function setMessage(string newMessage) public{
message = newMessage;
}
}
- 语法解析
4.编译合约
4.1 原理
-
使用remix,由高级语言变成机器语言
- solidity —> bytecode - 机器语言,区块链系统读取
- solidity —> ABI (application binary interface) - 方便程序员调用
-
图示
4.2 bytecode
6080604052610410806100136000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063368b87721461005c578063ce6d41de146100c5578063e21f37ce14610155575b600080fd5b34801561006857600080fd5b506100c3600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101e5565b005b3480156100d157600080fd5b506100da6101ff565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561011a5780820151818401526020810190506100ff565b50505050905090810190601f1680156101475780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561016157600080fd5b5061016a6102a1565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101aa57808201518184015260208101905061018f565b50505050905090810190601f1680156101d75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101fb92919061033f565b5050565b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102975780601f1061026c57610100808354040283529160200191610297565b820191906000526020600020905b81548152906001019060200180831161027a57829003601f168201915b5050505050905090565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103375780601f1061030c57610100808354040283529160200191610337565b820191906000526020600020905b81548152906001019060200180831161031a57829003601f168201915b505050505081565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061038057805160ff19168380011785556103ae565b828001600101855582156103ae579182015b828111156103ad578251825591602001919060010190610392565b5b5090506103bb91906103bf565b5090565b6103e191905b808211156103dd5760008160009055506001016103c5565b5090565b905600a165627a7a723058201486092ea8103d4816d5aa6177b30372d94ec850544312021159c20f3b7a505b0029
4.3 abi
-
(application binary interface,应用二进制接口)
json格式的描述文件:
[
{
"constant": false,
"inputs": [
{
"name": "newMessage",
"type": "string"
}
],
"name": "setMessage",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getMessage",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "message",
"outputs": [
{
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"payable": true,
"stateMutability": "payable",
"type": "constructor"
}
]
4.4 调用
以太坊为了方便智能合约的开发、学习和测试,开启了几条全新的区块链,与主网络特性相同,但测试网络中的以太币价值更低,也更容易得到。这样不至于在主网络上开发出现 BUG 造成以太币的损失。
当然我们也可以搭建私有的测试网络,不过区块链的去中心化特点,需要更多的节点运行才能达到理想效果,好在以太坊有公开的测试网络,而我们接入也更容易。
4.4.1 运行环境
-
vm
- account 虚拟账户5个,每个账户默认100eth。
- 没挖矿,直接返回结果,便于开发测试
-
网页环境
- injected web3
- 与metamask结合紧密
-
自定义环境
- web3 provider
- 链接自己本地启动的区块链环境(truffle,ganache,私有链等)
4.4.2 部署合约
-
部署成功后返回合约地址,如:
0x27baf63ea560973e9f8f333a39239967f96c5703
-
部署合约也是一笔交易,只不过不用填写
to
字段,且数据输入
字段填写为合约字节码。
- 查看浏览器
4.4.3 调用合约
-
手动调用,我们使用手动调用,调用合约的操作分为两种类型
- send:所有改变区块链数据状态的叫做是send操作,相当于发起一笔交易,特点是花钱,大约10s~30s(矿工确认)返回结果。
- call:所有读取区块链合约数据的操作成为call操作,它的特点是迅速返回,无需花钱
-
通过web3调用
4.4.4 对比总结
对比项 | 函数调用call | 函数调用 send transaction |
---|---|---|
数据状态 | 不修改智能合约的数据 | 修改智能合约的数据 |
返回值 | 函数可以返回数据 | 函数不能返回数据, 因为函数需要花时间执行 |
执行速度 | 立刻执行 | 需要十几秒才能执行完毕,返回值是transaction的hash |
成本 | 免费 | 要花钱 钱!!! |
4.5 析构函数
function destroy() {
//析构函数,销毁当前合约,并把它所有资金发送到给定的地址
//1.将合约里的 余额 转给 传入的地址
//2.标记当前合约 为 销毁状态
selfdestruct(msg.sender);
}
调用之后,合约仍然存在于区块链之上,但是函数无法被调用,调用会抛出异常。