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)) }