package app import ( "encoding/json" "io" "os" "os/user" "github.com/jinzhu/gorm" "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus" "github.com/wailsapp/wails" "github.com/grvlle/constellation_wallet/backend/api" "github.com/grvlle/constellation_wallet/backend/models" ) // WalletApplication 保存所有特定于应用程序的对象 // 例如客户端/服务器事件总线和记录器 type WalletApplication struct { Version string RT *wails.Runtime log *logrus.Logger wallet models.Wallet DB *gorm.DB killSignal chan struct{} Network struct { URL string Handles struct { Send string // Takes TX Object, returns TX Hash (200) Transaction string // Takes TX Object, returns TX Hash (200) Balance string // Polls the wallets available balance } BlockExplorer struct { URL string Handles struct { Transactions string // Takes TX Hash, returns TX info Checkpoints string // Takes Checkpoint block hash, returns checkpoint block info Snapshots string // Takes SnapshotHash, returns info CollectTX string // Takes DAG address, returns tx objects } } } paths struct { HomeDir string DAGDir string TMPDir string EncryptedDir string EmptyTXFile string PrevTXFile string LastTXFile string AddressFile string ImageDir string Java string } KeyStoreAccess bool TransactionFinished bool TransactionFailed bool UserLoggedIn bool NewUser bool WalletImported bool FirstTX bool SecondTX bool WidgetRunning struct { PassKeysToFrontend bool DashboardWidgets bool } WalletCLI struct { URL string Version string } } // WailsShutdown 在应用程序关闭时调用 func (a *WalletApplication) WailsShutdown() { a.wallet = models.Wallet{} close(a.killSignal) //杀死 Go 例程 a.DB.Close() } // WailsInit 初始化客户端和服务器端绑定 func (a *WalletApplication) WailsInit(runtime *wails.Runtime) error { var err error // 初始化日志对象 a.log = logrus.New() err = a.initDirectoryStructure() if err != nil { a.log.Errorln("无法设置目录结构。原因: ", err) } // 初始化输出日志 a.initLogger() // 初始化 RPC 服务 err = api.InitRPCServer() if err != nil { a.log.Panicf("Unable to initialize RPC Server. Reason: %v", err) } a.log.Infoln("RPC Server initialized.") a.UserLoggedIn = false a.NewUser = false a.TransactionFinished = true a.RT = runtime a.killSignal = make(chan struct{}) // 用于终止go例程并交还系统资源 a.wallet.Currency = "USD" // 设置默认货币 a.WalletCLI.URL = "https://github.com/Constellation-Labs/constellation/releases/download" a.WalletCLI.Version = "2.6.0" a.Version = "1.2.0" // 创建数据库连接 a.DB, err = gorm.Open("sqlite3", a.paths.DAGDir+"/store.db") if err != nil { a.log.Panicln("failed to connect database", err) } // Migrate the schema::迁移架构 a.DB.AutoMigrate(&models.Wallet{}, &models.TXHistory{}, &models.Path{}) // 查询 JDK 目录信息 a.detectJavaPath() a.initMainnetConnection() // TODO a.newReleaseAvailable() return nil } // initLogger writes logs to STDOUT and a.paths.DAGDir/wallet.log func (a *WalletApplication) initLogger() { logFile, err := os.OpenFile(a.paths.DAGDir+"/wallet.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664) if err != nil { a.log.Fatal("Unable to create log file.") } mw := io.MultiWriter(os.Stdout, logFile) a.log.SetOutput(mw) a.log.SetFormatter(&log.TextFormatter{ ForceColors: true, FullTimestamp: true, }) } // 初始化目录结构并存储 WalletApplication 结构的路径。 func (a *WalletApplication) initDirectoryStructure() error { user, err := user.Current() if err != nil { return err } a.paths.HomeDir = user.HomeDir // Home directory of the user a.paths.DAGDir = a.paths.HomeDir + "/.dag" // DAG directory for configuration files and wallet specific data a.paths.TMPDir = a.paths.DAGDir + "/tmp" a.paths.LastTXFile = a.paths.TMPDir + "/last_tx" a.paths.PrevTXFile = a.paths.TMPDir + "/prev_tx" a.paths.EmptyTXFile = a.paths.TMPDir + "/genesis_tx" a.paths.ImageDir = "./frontend/src/assets/img/" // Image Folder a.log.Info("DAG Directory: ", a.paths.DAGDir) err = a.directoryCreator(a.paths.DAGDir, a.paths.TMPDir) if err != nil { return err } return nil } // initMainnetConnection 用主网数据填充 WalletApplication 结构 func (a *WalletApplication) initMainnetConnection() { a.Network.URL = "http://lb.constellationnetwork.io:9000" // Temp a.Network.Handles.Send = "/send" a.Network.Handles.Transaction = "/transaction" a.Network.Handles.Balance = "/address/" a.Network.BlockExplorer.URL = "https://xju69fets2.execute-api.us-west-1.amazonaws.com/cl-block-explorer-mainnet" a.Network.BlockExplorer.Handles.Transactions = "/transactions/" a.Network.BlockExplorer.Handles.Checkpoints = "/checkpoints/" a.Network.BlockExplorer.Handles.Snapshots = "/snapshots/" a.Network.BlockExplorer.Handles.CollectTX = "/transactions?sender=" } // APIError 由 blockexplerer/loadbalancer 报告的 APIError 按以下格式报告 // {“error”: “找不到发件人的交易”} type APIError struct { Error string } // verifyAPIResponse 将 API 响应转换为字节数组,并检查 API 是否返回了一个错误。如果是这样,它将返回错误消息。 func (a *WalletApplication) verifyAPIResponse(r []byte) (bool, string) { APIErr := APIError{} if string(r[3:8]) == "error" { err := json.Unmarshal(r, &APIErr) if err != nil { a.log.Errorln("无法解析 API 错误。原因: ", err) } return false, APIErr.Error } return true, "" } func (a *WalletApplication) sendSuccess(msg string) { if len(msg) > 200 { msg = msg[:200] // 限制前端的错误大小 a.RT.Events.Emit("success", msg) return } a.RT.Events.Emit("success", msg) } func (a *WalletApplication) sendWarning(msg string) { if len(msg) > 200 { msg = msg[:200] // 限制前端的错误大小 a.RT.Events.Emit("warning", msg) return } a.RT.Events.Emit("warning", msg) } func (a *WalletApplication) sendError(msg string, err error) { var errStr string if err != nil { b := []byte(err.Error()) if len(b) > 80 { errStr = string(b[:80]) // 限制前端的错误大小 } else if len(b) < 80 { errStr = string(b) } else { errStr = "" } a.RT.Events.Emit("error_handling", msg, errStr+" ...") } else { a.RT.Events.Emit("error_handling", msg+" ...") } }