Cover photo

Web3技术架构与快速入门

什么是Web3?

web1,web2,web3
web1,web2,web3
  • web1:“静态HTML页面的集合”,大多数内容仅由网站所有者发布,而不是互联网用户。

  • web2:基于“作为平台的网络” 的理念,具有高度交互性,用户可以创建和共享内容。

  • web3:重建了互联网基础设施,“基于区块链的去中心化在线生态系统”。

我们为什么需要Web3,当前的Web技术和生态到底有什么问题?

以太坊的创建者 Vitalik Buterin :

容错性: 去中心化系统不太可能因为某一个局部的意外故障而停止工作,因为它依赖于许多独立工作的组件,它的容错能力更强。

抗攻击性: 去中心化系统的攻击、破坏或操纵成本更高,因为它们缺乏敏感的中心点,可以以比周围系统的经济规模低得多的成本进行攻击。

抗勾结性: 去中心化系统的参与者们,很难相互勾结。而传统企业和政府的领导层,往往会为了自身的利益,以损害客户、员工和公众利益的方式,相互勾结。

Web3 现在有一些局限:

  • 可扩展性——交易在 web3 上进度较慢,因为它们是去中心化的。 更改状态像付款一样,需要由矿工处理并在整个网络中传播。

  • UX — 与 web3 应用程序互动可能需要额外的步骤、软件和学习。 这可能是应用的一个障碍。

  • 可访问性 - 大多数用户因为缺少现代化的网页浏览器而无法访问 web3。

  • 成本 — 因为太过昂贵,大多数成功的去中心化应用程序仅将其代码的很小一部分放入区块链。

Web2和Web3的技术栈和架构的区别

Web2架构

web2 architecture
web2 architecture
  • 前端

    React,Vue,Angular,Bootstrap,ElementUI,WeUI...

  • 后端

    Java,NodeJs,Go,Python,Nginx,Apache...

  • 数据库

    NoSQL/SQL

Web3架构

可以说Web2和Web3的核心区别是去中心化。

DApps: Decentralized Applications,去中心化应用程序。

DApp是一个基于开放的,去中心化的,点对点基础架构服务的Web应用程序。 dApp至少由以下部分组成:

  • 区块链上的智能合约

  • 一个Web前端用户界面

另外,许多DApp还包括其他去中心化组件,例如:

  • 去中心化(P2P)存储协议和平台。

  • 去中心化(P2P)消息传递协议和平台。

DApp的后端架构

逻辑通过智能合约实现,逻辑对数据的状态修改会在一段时间同步到区块链网络中。

web3 backend
web3 backend

DApp的前端架构

  • 前端架构不同与常见的CS模式,专注与智能合约的通信。

  • 区块链网络中每个完整节点都持有所有数据的状态,这些节点可以是自己部署的节点,或者Infura,Alchemy这些第三方节点提供商(Node Provider)。

  • 开发人员使用HTTP或WebSocket通过JSON RPC发送请求来与 Node Provider 交互,获取区块链上的状态信息。

  • 所有的写请求交易都需要signer使用私钥签名。

  • 每笔上链的交易都要支付gas fee,使用虚拟资产支付。

  • 钱包程序如Metamask,既是signer,处理私钥的签名服务,又是provider,通过JSON RPC连接节点进行交互。

web3 frontend
web3 frontend

https://itnext.io/top-3-web-3-0-architecture-layers-explained-frontend-backend-and-data-e10200f7fc76

Web3技术栈

web3 stack
web3 stack
  • Protocol Layer 协议层-底层区块链架构

    • blockchain

    • bridge

  • Infrastructure / Category Primitives 基础设施/类别原语

    • Secure

    • Store

    • Identify

    • Transact-Borrow/Lend

  • Use Case Layer 用例层

    • NFT

    • Gaming

  • Access Layer 接入层

    • Wallet

Web3相关组件

Components
Components

区块链

区块链是什么?

区块链是一种共享的、不可变的分布式账本,有助于在业务网络中记录交易和跟踪资产的过程。资产可以是有形的 (房屋、汽车、现金、土地)或无形的(知识产权、专利、版权、品牌)。几乎任何有价值的东西都可以在区块链网络上进行跟踪和交易,从而降低所有相关人员的风险和成本。

区块链通常包括以下组件:

  • 一个连接参与各方的点对点网络,用于传播交易和包含已验证交易的区块数据,基于标准的“gossip“协议。

  • 以交易形式体现的消息,代表状态的转换。

  • 一组共识规则,用于管理构成交易的内容以及实现有效的状态转换。

  • 根据共识规则处理交易的状态机。

  • 一组串联在一起的、由加密算法保证其安全的区块,这些区块是所有已验证和已接受的状态转换的记录日志。

  • 一个共识算法,用于在区块链上实现控制的去中心化,这类算法通过强制参与者之间的合作以实现共识规则的有效执行。

  • 在博弈论上合理的激励方案(例如,工作量证明、区块奖励),以在开放环境中经济地保护状态机。

  • 上述内容的一个或多个开源软件实现(客户端)。

区块链的特性:

  • 分布式账本技术

  • 不可变的数据

  • 智能合约

区块链的优点:

  • 可信

  • 安全

  • 效率

区块链的工作机制

  • Bitcoin is just a computer program, 维护一个巨大的交易清单,按照特定结构和格式存储在本地机器。

post image
  • 交易会通过bitcoin网络在运行程序的节点间传播。

post image
  • 交易会放进节点的memory pool中,将交易打包成区块(block)

post image
  • 对block进行hash计算,一旦低于链上公认的目标值就获得奖励,这个过程叫做挖矿(mining)

post image
  • 挖矿行为也会在bitcoin网络间传播,新的block会连接到上一个区块,网络上的每个节点将始终采用他们收到的最长的区块链作为“官方”版本。

post image

https://learnmeabitcoin.com/

区块链的节点类型

  • Full node

维持包含全部交易信息的完整区块链的节点

  • Light node

仍需下载全部数据进行解析,获取与自身相关的交易数据,但无须本地保存全部数据。

交易验证。 账户余额验证、双重支付判断等。

  • SPV

不需要下载新区块的全部数据,只需要保存区块头部信息。

简单支付验证。

1)找到要验证的这笔交易在哪个区块;

2)确定这个交易是否被6次确认过了。

Gas and Fees

每笔transaction都会消耗gas,并支付fees(优先费用)。 Gas有两个用途: - 防止垃圾信息 - 激励矿工将交易打包到区块链数据库中 Fees = $GAS_COST_PER_OPERATION * $GAS_PRICE

区块链的数据模型

post image

Block Header

post image
  • Prev Hash

    • 指向前一个区块的hash

  • Merkle Root

    • 块中所有交易的唯一指纹。

    • 简单支付验证。

post image
post image
  • Nonce

    • 保证交易时序

    • 防止双花

Block Body

  • Transactions

    • 交易是区块链上的价值转移。

bitcoin和ethererum如何处理交易,有何不同?

bitcoin-UTXO

UTXO(Unspent Transaction Outputs)是未花费的交易输出。bitcoin没有账户的概念,每笔交易都会包括至少一条输入和一条输出,输出就是找零,每一笔UTXO都能往上追溯到源头,矿工获得的区块奖励。

将每个钱包地址下的UTXO归集起来,就得到了这个地址的所有可用输出,即我们常说的比特币数额。

UTXO
UTXO
post image

ethereum-Account Based

与比特币系统不同,以太坊是为了实现智能合约而提炼的账户模型。

账户分为两类:外部账户(EOAs)和合约账户(contract account),在程序逻辑上两类账户的数据结构一致:

post image

以太坊帐户有四个字段:

  • nonce – 显示从帐户发送的交易数量的计数器。 这将确保交易只处理一次。 在合约帐户中,这个数字代表该帐户创建的合约数量

  • balance – 这个地址拥有的 Wei 数量。 Wei 是以太币的计数单位,每个 ETH 有 1e+18 Wei。

  • codeHash - 该哈希表示以太坊虚拟机 (EVM) 上的帐户代码。 合约帐户具有编程的代码片段,可以执行不同的操作。 如果帐户收到消息调用,则执行此 EVM 代码。 与其他帐户字段不同,不能更改。 所有代码片段都被保存在状态数据库的相应哈希下,供后续检索。 此哈希值称为 codeHash。 对于外部帐户,codeHash 字段是空字符串的哈希。

  • storageRoot – 有时被称为存储哈希。 Merkle Patricia trie 根节点的 256 位哈希已编码了帐户的存储内容(256 位整数值映射),并编码为 Trie,作为来自 256 的 Keccak 256 位哈希的映射位整数键,用于 RLP 编码的 256 位整数值。 此 Trie 对此帐户存储内容的哈希进行编码,默认情况下为空。

