317 lines
9.2 KiB
Go
317 lines
9.2 KiB
Go
package main
|
||
|
||
import (
|
||
"context"
|
||
"crypto/ecdsa"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io/ioutil"
|
||
"log"
|
||
"math/big"
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||
"github.com/ethereum/go-ethereum/common"
|
||
"github.com/ethereum/go-ethereum/core/types"
|
||
"github.com/ethereum/go-ethereum/crypto"
|
||
"github.com/ethereum/go-ethereum/ethclient"
|
||
)
|
||
|
||
// SimpleStorage 是智能合约的绑定结构体
|
||
type SimpleStorage struct {
|
||
contract *bind.BoundContract
|
||
abi abi.ABI
|
||
}
|
||
type Config struct {
|
||
PrivateKey string `json:"privateKey"`
|
||
}
|
||
type Response struct {
|
||
Msg string `json:"msg"`
|
||
Data interface{} `json:"data"`
|
||
}
|
||
|
||
func writeResponse(w http.ResponseWriter, r *Response) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
json.NewEncoder(w).Encode(r)
|
||
}
|
||
|
||
// NewSimpleStorage 创建一个新的 SimpleStorage 实例
|
||
func NewSimpleStorage(address common.Address, backend bind.ContractBackend, abiJSON string) (*SimpleStorage, error) {
|
||
parsedAbi, err := abi.JSON(strings.NewReader(abiJSON))
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
contract := bind.NewBoundContract(address, parsedAbi, backend, backend, backend)
|
||
return &SimpleStorage{contract: contract, abi: parsedAbi}, nil
|
||
}
|
||
|
||
// Call 调用合约的只读方法
|
||
func (s *SimpleStorage) Call(opts *bind.CallOpts, method string, args ...interface{}) ([]string, error) {
|
||
var result []interface{}
|
||
err := s.contract.Call(opts, &result, method, args...)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var resultValues []string
|
||
for _, res := range result {
|
||
if str, ok := res.(string); ok {
|
||
resultValues = append(resultValues, str)
|
||
} else {
|
||
return nil, fmt.Errorf("unexpected result type: %T", res)
|
||
}
|
||
}
|
||
return resultValues, nil
|
||
}
|
||
|
||
// Transact 调用合约的写入方法
|
||
func (s *SimpleStorage) Transact(auth *bind.TransactOpts, method string, args ...interface{}) (*types.Transaction, error) {
|
||
tx, err := s.contract.Transact(auth, method, args...)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return tx, nil
|
||
}
|
||
|
||
var (
|
||
//改成以太坊分配的地址!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||
contractAddress = common.HexToAddress("0x37e875B79A7A89c96589a086e6e9bC577f326b24")
|
||
localNetworkURL = "http://localhost:7545"
|
||
abiJSON []byte
|
||
privateKey string
|
||
)
|
||
|
||
func loadConfig(configPath string) (*Config, error) {
|
||
data, err := ioutil.ReadFile(configPath)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
var config Config
|
||
err = json.Unmarshal(data, &config)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &config, nil
|
||
}
|
||
|
||
func setHandler(client *ethclient.Client, storage *SimpleStorage, privateKeyECDSA *ecdsa.PrivateKey) http.HandlerFunc {
|
||
return func(w http.ResponseWriter, r *http.Request) {
|
||
// 确保是 POST 请求
|
||
if r.Method != http.MethodPost {
|
||
writeResponse(w, &Response{Msg: "error", Data: "只接受 POST 请求"})
|
||
return
|
||
}
|
||
|
||
// 解析 JSON 请求体
|
||
var requestData struct {
|
||
Key string `json:"key"`
|
||
Value string `json:"value"`
|
||
}
|
||
|
||
if err := json.NewDecoder(r.Body).Decode(&requestData); err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: "无效的请求数据"})
|
||
return
|
||
}
|
||
|
||
keyUint, err := strconv.ParseUint(requestData.Key, 10, 64)
|
||
if err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: "无效的键值类型,必须为整数"})
|
||
return
|
||
}
|
||
|
||
// 动态获取当前网络的链 ID
|
||
chainID, err := client.NetworkID(context.Background())
|
||
if err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: "无法获取网络链 ID"})
|
||
return
|
||
}
|
||
|
||
auth, err := bind.NewKeyedTransactorWithChainID(privateKeyECDSA, chainID)
|
||
if err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: "创建交易授权失败"})
|
||
return
|
||
}
|
||
|
||
bigKey := big.NewInt(0).SetUint64(keyUint)
|
||
|
||
tx, err := storage.Transact(auth, "set", bigKey, requestData.Value)
|
||
if err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: fmt.Sprintf("设置值失败: %v", err)})
|
||
return
|
||
}
|
||
|
||
// 控制台日志打印
|
||
log.Printf("成功发起交易,Hash: %s,键: %d,值: %s", tx.Hash().Hex(), keyUint, requestData.Value)
|
||
writeResponse(w, &Response{Msg: "ok", Data: tx.Hash().Hex()})
|
||
}
|
||
}
|
||
func getHandler(storage *SimpleStorage) http.HandlerFunc {
|
||
return func(w http.ResponseWriter, r *http.Request) {
|
||
keyStr := r.URL.Query().Get("key")
|
||
if keyStr == "" {
|
||
writeResponse(w, &Response{Msg: "error", Data: "缺少键值"})
|
||
return
|
||
}
|
||
keyUint, err := strconv.ParseUint(keyStr, 10, 64)
|
||
if err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: "无效的键值类型,必须为整数"})
|
||
return
|
||
}
|
||
|
||
bigKey := big.NewInt(0).SetUint64(keyUint)
|
||
|
||
opts := &bind.CallOpts{
|
||
Context: context.Background(),
|
||
}
|
||
|
||
result, err := storage.Call(opts, "get", bigKey)
|
||
if err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: fmt.Sprintf("获取值失败: %v", err)})
|
||
return
|
||
}
|
||
|
||
writeResponse(w, &Response{Msg: "ok", Data: result[0]})
|
||
}
|
||
}
|
||
|
||
// getByTxHashHandler 通过交易哈希查询存储在智能合约中的值
|
||
func getByTxHashHandler(client *ethclient.Client, storage *SimpleStorage) http.HandlerFunc {
|
||
return func(w http.ResponseWriter, r *http.Request) {
|
||
// 记录请求开始
|
||
log.Printf("收到请求: %s %s", r.Method, r.URL.String())
|
||
txHashStr := r.URL.Query().Get("txHash")
|
||
log.Printf("请求参数 - txHash: %s", txHashStr)
|
||
if txHashStr == "" {
|
||
writeResponse(w, &Response{Msg: "error", Data: "缺少 txHash 参数"})
|
||
return
|
||
}
|
||
|
||
// 通过交易哈希获取键值
|
||
txHash := common.HexToHash(txHashStr)
|
||
log.Printf("转换后的交易哈希: %s", txHash.Hex())
|
||
// 获取交易详情
|
||
// 获取交易详情
|
||
log.Println("正在获取交易详情...")
|
||
tx, _, err := getTransactionDetails(client, txHash)
|
||
if err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: fmt.Sprintf("无法获取交易详情: %v", err)})
|
||
return
|
||
}
|
||
|
||
// 解析交易输入
|
||
log.Println("正在解析交易输入...")
|
||
methodName, args, err := parseTransactionInput(string(abiJSON), tx.Data())
|
||
if err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: fmt.Sprintf("无法解析交易输入: %v", err)})
|
||
return
|
||
}
|
||
log.Printf("解析结果 - 方法名: %s, 参数数量: %d", methodName, len(args))
|
||
// 方法名为 "set",并且第一个参数是键
|
||
if methodName == "set" && len(args) > 0 {
|
||
key, ok := args[0].(*big.Int)
|
||
if !ok {
|
||
writeResponse(w, &Response{Msg: "error", Data: "键值类型不匹配"})
|
||
return
|
||
}
|
||
// 通过键值调用智能合约的 get 方法
|
||
callOpts := &bind.CallOpts{
|
||
Pending: false,
|
||
}
|
||
result, err := storage.Call(callOpts, "get", key)
|
||
if err != nil {
|
||
writeResponse(w, &Response{Msg: "error", Data: fmt.Sprintf("获取数据失败: %v", err)})
|
||
return
|
||
}
|
||
|
||
// 结果只包含一个字符串值
|
||
if len(result) > 0 {
|
||
writeResponse(w, &Response{Msg: "ok", Data: result[0]})
|
||
} else {
|
||
writeResponse(w, &Response{Msg: "error", Data: "未从合约中获得任何结果"})
|
||
}
|
||
} else {
|
||
writeResponse(w, &Response{Msg: "error", Data: "无效的方法或参数"})
|
||
}
|
||
}
|
||
}
|
||
|
||
// getTransactionDetails 获取交易详情
|
||
func getTransactionDetails(client *ethclient.Client, txHash common.Hash) (*types.Transaction, *types.Receipt, error) {
|
||
tx, _, err := client.TransactionByHash(context.Background(), txHash)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("无法获取交易: %v", err)
|
||
}
|
||
|
||
receipt, err := client.TransactionReceipt(context.Background(), txHash)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("无法获取交易收据: %v", err)
|
||
}
|
||
|
||
return tx, receipt, nil
|
||
}
|
||
|
||
// parseTransactionInput 解析交易输入
|
||
func parseTransactionInput(abiJSON string, input []byte) (methodName string, args []interface{}, err error) {
|
||
parsedAbi, err := abi.JSON(strings.NewReader(abiJSON))
|
||
if err != nil {
|
||
return "", nil, fmt.Errorf("解析 ABI 失败: %v", err)
|
||
}
|
||
|
||
method, err := parsedAbi.MethodById(input[:4])
|
||
if err != nil {
|
||
return "", nil, fmt.Errorf("无法找到方法: %v", err)
|
||
}
|
||
|
||
decodedArgs, err := method.Inputs.Unpack(input[4:])
|
||
if err != nil {
|
||
return "", nil, fmt.Errorf("解包输入失败: %v", err)
|
||
}
|
||
|
||
return method.Name, decodedArgs, nil
|
||
}
|
||
|
||
func main() {
|
||
// 加载配置文件
|
||
config, err := loadConfig("config.json")
|
||
if err != nil {
|
||
log.Fatalf("加载配置文件失败: %v", err)
|
||
}
|
||
privateKey = config.PrivateKey
|
||
|
||
// 连接到本地以太坊节点
|
||
client, err := ethclient.Dial(localNetworkURL)
|
||
if err != nil {
|
||
log.Fatalf("连接到以太坊节点失败: %v", err)
|
||
}
|
||
|
||
// 加载 ABI JSON
|
||
abiJSON, err = ioutil.ReadFile("SimpleStorage.abi")
|
||
if err != nil {
|
||
log.Fatalf("加载 ABI 文件失败: %v", err)
|
||
}
|
||
|
||
// 创建 SimpleStorage 实例
|
||
storage, err := NewSimpleStorage(contractAddress, client, string(abiJSON))
|
||
if err != nil {
|
||
log.Fatalf("创建 SimpleStorage 实例失败: %v", err)
|
||
}
|
||
|
||
// 解析私钥
|
||
privateKeyECDSA, err := crypto.HexToECDSA(privateKey)
|
||
if err != nil {
|
||
log.Fatalf("解析私钥失败: %v", err)
|
||
}
|
||
|
||
// 设置 HTTP 处理函数
|
||
http.HandleFunc("/set", setHandler(client, storage, privateKeyECDSA))
|
||
http.HandleFunc("/get", getHandler(storage))
|
||
http.HandleFunc("/getByTxHash", getByTxHashHandler(client, storage))
|
||
|
||
// 启动 HTTP 服务器
|
||
log.Println("启动 HTTP 服务器...")
|
||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||
}
|