以太坊与 Node.js 的完美邂逅,通过 RPC 实现区块链交互

在区块链技术的浪潮中,以太坊(Ethereum)无疑占据了举足轻重的地位,它不仅是一个加密货币平台,更是一个去中心化的全球计算机,允许开发者构建和部署智能合约与去中心化应用(DApps),而 Node.js,凭借其异步、事件驱动的特性以及庞大的 npm 生态系统,成为了区块链开发中,尤其是与以太坊交互的热门选择,本文将深入探讨如何利用 Node.js 通过 RPC(Remote Procedure Call,远程过程调用)与以太坊节点进行通信,解锁区块链应用开发的大门。

理解核心概念

在深入代码之前,我们有必要清晰理解几个核心概念:

  1. 以太坊节点 (Ethereum Node):这是运行以太坊协议的软件实例,它维护着整个以太坊区块链的副本,能够验证交易、执行智能合约并与网络中的其他节点同步数据,常见的以太坊节点客户端有 Geth、OpenEthereum(原 Parity)等。
  2. RPC (Remote Procedure Call):这是一种网络协议,允许一台程序(客户端)请求另一台程序(服务器)上的服务,而无需了解底层网络的细节,在以太坊的语境下,节点通过 RPC 接口暴露其功能,使得外部应用(如我们的 Node.js 脚本)可以调用这些功能,例如查询账户余额、发送交易、调用智能合约方法等。
  3. Node.js:一个基于 Chrome V8 引擎的 JavaScript 运行时,它使得开发者可以使用 JavaScript 来编写服务器端应用程序,其非阻塞 I/O 模型非常适合处理网络请求,如与以太坊节点的 RPC 通信。

搭建开发环境

在开始之前,确保你的开发环境已准备就绪:

  1. 安装 Node.js 和 npm:访问 Node.js 官网 下载并安装适合你操作系统的 LTS 版本。

  2. 随机配图
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 库,极大地简化了与以太坊节点的交互。

    1. 初始化项目并安装 web3.js

      mkdir eth-rpc-demo
      cd eth-rpc-demo
      npm init -y
      npm install web3
    2. 编写 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 的强大之处在于它能执行几乎所有的以太坊操作。

    1. 发送交易: 发送交易需要解锁账户、指定接收方、金额和 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);
          });
    2. 调用智能合约: 与智能合约交互需要合约的 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);

    本文由用户投稿上传,若侵权请提供版权资料并联系删除!

    上一篇:

    下一篇: