# 介绍

本质是分布式数据库

# 术语

  1. APY: 年度百分比收益率

  2. BTC:比特币

  3. BTCB: 币安智能链上的1:1锚定比特币。对普通用户而言,BTCB的主要优势是可以在持有BTC的同时通过BTCB在DeFi生态系统进行增值。如果没有BTCB,用户则需要卖出BTC,买入DeFi资产才能参与DeFi生态系统和使用dApp,用户可能在买卖之间错过价格上涨的机会。

    有了BTCB,用户就无需卖出BTC,也不会错过价格上涨的机会。在BTC保持锁定的前提下,用户可以随时赎回BTCB,换成锚定价值的BTC。

  4. USDC:USD Coin,美元稳定币

  5. WBTC: 以加密货币抵押发行的稳定币。由BitGo、Kyber Network等发起以太坊网络上的BTC,是一种ERC20代币。WBTC与比特币1:1锚定,换句话说,每向指定地址存入1枚BTC,便生成一枚WBTC。由于比特币无法在以太坊网络直接流通,但通过WBTC间接实现了比特币在以太坊网络的流通。

  6. 全节点:指主网的一个副本,存储并维护脸上的所有数据,并随时验证新区块的合法性。运行全节点的配置最低要求:

    1. 双核
    2. 硬盘至少80g
    3. ssd的话需要4gb以上ram,hdd需要8gb以上ram
    4. 8m/s下载宽带
    5. 推荐配置:
      1. 四核
      2. 16gbram
      3. 500gb以上ssd
      4. 25mb/s宽带
  7. 以太坊客户端:运行节点的软件

  8. 远程客户端:不存储区块链的本地副本和校验交易,只提供钱包功能,比如metamask,可以创建和广播交易

  9. UTXO: 比特币基于UTXO结构,表示未使用的交易输出,系统中用户的余额是用户具有四要的utxo的总值

  10. message: 合约和合约的通信交互不是交易,应称为消息,可以看做函数调用。

  11. EIP: 以太坊改进提案

  12. 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)

例如,对morningbitcoin两个输入进行某种哈希运算,得到的结果是固定长度的数字:

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个比特币就是找零。所以,一个交易中,一个输入可以对应多个输出

在实际的交易中,输入比输出要稍微大一点点,这个差额就是隐含的交易费用,交易费用会算入当前区块的矿工收入中作为矿工奖励的一部分

交易费用 = 输入 - 输出

# 交易流程

  1. 申报交易:需要上一笔交易的Hash(从哪里获得的比特币)、本次交易双方的地址、支付方的公钥、支付方的私钥生成的数字签名
  2. 写入交易: 矿工把交易写入区块链

# 挖矿原理

新的交易打包成新的区块并附加到区块链上,这类节点就是矿工。因为每打包一个新的区块,打包该区块的矿工就可以获得一笔比特币作为奖励。所以,打包新区块就被称为挖矿。

由于必须保证节点之间的同步,所以新区块的添加速度不能太快。所以中本聪故意让添加新区块,变得很困难。区块头里面所有的特征值都是固定的,为了让区块头产生变化,中本聪故意增加了一个随机项,叫做 Nonce。

比特币网络的挖矿难度值是不断变化的,它的难度值保证大约每10分钟产生一个区块,而难度值在每2015个区块调整一次:如果区块平均生成时间小于10分钟,说明全网算力增加,难度值也会增加,如果区块平均生成时间大于10分钟,说明全网算力减少,难度值也会减少。因此,难度值随着全网算力的增减会动态调整。

当某个矿工成功找到特定哈希的新区块后,他会立刻向全网广播该区块。其他矿工在收到新区块后,会对新区块进行验证,如果有效,就把它添加到区块链的尾部。同时说明,在本轮工作量证明的竞争中,这个矿工胜出,而其他矿工都失败了。失败的矿工会抛弃自己当前正在计算还没有算完的区块,转而开始计算下一个区块,进行下一轮工作量证明的竞争。

如果区块链有分叉,将看哪个分支在分叉点后面,先达到6个新区块(称为"六次确认")。按照10分钟一个区块计算,一小时就可以确认。