以太坊区块结构和数据关系

post image

Node Provider

infura

https://infura.io/

infura API 套件通过 HTTPS 和 WebSockets 提供对以太坊和 IPFS 网络的即时访问。

  • 让dApp快速接入以太坊的平台,不需要本地运行以太坊节点。

  • HTTP / WebSocket

post image
curl https://mainnet.infura.io/v3/PROJECTID -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

Metamask

https://metamask.io/

区块链应用程序的加密钱包和网关。

  • 账户管理 MetaMask 允许用户以多种方式管理帐户及其密钥,包括硬件钱包,同时将它们与站点上下文隔离。 在登录和访问dApp时MetaMask会收到签名请求消息,对数据进行签名,以证明钱包所有者有权访问,而不是对他人的钱包/资金进行欺诈行为。

  • api provider MetaMask 将一个全局 API window.ethereum 注入到其用户访问的网站中。允许网站请求用户的以太坊账户,从用户连接的区块链读取数据,并建议用户签署消息和交易。

// 测试Metamask是否已安装
ethereum.isConnected();

// 请求一个用于访问的以太坊地址
ethereum.request({ method: 'eth_requestAccounts' });

// 查询某个区块的交易数量
ethereum.request({ method: 'eth_getTransactionCount', params: [
   '0xe7e3ae6b0e33e6288f389073685f77f12a83473b','0xe0451c'
] });

