一文吃透 Ethereum 转账流程:从输入 eth.sendTransaction 到交易广播的每一步

·


目录

  1. 初识 Ethereum 转账
  2. 在 Geth JS Console 发起转账
  3. 全链路:从键盘输入到节点广播
  4. RPC 服务端是如何找到 sendTransaction
  5. sendTransaction() 最终长什么样
  6. FAQ:常见疑惑一次说清
  7. 延伸阅读与工具

1. 初识 Ethereum 转账

关键词:Ethereum 转账ETH 交易Geth Console

在正式拆流程之前,先把核心角色认一遍:

👉 先掌握这 60 秒 Ethereum NFT 转账小实验,实战后回头看代码链路会秒懂!


2. 在 Geth JS Console 发起转账

步骤拆解

关键词:GethJS ConsolesendTransaction 参数

  1. 用命令行启动节点并进入交互式控制台

    geth console --datadir /path/to/chain
  2. 解锁账户(本地私钥控制权)

    personal.unlockAccount(eth.accounts[0], "密码", 30)
  3. 直接输入

    eth.sendTransaction({
        from: eth.accounts[0],
        to:   "0xRecipientAddress",
        value: web3.toWei(0.5, "ether"),
        gas:   21000,
        gasPrice: web3.toWei(20, "gwei")
    })

控制台立即回显一串 32 字节哈希,这就是交易 ID。但真正的故事,刚刚开始。


3. 全链路:从键盘输入到节点广播

3.1 Console → JSRE → RPC

关键词:JSREJS 桥接oto VM

阶段动作关键标识
InputInteractive() goroutine 捕获键盘输入scheduler chan string
转译JSRE 调用 otto VM vm.Run(code)jsre.Evaluate()
代理bridge.Send() 打包 JSON-RPC 请求id、method、params
传输client.Call(...) → IPC / HTTP / WebSocket端口:~/.ethereum/geth.ipc

3.2 RPC Server 登场

关键词:parseRequest反射命名空间 dispatch

RPC 服务端在 rpc/server.go 执行三部曲:

  1. readRequest:解析 JSON → service_method(例:eth_sendTransaction)。
  2. handle:利用 Golang 反射拿到对应的结构体方法指针。
  3. invoke:真正执行 PublicTransactionPoolAPI.SendTransaction()

4. RPC 服务端是如何找到 sendTransaction

4.1 注册子服务

关键词:RegisterEthServiceService Interface

节点启动流程中的关键函数:

stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
    return eth.New(ctx, cfg) // 返回 &eth.Ethereum{}
})

注册完成后,每个模块返回的接口统一放入 map[reflect.Type]Service,其中:

字段作用
Protocols()P2P 子协议列表
APIs()RPC 方法命名空间列表

4.2 命名空间反射

关键词:namespacecallback lookupRegisterName

eth 命名空间为例:

反射自动把首字母大写的方法转成小写+驼峰形式,与 eth_sendTransaction 精准匹配。


5. sendTransaction() 最终长什么样

5.1 关键类与函数

关键词:TxPoolEIP-155签名RLP 编码

精简代码(路径:internal/ethapi/api.go):

func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash, error) {
    tx := types.NewTransaction(
        args.Nonce.UInt64(),
        *args.To,
        args.Value.ToInt(),
        args.Gas.UInt64(),
        args.GasPrice.ToInt(),
        args.Data,
    )
    signedTx, _ := s.signTransaction(tx, args.From)
    if err := s.b.SendTx(signedTx); err != nil {
        return common.Hash{}, err
    }
    return signedTx.Hash(), nil
}

5.2 防重放保护:EIP-155

👉 点此 2 分钟理解防重放机制,避免转错链!


6. FAQ:常见疑惑一次说清

Q1:为什么 unlockAccount 后还要再次输入密码?
A:解锁是把私钥写入内存解密锁片,有效时长可自定义(例:30 秒)。超时后仍需密码。

Q2:eth.sendTransaction 报 “insufficient funds” 但钱包确实有余额?
A:大概率是 gas * gasPrice 计算出来的手续费超出了可用余额,查看 eth.getBalance(eth.accounts[0]) 实际值即可验证。

Q3:交易挂起(pending)怎么办?
A:检查:

若长时间未打包,可用 eth.resend 或重签更高 gasPrice 新交易覆盖原 nonce。

Q4:本地节点和 Infura 差别大吗?
A:流程完全一样,区别在于 Infura 把“启动节点”这一步抽象成远程服务;你在 js console 里用的是 web3.setProvider(new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/PROJECT_ID"))

Q5:如何通过 Python 发送交易?
A:使用 web3.py

from web3 import Web3
w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/PROJECT_ID'))
signed_txn = w3.eth.account.sign_transaction(tx, private_key)
tx_hash = w3.eth.send_raw_transaction(signed_txn.rawTransaction)

7. 延伸阅读与工具

  1. Go-Ethereum 源码导航
    cmd/geth/main.gonode.New()RegisterEthServiceinternal/ethapi/api.go
  2. 在线可视化交易
    Etherscan:输入 txHash 可查看实时状态。
  3. 调试利器

    • geth attach http://localhost:8545
    • debug.traceTransaction(txHash):EVM 指令级回溯。

希望通过这趟“旅程”,你不仅知道 Ethereum 转账 该怎么点按钮,更能一眼看清它背后的 RPC、反射、签名校验、链上广播 全链路细节。祝你在 ETH 世界里畅行无阻!