目录
- 初识 Ethereum 转账
- 在 Geth JS Console 发起转账
- 全链路:从键盘输入到节点广播
- RPC 服务端是如何找到 sendTransaction
- sendTransaction() 最终长什么样
- FAQ:常见疑惑一次说清
- 延伸阅读与工具
1. 初识 Ethereum 转账
关键词:Ethereum 转账、ETH 交易、Geth Console
在正式拆流程之前,先把核心角色认一遍:
eth.sendTransaction:最常被调用的 js 方法,表面看像一条“转账指令”,实际上会触发一整套 RPC/链上逻辑。- RPC:以太坊节点对外暴露的接口,这里扮演“翻译官”角色——把人类易读的命令翻译成节点内部函数。
- nonce & gas:决定交易顺序、手续费和成败的两把钥匙。
👉 先掌握这 60 秒 Ethereum NFT 转账小实验,实战后回头看代码链路会秒懂!
2. 在 Geth JS Console 发起转账
步骤拆解
关键词:Geth、JS Console、sendTransaction 参数
用命令行启动节点并进入交互式控制台
geth console --datadir /path/to/chain解锁账户(本地私钥控制权)
personal.unlockAccount(eth.accounts[0], "密码", 30)直接输入
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
关键词:JSRE、JS 桥接、oto VM
| 阶段 | 动作 | 关键标识 |
|---|---|---|
| Input | Interactive() 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 执行三部曲:
- readRequest:解析 JSON →
service_method(例:eth_sendTransaction)。 - handle:利用 Golang 反射拿到对应的结构体方法指针。
- invoke:真正执行
PublicTransactionPoolAPI.SendTransaction()。
4. RPC 服务端是如何找到 sendTransaction
4.1 注册子服务
关键词:RegisterEthService、Service Interface
节点启动流程中的关键函数:
stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return eth.New(ctx, cfg) // 返回 ð.Ethereum{}
})注册完成后,每个模块返回的接口统一放入 map[reflect.Type]Service,其中:
| 字段 | 作用 |
|---|---|
Protocols() | P2P 子协议列表 |
APIs() | RPC 方法命名空间列表 |
4.2 命名空间反射
关键词:namespace、callback lookup、RegisterName
以 eth 命名空间为例:
- API:
PublicTransactionPoolAPI - Method 关联:
SendTransaction(txArgs) - 注册语句:
handler.RegisterName("eth", &PublicTransactionPoolAPI{})
反射自动把首字母大写的方法转成小写+驼峰形式,与 eth_sendTransaction 精准匹配。
5. sendTransaction() 最终长什么样
5.1 关键类与函数
关键词:TxPool、EIP-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
- 时间:2016-11-22 区块 2,675,000
- 作用:修改 交易哈希生成规则——将 chainID 维度纳入签名,防止跨链重放。
- 加 3 个字段:
v = chainID * 2 + 35/36;r、s也参与哈希。
6. FAQ:常见疑惑一次说清
Q1:为什么 unlockAccount 后还要再次输入密码?
A:解锁是把私钥写入内存解密锁片,有效时长可自定义(例:30 秒)。超时后仍需密码。
Q2:eth.sendTransaction 报 “insufficient funds” 但钱包确实有余额?
A:大概率是 gas * gasPrice 计算出来的手续费超出了可用余额,查看 eth.getBalance(eth.accounts[0]) 实际值即可验证。
Q3:交易挂起(pending)怎么办?
A:检查:
- nonce 是否已断层
- gasPrice 是否过低(Ropsten、主网都可参考 GasNow)
若长时间未打包,可用 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. 延伸阅读与工具
- Go-Ethereum 源码导航
cmd/geth/main.go→node.New()→RegisterEthService→internal/ethapi/api.go - 在线可视化交易
Etherscan:输入 txHash 可查看实时状态。 调试利器
geth attach http://localhost:8545debug.traceTransaction(txHash):EVM 指令级回溯。
希望通过这趟“旅程”,你不仅知道 Ethereum 转账 该怎么点按钮,更能一眼看清它背后的 RPC、反射、签名校验、链上广播 全链路细节。祝你在 ETH 世界里畅行无阻!