// 发送交易
ethereum.request({ method: 'eth_sendTransaction', params: [{
  "from": FROM_ADDRESS,
  "to": TO_ADDRESS,
  "gas": "0x76c0", // 30400
  "gasPrice": "0x9184e72a000", // 10000000000000
  "value": "0x9184e72a", // 2441406250
  "data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}]});

Web3 JS library

ether.js

ethers.js库旨在为以太坊区块链及其生态系统提供一个小而完整的 JavaScript API 库。

  • 将私钥保存在客户端,安全 可信赖

  • 可支持导入和导出的 JSON钱包文件 (Geth,Parity和crowdsale)

  • 导入和导出 BIP39 助记词 (12个词备份短语)和 HD(分层确定性)钱包(英语,意大利语,日语,韩语,简体中文,繁体中文; 更多即将推出)

  • 从任何合同ABI创建JavaScript 元类对象,包括 ABIv2 和 可读的 ABI

  • 支持通过 JSON-RPC , INFURA , Etherscan 或 MetaMask 连接到以太坊节点。

  • ENS名称 是“一等公民”;它们可以在任何可以使用以太坊地址的地方使用

  • 库 非常小 (压缩~88kb;未压缩284kb)

  • 功能 完整 ,满足所有的以太坊相关需求

  • 文档全面: 中文文档 及 documentation

  • 大量维护和添加的 测试用例

  • 完全支持 TypeScript 准备好,有定义文件和完整的TypeScript源文件

  • 宽松的 MIT 协议许可 (包括所有依赖); 完全开源可以随意使用

// signer
import { ethers } from 'ethers';
const provider = new ehters.providers.Web3Provider(window.ethereum);

// 请求访问钱包
await provider.send("eth_requestAccounts", [])

const signer = provider.getSigner();

// call
const contractAddress = '0x...'
const abi = [
  "function greet() view returns (string)",
  "function setGreeting(string) returns (bool)"
]

// 初始化合约对象
const contract = new ethers.Contract(contractAddress, abi, provider);

// 只读方法调用
await contract.greet()

// 连接signer
const contractWithSigner = contract.connect(signer);

// 可写方法调用
tx = await contract.setGreeting('Hello, world!')
console.log(tx)

区块链浏览器

etherscan

https://etherscan.io/ 以太坊领先的区块链浏览器、搜索、API 和分析平台。 一个以太坊区块探索和分析的分布式智能合同平台, 由于区块链中的交易信息等数据都是公开透明的 , 而 Etherscan 作为探索以太坊的窗口, 用户可以使用其查看自己的交易详情以及以太坊中的任何信息。

  • 查看以太坊区块链上的数据

  • 智能合约交互

  • 可疑地址、钓鱼地址、诈骗地址标签化

去中心化存储 Decentralized Storage

ipfs

https://ipfs.io/ InterPlanetary File System星际文件系统,一种点对点超媒体协议。 IPFS的野心是取代现在的HTTP,去创建一个全新的去中心化网络。

  • 当今的网络效率低下且价格昂贵(中心化->p2p)

  • Web文件经常被删除(404)

  • 中心化网络限制了机会(封锁,监管)

  • 今天的网络高度依赖于骨干网(中心化)

开发环境

hardhat

Hardhat是一个编译、部署、测试和调试以太坊应用的开发环境。 https://hardhat.org/

remix Remix IDE是开发以太坊智能合约的在线IDE工具,部署简单的智能合约非常方便。 https://remix-project.org/

web3各组件的协同工作

NFT NFT (Non-Fungible Tokens)非同质化代币。基于ERC-721标准。 ERC-721与ERC-20区别:

  • 是否可以互换

  • 是否可以分割

NFT 可以被分解成几个核心部分: 链上信息: NFT:一个 ID 的链上表示(例如 CryptoPunk #2517 或 BAYC #1597),该 ID 有元数据与之相关。

TokenURI :指向 NFT 内容存储位置的唯一资源标识符(URI)。这可以指向网站(URL)、服务器、IPFS 或其他去中心化的数据协议。

链下信息: NFT 元数据:服务器或 IPFS 上由 URI 引用的关于 NFT 的信息,如描述、名称、属性、图像和其他数据。

丰富的数据:与 NFT 相关的数据(如实际的图像文件),这些数据未在区块链上生成,而是存储在其他地方,如 AWS 服务器或分布式文件存储协议上。

NFT交易案例

post image
  • 加载dApp

    • 图片从IPFS获取

    • NFT信息同样来自IPFS

post image
post image
post image
  • 购买小卡片

post image
  • dApp调用JS library(ethers.js)构造交易请求

    • dApp请求签名审批

post image
  • 使用Metamask钱包里的私钥进行签名

    • Metamask使用infura的api和节点广播交易

    • 交易经过节点在网络间传播

    • 交易进入节点内存池,等待mining

  • 查询购买结果

智能合约

// 简单的智能合约
pragma solidity ^0.4.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}

智能合约是不可改变的计算机程序,以确定性方式运行在以太坊的虚拟机上。

  • 计算机程序 智能合约=计算机程序=一组代码(函数)+数据(状态)

  • 不可改变 一旦部署之后,智能合约的代码就不能被更改。不像是传统的软件,更改智能合约的唯一办法就是部署一个新的实例。

  • 确定性 对于触发同个智能合约的交易上下文,或者执行时的区块链状态,智能合约的执行结果是一致的。

  • EVM EVM是一个执行字节码的基于堆栈的虚拟机。在以太坊中,执行模型指定了如何在给定一系列字节码指令和一小部分环境数据的情况下更改系统状态。 智能合约运行在一个非常有限的执行环境中。它们可以访问自己的状态,调用合约交易的上下文信息,以及有关最近区块的信息。

    EVM作为每一个以太坊节点的本地实例运行,但是因为所有EVM都是运行在相同的初始状态,并且会输出完全相同的最终状态。

solidity

Solidity 一种过程(命令式)编程语言,其语法类似于 JavaScript、C++ 或 Java,以太坊智能合约最流行和最常用的语言,可在EVM上使用其有限的函数集。

智能合约的运行流程

  1. 调用智能合约函数。

  • 举个栗子 Rinkeby: 0xF20D9bdd4A561319BaDf78cf77704E0A920c3cD9 Rinkeby: 0x880C80C6739C05F9ddB8Bc2597B65d1EC9B92C10

    使用etherscan查看,区别则是一个合约获得源码和ABI,另一个则没有,可以经由字节码反编译。

  • ABI ABI 全称是 Application Binary Interface,接口包含程序提供外界存取所需的 functions、variables 等, json格式,可以通过源码编译获得。 ABI并不保存在链上,智能合约在链上的数据只有字节码以及相关的状态数据,dApp需要从合约创建者处获得ABI。

如何基于 Hardhat 进行以太坊合约和dApp开发

以下例子来自hardhat官网

环境设置

nvm install 12.0.0
nvm use 12.0.0
mkdir hardhat-tutorial
cd hardhat-tutorial
npm init --yes
npm install --save-dev hardhat
npm install --save-dev @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai
npx hardhat

编写合约

// Solidity files have to start with this pragma.
// It will be used by the Solidity compiler to validate its version.
pragma solidity ^0.7.0;


// This is the main building block for smart contracts.
contract Token {
    // Some string type variables to identify the token.
    // The `public` modifier makes a variable readable from outside the contract.
    string public name = "My Hardhat Token";
    string public symbol = "MHT";

    // The fixed amount of tokens stored in an unsigned integer type variable.
    uint256 public totalSupply = 1000000;

    // An address type variable is used to store ethereum accounts.
    address public owner;

    // A mapping is a key/value map. Here we store each account balance.
    mapping(address => uint256) balances;

    /**
     * Contract initialization.
     *
     * The `constructor` is executed only once when the contract is created.
     */
    constructor() {
        // The totalSupply is assigned to transaction sender, which is the account
        // that is deploying the contract.
        balances[msg.sender] = totalSupply;
        owner = msg.sender;
    }

    /**
     * A function to transfer tokens.
     *
     * The `external` modifier makes a function *only* callable from outside
     * the contract.
     */
    function transfer(address to, uint256 amount) external {
        // Check if the transaction sender has enough tokens.
        // If `require`'s first argument evaluates to `false` then the
        // transaction will revert.
        require(balances[msg.sender] >= amount, "Not enough tokens");

        // Transfer the amount.
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }

    /**
     * Read only function to retrieve the token balance of a given account.
     *
     * The `view` modifier indicates that it doesn't modify the contract's
     * state, which allows us to call it without executing a transaction.
     */
    function balanceOf(address account) external view returns (uint256) {
        return balances[account];
    }
}

编译

npx hardhat compile

部署上链

这里需要配置一下对应网络

module.exports = {
  solidity: "0.8.4",
    networks: {
        localhost: {
            url: "http://127.0.0.1:8545"
        },
        hardhat: {
            chainId: 31337,
            gasPrice: "auto"
        },
        rinkeby: {
            url: "https://rinkeby.infura.io/v3/PROJECTID",  // 去infura注册
            accounts: [ADDRESS], // 私钥
        }
    }
};
npx hardhat run scripts/sample-script.js --network rinkeby

Hardhat Hackathon Boilerplate Project

https://github.com/nomiclabs/hardhat-hackathon-boilerplate

cd hardhat-hackathon-boilerplate
npm install
npx hardhat node

新开一个终端

npx hardhat --network localhost run scripts/deploy.js
cd frontend
npm install
npm run start

访问页面: http://localhost:3000/

facucet命令用于发送token测试,向指定账户发送100 MBT 和 1 ETH。

npx hardhat --network localhost faucet <your address>

参考资料

https://ethereum.github.io/yellowpaper/paper.pdf

https://blog.coinbase.com/a-simple-guide-to-the-web3-stack-785240e557f0

https://www.linkedin.com/pulse/ethereum-world-state-asif-bhat

https://medium.com/cybermiles/diving-into-ethereums-world-state-c893102030ed

https://learnblockchain.cn/books/geth/

https://eth.wiki/json-rpc/API

https://learnmeabitcoin.com/

https://docs.metamask.io/guide/

https://web3.coach/web3-architecture-cheatsheet#mempool

https://hardhat.org/tutorial/

https://learnblockchain.cn/docs/ethers.js/getting-started.html

https://ethereum.org/zh/developers/docs

https://www.preethikasireddy.com/post/the-architecture-of-a-web-3-0-application