Day 4
Spend 交易
中级阶段 · 预计学习时间 3-4 小时
学习目标
- 理解交易生命周期
- 使用 TTLNoncer 管理 Nonce
- 创建、签名、广播 SpendTx
交易生命周期
| 步骤 | 说明 |
|---|---|
| 1. 构建 | 创建交易结构(发送方、接收方、金额等) |
| 2. 签名 | 使用发送方私钥签名交易哈希 |
| 3. 广播 | 将签名后的交易发送到节点 |
| 4. 确认 | 等待交易被打包进区块 |
TTLNoncer 辅助工具
创建交易需要有效的 Nonce(账户序号)和 TTL(有效期)。SDK 提供 TTLNoncer 自动从节点获取这些值。
import "github.com/aeternity/aepp-sdk-go/v9/transactions"
// 创建 TTLNoncer
ttlnoncer := transactions.NewTTLNoncer(node)
创建 Spend 交易
package main
import (
"fmt"
"log"
"math/big"
"github.com/aeternity/aepp-sdk-go/v9/account"
"github.com/aeternity/aepp-sdk-go/v9/config"
"github.com/aeternity/aepp-sdk-go/v9/naet"
"github.com/aeternity/aepp-sdk-go/v9/transactions"
)
func main() {
// 1. 设置节点和账户
config.Node.NetworkID = "ae_uat"
node := naet.NewNode("https://testnet.aeternity.io", false)
// 加载发送方账户
alice, err := account.LoadFromKeyStoreFile("alice.json", "password")
if err != nil {
log.Fatal(err)
}
// 接收方地址
bobAddress := "ak_..." // 替换为实际地址
// 2. 创建 TTLNoncer
ttlnoncer := transactions.NewTTLNoncer(node)
// 3. 创建 Spend 交易
amount := big.NewInt(1000000000000000000) // 1 AE (18位小数)
payload := []byte("Payment for coffee")
tx, err := transactions.NewSpendTx(
alice.Address, // 发送方
bobAddress, // 接收方
amount, // 金额
payload, // 附加数据
ttlnoncer, // TTLNoncer
)
if err != nil {
log.Fatal("创建交易失败:", err)
}
fmt.Printf("交易已创建, Fee: %s\n", tx.Fee)
}
签名和广播
// 4. 签名交易
// 需要 Network ID 防止重放攻击
networkID := config.Node.NetworkID
signedTx, txHash, _, err := transactions.SignHashTx(alice, tx, networkID)
if err != nil {
log.Fatal("签名失败:", err)
}
// 5. 序列化交易
txStr, err := transactions.SerializeTx(signedTx)
if err != nil {
log.Fatal("序列化失败:", err)
}
// 6. 广播到网络
err = node.PostTransaction(txStr, txHash)
if err != nil {
log.Fatal("广播失败:", err)
}
fmt.Printf("✅ 交易已发送!\n")
fmt.Printf("Hash: %s\n", txHash)
自定义费用
SDK 自动计算最低费用,但你可以手动设置更高的费用以加快确认。
// 创建交易后,修改费用
customFee := big.NewInt(20000000000000) // 自定义费用
tx.SetFee(customFee)
// 然后继续签名和广播...
完整转账流程
func sendAE(node *naet.Node, sender *account.Account,
recipient string, amountAE float64, message string) (string, error) {
// 转换为 aettos
aettos := new(big.Float).SetFloat64(amountAE)
aettos.Mul(aettos, big.NewFloat(1e18))
amount, _ := aettos.Int(nil)
// 创建交易
ttlnoncer := transactions.NewTTLNoncer(node)
tx, err := transactions.NewSpendTx(sender.Address, recipient, amount,
[]byte(message), ttlnoncer)
if err != nil {
return "", err
}
// 签名
signedTx, txHash, _, err := transactions.SignHashTx(sender, tx, config.Node.NetworkID)
if err != nil {
return "", err
}
// 广播
txStr, _ := transactions.SerializeTx(signedTx)
err = node.PostTransaction(txStr, txHash)
return txHash, err
}
// 使用
hash, err := sendAE(node, alice, bobAddress, 1.5, "Coffee payment")
if err != nil {
log.Fatal(err)
}
fmt.Printf("交易哈希: %s\n", hash)
知识检查点