以太坊与 Node.js 的完美邂逅,通过 RPC 实现区块链交互
admin 发布于 2026-04-05 13:24
频道:默认分类
阅读:15
在区块链技术的浪潮中,以太坊(Ethereum)无疑占据了举足轻重的地位,它不仅是一个加密货币平台,更是一个去中心化的全球计算机,允许开发者构建和部署智能合约与去中心化应用(DApps),而 Node.js,凭借其异步、事件驱动的特性以及庞大的 npm 生态系统,成为了区块链开发中,尤其是与以太坊交互的热门选择,本文将深入探讨如何利用 Node.js 通过 RPC(Remote Procedure Call,远程过程调用)与以太坊节点进行通信,解锁区块链应用开发的大门。
理解核心概念
在深入代码之前,我们有必要清晰理解几个核心概念:
- 以太坊节点 (Ethereum Node):这是运行以太坊协议的软件实例,它维护着整个以太坊区块链的副本,能够验证交易、执行智能合约并与网络中的其他节点同步数据,常见的以太坊节点客户端有 Geth、OpenEthereum(原 Parity)等。
- RPC (Remote Procedure Call):这是一种网络协议,允许一台程序(客户端)请求另一台程序(服务器)上的服务,而无需了解底层网络的细节,在以太坊的语境下,节点通过 RPC 接口暴露其功能,使得外部应用(如我们的 Node.js 脚本)可以调用这些功能,例如查询账户余额、发送交易、调用智能合约方法等。
- Node.js:一个基于 Chrome V8 引擎的 JavaScript 运行时,它使得开发者可以使用 JavaScript 来编写服务器端应用程序,其非阻塞 I/O 模型非常适合处理网络请求,如与以太坊节点的 RPC 通信。
搭建开发环境
在开始之前,确保你的开发环境已准备就绪:
-
安装 Node.js 和 npm:访问 Node.js 官网 下载并安装适合你操作系统的 LTS 版本。

i>
安装以太坊节点客户端:这里以 Geth 为例,你可以从 Geth 官方 GitHub 下载对应系统的二进制文件,或者通过包管理器安装(如 brew install geth on macOS)。
启动以太坊节点并启用 RPC 服务:
启动 Geth 节点时,你需要指定一些参数来启用 RPC 服务,并设置访问权限,最简单的方式是启动一个测试网(如 Ropsten 或 Goerli)节点,并允许本地连接:
# 以 Goerli 测试网为例,启动节点并启用 RPC
geth --goerli --http --http.addr "0.0.0.0" --http.port "8545" --http.api "eth,net,web3,personal"
--goerli: 连接到 Goerli 测试网。
--http: 启用 HTTP-RPC 服务器。
--http.addr "0.0.0.0": 允许任何 IP 地址访问(开发环境,生产环境请谨慎设置)。
--http.port "8545": RPC 服务监听的端口号,默认是 8545。
--http.api "eth,net,web3,personal": 指定通过 RPC 可用的 API 命令集合。
你的以太坊节点已经在监听 8545 端口的 HTTP-RPC 请求了。
使用 Node.js 连接以太坊节点
Node.js 通过 HTTP 或 WebSocket 与以太坊节点的 RPC 接口通信,最常用的库是 web3.js,它是以太坊官方提供的 JavaScript API 库,极大地简化了与以太坊节点的交互。
-
初始化项目并安装 web3.js:
mkdir eth-rpc-demo
cd eth-rpc-demo
npm init -y
npm install web3
-
编写 Node.js 脚本连接 RPC 并调用方法:
创建一个名为 app.js 的文件,并编写以下代码:
const Web3 = require('web3');
// 1. 连接到以太坊节点的 RPC 接口
// 如果节点在本地运行,默认地址为 'http://localhost:8545'
const web3 = new Web3('http://localhost:8545');
// 2. 检查连接是否成功
web3.eth.getBlockNumber()
.then(blockNumber => {
console.log('当前区块号:', blockNumber);
})
.catch(err => {
console.error('连接失败或获取区块号出错:', err);
});
// 3. 获取节点信息
web3.eth.getNodeInfo()
.then(nodeInfo => {
console.log('节点信息:', nodeInfo);
})
.catch(err => {
console.error('获取节点信息出错:', err);
});
// 4. 获取账户列表
web3.eth.getAccounts()
.then(accounts => {
console.log('可用账户:', accounts);
if (accounts.length > 0) {
// 示例:获取第一个账户的余额
const account = accounts[0];
web3.eth.getBalance(account)
.then(balance => {
// 余额是 Wei,转换为 Ether
console.log(`账户 ${account} 的余额: ${web3.utils.fromWei(balance, 'ether')} ETH`);
})
.catch(err => {
console.error('获取余额出错:', err);
});
}
})
.catch(err => {
console.error('获取账户列表出错:', err);
});
运行这个脚本:
node app.js
如果一切正常,你应该能看到当前区块号、节点信息、可用账户及其余额(测试网账户可能初始没有 ETH,你需要从测试网水龙头获取一些)。
进阶应用:发送交易与调用智能合约
RPC 的强大之处在于它能执行几乎所有的以太坊操作。
-
发送交易:
发送交易需要解锁账户、指定接收方、金额和 gas 等参数,这通常需要账户的密码(如果节点是用 --password 参数启动的,或者使用 personal.unlockAccount RPC 方法解锁)。
// 注意:实际使用时请妥善处理密码,不要硬编码在代码中
const password = 'your_account_password'; // 替换为你的账户密码
const fromAccount = '0xYourAccountAddress'; // 替换为你的发送账户地址
const toAccount = '0xRecipientAddress'; // 替换为接收账户地址
const amount = web3.utils.toWei('0.01', 'ether'); // 转换为 Wei
web3.eth.personal.unlockAccount(fromAccount, password)
.then(unlocked => {
if (unlocked) {
console.log('账户解锁成功');
web3.eth.sendTransaction({
from: fromAccount,
to: toAccount,
value: amount,
gas: 21000 // 转账交易的最小 gas
})
.then(receipt => {
console.log('交易成功,收据:', receipt);
})
.catch(err => {
console.error('交易失败:', err);
})
.finally(() => {
// 记得锁定账户
web3.eth.personal.lockAccount(fromAccount);
console.log('账户已锁定');
});
}
})
.catch(err => {
console.error('解锁账户失败:', err);
});
-
调用智能合约:
与智能合约交互需要合约的 ABI(Application Binary Interface,应用程序二进制接口)和合约地址。
// 假设我们有一个简单的存储合约
const contractABI = [
{
"constant": false,
"inputs": [{"name": "_x", "type": "uint256"}],
"name": "set",
"outputs": [],
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "get",
"outputs": [{"name": "retVal", "type": "uint256"}],
"type": "function"
}
];
const contractAddress = '0xYourContractAddress'; // 替换为你的合约地址
const contract = new web3.eth.Contract(contractABI, contractAddress);
// 调用 view/pure 函数(不需要发送交易)
contract.methods.get()
.call()
.then(result => {
console.log('合约存储的值:', result);
})
.catch(err => {
console.error('调用合约 get 方法出错:', err);
});
// 发送交易调用非 constant 函数
contract.methods.set(42)
.send({ from: fromAccount, gas: 100000 })
.then(receipt => {
console.log('调用合约 set 方法成功,收据:', receipt);