根据比特币协议,一个区块的大小最大是 1MB,而一笔交易大概是500字节左右,因此一个区块最多可以包含2000多笔交易。矿工负责把这2000多笔交易打包在一起,组成一个区块,然后计算这个区块的哈希。

# 矿工的收益

  1. 挖矿奖励比特币
  2. 交易手续费

# 虚拟货币

# 单位

虚拟货币的实际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部分组成:

  1. nonce: 是一个递增的整数,每发送一次交易,nonce递增1,因此,nonce记录的就是交易次数 用于防止错误计算,强制来自任何地址的交易按顺序处理,没有间隔,无论节点接收它们的顺序如何
  2. balance: 账户余额,以wei为单位,1 Ether等于1018wei。
  3. storage
  4. 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

  1. ropsten是以太坊官方提供的测试网络,共识机制为Pow
  2. kovan是以太坊钱包Parity的开发团队基于PoA机制开发的测试网络,只有Parity钱包可以使用
  3. Rinkeby是以太坊官方提供的基于PoA机制开发的测试网络

可以从https://testnet.binance.org/faucet-smart获取一些测试网的Ether。

# 以太坊交易

合约不是自己运行的,以太坊也不会再后台运行,以太坊上的一切变换都始于交易。

交易的Hash data是如何计算的?

发送给合约的数据有效负责是32字节的十六进制序列化编码:

  1. 函数选择器:函数原型的Keccak245哈希的前4个字节,通过这个evm可以获取到将要调用的函数
  2. 函数参数:根据evm定义的各种基本类型的规则进行编码

也就是HEX DATA= 前几位为函数hash值 + n个0 + 参数的16进制值

# 合约开发

# 介绍

  1. 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;合约账户上剩余的以太币会发送给指定的目标,然后琪存储和代码从状态中被移除

# 前端调用

  1. 引入ethers.js,它封装了读写合约的逻辑
  2. 获取metamask注入的web3
  3. 链接钱包
  4. 写入合约(需要合约地址、合约abi、合约签名)

# 调试

通过交易hash去对应的scan网址查询交易详情,可查看报错信息。

# solc

solc 是以太坊官方出的一款Solidity语言开发的智能合约的编译工具,可以通过 solc 获得部署合约时所要使用的 bytecodeabi

# web3.js

web3.js是以太坊官方实现的一个使用 javascript与以太坊客户端进行交互的js库, web3.js可以与任何暴露RPC层的以太坊节点协同工作。

其实 web3就是对以太坊的 json-rpc接口使用 javascript进行了一次封装,既然是 rpc,我们在使用时,必须连接一个服务器,也就是以太坊的支持 json-rpc的节点。在这里,我们一般把连接 json-rpc节点称为注入以太坊节点。注入节点有两种方式:

  1. 注入自有节点或者开放节点web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
  2. 使用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()

# 学习路径

  1. 用“费曼学习法”理解区块链,你也可以做到 (opens new window)
  2. 区块链基础知识25讲 - 入门
  3. 以太坊官方开发者学习文档: https://ethereum.org/zh/developers/docs/
  4. 精通以太坊 (opens new window) - 业务
  5. 微软官网学习区块链:https://docs.microsoft.com/zh-cn/learn/paths/ethereum-blockchain-development/
  6. 廖雪峰区块链教学:https://www.liaoxuefeng.com/wiki/1207298049439968

参考:https://zhuanlan.zhihu.com/p/112038593

# 问题

# 如何估算gas?

eth.estimateGas

# bitcoin和Ethereum区别?

  1. 设计定位
    1. bitcoin: 现金系统
    2. Ethereun:去中心化应用平台
  2. 数据组成
    1. bitcoin:交易账本
    2. Ethereum:交易和账户状态
  3. 交易对象
    1. UTXO
    2. Accounts
  4. 代码控制
    1. 脚本
    2. 智能合约

# 为什么比特币能当钱用?

  1. 不会被偷。没有私钥无法获取其他人的钱
  2. 无法伪造。因为区块链技术可信性高
  3. 无法大批生成,因为每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