# 介绍
本质是分布式数据库
# 术语
APY: 年度百分比收益率
BTC:比特币
BTCB: 币安智能链上的1:1锚定比特币。对普通用户而言,BTCB的主要优势是可以在持有BTC的同时通过BTCB在DeFi生态系统进行增值。如果没有BTCB,用户则需要卖出BTC,买入DeFi资产才能参与DeFi生态系统和使用dApp,用户可能在买卖之间错过价格上涨的机会。
有了BTCB,用户就无需卖出BTC,也不会错过价格上涨的机会。在BTC保持锁定的前提下,用户可以随时赎回BTCB,换成锚定价值的BTC。
USDC:USD Coin,美元稳定币
WBTC: 以加密货币抵押发行的稳定币。由BitGo、Kyber Network等发起以太坊网络上的BTC,是一种ERC20代币。WBTC与比特币1:1锚定,换句话说,每向指定地址存入1枚BTC,便生成一枚WBTC。由于比特币无法在以太坊网络直接流通,但通过WBTC间接实现了比特币在以太坊网络的流通。
全节点:指主网的一个副本,存储并维护脸上的所有数据,并随时验证新区块的合法性。运行全节点的配置最低要求:
- 双核
- 硬盘至少80g
- ssd的话需要4gb以上ram,hdd需要8gb以上ram
- 8m/s下载宽带
- 推荐配置:
- 四核
- 16gbram
- 500gb以上ssd
- 25mb/s宽带
以太坊客户端:运行节点的软件
远程客户端:不存储区块链的本地副本和校验交易,只提供钱包功能,比如metamask,可以创建和广播交易
UTXO: 比特币基于UTXO结构,表示未使用的交易输出,系统中用户的余额是用户具有四要的utxo的总值
message: 合约和合约的通信交互不是交易,应称为消息,可以看做函数调用。
EIP: 以太坊改进提案
chainId:用于防止networkid值相同时造成的重放攻击,比如ETH和ETC的networkid都为1,于eip-155 (opens new window)实现
# 特点
每个区块包含两个部分:区块头和区块体
每个区块都有一个唯一的哈希标识,被称为区块哈希。
区块通过记录上一个区块的哈希来指向上一个区块。
每一个区块还有一个Merkle哈希用来确保该区块的所有交易记录无法被篡改。
区块链中的主要数据就是一系列交易,第一条交易通常是Coinbase交易,也就是矿工的挖矿奖励,后续交易都是用户的交易。
区块链的不可篡改特性是由哈希算法保证的。
# 以太坊区块头
记录当前区块的特征值,包含:
- ParentHash: 父区块的哈希值。
- Root:世界状态的哈希,stateDB的RLP编码后的哈希值。
- TxHash(transaction root hash):交易字典树的根哈希,由本区块所有交易的交易哈希算出。
- ReceptHash:收据树的哈希。
- Time:区块产生出来的Unix时间戳。
- Number:区块号。
- Bloom:布隆过滤器,快速定位日志是否在这个区块中。
区块hash是唯一值,而区块号不一定是,参考:https://zhuanlan.zhihu.com/p/34947833
# 区块体
实际的数据
# 哈希算法
哈希算法,又称散列算法,它是一个单向函数,可以把任意长度的输入数据转化为固定长度的输出:
h=H(x)
例如,对morning
和bitcoin
两个输入进行某种哈希运算,得到的结果是固定长度的数字:
H("morning") = c7c3169c21f1d92e9577871831d067c8
H("bitcoin") = cd5b1e4947e304476c788cd474fb579a
每个区块的哈希都是针对区块头计算的,也就是说,把区块头的各项特征值,按照顺序连接在一起,组成一个很长的字符串,再对这个字符串计算哈希。
# 哈希碰撞
一个安全的哈希算法还需要满足另一个条件:碰撞率低。
碰撞是指,如果两个输入数据不同,却恰好计算出了相同的哈希值,那么我们说发生了碰撞
# 常用hash算法
哈希算法 | 输出长度(bit) | 输出长度(字节) |
---|---|---|
MD5 | 128 bit | 16 bytes |
RipeMD160 | 160 bits | 20 bytes |
SHA-1 | 160 bits | 20 bytes |
SHA-256 | 256 bits | 32 bytes |
SHA-512 | 512 bits | 64 bytes |
比特币使用的哈希算法有两种:SHA-256和RipeMD160
比特币使用两种哈希算法,一种是对数据进行两次SHA-256计算,这种算法在比特币协议中通常被称为hash256或者dhash。
另一种算法是先计算SHA-256,再计算RipeMD160,这种算法在比特币协议中通常被称为hash160。
# Merkle Hash
在区块的头部,有一个Merkle Hash字段,它记录了本区块所有交易的Merkle Hash。它的作用就是保证交易记录永远无法修改。
# block hash
区块本身用block Hash--也就是区块哈希来标识。但是,一个区块自己的区块哈希并没有记录在区块头部,而是通过计算区块头部的哈希得到的。
区块头部的Prev Hash记录了上一个区块的Block Hash,这样可以通过Prev Hash追踪到上一个区块。由于下一个区块的Prev Hash又会指向当前区块,这样,每个区块的Prev Hash都指向自己的上一个区块,这些区块串起来就形成了区块链。
区块链的第一个区块(又称创世区块)并没有上一个区块,因此,它的Prev Hash被设置为00000000...000
。
# p2p交易
# 数字签名
数字签名的三个作用:防伪造,防篡改,防抵赖。
比特币采用的签名算法是椭圆形曲线签名算法:ECDSA
比特币的私钥是一个随机的非常大的256位整数。它的上限,确切地说,比2256要稍微小一点
而比特币的公钥是根据私钥推算出的两个256位整数。
# 比特币钱包
比特币钱包实际上就是帮助用户管理私钥的软件,它有几种分类:
- 本地钱包:是把私钥保存在本地计算机硬盘上的钱包软件,如Electrum (opens new window);
- 手机钱包:和本地钱包类似,但可以直接在手机上运行,如Bitpay (opens new window);
- 在线钱包:是把私钥委托给第三方在线服务商保存;
- 纸钱包:是指把私钥打印出来保存在纸上;
- 脑钱包:是指把私钥记在自己脑袋里。
# 交易
比特币协议规定一个输出必须一次性花完,假如小明有50个比特币,那么转账给小红2个比特币,同时小明又给自己48个比特币,这48个比特币就是找零。所以,一个交易中,一个输入可以对应多个输出
在实际的交易中,输入比输出要稍微大一点点,这个差额就是隐含的交易费用,交易费用会算入当前区块的矿工收入中作为矿工奖励的一部分
交易费用 = 输入 - 输出
# 交易流程
- 申报交易:需要上一笔交易的Hash(从哪里获得的比特币)、本次交易双方的地址、支付方的公钥、支付方的私钥生成的数字签名
- 写入交易: 矿工把交易写入区块链
# 挖矿原理
新的交易打包成新的区块并附加到区块链上,这类节点就是矿工。因为每打包一个新的区块,打包该区块的矿工就可以获得一笔比特币作为奖励。所以,打包新区块就被称为挖矿。
由于必须保证节点之间的同步,所以新区块的添加速度不能太快。所以中本聪故意让添加新区块,变得很困难。区块头里面所有的特征值都是固定的,为了让区块头产生变化,中本聪故意增加了一个随机项,叫做 Nonce。
比特币网络的挖矿难度值是不断变化的,它的难度值保证大约每10分钟产生一个区块,而难度值在每2015个区块调整一次:如果区块平均生成时间小于10分钟,说明全网算力增加,难度值也会增加,如果区块平均生成时间大于10分钟,说明全网算力减少,难度值也会减少。因此,难度值随着全网算力的增减会动态调整。
当某个矿工成功找到特定哈希的新区块后,他会立刻向全网广播该区块。其他矿工在收到新区块后,会对新区块进行验证,如果有效,就把它添加到区块链的尾部。同时说明,在本轮工作量证明的竞争中,这个矿工胜出,而其他矿工都失败了。失败的矿工会抛弃自己当前正在计算还没有算完的区块,转而开始计算下一个区块,进行下一轮工作量证明的竞争。
如果区块链有分叉,将看哪个分支在分叉点后面,先达到6个新区块(称为"六次确认")。按照10分钟一个区块计算,一小时就可以确认。
根据比特币协议,一个区块的大小最大是 1MB,而一笔交易大概是500字节左右,因此一个区块最多可以包含2000多笔交易。矿工负责把这2000多笔交易打包在一起,组成一个区块,然后计算这个区块的哈希。
# 矿工的收益
- 挖矿奖励比特币
- 交易手续费
# 虚拟货币
# 单位
虚拟货币的实际balance为前端显示数量 * 10**decimals
,参考智能合约代码注释:
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
# BCH
由于比特币协议规定,平均10分钟诞生一个区块,导致处理交易速度有限。2017年8月区块链发生了一次分叉,诞生了一个新协议,称为bitcoin Cash(简称BCH)。这种新货币其他方面都与比特币一致,就是每个区块的大小从 1MB 增加到了 8MB,因此处理速度提升了8倍,手续费也低得多。该协议是对原有区块链的分叉,因此当时持有比特币的人,等于一人获赠了一份同样数量的 BCH。
# 区块链平台
# 以太坊
以太坊区块链上创建的代币通常称为ERC-20代币 。Ethereum定义了一个和Ethereum节点通信的协议 JSON-RPC API (opens new window)。 以太坊内置编程语言支持,以太坊的目标是将区块链技术所具有的去中心化、开放、和安全这三大特点,引入到几乎所有能被计算的领域。
# 以太坊chains
查询所有的以太坊链信息:https://github.com/ethereum-lists/chains/tree/master/_data/chains
# 账户
以太坊账户有两种:外部账户和合约账户。
账户由4部分组成:
- nonce: 是一个递增的整数,每发送一次交易,nonce递增1,因此,nonce记录的就是交易次数 用于防止错误计算,强制来自任何地址的交易按顺序处理,没有间隔,无论节点接收它们的顺序如何
- balance: 账户余额,以wei为单位,1 Ether等于1018wei。
- storage
- codeHash
如果一个账户是合约账户,则storageRoot存储合约相关的状态数据,codeHash存储合约代码的Hash。对于外部账户,这两部分数据都是空
# 智能合约
比特币实现了分布式的数据存储,以太坊则用同样的方式实现了分布式的数据存储和计算。(从整体来看),以太坊就像一台计算机,而上面运行的计算机程序我们叫做 “智能合约”。(从实际上来说),一个网络参与者乃是在他的电脑上用一种叫 “以太坊虚拟机” 的操作系统运行这些程序(“智能合约”)。
合约作为地址,可以接收Ether,也可以发送Ether。合约内部也可以存储数据。合约可以互相调用,合约只能被动地被外部账户调用。
# 交易
任何交易都需要手续费,手续费称为gas,以太坊给每一个虚拟机指令都标记了一个Gas基本费用,称为gasUsed。标准的转账交易消耗gasUsed为21000
除了gasUsed外,用户还需要提供一个gasPrice,以Gwei(1Gwei=109Wei)为单位。通过竞价得到一个矿工愿意接受的gasPrice。如果一个交易消耗了120000的gasUsed,而gasPrice是50 Gwei,则交易费用是:
120000 x 50 Gwei = 6000000 Gwei = 0.006 Ether
Gas Price是全网用户竞价产生的,它时刻在波动。如果交易少,Gas Price将下降,如果交易多,网络拥堵,则Gas Price将上升。
执行代码的时候,存在条件判断、循环等语句,同一段代码,执行的结果也可能不同,因此,事前预计一个合约执行要花费多少Gas,不现实。
所以以太坊规定,一笔交易,先给出gasPrice和gasLimit(最多gasUsed数),如果执行完成后有剩余,剩余的退还,如果执行过程中消耗殆尽,那么交易执行失败,但已执行的Gas不会退。
交易的接受者:交易接收者在to字段中制定,是一个20字节的以太坊地址。以太坊没有进一步的验证,任何20字节的值都是有效的。如果没有地址为无效地址,则该交易仍然有效,将销毁发送的以太,使其永远无法访问
交易的value:仅有value没有data的交易就是一笔以太的付款
交易的data:仅有data的交易一般是合约调用
# 交易回执(Recipt)
以太坊区块为每一笔交易都会产生一笔回执(Recipt),表示交易的最终状态。一个回执信息主要包括:
- status:执行结果,1表示成功,0表示失败;
- gasUsed:已消耗的Gas数量;
- txHash:交易Hash;
- logs:交易产生的日志;
- ……
# 合约交易
合约交易就是指一个外部账号调用某个合约的某个public函数。
如果调用只读方法,因为不改变合约状态,所以任何时刻都可以调用,且不需要签名,也不需要消耗Gas。但如果调用写入方法,就需要签名提交一个交易,并消耗一定的Gas。
在一个交易中,只能调用一个合约的一个写入方法。无需考虑并发和同步的问题,因为以太坊交易的写入是严格串行的。
# MetaMask
浏览器钱包插件,提供一部分的 JSON-RPC API (opens new window)协议的封装,功能上和web3.js有小部分重叠,本质上都是调用JSON-RPC。
钱包中的token每一个都是一个合约的地址,而钱包中保存了钱包地址和该合约地址的map
无论钱包的操作是发送还是转入,from
都是钱包地址
# 测试网络(Test net)
与主网(Main net)对应,主网上线前的测试网络,主要是用作项目快速开发迭代以及社区成员早期参与。
测试网络有:Ropsten、kovan、Rinkeby
:
ropsten
是以太坊官方提供的测试网络,共识机制为Pow
。kovan
是以太坊钱包Parity的开发团队基于PoA
机制开发的测试网络,只有Parity
钱包可以使用Rinkeby
是以太坊官方提供的基于PoA
机制开发的测试网络
可以从https://testnet.binance.org/faucet-smart获取一些测试网的Ether。
# 以太坊交易
合约不是自己运行的,以太坊也不会再后台运行,以太坊上的一切变换都始于交易。
交易的Hash data是如何计算的?
发送给合约的数据有效负责是32字节的十六进制序列化编码:
- 函数选择器:函数原型的Keccak245哈希的前4个字节,通过这个evm可以获取到将要调用的函数
- 函数参数:根据evm定义的各种基本类型的规则进行编码
也就是HEX DATA= 前几位为函数hash值 + n个0 + 参数的16进制值
# 合约开发
# 介绍
msg.sender
表示调用方地址
# 执行流程
合约部署后会获得一个合约地址,把合约看做一个类,部署就相当于是实例化,如果部署两次,将得到两个不同的地址,相当于实例化两次。
构造函数在部署合约时就会立刻执行,且仅执行一次。合约部署后就无法调用构造函数。
以太坊合约具备类似数据库事务的特点,如果中途执行失败,则整个合约的状态保持不变,不存在修改某个成员变量后,后续断言失败导致部分修改生效的问题。
合约中的public字段会自动生成一个同名的只读函数
创建合约这笔交易会被发送到零地址
# 部署
以太坊官方提供了一个Remix (opens new window)的在线IDE,用于编写、编译和部署以太坊合约。这是从零开始部署一个合约的最简单的方式。
通过在workspace > contracts
新建*.sol
格式文件,然后编写合约,然后编译,编译成功之后就可以deploy
了,此时会弹出MetaMask的交易签名确认,确认后部署合约的交易即被发送至测试链。在MetaMask的账户 - 活动中可以看到正在发送的交易,查看详情可以在Etherscan查看该交易的详细信息 (opens new window)。当交易被打包确认后,即可获得部署后合约的地址。
对于熟练的Solidity开发者,可以使用Truffle (opens new window)这个JavaScript脚本全自动部署合约,减少手动操作导致的出错的可能
# 合约的创建
通过一个特殊的create call
。合约可以创建其他合约(不是简单的调用零地址)
# 合约的销毁
唯一方法是合约在合约地址上的执行自毁操作selfdestruct;合约账户上剩余的以太币会发送给指定的目标,然后琪存储和代码从状态中被移除
# 前端调用
- 引入ethers.js,它封装了读写合约的逻辑
- 获取metamask注入的web3
- 链接钱包
- 写入合约(需要合约地址、合约abi、合约签名)
# 调试
通过交易hash去对应的scan
网址查询交易详情,可查看报错信息。
# solc
solc
是以太坊官方出的一款Solidity语言开发的智能合约的编译工具,可以通过 solc
获得部署合约时所要使用的 bytecode
和 abi
。
# web3.js
web3.js
是以太坊官方实现的一个使用 javascript
与以太坊客户端进行交互的js库, web3.js
可以与任何暴露RPC层的以太坊节点协同工作。
其实 web3
就是对以太坊的 json-rpc
接口使用 javascript
进行了一次封装,既然是 rpc
,我们在使用时,必须连接一个服务器,也就是以太坊的支持 json-rpc
的节点。在这里,我们一般把连接 json-rpc
节点称为注入以太坊节点。注入节点有两种方式:
- 注入自有节点或者开放节点
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
- 使用MetaMask等浏览器插件
如果想要使用web3来部署合约,需要先获得合约的bytecode以及合约的abi,而编译可以使用solcjs进行。
npm install -g solc
安装成功后,我们可以通过命令行编译代码,获得code以及abi。
# 获得code
solcjs --bin name.sol
# 获得abi
solcjs --abi name.sol
参考:如何使用web3部署智能合约 (opens new window)
# 实例
new web3.eth.Contract()会返回合约实例,
方法调用:
使用contract.methods.methodName.call()
调用合约,不会发送交易
使用contract.methods.methodName.send()
调用合约,有可能发送交易
事件监听:
使用contract.events.eventName()
# 学习路径
- 用“费曼学习法”理解区块链,你也可以做到 (opens new window)
- 区块链基础知识25讲 - 入门
- 以太坊官方开发者学习文档: https://ethereum.org/zh/developers/docs/
- 精通以太坊 (opens new window) - 业务
- 微软官网学习区块链:https://docs.microsoft.com/zh-cn/learn/paths/ethereum-blockchain-development/
- 廖雪峰区块链教学:https://www.liaoxuefeng.com/wiki/1207298049439968
参考:https://zhuanlan.zhihu.com/p/112038593
# 问题
# 如何估算gas?
eth.estimateGas
# bitcoin和Ethereum区别?
- 设计定位
- bitcoin: 现金系统
- Ethereun:去中心化应用平台
- 数据组成
- bitcoin:交易账本
- Ethereum:交易和账户状态
- 交易对象
- UTXO
- Accounts
- 代码控制
- 脚本
- 智能合约
# 为什么比特币能当钱用?
- 不会被偷。没有私钥无法获取其他人的钱
- 无法伪造。因为区块链技术可信性高
- 无法大批生成,因为每4年减半所以不会导致通货膨胀
# token和coin区别?
token表示钱包金额的单位
# 调用合约函数发生了什么?
从外部施加给以太坊的行为都称之为向以太坊网络提交了一个交易, 调用合约函数其实是向合约地址(账户)提交了一个交易,这个交易有一个附加数据,这个附加的数据就是ABI的编码数据。
# 如何部署合约到以太坊区块链?
通过编写sol格式文件在remix上可以部署
# 如何更改以部署的智能合约?相关数据如何转移到新合约中?
已部署的合约无法更改,只能通过部署新合约
# 如何通过合约地址查看合约函数?
通过etherscan网站查看,例如:https://ropsten.etherscan.io/address/0x091424531D3eEDF0730B5b6C5e53d4042e58420d#code
# web3.js中call和sendTransaction区别?
eth_call调用只是本地查询调用,不会发送交易,记录在链。eth_sendTransaction调用会发起交易请求,交易会打包记录在链。
# 如何批量发送交易?
# 依赖钱包
如果该热点账户的私钥信息等都存放在Ethereum客户端中,那么在发送交易的时候不传递nonce值,Ethereum客户端会帮你处理好此nonce值的排序。
当然,此方案有两个弊端。第一个是安全性无法保障(未进行冷热账户分离),第二,在热点账户下如果想覆盖掉一笔交易,需要先查询一下该交易的信息,从中获取nonce值。
# 自行管理nonce
自行管理nonce适用于冷热账户模式,也就是适用sendRawTransaction发送已经签名好的交易时,此时nonce值已经存在于交易中,并且已经被签名。
这种模式下,需要在业务系统中维护nonce的自增序列,适用一个nonce之后,在业务系统中对nonce进行加一处理。
此种方案也有限制条件。第一,由于nonce统一进行维护,那么这个地址必须是内部地址,而且发起交易必须通过统一维护的nonce作为出口,否则在其他地方发起交易,原有维护的nonce将会出现混乱。第二,一旦已经发出的交易发生异常,异常交易的nonce未被使用,那么异常交易的nonce需要重新被使用之后它后面的nonce才会生效。
参考:https://cloud.tencent.com/developer/article/1019756?from=article.detail.1009399