You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

198 lines
4.6 KiB

package main
import (
"bufio"
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"os"
"strings"
"sync"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/widgets"
)
const (
keyFileName = "keys.txt"
nodeURLTxtName = "nodeURL.txt"
)
type window struct {
*widgets.QMainWindow
chainCB *widgets.QComboBox
outputTE *widgets.QTextEdit
}
type queryResult struct {
index int
message string
}
func main() {
app := widgets.NewQApplication(len(os.Args), os.Args)
// 1.5 倍全局放大
app.SetStyleSheet(`
* {
font-size: 15pt;
padding: 6px;
}
QPushButton {
min-height: 34px;
}
`)
win := &window{QMainWindow: widgets.NewQMainWindow(nil, 0)}
win.SetWindowTitle("区块链多链余额查询器")
win.SetMinimumSize2(1050, 750) // 700*1.5 , 500*1.5
central := widgets.NewQWidget(nil, 0)
layout := widgets.NewQVBoxLayout2(central)
// ---- 1. 读取节点列表 ----
nodeURLs, err := readLinesNoBlank(nodeURLTxtName)
if err != nil {
popupFatal(fmt.Sprintf("读取 %s 失败: %v", nodeURLTxtName, err))
}
if len(nodeURLs) == 0 {
popupFatal(fmt.Sprintf("%s 为空,请至少填一个节点 URL", nodeURLTxtName))
}
// ---- 2. 下拉框 ----
layout.AddWidget(widgets.NewQLabel2("选择节点:", nil, 0), 0, 0)
win.chainCB = widgets.NewQComboBox(nil)
for _, u := range nodeURLs {
win.chainCB.AddItem(u, core.NewQVariant1(u))
}
layout.AddWidget(win.chainCB, 0, 0)
// ---- 3. 按钮区域 ----
btnLayout := widgets.NewQHBoxLayout()
queryBtn := widgets.NewQPushButton2("查询余额", nil)
queryBtn.ConnectClicked(func(bool) { win.query() })
btnLayout.AddWidget(queryBtn, 0, 0)
clearBtn := widgets.NewQPushButton2("清除输出", nil)
clearBtn.ConnectClicked(func(bool) { win.outputTE.Clear() })
btnLayout.AddWidget(clearBtn, 0, 0)
layout.AddLayout(btnLayout, 0)
// ---- 4. 输出框 ----
win.outputTE = widgets.NewQTextEdit(nil)
win.outputTE.SetReadOnly(true)
layout.AddWidget(win.outputTE, 0, 0)
win.SetCentralWidget(central)
win.Show()
widgets.QApplication_Exec()
}
// ============ 查询逻辑 ============
func (w *window) query() {
w.outputTE.Clear()
// 当前选中节点
idx := w.chainCB.CurrentIndex()
if idx < 0 {
w.log("未选择节点")
return
}
nodeURL := w.chainCB.CurrentText()
w.log("当前节点: %s", nodeURL)
// 读取 keys
keys, err := readLinesNoBlank(keyFileName)
if err != nil {
w.log("读取 %s 失败: %v", keyFileName, err)
return
}
if len(keys) == 0 {
w.log("%s 为空,请先填入私钥(每行一个)", keyFileName)
return
}
// 连接
client, err := ethclient.Dial(nodeURL)
if err != nil {
w.log("连接节点失败: %v", err)
return
}
defer client.Close()
var wg sync.WaitGroup
results := make([]string, len(keys))
for i, hexKey := range keys {
wg.Add(1)
go func(i int, hexKey string) {
defer wg.Done()
privateKey, err := crypto.HexToECDSA(hexKey)
if err != nil {
results[i] = fmt.Sprintf("钱包 %d: 私钥解析失败,跳过。err=%v", i+1, err)
return
}
publicKey, ok := privateKey.Public().(*ecdsa.PublicKey)
if !ok {
results[i] = fmt.Sprintf("钱包 %d: 公钥转换失败,跳过", i+1)
return
}
addr := crypto.PubkeyToAddress(*publicKey)
balance, err := client.BalanceAt(context.Background(), addr, nil)
if err != nil {
results[i] = fmt.Sprintf("钱包 %d (%s): 查余额失败,err=%v", i+1, addr.Hex(), err)
return
}
nonce, err := client.PendingNonceAt(context.Background(), addr)
if err != nil {
results[i] = fmt.Sprintf("钱包 %d (%s): 查 nonce 失败,err=%v", i+1, addr.Hex(), err)
return
}
ether := new(big.Float).Quo(new(big.Float).SetInt(balance), big.NewFloat(1e18))
results[i] = fmt.Sprintf("钱包 %d %s \n余额(wei): %s\n余额Token %.6f\nNonce: %d",
i+1, addr.Hex(), balance.String(), ether, nonce)
}(i, hexKey)
}
wg.Wait()
// 全部结束后按顺序输出
for _, res := range results {
w.log(res)
}
}
// ============ 工具函数 ============
func readLinesNoBlank(filename string) ([]string, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
var out []string
sc := bufio.NewScanner(f)
for sc.Scan() {
line := strings.TrimSpace(sc.Text())
if line != "" {
out = append(out, line)
}
}
return out, sc.Err()
}
func (w *window) log(format string, a ...interface{}) {
s := fmt.Sprintf(format, a...)
w.outputTE.Append(s + "\n")
}
func popupFatal(msg string) {
widgets.QMessageBox_Critical(nil, "错误", msg, widgets.QMessageBox__Ok, widgets.QMessageBox__Ok)
os.Exit(1)
}