本地搭建一个简易版本的 Web3 服务
一、环境搭建与工具准备
(一)安装 Node.js 和 npm
Node.js 是一个基于 JavaScript 的运行时环境,npm 是其默认的包管理器。在 Web3 开发中,Node.js 和 npm 是必不可少的工具。
-
访问 Node.js 官网 并下载最新的 LTS 版本。
-
安装后,通过命令行检查安装是否成功
node -v npm -v
(二)安装 Truffle 和 Ganache
Truffle 是一个开发、测试和部署智能合约的框架,Ganache 是一个本地的以太坊区块链模拟器,用于在本地测试智能合约。
-
安装 Truffle
npm install -g truffle
-
安装 Ganache:
-
可以通过 Ganache 官网 下载桌面版,或通过命令行安装 CLI 版
npm install -g ganache-cli
-
(三)安装 MetaMask
MetaMask 是一个以太坊钱包,允许你在浏览器中与 dApp 交互。它充当你的数字钱包,并能连接到本地或远程的以太坊网络。
-
访问 MetaMask 官网,下载并安装适用于你浏览器的扩展插件。
-
安装后,创建一个新钱包并保存助记词。
二、创建和部署一个简单的智能合约
(一)初始化 Truffle 项目
-
创建项目文件夹
mkdir my-first-web3-project cd my-first-web3-project
-
初始化 Truffle 项目
truffle init
(二)编写智能合约
我们将编写一个简单的智能合约,用于存储和检索一条消息。
-
创建智能合约文件:
-
在
contracts
文件夹下创建一个名为SimpleStorage.sol
的文件,内容如下// SPDX-License-Identifier: MIT pragma solidity ^0.8.0;contract SimpleStorage {string private message;// 设置消息function setMessage(string memory newMessage) public {message = newMessage;}// 获取消息function getMessage() public view returns (string memory) {return message;} }
-
-
编译智能合约:
-
在项目根目录下运行以下命令来编译合约
truffle compile
-
(三)部署智能合约
将智能合约部署到本地的以太坊区块链(Ganache)上。
-
配置网络:
-
在
truffle-config.js
文件中,配置本地的 Ganache 网络module.exports = {networks: {development: {host: "127.0.0.1", // 本地主机地址port: 7545, // Ganache 默认端口network_id: "*", // 匹配任何网络 ID},},// 其他配置... };
-
-
编写部署脚本:
-
在
migrations
文件夹中创建一个名为2_deploy_contracts.js
的文件,内容如下const SimpleStorage = artifacts.require("SimpleStorage");module.exports = function(deployer) {deployer.deploy(SimpleStorage); };
-
-
启动 Ganache:
-
打开 Ganache 应用或通过命令行启动
ganache-cli
-
-
部署智能合约:
-
在项目根目录下运行以下命令,将合约部署到 Ganache
truffle migrate
-
成功部署后,Truffle 会显示智能合约的部署地址。
-
三、开发一个 dApp 并与智能合约交互
(一)设置前端开发环境
在项目中创建一个 client
文件夹,用于存放前端代码。
-
初始化前端项目
mkdir client cd client npm init -y
-
安装依赖:
-
安装 Web3.js 库和其他前端依赖
npm install web3 npm install lite-server --save-dev
-
-
创建前端文件:
-
在
client
文件夹中创建一个index.html
文件,内容如下<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>My First Web3 dApp</title> </head> <body><h1>Simple Storage dApp</h1><input type="text" id="messageInput" placeholder="Enter a message" /><button id="setMessageButton">Set Message</button><p>Stored Message: <span id="storedMessage"></span></p><script src="https://cdn.jsdelivr.net/npm/web3/dist/web3.min.js"></script><script src="app.js"></script> </body> </html>
-
创建
app.js
文件,用于与智能合约进行交互,内容如下const web3 = new Web3(Web3.givenProvider || "http://localhost:7545");const contractAddress = "YOUR_CONTRACT_ADDRESS"; // 部署的合约地址 const contractABI = [// ABI goes here... ];const contract = new web3.eth.Contract(contractABI, contractAddress);document.getElementById('setMessageButton').onclick = async () => {const message = document.getElementById('messageInput').value;const accounts = await web3.eth.getAccounts();await contract.methods.setMessage(message).send({ from: accounts[0] });alert('Message stored successfully'); };const loadMessage = async () => {const message = await contract.methods.getMessage().call();document.getElementById('storedMessage').innerText = message; };loadMessage();
-
将部署时生成的智能合约 ABI 复制到
app.js
中的contractABI
变量中,并替换contractAddress
为实际的合约地址。
-
(二)运行 dApp
-
启动开发服务器:
-
在
client
文件夹中,运行以下命令启动轻量级开发服务器npx lite-server
-
-
在浏览器中访问 dApp:
-
浏览器将自动打开并显示你的 dApp。你可以在输入框中输入消息并点击“Set Message”按钮,将消息存储到智能合约中。
-
-
与智能合约交互:
-
输入消息并提交后,dApp 会与智能合约进行交互,将消息存储到区块链中。页面将自动显示存储的消息。
-
四、C++ 智能合约开发
(一)环境依赖
-
操作系统:目前仅支持 Linux 和 MAC 系统。
-
软件依赖:
-
GCC:7.3+,C 编译器。
-
Mac:
brew install gcc
-
Linux:
-
Ubuntu/Debian:
apt-get install gcc
-
CentOS/Redhat:
yum install gcc
-
-
-
(二)编写 C++ 智能合约
1. 搭建开发环境
-
开发者可根据 ChainMaker 提供的 SDK 开发 C++ 合约,C++ 合约的 SDK 工程下载地址为:chainmaker-contract-sdk-cpp。
-
SDK 下载完成后,开发者可根据自身习惯选择熟悉的 C++ 编辑器或 IDE。推荐使用 CLion,CLion 下载和安装请参见官网:JetBrains CLion。
-
安装完成后,使用 CLion 打开 SDK 工程,通过编辑
main.cc
文件即可编辑自己的 C++ 合约。
2. 代码编写规则
-
外部方法声明:只有声明为外部方法的函数,才可以(被用户或其他合约)从外部调用,否则,只能用于合约内部调用。外部方法声明规则如下:
-
WASM_EXPORT
:必须,暴露声明。 -
void
:必须,无返回值。 -
method_name()
:必须,暴露方法名称// 示例 WASM_EXPORT void init_contract() { }
-
-
强制声明外部方法:强制声明外部方法为合约必须提供且必须对外暴露的方法,有以下两个:
-
init_contract
:创建合约会自动执行该方法,无需指定方法名。 -
upgrade
:升级合约会自动执行该方法,无需指定方法名// 在创建本合约时,调用一次init方法。ChainMaker不允许用户直接调用该方法。 WASM_EXPORT void init_contract() {// 安装时的业务逻辑,可为空 }// 在升级本合约时,对于每一个升级的版本调用一次upgrade方法。ChainMaker不允许用户直接调用该方法。 WASM_EXPORT void upgrade() {// 升级时的业务逻辑,可为空 }
-
-
获取 SDK 接口上下文:C++ 合约通过 SDK 接口上下文与链进行交互,具体信息可参考文章末尾 C++ SDK API描述
// 获取 SDK 接口上下文 Context* ctx = context();
3. 合约示例源码展示
-
下文代码框内为一个 C++ 编写的存证合约示例,该合约示例实现以下功能:
-
存储文件哈希、文件名称和该交易的 ID。
-
通过文件哈希查询该条记录。
#include "chainmaker/chainmaker.h"using namespace chainmaker;class Counter : public Contract { public:void init_contract() {}void upgrade() {}// 保存void save() {// 获取 SDK 接口上下文Context* ctx = context();// 定义变量std::string time;std::string file_hash;std::string file_name;std::string tx_id;// 获取参数ctx->arg("time", time);ctx->arg("file_hash", file_hash);ctx->arg("file_name", file_name);ctx->arg("tx_id", tx_id);// 发送合约事件// 向 topic:"topic_vx" 发送 2 个 event 数据,file_hash,file_namectx->emit_event("topic_vx", 2, file_hash.c_str(), file_name.c_str());// 存储数据ctx->put_object("fact" + file_hash, tx_id + " " + time + " " + file_hash + " " + file_name);// 记录日志ctx->log("call save() result:" + tx_id + " " + time + " " + file_hash + " " + file_name);// 返回结果ctx->success(tx_id + " " + time + " " + file_hash + " " + file_name);}// 查询void find_by_file_hash() {// 获取 SDK 接口上下文Context* ctx = context();// 获取参数std::string file_hash;ctx->arg("file_hash", file_hash);// 查询数据std::string value;ctx->get_object("fact" + file_hash, &value);// 记录日志ctx->log("call find_by_file_hash()-" + file_hash + ",result:" + value);// 返回结果ctx->success(value);} };// 在创建本合约时,调用一次 init 方法。ChainMaker 不允许用户直接调用该方法。 WASM_EXPORT void init_contract() {Counter counter;counter.init_contract(); }// 在升级本合约时,对于每一个升级的版本调用一次 upgrade 方法。ChainMaker 不允许用户直接调用该方法。 WASM_EXPORT void upgrade() {Counter counter;counter.upgrade(); }WASM_EXPORT void save() {Counter counter;counter.save(); }WASM_EXPORT void find_by_file_hash() {Counter counter;counter.find_by_file_hash(); }
-
4. 编译合约
-
搭建编译环境:开发者可使用 ChainMaker 已经打包好的 Docker 镜像编译 C++ 合约代码,ChainMaker 官方已经将容器发布至 docker hub。
-
拉取镜像:
docker pull chainmakerofficial/chainmaker-cpp-contract:2.1.0
-
启动镜像:启动镜像前,需要指定本地开发目录,用于映射为 docker 镜像的 home 目录。用于映射的本地开发目录一般为 SDK 工程目录,例如
/data/workspace/chainmaker-contract-sdk-cpp
,这样编辑开发的 C++ 合约就可以在 docker 容器内的 home 目录直接编译了# 启动并进入容器,$WORK_DIR 即本地工作目录 docker run -it --name chainmaker-cpp-contract -v $WORK_DIR:/home chainmakerofficial/chainmaker-cpp-contract:2.1.0 bash # 或者先后台启动 docker run -d --name chainmaker-cpp-contract -v $WORK_DIR:/home chainmakerofficial/chainmaker-cpp-contract:2.1.0 bash -c "while true; do echo hello world; sleep 5;done" # 再进入容器 docker exec -it chainmaker-cpp-contract bash
-
-
编译示例合约:进入编译容器后,切换到 home 目录,这个 home 目录对应启动编译容器时映射的本地开发目录,进入后执行以下命令
cd /home/ make clean emmake make
-
编译完成后,将生成合约的字节码文件
main.wasm
。
-
5. SDK 工程框架描述
-
chainmaker-contract-sdk-cpp
工程的结构和文件描述如下:-
chainmaker
-
basic_iterator.cc
:迭代器实现。 -
basic_iterator.h
:迭代器头文件声明。 -
chainmaker.h
:SDK 主要接口头文件声明,详情见 C++ SDK API描述。 -
context_impl.cc
:与链交互接口实现。 -
context_impl.h
:与链交互头文件声明。 -
contract.cc
:合约基础工具类。 -
error.h
:异常处理类。 -
exports.js
:编译合约导出函数。 -
safemath.h
:assert
异常处理。 -
syscall.cc
:与链交互入口。 -
syscall.h
:与链交互头文件声明。
-
-
pb
-
contract.pb.cc
:与链交互数据协议。 -
contract.pb.h
:与链交互数据协议头文件声明。
-
-
main.cc
:用户写合约入口。 -
Makefile
:常用 build 命令。
-
6. 编译说明
-
在 ChainMaker 提供的 Docker 容器中,已经预装了 Emscripten SDK 和相关的编译工具链,用户无需手动安装。
-
编译过程中,
make clean
命令用于清理之前的编译产物,emmake make
命令用于启动编译流程。 -
如果在编译过程中遇到错误,可以根据错误信息检查合约代码是否存在语法错误、依赖库是否正确链接等问题。
-
编译生成的
.wasm
文件是智能合约的可执行文件,需要通过 ChainMaker 的合约管理工具进行部署和发布,才能在区块链网络中运行。
博主争取到官网产品9折优惠券,有需要的可以通过以下链接领取。创作不易,感谢支持。
-
云小站_专享特惠_云产品推荐-阿里云上云优惠聚集地,新人专享优惠价格,可叠加专享代金券购买价格更低。
https://www.aliyun.com/minisite/goods?userCode=fvjl5o6e